{%MainUnit ../controls.pp} {****************************************************************************** TControl ****************************************************************************** ***************************************************************************** This file is part of the Lazarus Component Library (LCL) See the file COPYING.modifiedLGPL.txt, included in this distribution, for details about the license. ***************************************************************************** } {$IFOPT C-} // Uncomment for local trace // {$C+} // {$DEFINE ASSERT_IS_ON} {$ENDIF} { $DEFINE CHECK_POSITION} { TLazAccessibleObjectEnumerator } function TLazAccessibleObjectEnumerator.GetCurrent: TLazAccessibleObject; begin if Assigned(FCurrent) then Result:=TLazAccessibleObject(FCurrent.Data) else Result := nil; end; { TLazAccessibleObject } function TLazAccessibleObject.GetHandle: PtrInt; var WidgetsetClass: TWSLazAccessibleObjectClass; begin WidgetsetClass := TWSLazAccessibleObjectClass(GetWSLazAccessibleObject()); if (WidgetsetClass <> nil) and (FHandle = 0) then begin FHandle := WidgetsetClass.CreateHandle(Self); if FHandle <> 0 then InitializeHandle(); end; Result := FHandle; end; function TLazAccessibleObject.GetAccessibleValue: TCaption; begin Result := FAccessibleValue; end; function TLazAccessibleObject.GetPosition: TPoint; begin if (OwnerControl <> nil) and (OwnerControl.GetAccessibleObject() = Self) then begin Result := Point(OwnerControl.Left, OwnerControl.Top); Exit; end; Result := FPosition; end; function TLazAccessibleObject.GetSize: TSize; begin if (OwnerControl <> nil) and (OwnerControl.GetAccessibleObject() = Self) then begin Result := Types.Size(OwnerControl.Width, OwnerControl.Height); Exit; end; Result := FSize; end; procedure TLazAccessibleObject.SetHandle(AValue: PtrInt); begin if AValue = FHandle then Exit; FHandle := AValue; if FHandle <> 0 then InitializeHandle(); end; procedure TLazAccessibleObject.SetPosition(AValue: TPoint); var WidgetsetClass: TWSLazAccessibleObjectClass; begin if (FPosition.X=AValue.X) and (FPosition.Y=AValue.Y) then Exit; FPosition := AValue; WidgetsetClass := TWSLazAccessibleObjectClass(GetWSLazAccessibleObject()); WidgetsetClass.SetPosition(Self, AValue); end; procedure TLazAccessibleObject.SetSize(AValue: TSize); var WidgetsetClass: TWSLazAccessibleObjectClass; begin if (FSize.CX=AValue.CX) and (FSize.CY=AValue.CY) then Exit; FSize := AValue; WidgetsetClass := TWSLazAccessibleObjectClass(GetWSLazAccessibleObject()); WidgetsetClass.SetSize(Self, AValue); end; class procedure TLazAccessibleObject.WSRegisterClass; begin // inherited WSRegisterClass; RegisterLazAccessibleObject; end; constructor TLazAccessibleObject.Create(AOwner: TControl); begin inherited Create;//(AOwner); OwnerControl := AOwner; FChildrenSortedForDataObject := TAvlTree.Create(@CompareLazAccessibleObjectsByDataObject); WSRegisterClass(); end; destructor TLazAccessibleObject.Destroy; var WidgetsetClass: TWSLazAccessibleObjectClass; begin WidgetsetClass := TWSLazAccessibleObjectClass(GetWSLazAccessibleObject()); ClearChildAccessibleObjects(); if (WidgetsetClass <> nil) and (FHandle <> 0) then WidgetsetClass.DestroyHandle(Self); if Assigned(Parent) then Parent.RemoveChildAccessibleObject(self, False); FreeAndNil(FChildrenSortedForDataObject); inherited Destroy; end; function TLazAccessibleObject.HandleAllocated: Boolean; begin Result := FHandle <> 0; end; procedure TLazAccessibleObject.InitializeHandle; var WidgetsetClass: TWSLazAccessibleObjectClass; begin WidgetsetClass := TWSLazAccessibleObjectClass(GetWSLazAccessibleObject()); WidgetsetClass.SetAccessibleName(Self, FAccessibleName); WidgetsetClass.SetAccessibleDescription(Self, FAccessibleDescription); WidgetsetClass.SetAccessibleValue(Self, FAccessibleValue); WidgetsetClass.SetAccessibleRole(Self, FAccessibleRole); end; procedure TLazAccessibleObject.SetAccessibleName(const AName: TCaption); var WidgetsetClass: TWSLazAccessibleObjectClass; begin if FAccessibleName=AName then Exit; FAccessibleName := AName; WidgetsetClass := TWSLazAccessibleObjectClass(GetWSLazAccessibleObject()); WidgetsetClass.SetAccessibleName(Self, AName); end; procedure TLazAccessibleObject.SetAccessibleDescription(const ADescription: TCaption); var WidgetsetClass: TWSLazAccessibleObjectClass; begin if FAccessibleDescription=ADescription then Exit; FAccessibleDescription := ADescription; WidgetsetClass := TWSLazAccessibleObjectClass(GetWSLazAccessibleObject()); WidgetsetClass.SetAccessibleDescription(Self, ADescription); end; procedure TLazAccessibleObject.SetAccessibleValue(const AValue: TCaption); var WidgetsetClass: TWSLazAccessibleObjectClass; begin if FAccessibleValue=AValue then Exit; FAccessibleValue := AValue; WidgetsetClass := TWSLazAccessibleObjectClass(GetWSLazAccessibleObject()); WidgetsetClass.SetAccessibleValue(Self, AValue); end; procedure TLazAccessibleObject.SetAccessibleRole(const ARole: TLazAccessibilityRole); var WidgetsetClass: TWSLazAccessibleObjectClass; begin if FAccessibleRole=ARole then Exit; FAccessibleRole := ARole; WidgetsetClass := TWSLazAccessibleObjectClass(GetWSLazAccessibleObject()); WidgetsetClass.SetAccessibleRole(Self, ARole); end; function TLazAccessibleObject.FindOwnerWinControl: TWinControl; begin Result := nil; if OwnerControl is TWinControl then Exit(TWinControl(OwnerControl)); if OwnerControl is TControl then Exit(OwnerControl.Parent); if Self.Parent = nil then Exit; Result := Self.Parent.FindOwnerWinControl(); end; function TLazAccessibleObject.AddChildAccessibleObject( ADataObject: TObject = nil): TLazAccessibleObject;begin Result := nil; if FChildrenSortedForDataObject = nil then Exit; if (ADataObject <> nil) then begin Result := GetChildAccessibleObjectWithDataObject(ADataObject); if Result <> nil then Exit; end; Result := TLazAccessibleObject.Create(OwnerControl); Result.Parent := Self; Result.DataObject := ADataObject; FChildrenSortedForDataObject.Add(Result); //DebugLn('[TControl.AddChildAccessibleObject] Name=%s', [Name]); end; procedure TLazAccessibleObject.InsertChildAccessibleObject( AObject: TLazAccessibleObject); begin if FChildrenSortedForDataObject = nil then Exit; if (AObject.Parent <> nil) and (AObject.Parent <> Self) then AObject.Parent.RemoveChildAccessibleObject(AObject, False); AObject.Parent := Self; if (FChildrenSortedForDataObject.Find(AObject) <> nil) then exit; FChildrenSortedForDataObject.Add(AObject); end; procedure TLazAccessibleObject.ClearChildAccessibleObjects; var lXObject: TLazAccessibleObject; AVLNode: TAvlTreeNode; begin if FChildrenSortedForDataObject = nil then Exit; //DebugLn(Format('[TControl.ClearChildAccessibleObjects] Name=%s Count=%d', [Name, FAccessibleChildren.Count])); // Free only the non-control children AVLNode:=FChildrenSortedForDataObject.FindLowest; while AVLNode<>nil do begin lXObject := TLazAccessibleObject(AVLNode.Data); if lXObject.OwnerControl = OwnerControl then begin lXObject.Parent := nil; // Clear parent so .Free doesn't recurse lXObject.Free; end; AVLNode:=FChildrenSortedForDataObject.FindSuccessor(AVLNode); end; FChildrenSortedForDataObject.Clear; end; procedure TLazAccessibleObject.RemoveChildAccessibleObject( AObject: TLazAccessibleObject; AFreeObject: Boolean = True); var Node: TAvlTreeNode; begin if FChildrenSortedForDataObject = nil then Exit; if Assigned(AObject.Parent) then AObject.Parent := nil; Node:=FChildrenSortedForDataObject.Find(AObject); if Node=nil then exit; FChildrenSortedForDataObject.Delete(Node); if AFreeObject then AObject.Free; end; function TLazAccessibleObject.GetChildAccessibleObjectWithDataObject( ADataObject: TObject): TLazAccessibleObject; var Node: TAvlTreeNode; begin Result := nil; if FChildrenSortedForDataObject = nil then Exit; Node:=FChildrenSortedForDataObject.FindKey(ADataObject,@CompareDataObjectWithLazAccessibleObject); if Node<>nil then Result:=TLazAccessibleObject(Node.Data); end; function TLazAccessibleObject.GetChildAccessibleObjectsCount: Integer; begin Result := 0; if FChildrenSortedForDataObject <> nil then Result := FChildrenSortedForDataObject.Count; end; function TLazAccessibleObject.GetChildAccessibleObject(AIndex: Integer): TLazAccessibleObject; var lNode: TAvlTreeNode = nil; begin Result := nil; if AIndex = 0 then lNode := FChildrenSortedForDataObject.FindLowest() else if AIndex = GetChildAccessibleObjectsCount()-1 then lNode := FChildrenSortedForDataObject.FindHighest() else if AIndex = FLastSearchIndex then lNode := FLastSearchNode else if AIndex = FLastSearchIndex+1 then lNode := FChildrenSortedForDataObject.FindSuccessor(FLastSearchNode) else if AIndex = FLastSearchIndex-1 then lNode := FChildrenSortedForDataObject.FindPrecessor(FLastSearchNode); FLastSearchIndex := AIndex; FLastSearchNode := lNode; if lNode = nil then Exit; Result := TLazAccessibleObject(lNode.Data); end; function TLazAccessibleObject.GetFirstChildAccessibleObject: TLazAccessibleObject; begin Result := nil; FLastSearchInSubcontrols := False; if GetChildAccessibleObjectsCount() > 0 then Result := GetChildAccessibleObject(0) else if OwnerControl is TWinControl then begin FLastSearchIndex := 1; FLastSearchInSubcontrols := True; if (TWinControl(OwnerControl).ControlCount > 0) then Result := TWinControl(OwnerControl).Controls[0].GetAccessibleObject(); end; end; function TLazAccessibleObject.GetNextChildAccessibleObject: TLazAccessibleObject; begin Result := nil; if not FLastSearchInSubcontrols then begin if FLastSearchIndex < GetChildAccessibleObjectsCount() then Result := GetChildAccessibleObject(FLastSearchIndex + 1) else if OwnerControl is TWinControl then begin FLastSearchIndex := 1; FLastSearchInSubcontrols := True; if (TWinControl(OwnerControl).ControlCount > 0) then Result := TWinControl(OwnerControl).Controls[0].GetAccessibleObject(); end; end else begin if TWinControl(OwnerControl).ControlCount > FLastSearchIndex then begin Result := TWinControl(OwnerControl).Controls[FLastSearchIndex].GetAccessibleObject(); Inc(FLastSearchIndex); end; end; end; function TLazAccessibleObject.GetSelectedChildAccessibleObject: TLazAccessibleObject; begin Result := nil; if OwnerControl = nil then Exit; Result := OwnerControl.GetSelectedChildAccessibleObject(); end; function TLazAccessibleObject.GetChildAccessibleObjectAtPos(APos: TPoint): TLazAccessibleObject; begin Result := nil; if OwnerControl = nil then Exit; Result := OwnerControl.GetChildAccessibleObjectAtPos(APos); end; function TLazAccessibleObject.GetEnumerator: TLazAccessibleObjectEnumerator; begin Result:=TLazAccessibleObjectEnumerator.Create(FChildrenSortedForDataObject); end; {------------------------------------------------------------------------------ TControl.AdjustSize Calls DoAutoSize smart. During loading and handle creation the calls are delayed. This method does the same as TWinControl.DoAutoSize at the beginning. But since DoAutoSize is commonly overriden by existing Delphi components, they do not all tests, which can result in too much overhead. To reduce this the LCL calls AdjustSize instead. ------------------------------------------------------------------------------} procedure TControl.AdjustSize; procedure RaiseLoop; begin raise ELayoutException.Create('TControl.AdjustSize loop detected '+DbgSName(Self)+' Bounds='+dbgs(BoundsRect)); end; begin {$IFDEF VerboseAdjustSize} if (not (cfAutoSizeNeeded in FControlFlags)) and (Parent=nil) and (Self is TCustomForm) then begin DebugLn(['TControl.AdjustSize ',DbgSName(Self)]); end; {$ENDIF} Include(FControlFlags, cfAutoSizeNeeded); if IsControlVisible then begin if Parent <> nil then Parent.AdjustSize else begin if cfKillAdjustSize in FControlFlags then RaiseLoop; if not AutoSizeDelayed then DoAllAutoSize; end; end; end; {------------------------------------------------------------------------------ Method: TControl.BeginDrag Params: Immediate: Drag behaviour Threshold: distance to move before dragging starts -1 uses the default value of DragManager.DragThreshold Returns: Nothing Starts the dragging of a control. If the Immediate flag is set, dragging starts immediately. A drag-dock should not normally start immediately! ------------------------------------------------------------------------------} procedure TControl.BeginDrag(Immediate: Boolean; Threshold: Integer); begin DragManager.DragStart(Self, Immediate, Threshold); end; procedure TControl.EndDrag(Drop: Boolean); begin if Dragging then DragManager.DragStop(Drop); end; {------------------------------------------------------------------------------ TControl.BeginAutoDrag ------------------------------------------------------------------------------} procedure TControl.BeginAutoDrag; begin {$IFDEF VerboseDrag} debugln(['TControl.BeginAutoDrag ',DbgSName(Self)]); {$ENDIF} BeginDrag(DragManager.DragImmediate, DragManager.DragThreshold); end; {------------------------------------------------------------------------------ TControl.BeginAutoSizing ------------------------------------------------------------------------------} procedure TControl.BeginAutoSizing; procedure Error; begin RaiseGDBException('TControl.BeginAutoSizing'); end; begin if FAutoSizingSelf then Error; FAutoSizingSelf := True; end; {------------------------------------------------------------------------------ procedure TControl.DoEndDock(Target: TObject; X, Y: Integer); ------------------------------------------------------------------------------} procedure TControl.DoEndDock(Target: TObject; X, Y: Integer); begin if Assigned(FOnEndDock) then FOnEndDock(Self,Target,X,Y); end; {------------------------------------------------------------------------------ procedure TControl.DoDock(NewDockSite: TWinControl; var ARect: TRect); ------------------------------------------------------------------------------} procedure TControl.DoDock(NewDockSite: TWinControl; var ARect: TRect); begin if (NewDockSite = nil) then Parent := nil; if NewDockSite<>nil then begin //DebugLn('TControl.DoDock BEFORE Adjusting ',DbgSName(Self),' ',dbgs(ARect)); // adjust new bounds, so that they at least fit into the client area of // its parent if NewDockSite.AutoSize then begin case align of alLeft, alRight : ARect:=Rect(0,0,Width,NewDockSite.ClientHeight); alTop, alBottom : ARect:=Rect(0,0,NewDockSite.ClientWidth,Height); else ARect:=Rect(0,0,Width,Height); end; end else begin MoveRectToFit(ARect, NewDockSite.GetLogicalClientRect); // consider Align to increase chance the width/height is kept case Align of alLeft: Types.OffsetRect(ARect,-ARect.Left,0); alTop: Types.OffsetRect(ARect,0,-ARect.Top); alRight: Types.OffsetRect(ARect,NewDockSite.ClientWidth-ARect.Right,0); alBottom: Types.OffsetRect(ARect,0,NewDockSite.ClientHeight-ARect.Bottom); end; end; //DebugLn('TControl.DoDock AFTER Adjusting ',DbgSName(Self),' ',dbgs(ARect),' Align=',DbgS(Align),' NewDockSite.ClientRect=',dbgs(NewDockSite.ClientRect)); end; //debugln('TControl.DoDock BEFORE MOVE ',Name,' BoundsRect=',dbgs(BoundsRect),' NewRect=',dbgs(ARect)); if Parent<>NewDockSite then BoundsRectForNewParent := ARect else BoundsRect := ARect; //debugln('TControl.DoDock AFTER MOVE ',DbgSName(Self),' BoundsRect=',dbgs(BoundsRect),' TriedRect=',dbgs(ARect)); end; {------------------------------------------------------------------------------ procedure TControl.DoStartDock(var DragObject: TDragObject); ------------------------------------------------------------------------------} procedure TControl.DoStartDock(var DragObject: TDragObject); begin if Assigned(FOnStartDock) then FOnStartDock(Self,TDragDockObject(DragObject)); end; {------------------------------------------------------------------------------ function TControl.GetDockEdge(const MousePos: TPoint): TAlign; Calculate the dock side depending on current MousePos. Important: MousePos is relative to this control's Left, Top. ------------------------------------------------------------------------------} function TControl.GetDockEdge(const MousePos: TPoint): TAlign; var BestDistance: Integer; procedure FindMinDistance(CurAlign: TAlign; CurDistance: integer); begin if CurDistance<0 then CurDistance:=-CurDistance; if CurDistance>=BestDistance then exit; Result:=CurAlign; BestDistance:=CurDistance; end; begin Result:=alNone; BestDistance:=High(Integer); FindMinDistance(alLeft,MousePos.X); FindMinDistance(alRight,Width-MousePos.X); FindMinDistance(alTop,MousePos.Y); FindMinDistance(alBottom,Height-MousePos.Y); end; {------------------------------------------------------------------------------ function TControl.GetDragImages: TDragImageList; Returns Drag image list that will be used while drag opetations ------------------------------------------------------------------------------} function TControl.GetDragImages: TDragImageList; begin Result := nil; end; {------------------------------------------------------------------------------ procedure TControl.PositionDockRect(DragDockObject: TDragDockObject); ------------------------------------------------------------------------------} procedure TControl.PositionDockRect(DragDockObject: TDragDockObject); var WinDragTarget: TWinControl; begin with DragDockObject do begin if (DragTarget is TWinControl) and TWinControl(DragTarget).UseDockManager then begin WinDragTarget := TWinControl(DragTarget); GetWindowRect(WinDragTarget.Handle, FDockRect); if (WinDragTarget.DockManager <> nil) then WinDragTarget.DockManager.PositionDockRect(DragDockObject); end else begin with FDockRect do begin Left := DragPos.X; Top := DragPos.Y; Right := Left + Control.UndockWidth; Bottom := Top + Control.UndockHeight; end; // let user adjust dock rect AdjustDockRect(FDockRect); end; end; end; {------------------------------------------------------------------------------ TControl.BoundsChanged ------------------------------------------------------------------------------} procedure TControl.BoundsChanged; begin { Notifications can be performed here } end; {------------------------------------------------------------------------------ TControl.Bringtofront ------------------------------------------------------------------------------} procedure TControl.BringToFront; begin SetZOrder(true); end; {------------------------------------------------------------------------------ TControl.CanTab ------------------------------------------------------------------------------} function TControl.CanTab: Boolean; begin Result := False; end; {------------------------------------------------------------------------------ TControl.Change ------------------------------------------------------------------------------} procedure TControl.Changed; begin Perform(CM_CHANGED, 0, LParam(self)); end; {------------------------------------------------------------------------------ TControl.EditingDone Called when user has finished editing. This procedure can be used by data links to commit the changes. For example: - When focus switches to another control (default) - When user selected another item It's totally up to the control, what events will commit. ------------------------------------------------------------------------------} procedure TControl.EditingDone; begin if Assigned(OnEditingDone) then OnEditingDone(Self); end; procedure TControl.FontChanged(Sender: TObject); begin FParentFont := False; FDesktopFont := False; Invalidate; Perform(CM_FONTCHANGED, 0, 0); if AutoSize then begin InvalidatePreferredSize; AdjustSize; end; end; procedure TControl.ParentFontChanged; begin //kept for compatibility. The real work is done in CMParentFontChanged end; procedure TControl.SetAction(Value: TBasicAction); begin //debugln('TControl.SetAction A ',Name,':',ClassName,' Old=',DbgS(Action),' New=',DbgS(Value)); if Value = nil then begin ActionLink.Free; ActionLink := nil; Exclude(FControlStyle, csActionClient); end else begin Include(FControlStyle, csActionClient); if ActionLink = nil then ActionLink := GetActionLinkClass.Create(Self); ActionLink.Action := Value; ActionLink.OnChange := @DoActionChange; ActionChange(Value, csLoading in Value.ComponentState); Value.FreeNotification(Self); end; end; {------------------------------------------------------------------------------ TControl.ChangeBounds ------------------------------------------------------------------------------} procedure TControl.ChangeBounds(ALeft, ATop, AWidth, AHeight: Integer; KeepBase: Boolean); var SizeChanged, PosChanged : boolean; OldLeft, OldTop, OldWidth, OldHeight: Integer; function PosSizeChanged: boolean; begin SizeChanged:= (FWidth <> OldWidth) or (FHeight <> OldHeight); PosChanged:= (FLeft <> OldLeft) or (FTop <> OldTop); Result:= SizeChanged or PosChanged; end; procedure DebugInvalidPos(N: integer); begin if (FLeft < Low(Smallint)) or (FLeft > High(Smallint)) or (FTop < Low(Smallint)) or (FTop > High(Smallint)) then DebugLn(['TControl.ChangeBounds test(',N,')',DbgSName(Self), ' Old=',OldLeft,',',OldTop,',',OldWidth,',',OldHeight, ' New=',ALeft,',',ATop,',',AWidth,',',AHeight, ' Real=',FLeft,',',FTop,',',FWidth,',',FHeight]); end; begin {$IFDEF VerboseSizeMsg} DebugLn(['TControl.ChangeBounds A ',DbgSName(Self), ' Old=',Left,',',Top,',',Width,',',Height, ' New=',ALeft,',',ATop,',',AWidth,',',AHeight, ' KeepBase=',KeepBase]); //if (Parent=nil) and (Left>0) and (ALeft=0) then DumpStack; // This can happen if the interface has not yet moved the window and for some reason something applies the interface coords back to the LCL {$ENDIF} if Assigned(Parent) and not KeepBase then Parent.UpdateAlignIndex(Self); // constraint the size DoConstrainedResize(ALeft, ATop, AWidth, AHeight); // check if something would change SizeChanged := (FWidth <> AWidth) or (FHeight <> AHeight); PosChanged := (FLeft <> ALeft) or (FTop <> ATop); if not (SizeChanged or PosChanged) then Exit; // check for loop. if (not KeepBase) and (cfKillChangeBounds in GetTopParent.FControlFlags) then raise ELayoutException.CreateFmt('TControl.ChangeBounds loop detected %s '+ 'Left=%d,Top=%d,Width=%d,Height=%d NewLeft=%d,NewTop=%d,NewWidth=%d,NewHeight=%d', [DbgSName(Self), Left,Top,Width,Height, aLeft,aTop,aWidth,aHeight]); OldLeft := FLeft; OldTop := FTop; OldWidth := FWidth; OldHeight := FHeight; //DebugLn('TControl.ChangeBounds A ',DbgSName(Self),' Old=',dbgs(BoundsRect),' New=',dbgs(Bounds(ALeft,ATop,AWidth,AHeight))); if not ((csLoading in ComponentState) or (Self is TWinControl)) then InvalidateControl(IsControlVisible, False, true); //DebugLn('TControl.ChangeBounds B ',Name,':',ClassName); DoSetBounds(ALeft, ATop, AWidth, AHeight); DebugInvalidPos(1); // change base bounds // (base bounds are the base for the automatic resizing) if not KeepBase then UpdateAnchorRules; DebugInvalidPos(2); // lock size messages inc(FSizeLock); try // notify before autosizing BoundsChanged; if not PosSizeChanged then exit; if (Parent<>nil) or SizeChanged then AdjustSize; finally dec(FSizeLock); end; if not PosSizeChanged then exit; DebugInvalidPos(3); // send messages, if this is the top level call if FSizeLock > 0 then exit; // invalidate if (csDesigning in ComponentState) and (Parent <> nil) then Parent.Invalidate else if (not (csLoading in ComponentState)) and (not (Self is TWinControl)) then Invalidate; DebugInvalidPos(4); // notify user about resize if (not (csLoading in ComponentState)) then begin Resize; DebugInvalidPos(5); CheckOnChangeBounds; DebugInvalidPos(6); // for delphi compatibility send size/move messages if PosSizeChanged then SendMoveSizeMessages(SizeChanged,PosChanged); end; end; {------------------------------------------------------------------------------- TControl.DoSetBounds Params: ALeft, ATop, AWidth, AHeight : integer store bounds in private variables -------------------------------------------------------------------------------} procedure TControl.DoSetBounds(ALeft, ATop, AWidth, AHeight: Integer); procedure BoundsOutOfBounds; begin DebugLn('TControl.DoSetBounds ',Name,':',ClassName, ' Old=',dbgs(Left,Top,Width,Height), ' New=',dbgs(aLeft,aTop,aWidth,aHeight), ''); RaiseGDBException('TControl.DoSetBounds '+Name+':'+ClassName+' Invalid bounds'); end; begin if (AWidth>100000) or (AHeight>100000) then BoundsOutOfBounds; {$IFDEF CHECK_POSITION} if CheckPosition(Self) then DebugLn(['TControl.DoSetBounds ',DbgSName(Self), ' Old=',Left,',',Top,',',Width,'x',Height, ' New=',aLeft,',',aTop,',',aWidth,'x',aHeight]); {$ENDIF} FLeft := ALeft; FTop := ATop; FWidth := AWidth; FHeight := AHeight; if Parent <> nil then Parent.InvalidatePreferredSize; end; procedure TControl.ScaleConstraints(Multiplier, Divider: Integer); begin with Constraints do begin if MinWidth > 0 then MinWidth := MulDiv(MinWidth, Multiplier, Divider); if MaxWidth > 0 then MaxWidth := MulDiv(MaxWidth, Multiplier, Divider); if MinHeight > 0 then MinHeight := MulDiv(MinHeight, Multiplier, Divider); if MaxHeight > 0 then MaxHeight := MulDiv(MaxHeight, Multiplier, Divider); end; end; function TControl.ScaleDesignToForm(const ASize: Integer): Integer; var ParentForm: TCustomDesignControl; begin ParentForm := NeedParentDesignControl(Self); Result := MulDiv(ASize, ParentForm.PixelsPerInch, ParentForm.DesignTimePPI); end; function TControl.Scale96ToForm(const ASize: Integer): Integer; var ParentForm: TCustomDesignControl; begin ParentForm := NeedParentDesignControl(Self); Result := MulDiv(ASize, ParentForm.PixelsPerInch, 96); end; function TControl.Scale96ToScreen(const ASize: Integer): Integer; begin Result := MulDiv(ASize, Screen.PixelsPerInch, 96); end; function TControl.ScaleFormTo96(const ASize: Integer): Integer; var ParentForm: TCustomDesignControl; begin ParentForm := NeedParentDesignControl(Self); Result := MulDiv(ASize, 96, ParentForm.PixelsPerInch); end; function TControl.ScaleFormToDesign(const ASize: Integer): Integer; var ParentForm: TCustomDesignControl; begin ParentForm := NeedParentDesignControl(Self); Result := MulDiv(ASize, ParentForm.DesignTimePPI, ParentForm.PixelsPerInch); end; function TControl.ScaleScreenTo96(const ASize: Integer): Integer; begin Result := MulDiv(ASize, 96, Screen.PixelsPerInch); end; function TControl.Scale96ToFont(const ASize: Integer): Integer; begin Result := MulDiv(ASize, Font.PixelsPerInch, 96); end; function TControl.ScaleFontTo96(const ASize: Integer): Integer; begin Result := MulDiv(ASize, 96, Font.PixelsPerInch); end; function TControl.ScaleScreenToFont(const ASize: Integer): Integer; begin Result := MulDiv(ASize, Font.PixelsPerInch, Screen.PixelsPerInch); end; function TControl.ScaleFontToScreen(const ASize: Integer): Integer; begin Result := MulDiv(ASize, Screen.PixelsPerInch, Font.PixelsPerInch); end; procedure TControl.ScaleFontsPPI(const AToPPI: Integer; const AProportion: Double); begin // Problem: all fonts have to be scaled. // Override this function - list all custom fonts in the overriden procedure DoScaleFontPPI(Font, AToPPI, AProportion); end; {------------------------------------------------------------------------------ TControl.ChangeScale Scale contorl by factor Multiplier/Divider ------------------------------------------------------------------------------} procedure TControl.ChangeScale(Multiplier, Divider: Integer); var R: TRect; begin if Multiplier <> Divider then begin ScaleConstraints(Multiplier, Divider); if not ParentFont then Font.Height := MulDiv(GetFontData(Font.Reference.Handle).Height, Multiplier, Divider); R := BaseBounds; if (Self is TCustomForm) and (GetParentForm(Self, True) = Self) then begin //Dont change Left,Top if this is the topmost form R.Right := R.Left + MulDiv(R.Right-R.Left, Multiplier, Divider); R.Bottom := R.Top + MulDiv(R.Bottom-R.Top, Multiplier, Divider); end else begin R.Left := MulDiv(R.Left, Multiplier, Divider); R.Top := MulDiv(R.Top, Multiplier, Divider); R.Right := MulDiv(R.Right, Multiplier, Divider); R.Bottom := MulDiv(R.Bottom, Multiplier, Divider); end; BoundsRect := R; end; end; {------------------------------------------------------------------------------ procedure TControl.CalculateDockSizes; Compute docking width, height based on docking properties. ------------------------------------------------------------------------------} procedure TControl.CalculateDockSizes; begin if Floating then begin // if control is floating then save it size for further undocking UndockHeight := Height; UndockWidth := Width; end else if HostDockSite <> nil then begin // the control is docked into a HostSite. That means some of it bounds // were maximized to fit into the HostSite. if (DockOrientation = doHorizontal) or (HostDockSite.Align in [alLeft,alRight]) then // the control is aligned left/right, that means its width is not // maximized. Save Width for docking. LRDockWidth := Width else if (DockOrientation = doVertical) or (HostDockSite.Align in [alTop,alBottom]) then // the control is aligned top/bottom, that means its height is not // maximized. Save Height for docking. TBDockHeight := Height; end; end; {------------------------------------------------------------------------------ function TControl.CreateFloatingDockSite(const Bounds: TRect): TWinControl; ------------------------------------------------------------------------------} function TControl.CreateFloatingDockSite(const Bounds: TRect): TWinControl; var FloatingClass: TWinControlClass; NewWidth: Integer; NewHeight: Integer; NewClientWidth: Integer; NewClientHeight: Integer; begin Result := nil; FloatingClass:=FloatingDockSiteClass; if (FloatingClass<>nil) and (FloatingClass<>TWinControlClass(ClassType)) then begin Result := TWinControl(FloatingClass.NewInstance); Result.DisableAutoSizing{$IFDEF DebugDisableAutoSizing}('TControl.CreateFloatingDockSite'){$ENDIF}; Result.Create(Self); // resize with minimal resizes NewClientWidth:=Bounds.Right-Bounds.Left; NewClientHeight:=Bounds.Bottom-Bounds.Top; NewWidth:=Result.Width-Result.ClientWidth+NewClientWidth; NewHeight:=Result.Height-Result.ClientHeight+NewClientHeight; Result.SetBounds(Bounds.Left,Bounds.Top,NewWidth,NewHeight); Result.SetClientSize(Point(NewClientWidth,NewClientHeight)); {$IFDEF DebugDisableAutoSizing} debugln('TControl.CreateFloatingDockSite A ',DbgSName(Self),' ',DbgSName(Result),' ',dbgs(Result.BoundsRect)); {$ENDIF} Result.EnableAutoSizing{$IFDEF DebugDisableAutoSizing}('TControl.CreateFloatingDockSite'){$ENDIF}; end; end; procedure TControl.ExecuteDefaultAction; begin end; procedure TControl.FixDesignFontsPPI(const ADesignTimePPI: Integer); begin // Problem: Font.PixelsPerInch isn't saved in the LFM, therefore the // design-time font PPI is different from the one that is loaded on target // machine, which results in different font scaling. // DoFixDesignFont restores the corrent design-time font PPI so that it can // be used for LCL HighDPI scaling. // Override this function - list all custom fonts in the overriden procedure // To-Do: maybe save Font.PixelsPerInch in the LFM and remove this? DoFixDesignFontPPI(Font, ADesignTimePPI); end; procedure TControl.ExecuteCancelAction; begin end; {------------------------------------------------------------------------------ function TControl.GetFloating: Boolean; ------------------------------------------------------------------------------} function TControl.GetFloating: Boolean; begin // a non-windowed control can never float for itself Result := (HostDockSite is FloatingDockSiteClass) and (HostDockSite.DockClientCount<=1); end; {------------------------------------------------------------------------------ function TControl.GetFloatingDockSiteClass: TWinControlClass; ------------------------------------------------------------------------------} function TControl.GetFloatingDockSiteClass: TWinControlClass; begin Result := FFloatingDockSiteClass; end; procedure TControl.BeforeDragStart; begin end; {------------------------------------------------------------------------------ function TControl.GetLRDockWidth: Integer; ------------------------------------------------------------------------------} function TControl.GetLRDockWidth: Integer; begin if FLRDockWidth>0 then Result := FLRDockWidth else Result := UndockWidth; end; {------------------------------------------------------------------------------ function TControl.IsHelpContextStored: boolean; ------------------------------------------------------------------------------} function TControl.IsHelpContextStored: Boolean; begin Result := (ActionLink = nil) or not ActionLink.IsHelpLinked; end; {------------------------------------------------------------------------------ function TControl.IsHelpKeyWordStored: boolean; ------------------------------------------------------------------------------} // Using IsHelpContextLinked() for controlling HelpKeyword // is not correct. Therefore, use IsHelpLinked which means that all 3 Help* properties // must be equal. Also, this function becomes exactly the same as one just above. function TControl.IsHelpKeyWordStored: Boolean; begin Result := (ActionLink = nil) or not ActionLink.IsHelpLinked; end; function TControl.IsShowHintStored: Boolean; begin Result := not ParentShowHint; end; function TControl.IsVisibleStored: Boolean; begin Result := (ActionLink = nil) or not ActionLink.IsVisibleLinked; end; function TControl.GetUndockHeight: Integer; begin if FUndockHeight > 0 then Result := FUndockHeight else Result := Height; end; function TControl.GetUndockWidth: Integer; begin if FUndockWidth > 0 then Result := FUndockWidth else Result := Width; end; function TControl.IsAnchorsStored: Boolean; begin Result:=(Anchors<>AnchorAlign[Align]); end; function TControl.IsVisible: Boolean; begin Result := IsControlVisible and ((Parent = nil) or (Parent.IsVisible)); end; function TControl.IsControlVisible: Boolean; begin Result := (FVisible or ((csDesigning in ComponentState) and (not (csNoDesignVisible in ControlStyle)))); end; {------------------------------------------------------------------------------ Method: TControl.IsEnabled Params: none Returns: Boolean Returns True only if both TControl and it's parent hierarchy are enabled. Used internally by TGraphicControls for painting and various states during runtime. ------------------------------------------------------------------------------} function TControl.IsEnabled: Boolean; var TheControl: TControl; begin TheControl := Self; repeat Result := TheControl.Enabled; TheControl := TheControl.Parent; until (TheControl = nil) or (not Result); end; {------------------------------------------------------------------------------ Method: TControl.IsParentColor Params: none Returns: Boolean Used at places where we need to check ParentColor property from TControl. Property is protected, so this function avoids hacking to get protected property value. ------------------------------------------------------------------------------} function TControl.IsParentColor: Boolean; begin Result := FParentColor; end; {------------------------------------------------------------------------------ Method: TControl.IsParentFont Params: none Returns: Boolean Used at places where we need to check ParentFont property from TControl. Property is protected, so this function avoids hacking to get protected property value. ------------------------------------------------------------------------------} function TControl.IsParentFont: Boolean; begin Result := FParentFont; end; function TControl.FormIsUpdating: Boolean; begin Result := Assigned(Parent) and Parent.FormIsUpdating; end; function TControl.IsProcessingPaintMsg: Boolean; begin Result:=cfProcessingWMPaint in FControlFlags; end; {------------------------------------------------------------------------------ TControl.LMCaptureChanged ------------------------------------------------------------------------------} procedure TControl.LMCaptureChanged(var Message: TLMessage); begin //DebugLn('[LMCaptureChanged for '+Name+':'+Classname+']'); CaptureChanged; end; {------------------------------------------------------------------------------ TControl.CMENABLEDCHANGED ------------------------------------------------------------------------------} procedure TControl.CMEnabledChanged(var Message: TLMEssage); begin Invalidate; end; {------------------------------------------------------------------------------ TControl.CMHITTEST ------------------------------------------------------------------------------} procedure TControl.CMHitTest(var Message: TCMHittest); begin Message.Result := 1; end; {------------------------------------------------------------------------------ TControl.CMMouseEnter ------------------------------------------------------------------------------} procedure TControl.CMMouseEnter(var Message: TLMessage); begin if FMouseInClient then Exit; FMouseInClient := True; // broadcast to parents first if Assigned(Parent) then Parent.Perform(CM_MOUSEENTER, 0, LParam(Self)); // if it is not a child message then perform an event if (Message.LParam = 0) then MouseEnter; end; {------------------------------------------------------------------------------ TControl.CMMouseLeave ------------------------------------------------------------------------------} procedure TControl.CMMouseLeave(var Message: TLMessage); begin if not FMouseInClient then Exit; FMouseInClient := False; // broadcast to parents first if Assigned(Parent) then Parent.Perform(CM_MOUSELEAVE, 0, LParam(Self)); // if it is not a child message then perform an event if (Message.LParam = 0) then MouseLeave; end; {------------------------------------------------------------------------------ procedure TControl.CMHintShow(var Message: TLMessage); ------------------------------------------------------------------------------} procedure TControl.CMHintShow(var Message: TLMessage); begin DoOnShowHint(TCMHintShow(Message).HintInfo); if (ActionLink <> nil) and not ActionLink.DoShowHint(TCMHintShow(Message).HintInfo^.HintStr) then Message.Result := 1; end; {------------------------------------------------------------------------------ TControl.CMVisibleChanged ------------------------------------------------------------------------------} procedure TControl.CMVisibleChanged(var Message : TLMessage); begin if (not (csDesigning in ComponentState) or (csNoDesignVisible in ControlStyle)) and (not (csLoading in ComponentState)) then InvalidateControl(True, FVisible and (csOpaque in ControlStyle), True); end; procedure TControl.CMTextChanged(var Message: TLMessage); begin TextChanged; end; procedure TControl.CMCursorChanged(var Message: TLMessage); begin if not (csDesigning in ComponentState) then SetTempCursor(Cursor); end; {------------------------------------------------------------------------------ TControl.CMParentColorChanged assumes: FParent <> nil ------------------------------------------------------------------------------} procedure TControl.CMParentColorChanged(var Message: TLMessage); begin if csLoading in ComponentState then Exit; if FParentColor then begin Color := FParent.Color; FParentColor := True; end; end; {------------------------------------------------------------------------------ TControl.CMParentFontChanged assumes: FParent <> nil ------------------------------------------------------------------------------} procedure TControl.CMParentFontChanged(var Message: TLMessage); begin if csLoading in ComponentState then exit; if FParentFont then begin if Assigned(FParent) then begin Font.BeginUpdate; try Font.PixelsPerInch := FParent.Font.PixelsPerInch; // PixelsPerInch isn't assigned Font := FParent.Font; finally Font.EndUpdate; end; end; FParentFont := True; end; //call here for compatibility with older LCL code ParentFontChanged; end; {------------------------------------------------------------------------------ TControl.CMParentShowHintChanged assumes: FParent <> nil ------------------------------------------------------------------------------} procedure TControl.CMParentShowHintChanged(var Message: TLMessage); begin if csLoading in ComponentState then Exit; if FParentShowHint then begin ShowHint := FParent.ShowHint; FParentShowHint := True; end; end; {------------------------------------------------------------------------------} { TControl.ConstrainedResize } {------------------------------------------------------------------------------} procedure TControl.ConstrainedResize(var MinWidth, MinHeight, MaxWidth, MaxHeight : TConstraintSize); begin if Assigned(FOnConstrainedResize) then FOnConstrainedResize(Self, MinWidth, MinHeight, MaxWidth, MaxHeight); end; {------------------------------------------------------------------------------ procedure TControl.CalculatePreferredSize(var PreferredWidth, PreferredHeight: integer; WithThemeSpace: Boolean); Calculates the default/preferred width and height for a control, which is used by the LCL autosizing algorithms as default size. Only positive values are valid. Negative or 0 are treated as undefined and the LCL uses other sizes instead. TWinControl overrides this and asks the interface for theme dependent values. See TWinControl.GetPreferredSize for more information. WithThemeSpace: If true, adds space for stacking. For example: TRadioButton has a minimum size. But for staking multiple TRadioButtons there should be some space around. This space is theme dependent, so it passed parameter to the widgetset. ------------------------------------------------------------------------------} procedure TControl.CalculatePreferredSize(var PreferredWidth, PreferredHeight: Integer; WithThemeSpace: Boolean); begin PreferredWidth:=0; PreferredHeight:=0; end; {------------------------------------------------------------------------------ function TControl.GetPalette: HPalette; ------------------------------------------------------------------------------} function TControl.GetPalette: HPalette; begin Result:=0; end; function TControl.GetParentBackground: Boolean; begin Result := csParentBackground in ControlStyle; end; function TControl.ChildClassAllowed(ChildClass: TClass): Boolean; begin Result:=false; end; {------------------------------------------------------------------------------ procedure TControl.DoOnResize; Call events ------------------------------------------------------------------------------} procedure TControl.DoOnResize; begin if Assigned(FOnResize) then FOnResize(Self); DoCallNotifyHandler(chtOnResize); end; {------------------------------------------------------------------------------ procedure TControl.DoOnChangeBounds; Call events ------------------------------------------------------------------------------} procedure TControl.DoOnChangeBounds; begin Exclude(FControlFlags,cfOnChangeBoundsNeeded); if Assigned(FOnChangeBounds) then FOnChangeBounds(Self); DoCallNotifyHandler(chtOnChangeBounds); end; procedure TControl.CheckOnChangeBounds; var CurBounds: TRect; CurClientSize: TPoint; begin if [csLoading,csDestroying]*ComponentState<>[] then exit; CurBounds:=BoundsRect; CurClientSize:=Point(ClientWidth,ClientHeight); if (not CompareRect(@FLastDoChangeBounds,@CurBounds)) or (ComparePoints(CurClientSize,FLastDoChangeClientSize)<>0) then begin if FormIsUpdating then begin Include(FControlFlags,cfOnChangeBoundsNeeded); exit; end; FLastDoChangeBounds:=CurBounds; FLastDoChangeClientSize:=CurClientSize; DoOnChangeBounds; end; end; {------------------------------------------------------------------------------ procedure TControl.DoBeforeMouseMessage; ------------------------------------------------------------------------------} procedure TControl.DoBeforeMouseMessage(TheMessage: TLMessage); var MouseMessage: TLMMouse absolute TheMessage; P: TPoint; NewMouseControl: TControl; begin if Assigned(Application) then begin NewMouseControl := GetCaptureControl; if NewMouseControl = nil then begin P := GetMousePosFromMessage(MouseMessage.Pos); p := ClientToScreen(P); NewMouseControl := Application.GetControlAtPos(P); end; Application.DoBeforeMouseMessage(NewMouseControl); end; end; {------------------------------------------------------------------------------ function TControl.ColorIsStored: boolean; ------------------------------------------------------------------------------} function TControl.ColorIsStored: Boolean; begin Result := not ParentColor; end; function TControl.GetDefaultColor(const DefaultColorType: TDefaultColorType): TColor; const DefColors: array[TDefaultColorType] of TColor = ( { dctBrush } clWindow, { dctFont } clWindowText ); begin Result := TWSControlClass(WidgetSetClass).GetDefaultColor(Self, DefaultColorType); if (Result = clDefault) then if ParentColor and Assigned(Parent) then Result := Parent.GetDefaultColor(DefaultColorType) else Result := DefColors[DefaultColorType]; end; function TControl.GetColorResolvingParent: TColor; begin if Color = clDefault then Result := GetDefaultColor(dctBrush) // GetDefaultColor resolves the parent else Result := Color; end; function TControl.GetRGBColorResolvingParent: TColor; begin Result := ColorToRGB(GetColorResolvingParent()); end; {------------------------------------------------------------------------------ TControl.DoConstrainedResize ------------------------------------------------------------------------------} procedure TControl.DoConstrainedResize(var NewLeft, NewTop, NewWidth, NewHeight: Integer); var MinWidth, MinHeight, MaxWidth, MaxHeight : TConstraintSize; begin if NewWidth<0 then NewWidth:=0; if NewHeight<0 then NewHeight:=0; MinWidth := Constraints.EffectiveMinWidth; MinHeight := Constraints.EffectiveMinHeight; MaxWidth := Constraints.EffectiveMaxWidth; MaxHeight := Constraints.EffectiveMaxHeight; ConstrainedResize(MinWidth, MinHeight, MaxWidth, MaxHeight); if (MinWidth > 0) and (NewWidth < MinWidth) then begin // right kept position ? interpret as resizing left border if (NewLeft+NewWidth) = (Left+Width) then begin Dec(NewLeft, MinWidth - NewWidth); if NewLeft < Left then NewLeft := Left; end; NewWidth := MinWidth end else if (MaxWidth > 0) and (NewWidth > MaxWidth) then begin if (NewLeft+NewWidth) = (Left+Width) then begin Inc(NewLeft, NewWidth - MaxWidth); if NewLeft > Left then NewLeft := Left; end; NewWidth := MaxWidth; end; if (MinHeight > 0) and (NewHeight < MinHeight) then begin // bottom kept position ? interpret as resizing bottom border if (NewTop+NewHeight) = (Top+Height) then begin Dec(NewTop, MinHeight - NewHeight); if NewTop < Top then NewTop := Top; end; NewHeight := MinHeight end else if (MaxHeight > 0) and (NewHeight > MaxHeight) then begin if (NewTop+NewHeight) = (Top+Height) then begin Inc(NewTop, NewHeight - MaxHeight); if NewTop > Top then NewTop := Top; end; NewHeight := MaxHeight; end; //debugln('TControl.DoConstrainedResize ',DbgSName(Self),' ',dbgs(NewWidth),',',dbgs(NewHeight)); end; {------------------------------------------------------------------------------ TControl.DoConstraintsChange ------------------------------------------------------------------------------} procedure TControl.DoConstraintsChange(Sender : TObject); begin AdjustSize; end; procedure TControl.DoBorderSpacingChange(Sender: TObject; InnerSpaceChanged: Boolean); begin if Parent <> nil then Parent.InvalidatePreferredSize; AdjustSize; end; function TControl.IsBorderSpacingInnerBorderStored: Boolean; begin Result:=BorderSpacing.InnerBorder<>0; end; {------------------------------------------------------------------------------ TControl IsCaptionStored ------------------------------------------------------------------------------} function TControl.IsCaptionStored: Boolean; begin Result := (ActionLink = nil) or not ActionLink.IsCaptionLinked; end; {------------------------------------------------------------------------------ procedure TControl.SendMoveSizeMessages(SizeChanged, PosChanged: boolean); ------------------------------------------------------------------------------} procedure TControl.SendMoveSizeMessages(SizeChanged, PosChanged: Boolean); begin end; {------------------------------------------------------------------------------ TControl.DragCanceled ------------------------------------------------------------------------------} procedure TControl.DragCanceled; begin {$IFDEF VerboseDrag} DebugLn('TControl.DragCanceled'); {$ENDIF} end; {------------------------------------------------------------------------------ TControl.DoStartDrag ------------------------------------------------------------------------------} procedure TControl.DoStartDrag(var DragObject: TDragObject); begin {$IFDEF VerboseDrag} DebugLn('TControl.DoStartDrag ',Name,':',ClassName); {$ENDIF} if Assigned(FOnStartDrag) then FOnStartDrag(Self, DragObject); end; {------------------------------------------------------------------------------ TControl.DoEndDrag ------------------------------------------------------------------------------} procedure TControl.DoEndDrag(Target: TObject; X,Y: Integer); begin {$IFDEF VerboseDrag} DebugLn('TControl.DoEndDrag ',Name,':',ClassName,' XY=',IntToStr(X),',',IntToStr(Y)); {$ENDIF} if Assigned(FOnEndDrag) then FOnEndDrag(Self,Target,X,Y); end; {------------------------------------------------------------------------------ TControl.DoFixDesignFontPPI ------------------------------------------------------------------------------} procedure TControl.DoFixDesignFontPPI(const AFont: TFont; const ADesignTimePPI: Integer); var H: Integer; OldParentFont: Boolean; begin if AFont.PixelsPerInch <> ADesignTimePPI then begin OldParentFont := ParentFont; try H := AFont.Height; AFont.BeginUpdate; try AFont.Height := MulDiv(H, AFont.PixelsPerInch, ADesignTimePPI); AFont.PixelsPerInch := ADesignTimePPI; finally AFont.EndUpdate; end; finally FParentFont := OldParentFont; // change ParentFont without triggering CM_PARENTFONTCHANGED end; end; end; {------------------------------------------------------------------------------ TControl.Perform ------------------------------------------------------------------------------} function TControl.Perform(Msg: Cardinal; WParam: WParam; LParam: LParam): LRESULT; var Message : TLMessage; begin Message.Msg := Msg; Message.WParam := WParam; Message.LParam := LParam; Message.Result := 0; if Self <> nil then WindowProc(Message); Result := Message.Result; end; {------------------------------------------------------------------------------ TControl.GetClientOrigin ------------------------------------------------------------------------------} function TControl.GetClientOrigin: TPoint; begin if Parent = nil then raise EInvalidOperation.CreateFmt(sParentRequired, [Name]); Result := Parent.ClientOrigin; Inc(Result.X, FLeft); Inc(Result.Y, FTop); end; {------------------------------------------------------------------------------ TControl.ScreenToClient ------------------------------------------------------------------------------} function TControl.ScreenToClient(const APoint: TPoint): TPoint; var P : TPoint; begin P := ClientOrigin; Result.X := APoint.X - P.X; Result.Y := APoint.Y - P.Y; end; {------------------------------------------------------------------------------ function TControl.ClientToScreen(const APoint: TPoint): TPoint; ------------------------------------------------------------------------------} function TControl.ClientToScreen(const APoint: TPoint): TPoint; var P : TPoint; begin P := ClientOrigin; Result.X := APoint.X + P.X; Result.Y := APoint.Y + P.Y; end; function TControl.ClientToScreen(const ARect: TRect): TRect; var P : TPoint; begin P := ClientToScreen(Point(0, 0)); Result := ARect; Result.Offset(P); end; {------------------------------------------------------------------------------ function TControl.ScreenToControl(const APoint: TPoint): TPoint; ------------------------------------------------------------------------------} function TControl.ScreenToControl(const APoint: TPoint): TPoint; var P : TPoint; begin P := ControlOrigin; Result.X := APoint.X - P.X; Result.Y := APoint.Y - P.Y; end; {------------------------------------------------------------------------------ function TControl.ControlToScreen(const APoint: TPoint): TPoint; ------------------------------------------------------------------------------} function TControl.ControlToScreen(const APoint: TPoint): TPoint; var P : TPoint; begin P := ControlOrigin; Result.X := APoint.X + P.X; Result.Y := APoint.Y + P.Y; end; function TControl.ClientToParent(const Point: TPoint; AParent: TWinControl): TPoint; begin if not Assigned(AParent) then AParent := Parent; if not AParent.IsParentOf(Self) then raise EInvalidOperation.CreateFmt(rsControlIsNotAParent, [AParent.Name, Name]); Result := AParent.ScreenToClient(ClientToScreen(Point)); end; function TControl.ParentToClient(const Point: TPoint; AParent: TWinControl): TPoint; begin if not Assigned(AParent) then AParent := Parent; if not AParent.IsParentOf(Self) then raise EInvalidOperation.CreateFmt(rsControlIsNotAParent, [AParent.Name, Name]); Result := ScreenToClient(AParent.ClientToScreen(Point)); end; {------------------------------------------------------------------------------ TControl.DblClick ------------------------------------------------------------------------------} procedure TControl.DblClick; begin if Assigned(FOnDblClick) then FOnDblClick(Self); end; {------------------------------------------------------------------------------ TControl.TripleClick ------------------------------------------------------------------------------} procedure TControl.TripleClick; begin if Assigned(FOnTripleClick) then FOnTripleClick(Self); end; {------------------------------------------------------------------------------ TControl.QuadClick ------------------------------------------------------------------------------} procedure TControl.QuadClick; begin if Assigned(FOnQuadClick) then FOnQuadClick(Self); end; {------------------------------------------------------------------------------ TControl.DoDragMsg ------------------------------------------------------------------------------} function TControl.DoDragMsg(ADragMessage: TDragMessage; APosition: TPoint; ADragObject: TDragObject; ATarget: TControl; ADocking: Boolean): LRESULT; function GetDragObject: TObject; inline; begin if ADragObject.AutoCreated then Result := ADragObject.Control else Result := ADragObject; end; var AWinTarget: TWinControl; Accepts: Boolean; P: TPoint; begin Result := 0; {$IFDEF VerboseDrag} DebugLn('TControl.DoDragMsg ',Name,':',ClassName,' DragMsg.DragMessage=', GetEnumName(TypeInfo(TDragMessage), Ord(ADragMessage))); {$ENDIF} case ADragMessage of dmFindTarget: Result := PtrInt(Self); dmDragEnter, dmDragLeave, dmDragMove: begin Accepts := True; P := ScreenToClient(APosition); if ADragObject is TDragDockObject then begin AWinTarget:= TWinControl(ADragObject.DragTarget); AWinTarget.DockOver(TDragDockObject(ADragObject), P.X, P.Y, TDragState(ADragMessage), Accepts); end else DragOver(GetDragObject, P.X, P.Y, TDragState(ADragMessage), Accepts); Result := Ord(Accepts); end; dmDragDrop: begin P := ScreenToClient(APosition); if ADragObject is TDragDockObject then begin AWinTarget:= TWinControl(ADragObject.DragTarget); AWinTarget.DockDrop(TDragDockObject(ADragObject), P.X, P.Y); end else DragDrop(GetDragObject, P.X, P.Y); end; end; end; {------------------------------------------------------------------------------ TControl.DragOver ------------------------------------------------------------------------------} procedure TControl.DragOver(Source: TObject; X,Y : Integer; State: TDragState; var Accept:Boolean); begin {$IFDEF VerboseDrag} DebugLn('TControl.DragOver ',Name,':',ClassName,' XY=',IntToStr(X),',',IntToStr(Y)); {$ENDIF} Accept := Assigned(FOnDragOver); if Accept then FOnDragOver(Self,Source,X,Y,State,Accept); end; {------------------------------------------------------------------------------ TControl.DragDrop ------------------------------------------------------------------------------} procedure TControl.DragDrop(Source: TObject; X,Y : Integer); begin {$IFDEF VerboseDrag} DebugLn('TControl.DragDrop ',Name,':',ClassName,' XY=',IntToStr(X),',',IntToStr(Y)); {$ENDIF} if Assigned(FOnDragDrop) then FOnDragDrop(Self, Source,X,Y); end; procedure TControl.SetAccessibleName(AValue: TCaption); begin FAccessibleObject.AccessibleName := AValue; end; procedure TControl.SetAccessibleDescription(AValue: TCaption); begin FAccessibleObject.AccessibleDescription := AValue; end; procedure TControl.SetAccessibleValue(AValue: TCaption); begin FAccessibleObject.AccessibleValue := AValue; end; procedure TControl.SetAccessibleRole(AValue: TLazAccessibilityRole); begin FAccessibleObject.AccessibleRole := AValue; end; {------------------------------------------------------------------------------ TControl Method SetColor "Sets the default color and tells the widget set" ------------------------------------------------------------------------------} procedure TControl.SetColor(Value: TColor); begin if FColor <> Value then begin FColor := Value; ParentColor := False; ParentBackground := False; Perform(CM_COLORCHANGED, 0, 0); Invalidate; end; end; {------------------------------------------------------------------------------ TControl CanAutoSize ------------------------------------------------------------------------------} function TControl.CanAutoSize(var NewWidth, NewHeight : Integer): Boolean; begin Result := True; end; {------------------------------------------------------------------------------ TControl Dragging ------------------------------------------------------------------------------} function TControl.Dragging: Boolean; begin Result := DragManager.Dragging(Self); end; // accessibility function TControl.GetAccessibleObject: TLazAccessibleObject; begin Result := FAccessibleObject; end; function TControl.CreateAccessibleObject: TLazAccessibleObject; begin Result := TLazAccessibleObject.Create(Self); end; function TControl.GetSelectedChildAccessibleObject: TLazAccessibleObject; begin Result := nil; end; function TControl.GetChildAccessibleObjectAtPos(APos: TPoint): TLazAccessibleObject; begin Result := nil; end; {------------------------------------------------------------------------------ TControl GetBoundsRect ------------------------------------------------------------------------------} function TControl.GetBoundsRect: TRect; begin Result.Left := FLeft; Result.Top := FTop; Result.Right := FLeft+FWidth; Result.Bottom := FTop+FHeight; end; function TControl.GetClientHeight: Integer; begin Result:=ClientRect.Bottom; end; function TControl.GetClientWidth: Integer; begin Result:=ClientRect.Right; end; {------------------------------------------------------------------------------ TControl GetEnabled ------------------------------------------------------------------------------} function TControl.GetEnabled: Boolean; begin Result := FEnabled; end; {------------------------------------------------------------------------------ TControl GetMouseCapture ------------------------------------------------------------------------------} function TControl.GetMouseCapture : Boolean; begin Result := (Parent<>nil) and Parent.HandleAllocated and (GetCaptureControl = Self); end; function TControl.GetMousePosFromMessage(const MessageMousePos: TSmallPoint ): TPoint; begin if (Width>32767) or (Height>32767) then begin GetCursorPos(Result); Result := ScreenToClient(Result); end else Result := SmallPointToPoint(MessageMousePos); end; function TControl.GetTBDockHeight: Integer; begin if FTBDockHeight>0 then Result := FTBDockHeight else Result := UndockHeight; end; {------------------------------------------------------------------------------ TControl GetPopupMenu ------------------------------------------------------------------------------} function TControl.GetPopupMenu: TPopupMenu; begin Result := FPopupMenu; end; {------------------------------------------------------------------------------ procedure TControl.DoOnShowHint(HintInfo: Pointer); ------------------------------------------------------------------------------} procedure TControl.DoOnShowHint(HintInfo: PHintInfo); begin if Assigned(OnShowHint) then OnShowHint(Self,HintInfo); end; procedure TControl.DoScaleFontPPI(const AFont: TFont; const AToPPI: Integer; const AProportion: Double); begin // If AFont.PixelsPerInch is different from "Screen.PixelsPerInch" (=GetDeviceCaps(DC, LOGPIXELSX)) // then the font doesn't scale -> we have to assign a nonzero height value. if (AFont.Height=0) and not (csDesigning in ComponentState) then AFont.Height := MulDiv(GetFontData(AFont.Reference.Handle).Height, AFont.PixelsPerInch, Screen.PixelsPerInch); if AToPPI>0 then AFont.PixelsPerInch := AToPPI else AFont.PixelsPerInch := Round(AFont.PixelsPerInch*AProportion); end; function TControl.IsAParentAligning: Boolean; var p: TWinControl; begin p:=Parent; while (p<>nil) do begin if (wcfAligningControls in p.FWinControlFlags) then exit(true); p:=p.Parent; end; Result:=false; end; {------------------------------------------------------------------------------ procedure TControl.VisibleChanging; ------------------------------------------------------------------------------} procedure TControl.VisibleChanging; begin DoCallNotifyHandler(chtOnVisibleChanging); end; procedure TControl.VisibleChanged; begin DoCallNotifyHandler(chtOnVisibleChanged); end; {------------------------------------------------------------------------------ procedure TControl.EnabledChanging; ------------------------------------------------------------------------------} procedure TControl.EnabledChanging; begin DoCallNotifyHandler(chtOnEnabledChanging); end; procedure TControl.EnabledChanged; begin DoCallNotifyHandler(chtOnEnabledChanged); end; procedure TControl.AddHandler(HandlerType: TControlHandlerType; const AMethod: TMethod; AsFirst: Boolean); begin if FControlHandlers[HandlerType]=nil then FControlHandlers[HandlerType]:=TMethodList.Create; FControlHandlers[HandlerType].Add(AMethod,not AsFirst); end; procedure TControl.RemoveHandler(HandlerType: TControlHandlerType; const AMethod: TMethod); begin FControlHandlers[HandlerType].Remove(AMethod); end; procedure TControl.DoCallNotifyHandler(HandlerType: TControlHandlerType); begin FControlHandlers[HandlerType].CallNotifyEvents(Self); end; procedure TControl.DoCallKeyEventHandler(HandlerType: TControlHandlerType; var Key: Word; Shift: TShiftState); var i: Integer; begin i := FControlHandlers[HandlerType].Count; while FControlHandlers[HandlerType].NextDownIndex(i) do TKeyEvent(FControlHandlers[HandlerType][i])(Self, Key, Shift); end; procedure TControl.DoCallMouseWheelEventHandler(HandlerType: TControlHandlerType; Shift: TShiftState; WheelDelta: Integer; MousePos: TPoint; var Handled: Boolean); var i: Integer; begin i := FControlHandlers[HandlerType].Count; //debugln('TControl.DoCallMouseWheelEventHandler A: Handled = ',DbgS(Handled),', Count = ',DbgS(i)); while (not Handled) and FControlHandlers[HandlerType].NextDownIndex(i) do begin TMouseWheelEvent(FControlHandlers[HandlerType][i])(Self, Shift, WheelDelta, MousePos, Handled); //debugln('TControl.DoCallMouseWheelEventHandler B: i = ',Dbgs(i),', Handled = ',DbgS(Handled)); end; //debugln('TControl.DoCallMouseWheelEventHandler End: Handled = ',DbgS(Handled)); end; {------------------------------------------------------------------------------ procedure TControl.DoContextPopup(const MousePos: TPoint; var Handled: Boolean); ------------------------------------------------------------------------------} procedure TControl.DoContextPopup(MousePos: TPoint; var Handled: Boolean); begin if Assigned(FOnContextPopup) then FOnContextPopup(Self, MousePos, Handled); end; procedure TControl.ActionChange(Sender: TObject; CheckDefaults: Boolean); var NewAction: TCustomAction; begin if Sender is TCustomAction then begin NewAction:=TCustomAction(Sender); if (not CheckDefaults) or (Caption = '') or (Caption = Name) then Caption := NewAction.Caption; if not CheckDefaults or Enabled then Enabled := NewAction.Enabled; if not CheckDefaults or (Hint = '') then Hint := NewAction.Hint; if not CheckDefaults or Visible then Visible := NewAction.Visible; if not CheckDefaults or (Self.HelpContext = 0) then Self.HelpContext := HelpContext; if not CheckDefaults or (Self.HelpKeyword = '') then Self.HelpKeyword := HelpKeyword; // HelpType is set implicitly when assigning HelpContext or HelpKeyword end; end; procedure TControl.DoActionChange(Sender: TObject); begin if Sender = Action then ActionChange(Sender, False); end; function TControl.GetAccessibleName: TCaption; begin Result := FAccessibleObject.AccessibleName; end; function TControl.GetAccessibleDescription: TCaption; begin Result := FAccessibleObject.AccessibleDescription; end; function TControl.GetAccessibleValue: TCaption; begin Result := FAccessibleObject.AccessibleValue; end; function TControl.GetAccessibleRole: TLazAccessibilityRole; begin Result := FAccessibleObject.AccessibleRole; end; function TControl.CaptureMouseButtonsIsStored: Boolean; begin Result := FCaptureMouseButtons <> [mbLeft]; end; function TControl.GetAnchorSide(Kind: TAnchorKind): TAnchorSide; begin Result:=FAnchorSides[Kind]; end; function TControl.GetAnchoredControls(Index: Integer): TControl; begin Result := TControl(FAnchoredControls[Index]); end; function TControl.GetAutoSizingAll: Boolean; begin if Parent <> nil then Result := Parent.AutoSizingAll else Result := FAutoSizingAll; end; {------------------------------------------------------------------------------ TControl GetClientRect Returns the size of visual client area. For example the inner size of a TGroupBox. For a TScrollBox it is the visual size, not the logical size. ------------------------------------------------------------------------------} function TControl.GetClientRect: TRect; begin Result.Left := 0; Result.Top := 0; Result.Right := Width; Result.Bottom := Height; end; {------------------------------------------------------------------------------ TControl GetLogicalClientRect Returns the size of complete client area. It can be bigger or smaller than the visual size, but normally it is the same. For example a TScrollBox can have different sizes. ------------------------------------------------------------------------------} function TControl.GetLogicalClientRect: TRect; begin Result:=ClientRect; end; {------------------------------------------------------------------------------ function TControl.GetScrolledClientRect: TRect; ------------------------------------------------------------------------------} function TControl.GetScrolledClientRect: TRect; var ScrolledOffset: TPoint; begin Result:=GetClientRect; ScrolledOffset:=GetClientScrollOffset; inc(Result.Left,ScrolledOffset.X); inc(Result.Top,ScrolledOffset.Y); inc(Result.Right,ScrolledOffset.X); inc(Result.Bottom,ScrolledOffset.Y); end; {------------------------------------------------------------------------------ function TControl.GetChildrenRect(Scrolled: boolean): TRect; Returns the Client rectangle relative to the controls left, top. If Scrolled is true, the rectangle is moved by the current scrolling values (for an example see TScrollingWincontrol). ------------------------------------------------------------------------------} function TControl.GetChildrenRect(Scrolled: Boolean): TRect; var ScrolledOffset: TPoint; begin Result:=ClientRect; if Scrolled then begin ScrolledOffset:=GetClientScrollOffset; inc(Result.Left,ScrolledOffset.X); inc(Result.Top,ScrolledOffset.Y); inc(Result.Right,ScrolledOffset.X); inc(Result.Bottom,ScrolledOffset.Y); end; end; {------------------------------------------------------------------------------ function TControl.GetClientScrollOffset: TPoint; Returns the scrolling offset of the client area. ------------------------------------------------------------------------------} function TControl.GetClientScrollOffset: TPoint; begin Result:=Point(0,0); end; {------------------------------------------------------------------------------ function TControl.GetControlOrigin: TPoint; Returns the screen coordinate of the topleft pixel of the control. ------------------------------------------------------------------------------} function TControl.GetControlOrigin: TPoint; var ParentsClientOrigin: TPoint; begin Result:=Point(Left,Top); if Parent<>nil then begin ParentsClientOrigin:=Parent.ClientOrigin; inc(Result.X,ParentsClientOrigin.X); inc(Result.Y,ParentsClientOrigin.Y); end; end; {------------------------------------------------------------------------------ TControl WndPRoc ------------------------------------------------------------------------------} procedure TControl.WndProc(var TheMessage : TLMessage); var Form : TCustomForm; begin //DebugLn('CCC TControl.WndPRoc ',Name,':',ClassName); if (csDesigning in ComponentState) then begin // redirect messages to designer Form := GetDesignerForm(Self); //debugln(['TControl.WndProc ',dbgsname(Self)]); if Assigned(Form) and Assigned(Form.Designer) and Form.Designer.IsDesignMsg(Self, TheMessage) then Exit; end else if (TheMessage.Msg >= LM_KEYFIRST) and (TheMessage.Msg <= LM_KEYLAST) then begin // keyboard messages Form := GetParentForm(Self); if (Form <> nil) and (Form.WantChildKey(Self,TheMessage)) then exit; end else if ((TheMessage.Msg>=LM_MOUSEFIRST) and (TheMessage.Msg<=LM_MOUSELAST)) or ((TheMessage.Msg>=LM_MOUSEFIRST2) and (TheMessage.Msg<=LM_MOUSELAST2)) then begin // mouse messages case TheMessage.Msg of LM_MOUSEMOVE: begin Application.HintMouseMessage(Self, TheMessage); end; LM_LBUTTONDOWN, LM_LBUTTONDBLCLK: begin Include(FControlState, csLButtonDown); { The VCL holds up the mouse down for dmAutomatic and sends it, when it decides, if it is a drag operation or not. This decision requires full control of focus and mouse, which do not all LCL interfaces provide. Therefore the mouse down event is sent immediately. Further Note: Under winapi a LM_LBUTTONDOWN ends the drag immediate. For example: If we exit here, then mouse down on TTreeView does not work any longer under gtk. } if FDragMode = dmAutomatic then BeginAutoDrag; end; LM_LBUTTONUP: begin Exclude(FControlState, csLButtonDown); end; end; end; //debugln(['TControl.WndProc ',DbgSName(Self),' ',TheMessage.Msg]); if TheMessage.Msg=LM_PAINT then begin Include(FControlFlags,cfProcessingWMPaint); try Dispatch(TheMessage); finally Exclude(FControlFlags,cfProcessingWMPaint); end; end else Dispatch(TheMessage); end; {------------------------------------------------------------------------------ procedure TControl.ParentFormHandleInitialized; called by ChildHandlesCreated of parent form ------------------------------------------------------------------------------} procedure TControl.ParentFormHandleInitialized; begin // The form is really connection to the target screen. For example, the gtk // under X gathers some screen information not before form creation. // But this information is needed to create DeviceContexts, which // are needed to calculate Text Size and such stuff needed for AutoSizing. // That's why AdjustSize delays AutoSizing till this moment. Now do the // AutoSize. AdjustSize; end; {------------------------------------------------------------------------------ TControl Invalidate ------------------------------------------------------------------------------} procedure TControl.Invalidate; begin //DebugLn(['TControl.Invalidate ',DbgSName(Self)]); InvalidateControl(IsVisible, csOpaque in ControlStyle); end; {------------------------------------------------------------------------------ TControl DoMouseDown "Event Handler" ------------------------------------------------------------------------------} procedure TControl.DoMouseDown(var Message: TLMMouse; Button: TMouseButton; Shift: TShiftState); var MP: TPoint; begin //DebugLn('TControl.DoMouseDown ',DbgSName(Self),' '); if not (csNoStdEvents in ControlStyle) then begin MP := GetMousePosFromMessage(Message.Pos); MouseDown(Button, KeysToShiftState(Message.Keys) + Shift, MP.X, MP.Y); end; end; {------------------------------------------------------------------------------ TControl DoMouseUp "Event Handler" ------------------------------------------------------------------------------} procedure TControl.DoMouseUp(var Message: TLMMouse; Button: TMouseButton); var P, MP: TPoint; begin if not (csNoStdEvents in ControlStyle) then begin MP := GetMousePosFromMessage(Message.Pos); if (Button in [mbLeft, mbRight]) and DragManager.IsDragging then begin P := ClientToScreen(MP); DragManager.MouseUp(Button, KeysToShiftState(Message.Keys), P.X, P.Y); Message.Result := 1; end; MouseUp(Button, KeysToShiftState(Message.Keys), MP.X, MP.Y); end; end; {------------------------------------------------------------------------------ TControl DoMouseWheel "Event Handler" ------------------------------------------------------------------------------} function TControl.DoMouseWheel(Shift: TShiftState; WheelDelta: Integer; MousePos: TPoint): Boolean; begin Result := False; if Assigned(FOnMouseWheel) then FOnMouseWheel(Self, Shift, WheelDelta, MousePos, Result); if not Result then begin //debugln('TControl.DoMouseWheel calling DoCallMouseWheelEventHandler'); DoCallMouseWheelEventHandler(chtOnMouseWheel, Shift, WheelDelta, MousePos, Result); end; if not Result then begin if WheelDelta < 0 then Result := DoMouseWheelDown(Shift, MousePos) else Result := DoMouseWheelUp(Shift, MousePos); end; end; function TControl.DoMouseWheelHorz(Shift: TShiftState; WheelDelta: Integer; MousePos: TPoint): Boolean; begin Result := False; if Assigned(FOnMouseWheelHorz) then FOnMouseWheelHorz(Self, Shift, WheelDelta, MousePos, Result); if not Result then begin //debugln('TControl.DoMouseWheelHorz calling DoCallMouseWheelEventHandler'); DoCallMouseWheelEventHandler(chtOnMouseWheelHorz, Shift, WheelDelta, MousePos, Result); end; if not Result then begin if WheelDelta < 0 then Result := DoMouseWheelLeft(Shift, MousePos) else Result := DoMouseWheelRight(Shift, MousePos); end; end; {------------------------------------------------------------------------------ TControl DoMouseWheelDown "Event Handler" ------------------------------------------------------------------------------} function TControl.DoMouseWheelDown(Shift: TShiftState; MousePos: TPoint): Boolean; begin Result := False; if Assigned(FOnMouseWheelDown) then FOnMouseWheelDown(Self, Shift, MousePos, Result); end; {------------------------------------------------------------------------------ TControl DoMouseWheelUp "Event Handler" ------------------------------------------------------------------------------} function TControl.DoMouseWheelUp(Shift: TShiftState; MousePos: TPoint): Boolean; begin Result := False; if Assigned(FOnMouseWheelUp) then FOnMouseWheelUp(Self, Shift, MousePos, Result); end; {------------------------------------------------------------------------------ TControl DoMouseWheelLeft "Event Handler" ------------------------------------------------------------------------------} function TControl.DoMouseWheelLeft(Shift: TShiftState; MousePos: TPoint): Boolean; begin Result := False; if Assigned(FOnMouseWheelLeft) then FOnMouseWheelLeft(Self, Shift, MousePos, Result); end; {------------------------------------------------------------------------------ TControl DoMouseWheelRight "Event Handler" ------------------------------------------------------------------------------} function TControl.DoMouseWheelRight(Shift: TShiftState; MousePos: TPoint): Boolean; begin Result := False; if Assigned(FOnMouseWheelRight) then FOnMouseWheelRight(Self, Shift, MousePos, Result); end; procedure TControl.SetAnchorSide(Kind: TAnchorKind; AValue: TAnchorSide); begin GetAnchorSide(Kind).Assign(AValue); end; procedure TControl.SetBorderSpacing(const AValue: TControlBorderSpacing); begin if FBorderSpacing=AValue then exit; FBorderSpacing.Assign(AValue); end; {------------------------------------------------------------------------------ Method: TControl.WMContextMenu Params: Message Returns: Nothing ContextMenu event handler ------------------------------------------------------------------------------} procedure TControl.WMContextMenu(var Message: TLMContextMenu); var TempPopupMenu: TPopupMenu; P: TPoint; Handled: Boolean; begin if (csDesigning in ComponentState) or (Message.Result <> 0) then Exit; P := GetMousePosFromMessage(Message.Pos); // X and Y = -1 when user clicks on keyboard menu button if P.X <> -1 then P := ScreenToClient(P); Handled := False; DoContextPopup(P, Handled); if Handled then begin Message.Result := 1; Exit; end; TempPopupMenu := GetPopupMenu; if (TempPopupMenu <> nil) then begin if not TempPopupMenu.AutoPopup then Exit; TempPopupMenu.PopupComponent := Self; if P.X = -1 then P := Point(0, 0); P := ClientToScreen(P); TempPopupMenu.Popup(P.X, P.Y); Message.Result := 1; end; end; {------------------------------------------------------------------------------ Method: TControl.WMLButtonDown Params: Message Returns: Nothing Mouse event handler ------------------------------------------------------------------------------} procedure TControl.WMLButtonDown(var Message: TLMLButtonDown); begin if (csCaptureMouse in ControlStyle) and (mbLeft in CaptureMouseButtons) then begin {$IFDEF VerboseMouseCapture} DebugLn('TControl.WMLButtonDown ',Name,':',ClassName); {$ENDIF} MouseCapture := True; end; if csClickEvents in ControlStyle then Include(FControlState, csClicked); DoMouseDown(Message, mbLeft, []); //DebugLn('TCONTROL WMLBUTTONDOWN B ',Name,':',ClassName); end; {------------------------------------------------------------------------------ Method: TControl.WMRButtonDown Params: Message Returns: Nothing Mouse event handler ------------------------------------------------------------------------------} procedure TControl.WMRButtonDown(var Message: TLMRButtonDown); begin if (csCaptureMouse in ControlStyle) and (mbRight in CaptureMouseButtons) then begin {$IFDEF VerboseMouseCapture} DebugLn('TControl.WMRButtonDown ',Name,':',ClassName); {$ENDIF} MouseCapture := True; end; DoMouseDown(Message, mbRight, []); end; {------------------------------------------------------------------------------ Method: TControl.WMMButtonDown Params: Message Returns: Nothing Mouse event handler ------------------------------------------------------------------------------} procedure TControl.WMMButtonDown(var Message: TLMMButtonDown); begin if (csCaptureMouse in ControlStyle) and (mbMiddle in CaptureMouseButtons) then begin {$IFDEF VerboseMouseCapture} DebugLn('TControl.WMMButtonDown ',Name,':',ClassName); {$ENDIF} MouseCapture := True; end; DoMouseDown(Message, mbMiddle, []); end; procedure TControl.WMXButtonDown(var Message: TLMXButtonDown); var Btn: TMouseButton; begin if (Message.Keys and MK_XBUTTON1) <> 0 then Btn := mbExtra1 else if (Message.Keys and MK_XBUTTON2) <> 0 then Btn := mbExtra2 else Exit; if (csCaptureMouse in ControlStyle) and (Btn in CaptureMouseButtons) then begin {$IFDEF VerboseMouseCapture} DebugLn('TControl.WMXButtonDown ',Name,':',ClassName); {$ENDIF} MouseCapture := True; end; DoMouseDown(Message, Btn, []); end; {------------------------------------------------------------------------------ Method: TControl.WMLButtonDblClk Params: Message Returns: Nothing Mouse event handler ------------------------------------------------------------------------------} procedure TControl.WMLButtonDBLCLK(var Message: TLMLButtonDblClk); begin //TODO: SendCancelMode(self); if (csCaptureMouse in ControlStyle) and (mbLeft in CaptureMouseButtons) then begin {$IFDEF VerboseMouseCapture} DebugLn('TControl.WMLButtonDblClk ',Name,':',ClassName); {$ENDIF} MouseCapture := True; end; // first send a mouse down DoMouseDown(Message, mbLeft ,[ssDouble]); // then send the double click if csClickEvents in ControlStyle then DblClick; end; {------------------------------------------------------------------------------ Method: TControl.WMRButtonDblClk Params: Message Returns: Nothing Mouse event handler ------------------------------------------------------------------------------} procedure TControl.WMRButtonDBLCLK(var Message: TLMRButtonDblClk); begin if (csCaptureMouse in ControlStyle) and (mbRight in CaptureMouseButtons) then begin {$IFDEF VerboseMouseCapture} DebugLn('TControl.WMRButtonDblClk ',Name,':',ClassName); {$ENDIF} MouseCapture := True; end; DoMouseDown(Message, mbRight ,[ssDouble]); end; {------------------------------------------------------------------------------ Method: TControl.WMMButtonDblClk Params: Message Returns: Nothing Mouse event handler ------------------------------------------------------------------------------} procedure TControl.WMMButtonDBLCLK(var Message: TLMMButtonDblClk); begin if (csCaptureMouse in ControlStyle) and (mbMiddle in CaptureMouseButtons) then begin {$IFDEF VerboseMouseCapture} DebugLn('TControl.WMMButtonDblClk ',Name,':',ClassName); {$ENDIF} MouseCapture := True; end; DoMouseDown(Message, mbMiddle ,[ssDouble]); end; procedure TControl.WMXButtonDBLCLK(var Message: TLMXButtonDblClk); var Btn: TMouseButton; begin if (Message.Keys and MK_XBUTTON1) <> 0 then Btn := mbExtra1 else if (Message.Keys and MK_XBUTTON2) <> 0 then Btn := mbExtra2 else Exit; if (csCaptureMouse in ControlStyle) and (Btn in CaptureMouseButtons) then begin {$IFDEF VerboseMouseCapture} DebugLn('TControl.WMXButtonDblClk ',Name,':',ClassName); {$ENDIF} MouseCapture := True; end; DoMouseDown(Message, Btn, [ssDouble]); end; {------------------------------------------------------------------------------ Method: TControl.WMLButtonTripleClk Params: Message Returns: Nothing Mouse event handler ------------------------------------------------------------------------------} procedure TControl.WMLButtonTripleCLK(var Message: TLMLButtonTripleClk); begin //TODO: SendCancelMode(self); if (csCaptureMouse in ControlStyle) and (mbLeft in CaptureMouseButtons) then begin {$IFDEF VerboseMouseCapture} DebugLn('TControl.WMLButtonTripleClk ',Name,':',ClassName); {$ENDIF} MouseCapture := True; end; if csClickEvents in ControlStyle then TripleClick; DoMouseDown(Message, mbLeft ,[ssTriple]); end; {------------------------------------------------------------------------------ Method: TControl.WMRButtonTripleClk Params: Message Returns: Nothing Mouse event handler ------------------------------------------------------------------------------} procedure TControl.WMRButtonTripleCLK(var Message: TLMRButtonTripleClk); begin if (csCaptureMouse in ControlStyle) and (mbRight in CaptureMouseButtons) then begin {$IFDEF VerboseMouseCapture} DebugLn('TControl.WMRButtonTripleClk ',Name,':',ClassName); {$ENDIF} MouseCapture := True; end; DoMouseDown(Message, mbRight ,[ssTriple]); end; {------------------------------------------------------------------------------ Method: TControl.WMMButtonTripleClk Params: Message Returns: Nothing Mouse event handler ------------------------------------------------------------------------------} procedure TControl.WMMButtonTripleCLK(var Message: TLMMButtonTripleClk); begin if (csCaptureMouse in ControlStyle) and (mbMiddle in CaptureMouseButtons) then begin {$IFDEF VerboseMouseCapture} DebugLn('TControl.WMMButtonTripleClk ',Name,':',ClassName); {$ENDIF} MouseCapture := True; end; DoMouseDown(Message, mbMiddle ,[ssTriple]); end; procedure TControl.WMXButtonTripleCLK(var Message: TLMXButtonTripleClk); var Btn: TMouseButton; begin if (Message.Keys and MK_XBUTTON1) <> 0 then Btn := mbExtra1 else if (Message.Keys and MK_XBUTTON2) <> 0 then Btn := mbExtra2 else Exit; if (csCaptureMouse in ControlStyle) and (Btn in CaptureMouseButtons) then begin {$IFDEF VerboseMouseCapture} DebugLn('TControl.WMXButtonTripleClk ',Name,':',ClassName); {$ENDIF} MouseCapture := True; end; DoMouseDown(Message, Btn, [ssTriple]); end; {------------------------------------------------------------------------------ Method: TControl.WMLButtonQuadClk Params: Message Returns: Nothing Mouse event handler ------------------------------------------------------------------------------} procedure TControl.WMLButtonQuadCLK(var Message: TLMLButtonQuadClk); begin //TODO: SendCancelMode(self); if (csCaptureMouse in ControlStyle) and (mbLeft in CaptureMouseButtons) then begin {$IFDEF VerboseMouseCapture} DebugLn('TControl.WMLButtonQuadClk ',Name,':',ClassName); {$ENDIF} MouseCapture := True; end; if csClickEvents in ControlStyle then QuadClick; DoMouseDown(Message, mbLeft ,[ssQuad]); end; {------------------------------------------------------------------------------ Method: TControl.WMRButtonQuadClk Params: Message Returns: Nothing Mouse event handler ------------------------------------------------------------------------------} procedure TControl.WMRButtonQuadCLK(var Message: TLMRButtonQuadClk); begin if (csCaptureMouse in ControlStyle) and (mbRight in CaptureMouseButtons) then begin {$IFDEF VerboseMouseCapture} DebugLn('TControl.WMRButtonQuadClk ',Name,':',ClassName); {$ENDIF} MouseCapture := True; end; DoMouseDown(Message, mbRight ,[ssQuad]); end; {------------------------------------------------------------------------------ Method: TControl.WMMButtonQuadClk Params: Message Returns: Nothing Mouse event handler ------------------------------------------------------------------------------} procedure TControl.WMMButtonQuadCLK(var Message: TLMMButtonQuadClk); begin if (csCaptureMouse in ControlStyle) and (mbMiddle in CaptureMouseButtons) then begin {$IFDEF VerboseMouseCapture} DebugLn('TControl.WMMButtonQuadClk ',Name,':',ClassName); {$ENDIF} MouseCapture := True; end; DoMouseDown(Message, mbMiddle ,[ssQuad]); end; procedure TControl.WMXButtonQuadCLK(var Message: TLMXButtonQuadClk); var Btn: TMouseButton; begin if (Message.Keys and MK_XBUTTON1) <> 0 then Btn := mbExtra1 else if (Message.Keys and MK_XBUTTON2) <> 0 then Btn := mbExtra2 else Exit; if (csCaptureMouse in ControlStyle) and (Btn in CaptureMouseButtons) then begin {$IFDEF VerboseMouseCapture} DebugLn('TControl.WMMButtonQuadClk ',Name,':',ClassName); {$ENDIF} MouseCapture := True; end; DoMouseDown(Message, Btn, [ssQuad]); end; {------------------------------------------------------------------------------ Method: TControl.WMLButtonUp Params: Message Returns: Nothing Mouse event handler ------------------------------------------------------------------------------} procedure TControl.WMLButtonUp(var Message: TLMLButtonUp); begin //DebugLn('TControl.WMLButtonUp A ',Name,':',ClassName,' csCaptureMouse=',DbgS(csCaptureMouse in ControlStyle),' csClicked=',DbgS(csClicked in ControlState)); if (csCaptureMouse in ControlStyle) and (mbLeft in CaptureMouseButtons) then begin {$IFDEF VerboseMouseCapture} DebugLn('TControl.WMLButtonUp ',Name,':',ClassName); {$ENDIF} MouseCapture := False; end; if csClicked in ControlState then begin Exclude(FControlState, csClicked); //DebugLn('TControl.WMLButtonUp B ',dbgs(ClientRect.Left),',',dbgs(ClientRect.Top),',',dbgs(ClientRect.Right),',',dbgs(ClientRect.Bottom),' ',dbgs(Message.Pos.X),',',dbgs(Message.Pos.Y)); if PtInRect(ClientRect, GetMousePosFromMessage(Message.Pos)) then begin //DebugLn('TControl.WMLButtonUp C'); Click; end; end; DoMouseUp(Message, mbLeft); //DebugLn('TControl.WMLButtonUp END'); end; {------------------------------------------------------------------------------ Method: TControl.WMRButtonUp Params: Message Returns: Nothing Mouse event handler ------------------------------------------------------------------------------} procedure TControl.WMRButtonUp(var Message: TLMRButtonUp); begin if (csCaptureMouse in ControlStyle) and (mbRight in CaptureMouseButtons) then begin {$IFDEF VerboseMouseCapture} DebugLn('TControl.WMRButtonUp ',Name,':',ClassName); {$ENDIF} MouseCapture := False; end; //MouseUp event is independent of return values of contextmenu DoMouseUp(Message, mbRight); end; {------------------------------------------------------------------------------ Method: TControl.WMMButtonUp Params: Message Returns: Nothing Mouse event handler ------------------------------------------------------------------------------} procedure TControl.WMMButtonUp(var Message: TLMMButtonUp); begin if (csCaptureMouse in ControlStyle) and (mbMiddle in CaptureMouseButtons) then begin {$IFDEF VerboseMouseCapture} DebugLn('TControl.WMMButtonUp ',Name,':',ClassName); {$ENDIF} MouseCapture := False; end; DoMouseUp(Message, mbMiddle); end; procedure TControl.WMXButtonUp(var Message: TLMXButtonUp); var Btn: TMouseButton; begin if (Message.Keys and MK_XBUTTON1) <> 0 then Btn := mbExtra1 else if (Message.Keys and MK_XBUTTON2) <> 0 then Btn := mbExtra2 else Exit; if (csCaptureMouse in ControlStyle) and (Btn in CaptureMouseButtons) then begin {$IFDEF VerboseMouseCapture} DebugLn('TControl.WMMButtonUp ',Name,':',ClassName); {$ENDIF} MouseCapture := False; end; DoMouseUp(Message, Btn); end; {------------------------------------------------------------------------------ Method: TControl.WMMouseWheel Params: Msg: The message Returns: nothing event handler. ------------------------------------------------------------------------------} procedure TControl.WMMouseWheel(var Message: TLMMouseEvent); var MousePos: TPoint; lState: TShiftState; SP: TSmallPoint; begin SP.X := Message.X; // cannot use SmallPoint() here due to FPC inconsistency in Classes.TSmallPoint<>Types.TSmallPoint on Linux SP.Y := Message.Y; MousePos := GetMousePosFromMessage(SP); lState := Message.State - [ssCaps, ssNum, ssScroll]; // Remove unreliable states, see http://bugs.freepascal.org/view.php?id=20065 if DoMouseWheel(lState, Message.WheelDelta, MousePos) then Message.Result := 1 // handled, skip further handling by interface else inherited; end; procedure TControl.WMMouseHWheel(var Message: TLMMouseEvent); var MousePos: TPoint; lState: TShiftState; SP: TSmallPoint; begin SP.X := Message.X; // cannot use SmallPoint() here due to FPC inconsistency in Classes.TSmallPoint<>Types.TSmallPoint on Linux SP.Y := Message.Y; MousePos := GetMousePosFromMessage(SP); lState := Message.State - [ssCaps, ssNum, ssScroll]; // Remove unreliable states, see http://bugs.freepascal.org/view.php?id=20065 if DoMouseWheelHorz(lState, Message.WheelDelta, MousePos) then Message.Result := 1 // handled, skip further handling by interface else inherited; end; {------------------------------------------------------------------------------ TControl Click ------------------------------------------------------------------------------} procedure TControl.Click; function OnClickIsActionExecute: boolean; begin Result:=false; if Action=nil then exit; if not Assigned(Action.OnExecute) then exit; if not Assigned(FOnClick) then exit; Result:=SameMethod(TMethod(FOnClick),TMethod(Action.OnExecute)); end; var CallAction: Boolean; begin //DebugLn(['TControl.Click ',DbgSName(Self)]); CallAction:=(not (csDesigning in ComponentState)) and (ActionLink <> nil); // first call our own OnClick if it differs from Action.OnExecute if Assigned(FOnClick) and ((not CallAction) or (not OnClickIsActionExecute)) then FOnClick(Self); // then trigger the Action if CallAction then ActionLink.Execute(Self); end; {------------------------------------------------------------------------------ TControl DialogChar Do something useful with accelerators etc. ------------------------------------------------------------------------------} function TControl.DialogChar(var Message: TLMKey): Boolean; begin Result := False; end; procedure TControl.UpdateMouseCursor(X, Y: Integer); begin //DebugLn(['TControl.UpdateMouseCursor ',DbgSName(Self)]); if csDesigning in ComponentState then Exit; if Screen.RealCursor <> crDefault then Exit; SetTempCursor(Cursor); end; {------------------------------------------------------------------------------ function TControl.CheckChildClassAllowed(ChildClass: TClass; ExceptionOnInvalid: boolean): boolean; Checks if this control can be the parent of a control of class ChildClass. ------------------------------------------------------------------------------} function TControl.CheckChildClassAllowed(ChildClass: TClass; ExceptionOnInvalid: Boolean): Boolean; begin Result := ChildClassAllowed(ChildClass); if (not Result) and ExceptionOnInvalid then raise EInvalidOperation.CreateFmt(rsControlClassCantContainChildClass, [ClassName, ChildClass.ClassName]); end; {------------------------------------------------------------------------------ procedure TControl.CheckNewParent(AParent: TWinControl); Checks if this control can be the child of AParent. This check is executed in SetParent. ------------------------------------------------------------------------------} procedure TControl.CheckNewParent(AParent: TWinControl); begin if (AParent <> nil) then AParent.CheckChildClassAllowed(ClassType, True); if AParent = Self then raise EInvalidOperation.Create(rsAControlCanNotHaveItselfAsParent); end; {------------------------------------------------------------------------------ TControl SetAutoSize ------------------------------------------------------------------------------} procedure TControl.SetAutoSize(Value: Boolean); begin If AutoSize <> Value then begin FAutoSize := Value; //debugln('TControl.SetAutoSize ',DbgSName(Self)); if FAutoSize then AdjustSize; end; end; {------------------------------------------------------------------------------ TControl DoAutoSize IMPORTANT: Many Delphi controls override this method and many call this method directly after setting some properties. During handle creation not all interfaces can create complete Device Contexts which are needed to calculate things like text size. That's why you should always call AdjustSize instead of DoAutoSize. ------------------------------------------------------------------------------} procedure TControl.DoAutoSize; var PreferredWidth: integer; PreferredHeight: integer; ResizeWidth: Boolean; ResizeHeight: Boolean; begin // handled by TWinControl, or other descendants ResizeWidth:=not WidthIsAnchored; ResizeHeight:=not HeightIsAnchored; if ResizeWidth or ResizeHeight then begin PreferredWidth:=0; PreferredHeight:=0; GetPreferredSize(PreferredWidth,PreferredHeight); if (not ResizeWidth) or (PreferredWidth<=0) then PreferredWidth:=Width; if (not ResizeHeight) or (PreferredHeight<=0) then PreferredHeight:=Height; SetBoundsKeepBase(Left,Top,PreferredWidth,PreferredHeight); end; end; {------------------------------------------------------------------------------ TControl DoAllAutoSize Run DoAutoSize until done. ------------------------------------------------------------------------------} procedure TControl.DoAllAutoSize; procedure AutoSizeControl(AControl: TControl); var AWinControl: TWinControl; i: Integer; Needed: Boolean; begin if AControl.AutoSizeDelayed then exit; Needed:=cfAutoSizeNeeded in AControl.FControlFlags; //DebugLn(['TControl.DoAllAutoSize.AutoSizeControl ',DbgSName(AControl),' AutoSize=',AControl.AutoSize,' IsControlVisible=',AControl.IsControlVisible,' cfAutoSizeNeeded=',Needed]); Exclude(AControl.FControlFlags, cfAutoSizeNeeded); if not AControl.IsControlVisible then exit; if Needed and AControl.AutoSize and (not ((AControl.Parent = nil) and (csDesigning in AControl.ComponentState))) then AControl.DoAutoSize; if AControl is TWinControl then begin // recursive AWinControl := TWinControl(AControl); //DebugLn(['AutoSizeControl ',DbgSName(AWinControl)]); AWinControl.AlignControl(nil); for i := 0 to AWinControl.ControlCount - 1 do AutoSizeControl(AWinControl.Controls[i]); end; end; function CallAllOnResize(AControl: TControl): boolean; // The OnResize event is called for Delphi compatibility after child resizes. // Call all OnResize events so they will hopefully only invoke one more // loop, instead of one per OnResize. var AWinControl: TWinControl; i: Integer; begin if AControl = nil then Exit(True); Result := False; if AControl is TWinControl then begin AWinControl := TWinControl(AControl); for i := 0 to AWinControl.ControlCount - 1 do if AWinControl.Controls[i].IsControlVisible and not CallAllOnResize(AWinControl.Controls[i]) then exit; end; {$IFDEF VerboseOnResize} debugln(['TControl.DoAllAutoSize ',DbgSName(AControl),' calling Resize ...']); {$ENDIF} AControl.Resize; Result := True; end; var i: Integer; begin if Parent <> nil then raise EInvalidOperation.Create('TControl.DoAllAutoSize Parent <> nil'); if AutoSizingAll then exit; FAutoSizingAll := True; if not (Self is TWinControl) then exit; {$IFDEF VerboseAllAutoSize} DebugLn(['TControl.DoAllAutoSize START ',DbgSName(Self)]); {$ENDIF} //writeln(GetStackTrace(true)); try i:=0; while (not AutoSizeDelayed) and (cfAutoSizeNeeded in FControlFlags) do begin {$IFDEF VerboseAllAutoSize} DebugLn(['TControl.DoAllAutoSize LOOP ',DbgSName(Self),' ',dbgs(BoundsRect)]); {$ENDIF} AutoSizeControl(Self); if not (cfAutoSizeNeeded in FControlFlags) then CallAllOnResize(Self); inc(i); if i=1000 then Include(FControlFlags,cfKillChangeBounds); if i=2000 then Include(FControlFlags,cfKillInvalidatePreferredSize); if i=3000 then Include(FControlFlags,cfKillAdjustSize); end; finally FControlFlags:=FControlFlags-[cfKillChangeBounds, cfKillInvalidatePreferredSize,cfKillAdjustSize]; FAutoSizingAll := False; end; {$IFDEF VerboseAllAutoSize} DebugLn(['TControl.DoAllAutoSize END ',DbgSName(Self),' ',dbgs(BoundsRect)]); {$ENDIF} end; procedure TControl.DoAutoAdjustLayout(const AMode: TLayoutAdjustmentPolicy; const AXProportion, AYProportion: Double); var AAWidth, AAHeight: Boolean; NewLeft, NewTop, NewWidth, NewHeight, NewRight, NewBottom, OldWidth, OldHeight, NewBaseLeft, NewBaseTop, NewBaseWidth, NewBaseHeight: Integer; begin // Apply the changes if AMode in [lapAutoAdjustWithoutHorizontalScrolling, lapAutoAdjustForDPI] then begin // Dimensions AAWidth := False; AAHeight := False; NewLeft := Left; NewTop := Top; NewWidth := Width; NewHeight := Height; OldWidth := Width; OldHeight := Height; ShouldAutoAdjust(AAWidth, AAHeight); AAWidth := AAWidth and (Align in [alNone, alLeft, alRight]) and not((akLeft in Anchors) and (akRight in Anchors)); AAHeight := AAHeight and (Align in [alNone, alTop, alBottom]) and not((akTop in Anchors) and (akBottom in Anchors)); if (Align=alNone) and (akLeft in Anchors) then NewLeft := Round(NewLeft * AXProportion); if (Align=alNone) and (akRight in Anchors) and (Parent<>nil) and (AnchorSideRight.Control=nil) then begin if not(akLeft in Anchors) then begin NewRight := Round((Parent.ClientWidth-NewLeft-OldWidth) * AXProportion); NewLeft := Parent.ClientWidth-NewRight-OldWidth end else begin NewRight := Round((Parent.ClientWidth-Left-OldWidth) * AXProportion); NewWidth := Parent.ClientWidth-NewLeft-NewRight; end; end; if (Align=alNone) and (akTop in Anchors) then NewTop := Round(NewTop * AYProportion); if (Align=alNone) and (akBottom in Anchors) and (Parent<>nil) and (AnchorSideBottom.Control=nil) then begin if not(akTop in Anchors) then begin NewBottom := Round((Parent.ClientHeight-NewTop-OldHeight) * AYProportion); NewTop := Parent.ClientHeight-NewBottom-OldHeight end else begin NewBottom := Round((Parent.ClientHeight-Top-OldHeight) * AYProportion); NewHeight := Parent.ClientHeight-NewTop-NewBottom; end; end; if AAWidth then NewWidth := Round(Width * AXProportion); if AAHeight then NewHeight := Round(Height * AYProportion); BorderSpacing.AutoAdjustLayout(AXProportion, AYProportion); Constraints.AutoAdjustLayout(AXProportion, AYProportion); NewBaseLeft := NewLeft; NewBaseTop := NewTop; NewBaseWidth := NewWidth; NewBaseHeight := NewHeight; NewWidth := Constraints.MinMaxWidth(NewWidth); NewHeight := Constraints.MinMaxHeight(NewHeight); if AAWidth or (NewBaseWidth<>NewWidth) then begin if akRight in Anchors then NewLeft := NewLeft-NewWidth+OldWidth; end; if AAHeight or (NewBaseHeight<>NewHeight) then begin if akBottom in Anchors then NewTop := NewTop-NewHeight+OldHeight; end; if AAWidth and (akRight in Anchors) then NewBaseLeft := NewBaseLeft-NewBaseWidth+OldWidth; if AAHeight and (akBottom in Anchors) then NewBaseTop := NewBaseTop-NewBaseHeight+OldHeight; FBaseBounds.Left:=NewBaseLeft; FBaseBounds.Top:=NewBaseTop; FBaseBounds.Right:=NewBaseLeft+NewBaseWidth; FBaseBounds.Bottom:=NewBaseTop+NewBaseHeight; if Parent<>nil then begin FBaseParentClientSize.cx:=Parent.ClientWidth; FBaseParentClientSize.cy:=Parent.ClientHeight; end; SetBoundsKeepBase(NewLeft, NewTop, NewWidth, NewHeight); end; end; procedure TControl.AnchorSideChanged(TheAnchorSide: TAnchorSide); begin //debugln('TControl.AnchorSideChanged ',DbgSName(Self)); RequestAlign; end; procedure TControl.ForeignAnchorSideChanged(TheAnchorSide: TAnchorSide; Operation: TAnchorSideChangeOperation); var Side: TAnchorKind; AControl: TControl; begin AControl:=TheAnchorSide.Owner; //debugln('TControl.ForeignAnchorSideChanged A Self=',DbgSName(Self),' TheAnchorSide.Owner=',DbgSName(TheAnchorSide.Owner),' Operation=',dbgs(ord(Operation)),' Anchor=',dbgs(TheAnchorSide.Kind)); if TheAnchorSide.Control=Self then begin if FAnchoredControls=nil then FAnchoredControls:=TFPList.Create; if FAnchoredControls.IndexOf(AControl)<0 then FAnchoredControls.Add(AControl); end else if FAnchoredControls<>nil then begin if TheAnchorSide.Owner<>nil then begin for Side:=Low(TAnchorKind) to High(TAnchorKind) do begin if (AControl.FAnchorSides[Side]<>nil) and (AControl.FAnchorSides[Side].Control=Self) then begin // still anchored exit; end; end; end; FAnchoredControls.Remove(AControl); end; end; function TControl.AutoSizePhases: TControlAutoSizePhases; begin if Parent<>nil then Result:=Parent.AutoSizePhases else Result:=[]; end; {------------------------------------------------------------------------------ function TControl.AutoSizeDelayed: boolean; Returns true, if the DoAutoSize should skip now, because not all parameters needed to calculate the AutoSize bounds are loaded or initialized. ------------------------------------------------------------------------------} function TControl.AutoSizeDelayed: Boolean; begin Result:=(FAutoSizingLockCount>0) // no autosize during loading or destruction or ([csLoading,csDestroying]*ComponentState<>[]) or (cfLoading in FControlFlags) // no autosize for invisible controls or (not IsControlVisible) // if there is no parent, then this control is not visible // (TWinControl and TCustomForm override this) or AutoSizeDelayedHandle // if there is a parent, ask it or ((Parent<>nil) and Parent.AutoSizeDelayed); {$IFDEF VerboseCanAutoSize} if Result {and AutoSize} then begin DbgOut('TControl.AutoSizeDelayed Self='+DbgSName(Self)+' '); if FAutoSizingLockCount>0 then debugln('FAutoSizingLockCount=',dbgs(FAutoSizingLockCount)) else if csLoading in ComponentState then debugln('csLoading') else if csDestroying in ComponentState then debugln('csDestroying') else if cfLoading in FControlFlags then debugln('cfLoading') else if not IsControlVisible then debugln('not IsControlVisible') else if AutoSizeDelayedHandle then debugln('AutoSizeDelayedHandle') else if ((Parent<>nil) and Parent.AutoSizeDelayed) then debugln('Parent.AutoSizeDelayed') else debugln('?'); end; {$ENDIF} end; function TControl.AutoSizeDelayedReport: string; begin if (FAutoSizingLockCount>0) then Result:='FAutoSizingLockCount='+dbgs(FAutoSizingLockCount) else if csLoading in ComponentState then Result:='csLoading' else if csDestroying in ComponentState then Result:='csDestroying' else if cfLoading in FControlFlags then Result:='cfLoading' else if IsControlVisible then Result:='not IsControlVisible' else if AutoSizeDelayedHandle then Result:='AutoSizeDelayedHandle' else if Parent<>nil then Result:=Parent.AutoSizeDelayedReport else Result:='?'; end; {------------------------------------------------------------------------------ TControl AutoSizeDelayedHandle Returns true if AutoSize should be skipped / delayed because of its handle. A TControl does not have a handle, so it needs a parent. ------------------------------------------------------------------------------} function TControl.AutoSizeDelayedHandle: Boolean; begin Result := Parent = nil; end; {------------------------------------------------------------------------------ TControl SetBoundsRect ------------------------------------------------------------------------------} procedure TControl.SetBoundsRect(const ARect: TRect); begin {$IFDEF CHECK_POSITION} if CheckPosition(Self) then DebugLn('[TControl.SetBoundsRect] ',Name,':',ClassName); {$ENDIF} SetBounds(ARect.Left, ARect.Top, Max(ARect.Right - ARect.Left, 0), Max(ARect.Bottom - ARect.Top, 0)); end; procedure TControl.SetBoundsRectForNewParent(const AValue: TRect); begin Include(FControlFlags,cfBoundsRectForNewParentValid); FBoundsRectForNewParent:=AValue; end; {------------------------------------------------------------------------------ TControl SetClientHeight ------------------------------------------------------------------------------} procedure TControl.SetClientHeight(Value: Integer); begin if csLoading in ComponentState then begin FLoadedClientSize.cy:=Value; Include(FControlFlags,cfClientHeightLoaded); end else begin // during loading the ClientHeight is not used to set the Height of the // control, but only to restore autosizing. For example Anchors=[akBottom] // needs ClientHeight. SetClientSize(Point(ClientWidth, Value)); end; end; {------------------------------------------------------------------------------ TControl SetClientSize ------------------------------------------------------------------------------} procedure TControl.SetClientSize(const Value: TPoint); var Client: TRect; begin Client := GetClientRect; SetBounds(FLeft, FTop, Width - Client.Right + Value.X, Height - Client.Bottom + Value.Y); end; {------------------------------------------------------------------------------ TControl SetClientWidth ------------------------------------------------------------------------------} procedure TControl.SetClientWidth(Value: Integer); begin if csLoading in ComponentState then begin FLoadedClientSize.cx:=Value; Include(FControlFlags,cfClientWidthLoaded); end else begin // during loading the ClientWidth is not used to set the Width of the // control, but only to restore autosizing. For example Anchors=[akRight] // needs ClientWidth. SetClientSize(Point(Value, ClientHeight)); end; end; {------------------------------------------------------------------------------ TControl SetTempCursor ------------------------------------------------------------------------------} procedure TControl.SetTempCursor(Value: TCursor); begin if Parent<>nil then Parent.SetTempCursor(Value); end; procedure TControl.ActiveDefaultControlChanged(NewControl: TControl); begin end; procedure TControl.UpdateRolesForForm; begin // called by the form when the "role" controls DefaultControl or CancelControl // has changed end; {------------------------------------------------------------------------------ TControl SetCursor ------------------------------------------------------------------------------} procedure TControl.SetCursor(Value: TCursor); begin if FCursor <> Value then begin FCursor := Value; Perform(CM_CURSORCHANGED, 0, 0); end; end; procedure TControl.SetDragCursor(const AValue: TCursor); begin if FDragCursor=AValue then exit; FDragCursor:=AValue; end; procedure TControl.SetFont(Value: TFont); begin if FFont.IsEqual(Value) then exit; FFont.Assign(Value); Invalidate; end; {------------------------------------------------------------------------------ TControl SetEnabled ------------------------------------------------------------------------------} procedure TControl.SetEnabled(Value: Boolean); begin if FEnabled <> Value then begin EnabledChanging; FEnabled := Value; Perform(CM_ENABLEDCHANGED, 0, 0); EnabledChanged; end; end; {------------------------------------------------------------------------------ TControl SetMouseCapture ------------------------------------------------------------------------------} procedure TControl.SetMouseCapture(Value : Boolean); begin if (MouseCapture <> Value) or (not Value and (CaptureControl=Self)) then begin {$IFDEF VerboseMouseCapture} DebugLn('TControl.SetMouseCapture ',DbgSName(Self),' NewValue=',DbgS(Value)); {$ENDIF} if Value then SetCaptureControl(Self) else SetCaptureControl(nil); end end; {------------------------------------------------------------------------------ Method: TControl.SetHint Params: Value: the text of the hint to be set Returns: Nothing Sets the hint text of a control ------------------------------------------------------------------------------} procedure TControl.SetHint(const Value: TTranslateString); begin if FHint = Value then exit; FHint := Value; end; {------------------------------------------------------------------------------ TControl SetName ------------------------------------------------------------------------------} procedure TControl.SetName(const Value: TComponentName); var ChangeText: Boolean; begin if Name=Value then exit; ChangeText := (csSetCaption in ControlStyle) and not (csLoading in ComponentState) and (Name = Text) and ((Owner = nil) or not (Owner is TControl) or not (csLoading in Owner.ComponentState)); inherited SetName(Value); if ChangeText then Text := Value; end; {------------------------------------------------------------------------------ TControl Show ------------------------------------------------------------------------------} procedure TControl.Show; begin if Parent <> nil then Parent.ShowControl(Self); // do not switch the visible flag in design mode if not (csDesigning in ComponentState) or (csNoDesignVisible in ControlStyle) then Visible := True; end; {------------------------------------------------------------------------------ TControl Notification ------------------------------------------------------------------------------} procedure TControl.Notification(AComponent: TComponent; Operation: TOperation); var Kind: TAnchorKind; begin inherited Notification(AComponent, Operation); if Operation = opRemove then begin if AComponent = PopupMenu then PopupMenu := nil else if AComponent = Action then Action := nil else if AComponent = FHostDockSite then FHostDockSite := nil; //debugln('TControl.Notification A ',DbgSName(Self),' ',DbgSName(AComponent)); for Kind := Low(TAnchorKind) to High(TAnchorKind) do begin if (FAnchorSides[Kind] <> nil) and (FAnchorSides[Kind].Control = AComponent) then FAnchorSides[Kind].FControl := nil; end; end; end; procedure TControl.DoFloatMsg(ADockSource: TDragDockObject); var P: TPoint; FloatHost: TWinControl; R: TRect; begin //DebugLn(['TControl.DoFloatMsg ',DbgSName(Self),' Floating=',Floating]); if Floating and (Parent <> nil) then begin P := Parent.ClientToScreen(Point(Left, Top)); R := ADockSource.DockRect; Parent.BoundsRect := Bounds(R.Left + Parent.Left - P.X, R.Top + Parent.Top - P.Y, R.Right - R.Left + Parent.Width - Width, R.Bottom - R.Top + Parent.Height - Height); end else begin FloatHost := CreateFloatingDockSite(ADockSource.DockRect); if FloatHost <> nil then begin FloatHost.Caption := FloatHost.GetDockCaption(Self); ADockSource.DragTarget := FloatHost; FloatHost.Show; end; end; end; {------------------------------------------------------------------------------ TControl GetText ------------------------------------------------------------------------------} function TControl.GetText: TCaption; var len: Integer; GetTextMethod: TMethod; begin // Check if GetTextBuf is overridden, otherwise we can call RealGetText directly Assert(Assigned(@Self.GetTextBuf), 'TControl.GetText: GetTextBuf Method is Nil'); GetTextMethod := TMethod(@Self.GetTextBuf); if GetTextMethod.Code = Pointer(@TControl.GetTextBuf) then begin Result := RealGetText; end else begin // Bummer, we have to do it the compatible way. DebugLn('Note: GetTextBuf is overridden for: ', Classname); len := GetTextLen; if len = 0 then begin Result := ''; end else begin SetLength(Result, len+1); // make sure there is room for the extra #0 FillChar(Result[1], len, #0); len := GetTextBuf(@Result[1], len+1); SetLength(Result, len); end; end; end; {------------------------------------------------------------------------------ TControl RealGetText ------------------------------------------------------------------------------} function TControl.RealGetText: TCaption; begin Result := FCaption; end; function TControl.GetTextLen: Integer; begin Result := Length(FCaption); end; function TControl.GetAction: TBasicAction; begin if ActionLink <> nil then Result := ActionLink.Action else Result := nil; end; function TControl.GetActionLinkClass: TControlActionLinkClass; begin Result := TControlActionLink; end; function TControl.IsClientHeightStored: Boolean; begin Result:=false; end; function TControl.IsClientWidthStored: Boolean; begin Result:=false; end; function TControl.WidthIsAnchored: Boolean; var CurAnchors: TAnchors; begin if Align=alCustom then exit(true); // width depends on parent CurAnchors:=Anchors; if Align<>alNone then CurAnchors:=CurAnchors+AnchorAlign[Align]; Result:=(CurAnchors*[akLeft,akRight]=[akLeft,akRight]); if not Result then begin if Parent<>nil then Result:=Parent.ChildSizing.Layout<>cclNone; end; end; function TControl.HeightIsAnchored: Boolean; var CurAnchors: TAnchors; begin if Align=alCustom then exit(true); // height depends on parent CurAnchors:=Anchors; if Align<>alNone then CurAnchors:=CurAnchors+AnchorAlign[Align]; Result:=(CurAnchors*[akTop,akBottom]=[akTop,akBottom]); if not Result then begin if Parent<>nil then Result:=Parent.ChildSizing.Layout<>cclNone; end; end; procedure TControl.WMCancelMode(var Message: TLMessage); begin SetCaptureControl(nil); end; function TControl.IsEnabledStored: Boolean; begin Result := (ActionLink = nil) or not ActionLink.IsEnabledLinked; end; function TControl.IsFontStored: Boolean; begin Result := not ParentFont; end; function TControl.IsHintStored: Boolean; begin Result := (ActionLink = nil) or not ActionLink.IsHintLinked; end; {------------------------------------------------------------------------------ TControl InvalidateControl ------------------------------------------------------------------------------} procedure TControl.InvalidateControl(CtrlIsVisible, CtrlIsOpaque: Boolean); var Rect: TRect; function BackgroundClipped: Boolean; var R: TRect; List: TFPList; I: Integer; C: TControl; begin Result := True; List := FParent.FControls; if List<>nil then begin I := List.IndexOf(Self); while I > 0 do begin Dec(I); C := TControl(List[I]); if not (C is TWinControl) then with C do if IsControlVisible and (csOpaque in ControlStyle) then begin IntersectRect(R, Rect, BoundsRect); if EqualRect(R, Rect) then Exit; end; end; end; Result := False; end; begin //DebugLn(['TControl.InvalidateControl ',DbgSName(Self)]); if (Parent=nil) or (not Parent.HandleAllocated) or ([csLoading,csDestroying]*Parent.ComponentState<>[]) then exit; // Note: it should invalidate, when this control is loaded/destroyed, but parent not if (CtrlIsVisible or ((csDesigning in ComponentState) and not (csNoDesignVisible in ControlStyle))) then begin Rect := BoundsRect; InvalidateRect(Parent.Handle, @Rect, not (CtrlIsOpaque or (csOpaque in Parent.ControlStyle) or BackgroundClipped)); end; end; {------------------------------------------------------------------------------ procedure TControl.InvalidateControl(CtrlIsVisible, CtrlIsOpaque, IgnoreWinControls: Boolean); ------------------------------------------------------------------------------} procedure TControl.InvalidateControl(CtrlIsVisible, CtrlIsOpaque, IgnoreWinControls: Boolean); begin //DebugLn(['TControl.InvalidateControl ',DbgSName(Self)]); if IgnoreWinControls and (Self is TWinControl) then exit; InvalidateControl(CtrlIsVisible,CtrlIsOpaque); end; {------------------------------------------------------------------------------ TControl Refresh ------------------------------------------------------------------------------} procedure TControl.Refresh; begin Repaint; end; {------------------------------------------------------------------------------ TControl Repaint ------------------------------------------------------------------------------} procedure TControl.Repaint; var DC: HDC; begin if (Parent=nil) or (not Parent.HandleAllocated) or (csDestroying in ComponentState) then exit; if IsVisible then if csOpaque in ControlStyle then begin {$IFDEF VerboseDsgnPaintMsg} if csDesigning in ComponentState then DebugLn('TControl.Repaint A ',Name,':',ClassName); {$ENDIF} DC := GetDC(Parent.Handle); try IntersectClipRect(DC, Left, Top, Left + Width, Top + Height); Parent.PaintControls(DC, Self); finally ReleaseDC(Parent.Handle, DC); end; end else begin Invalidate; Update; end; end; {------------------------------------------------------------------------------ TControl Resize Calls OnResize -------------------------------------------------------------------------------} procedure TControl.Resize; begin if ([csLoading,csDestroying]*ComponentState<>[]) then exit; if AutoSizeDelayed then exit; if (FLastResizeWidth<>Width) or (FLastResizeHeight<>Height) or (FLastResizeClientWidth<>ClientWidth) or (FLastResizeClientHeight<>ClientHeight) then begin {if CompareText('SubPanel',Name)=0 then begin DebugLn(['[TControl.Resize] ',Name,':',ClassName, ' Last=',FLastResizeWidth,',',FLastResizeHeight, ' LastClient=',FLastResizeClientWidth,',',FLastResizeClientHeight, ' New=',Width,',',Height, ' NewClient=',ClientWidth,',',ClientHeight]); DumpStack; end;} FLastResizeWidth:=Width; FLastResizeHeight:=Height; FLastResizeClientWidth:=ClientWidth; FLastResizeClientHeight:=ClientHeight; DoOnResize; end; end; procedure TControl.Loaded; function FindLoadingControl(AControl: TControl): TControl; var i: Integer; AWinControl: TWinControl; begin if csLoading in AControl.ComponentState then exit(AControl); if AControl is TWinControl then begin AWinControl:=TWinControl(AControl); for i:=0 to AWinControl.ControlCount-1 do begin Result:=FindLoadingControl(AWinControl.Controls[i]); if Result<>nil then exit; end; end; Result:=nil; end; procedure ClearLoadingFlags(AControl: TControl); var i: Integer; AWinControl: TWinControl; begin Exclude(AControl.FControlFlags,cfLoading); if AControl is TWinControl then begin AWinControl:=TWinControl(AControl); for i:=0 to AWinControl.ControlCount-1 do ClearLoadingFlags(AWinControl.Controls[i]); end; end; procedure CheckLoading(AControl: TControl); var TopParent: TControl; begin TopParent:=AControl; while (TopParent.Parent<>nil) and (cfLoading in TopParent.Parent.FControlFlags) do TopParent:=TopParent.Parent; if FindLoadingControl(TopParent)<>nil then exit; // all components on the form finished loading ClearLoadingFlags(TopParent); // call LoadedAll DisableAutoSizing{$IFDEF DebugDisableAutoSizing}('TControl.Loaded.CheckLoading'){$ENDIF}; try AControl.LoadedAll; finally EnableAutoSizing{$IFDEF DebugDisableAutoSizing}('TControl.Loaded.CheckLoading'){$ENDIF}; end; end; var UseClientWidthForWidth: boolean; UseClientHeightForHeight: boolean; NewWidth: LongInt; NewHeight: LongInt; begin inherited Loaded; {DebugLn(['TControl.Loaded A ',DbgSName(Self), ' LoadedClientWidth=',cfClientWidthLoaded in FControlFlags,'=',FLoadedClientSize.X, ' LoadedClientHeight=',cfClientHeightLoaded in FControlFlags,'=',FLoadedClientSize.Y, ' LoadedBounds=',DbgS(FReadBounds), '']);} UseClientWidthForWidth:=(not (cfWidthLoaded in FControlFlags)) and (cfClientWidthLoaded in FControlFlags); UseClientHeightForHeight:=(not (cfHeightLoaded in FControlFlags)) and (cfClientHeightLoaded in FControlFlags); if UseClientWidthForWidth or UseClientHeightForHeight then begin //DebugLn(['TControl.Loaded ',DbgSName(Self),' Note: Width and/or Height were not set during loading, using ClientWidth/ClientHeight']); NewWidth:=Width; if UseClientWidthForWidth then NewWidth:=FLoadedClientSize.cx; NewHeight:=Height; if UseClientHeightForHeight then NewHeight:=FLoadedClientSize.cy; SetBoundsKeepBase(Left,Top,NewWidth,NewHeight); end; if Assigned(Parent) then begin if ParentColor then begin Color := Parent.Color; FParentColor := True; end; if ParentFont then begin Font := Parent.Font; FParentFont := True; end; if ParentBidiMode then begin BiDiMode := Parent.BiDiMode; FParentBidiMode := True; end; if ParentShowHint then begin ShowHint := Parent.ShowHint; FParentShowHint := True; end; end; UpdateBaseBounds(true,true,true); // store designed width and height for undocking FUndockHeight := Height; FUndockWidth := Width; if Action <> nil then ActionChange(Action, True); CheckLoading(Self); end; procedure TControl.LoadedAll; begin AdjustSize; {$IFDEF VerboseOnResize} debugln(['TControl.LoadedAll ',DbgSName(Self),' calling Resize ...']); {$ENDIF} Resize; CheckOnChangeBounds; end; {------------------------------------------------------------------------------ procedure TControl.DefineProperties(Filer: TFiler); ------------------------------------------------------------------------------} procedure TControl.DefineProperties(Filer: TFiler); begin // Optimiziation: // do not call inherited: TComponent only defines 'Left' and 'Top' and // TControl has them as regular properties. end; {------------------------------------------------------------------------------ procedure TControl.AssignTo(Dest: TPersistent); ------------------------------------------------------------------------------} procedure TControl.AssignTo(Dest: TPersistent); begin if Dest is TCustomAction then with TCustomAction(Dest) do begin Enabled := Self.Enabled; Hint := Self.Hint; Caption := Self.Caption; Visible := Self.Visible; OnExecute := Self.OnClick; HelpContext := Self.HelpContext; HelpKeyword := Self.HelpKeyword; HelpType := Self.HelpType; end else inherited AssignTo(Dest); end; procedure TControl.ReadState(Reader: TReader); begin Include(FControlFlags, cfLoading); DisableAutoSizing{$IFDEF DebugDisableAutoSizing}('TControl.ReadState'){$ENDIF}; try Include(FControlState, csReadingState); inherited ReadState(Reader); finally Exclude(FControlState, csReadingState); EnableAutoSizing{$IFDEF DebugDisableAutoSizing}('TControl.ReadState'){$ENDIF}; end; end; procedure TControl.FormEndUpdated; // called when control is on a form and EndFormUpdate reached 0 // it is called recursively begin end; {------------------------------------------------------------------------------ TControl SetBounds ------------------------------------------------------------------------------} procedure TControl.SetBounds(aLeft, aTop, aWidth, aHeight: Integer); begin ChangeBounds(ALeft, ATop, AWidth, AHeight, false); end; {------------------------------------------------------------------------------ TControl SetConstraints ------------------------------------------------------------------------------} procedure TControl.SetConstraints(const Value : TSizeConstraints); begin FConstraints.Assign(Value); end; procedure TControl.SetDesktopFont(const AValue: Boolean); begin if FDesktopFont <> AValue then begin FDesktopFont := AValue; Perform(CM_SYSFONTCHANGED, 0, 0); end; end; {------------------------------------------------------------------------------ TControl SetAlign ------------------------------------------------------------------------------} procedure TControl.SetAlign(Value: TAlign); var OldAlign: TAlign; a: TAnchorKind; OldBaseBounds: TRect; begin if FAlign = Value then exit; //DebugLn(['TControl.SetAlign ',DbgSName(Self),' Old=',DbgS(FAlign),' New=',DbgS(Value),' ',Anchors<>AnchorAlign[FAlign]]); DisableAutoSizing{$IFDEF DebugDisableAutoSizing}('TControl.SetAlign'){$ENDIF}; try OldBaseBounds:=BaseBounds; OldAlign := FAlign; FAlign := Value; if (not (csLoading in ComponentState)) and (Align in [alLeft,alTop,alRight,alBottom,alClient]) then begin // Align for alLeft,alTop,alRight,alBottom,alClient takes precedence // over AnchorSides => clean up for a:=low(TAnchorKind) to High(TAnchorKind) do begin if not (a in AnchorAlign[FAlign]) then continue; AnchorSide[a].Control:=nil; AnchorSide[a].Side:=asrTop; end; end; // Notes: // - if anchors had default values then change them to new default values // This is done for Delphi compatibility. // - Anchors are not stored if they are AnchorAlign[Align] if (Anchors = AnchorAlign[OldAlign]) and (Anchors <> AnchorAlign[FAlign]) then Anchors := AnchorAlign[FAlign]; if not (csLoading in ComponentState) then BoundsRect:=OldBaseBounds; //DebugLn(['TControl.SetAlign ',DbgSName(Self),' Cur=',DbgS(FAlign),' New=',DbgS(Value),' ',Anchors<>AnchorAlign[FAlign],' Anchors=',dbgs(Anchors)]); finally EnableAutoSizing{$IFDEF DebugDisableAutoSizing}('TControl.SetAlign'){$ENDIF}; end; end; {------------------------------------------------------------------------------ TControl SetAnchors ------------------------------------------------------------------------------} procedure TControl.SetAnchors(const AValue: TAnchors); var NewAnchors: TAnchors; a: TAnchorKind; begin if Anchors = AValue then Exit; NewAnchors:=AValue-FAnchors; FAnchors := AValue; for a:=Low(TAnchorKind) to high(TAnchorKind) do if (a in NewAnchors) and (AnchorSide[a].Side=asrCenter) then AnchorSide[a].FixCenterAnchoring; // Delphi Anchors depend on the current bounds of Self and Parent.ClientRect // => fetch current BaseBounds // for example: // during disabled autosizing: Width:=100; Anchors:=Anchors+[akRight]; UpdateAnchorRules; AdjustSize; end; {------------------------------------------------------------------------------ TControl RequestAlign Requests the parent to realign all brothers ------------------------------------------------------------------------------} procedure TControl.RequestAlign; begin AdjustSize; end; procedure TControl.UpdateBaseBounds(StoreBounds, StoreParentClientSize, UseLoadedValues: Boolean); var NewBaseBounds: TRect; NewBaseParentClientSize: TSize; begin if (csLoading in ComponentState) or (fBaseBoundsLock>0) then exit; if StoreBounds then NewBaseBounds:=BoundsRect else NewBaseBounds:=FBaseBounds; if StoreParentClientSize then begin if Parent<>nil then begin NewBaseParentClientSize:=Size(Parent.ClientWidth,Parent.ClientHeight); if UseLoadedValues then begin if cfClientWidthLoaded in Parent.FControlFlags then NewBaseParentClientSize.cx:=Parent.FLoadedClientSize.cx; if cfClientHeightLoaded in Parent.FControlFlags then NewBaseParentClientSize.cy:=Parent.FLoadedClientSize.cy; end; end else NewBaseParentClientSize:=Size(0,0); end else NewBaseParentClientSize:=FBaseParentClientSize; if (not CompareRect(@NewBaseBounds,@FBaseBounds)) or (NewBaseParentClientSize.cx<>FBaseParentClientSize.cx) or (NewBaseParentClientSize.cy<>FBaseParentClientSize.cy) then begin //if csDesigning in ComponentState then {$IFDEF CHECK_POSITION} if CheckPosition(Self) then DebugLn(['TControl.UpdateBaseBounds '+DbgSName(Self), ' OldBounds='+dbgs(FBaseBounds), ' OldParentClientSize='+dbgs(FBaseParentClientSize), ' NewBounds='+dbgs(NewBaseBounds), ' NewParentClientSize='+dbgs(NewBaseParentClientSize), '']); {$ENDIF} FBaseBounds:=NewBaseBounds; FBaseParentClientSize:=NewBaseParentClientSize; end; Include(FControlFlags,cfBaseBoundsValid); end; procedure TControl.WriteLayoutDebugReport(const Prefix: string); var a: TAnchorKind; NeedSeparator: Boolean; begin DbgOut(Prefix,'TControl.WriteLayoutDebugReport '); DbgOut(DbgSName(Self),' Bounds=',dbgs(BoundsRect)); if Align<>alNone then DbgOut(' Align=',DbgS(Align)); DbgOut(' Anchors=['); NeedSeparator:=false; for a:=Low(TAnchorKind) to High(TAnchorKind) do begin if a in Anchors then begin if NeedSeparator then DbgOut(','); DbgOut(dbgs(a)); if AnchorSide[a].Control<>nil then begin DbgOut('(',DbgSName(AnchorSide[a].Control),')'); end; NeedSeparator:=true; end; end; DbgOut(']'); DebugLn; end; procedure TControl.AutoAdjustLayout(AMode: TLayoutAdjustmentPolicy; const AFromPPI, AToPPI, AOldFormWidth, ANewFormWidth: Integer); var lXProportion, lYProportion: Double; lMode: TLayoutAdjustmentPolicy; savedParentFont: Boolean; begin // First resolve ladDefault lMode := AMode; if lMode = lapDefault then lMode := Application.LayoutAdjustmentPolicy; // X-axis adjustment proportion lXProportion := 1.0; if lMode = lapAutoAdjustWithoutHorizontalScrolling then begin if AOldFormWidth > 0 then lXProportion := ANewFormWidth / AOldFormWidth; end else if lMode = lapAutoAdjustForDPI then begin if AFromPPI > 0 then lXProportion := AToPPI / AFromPPI; end; // y-axis adjustment proportion if AFromPPI > 0 then lYProportion := AToPPI / AFromPPI else lYProportion := 1.0; DisableAutoSizing{$IFDEF DebugDisableAutoSizing}('TControl.AutoAdjustLayout'){$ENDIF}; savedParentFont := ParentFont; try if AMode in [lapAutoAdjustWithoutHorizontalScrolling, lapAutoAdjustForDPI] then ScaleFontsPPI(AToPPI, lYProportion); DoAutoAdjustLayout(lMode, lXProportion, lYProportion); finally ParentFont := savedParentFont; EnableAutoSizing{$IFDEF DebugDisableAutoSizing}('TControl.AutoAdjustLayout'){$ENDIF}; end; end; // Auto-adjust the layout of controls. procedure TControl.ShouldAutoAdjust(var AWidth, AHeight: Boolean); begin AWidth := not AutoSize; AHeight := not AutoSize; end; procedure TControl.UpdateAnchorRules; begin UpdateBaseBounds(true,true,false); end; {------------------------------------------------------------------------------ TControl SetDragmode ------------------------------------------------------------------------------} procedure TControl.SetDragMode(Value: TDragMode); begin if FDragMode = Value then exit; FDragMode := Value; end; function TControl.GetDefaultDockCaption: string; begin Result := Caption; end; {------------------------------------------------------------------------------ TControl DockTrackNoTarget ------------------------------------------------------------------------------} procedure TControl.DockTrackNoTarget(Source: TDragDockObject; X, Y: Integer); begin PositionDockRect(Source); end; {------------------------------------------------------------------------------ TControl SetLeft ------------------------------------------------------------------------------} procedure TControl.SetLeft(Value: Integer); begin {$IFDEF CHECK_POSITION} if CheckPosition(Self) then DebugLn('[TControl.SetLeft] ',Name,':',ClassName,' ',DbgS(Value)); {$ENDIF} if csLoading in ComponentState then begin inc(FReadBounds.Right, Value - FReadBounds.Left); FReadBounds.Left := Value; Include(FControlFlags, cfLeftLoaded); end; SetBounds(Value, FTop, FWidth, FHeight); end; {------------------------------------------------------------------------------ TControl SetTop ------------------------------------------------------------------------------} procedure TControl.SetTop(Value: Integer); begin {$IFDEF CHECK_POSITION} if CheckPosition(Self) then DebugLn('[TControl.SetTop] ',Name,':',ClassName,' ',Dbgs(Value)); {$ENDIF} if csLoading in ComponentState then begin inc(FReadBounds.Bottom,Value - FReadBounds.Top); FReadBounds.Top := Value; Include(FControlFlags, cfTopLoaded); end; SetBounds(FLeft, Value, FWidth, FHeight); end; {------------------------------------------------------------------------------ TControl SetWidth ------------------------------------------------------------------------------} procedure TControl.SetWidth(Value: Integer); procedure CheckDesignBounds; begin // the user changed the width if Value<0 then raise ELayoutException.CreateFmt('TWinControl.SetBounds (%s): Negative width %d not allowed.', [DbgSName(Self), Value]); if Value>=10000 then raise ELayoutException.CreateFmt('TWinControl.SetBounds (%s): Width %d not allowed.', [DbgSName(Self), Value]); end; begin {$IFDEF CHECK_POSITION} if CheckPosition(Self) then DebugLn('[TControl.SetWidth] ',Name,':',ClassName,' ',dbgs(Value)); {$ENDIF} if csLoading in ComponentState then begin FReadBounds.Right := FReadBounds.Left+Value; Include(FControlFlags, cfWidthLoaded); end; if [csDesigning, csDestroying, csLoading] * ComponentState = [csDesigning] then CheckDesignBounds; SetBounds(FLeft, FTop, Max(0, Value), FHeight); end; class procedure TControl.WSRegisterClass; const Registered : boolean = False; begin if Registered then Exit; inherited WSRegisterClass; RegisterControl; RegisterPropertyToSkip(TControl, 'AlignWithMargins', 'VCL compatibility property', ''); RegisterPropertyToSkip(TControl, 'Ctl3D', 'VCL compatibility property', ''); RegisterPropertyToSkip(TControl, 'ParentCtl3D', 'VCL compatibility property', ''); RegisterPropertyToSkip(TControl, 'IsControl', 'VCL compatibility property', ''); RegisterPropertyToSkip(TControl, 'DesignSize', 'VCL compatibility property', ''); RegisterPropertyToSkip(TControl, 'ExplicitLeft', 'VCL compatibility property', ''); RegisterPropertyToSkip(TControl, 'ExplicitHeight', 'VCL compatibility property', ''); RegisterPropertyToSkip(TControl, 'ExplicitTop', 'VCL compatibility property', ''); RegisterPropertyToSkip(TControl, 'ExplicitWidth', 'VCL compatibility property', ''); Registered := True; end; function TControl.GetCursor: TCursor; begin Result := FCursor; end; {------------------------------------------------------------------------------ TControl SetHeight ------------------------------------------------------------------------------} procedure TControl.SetHeight(Value: Integer); procedure CheckDesignBounds; begin // the user changed the height if Value<0 then raise ELayoutException.CreateFmt('TWinControl.SetHeight (%s): Negative height %d not allowed.', [DbgSName(Self), Value]); if Value>=10000 then raise ELayoutException.CreateFmt('TWinControl.SetBounds (%s): Height %d not allowed.', [DbgSName(Self), Value]); end; begin {$IFDEF CHECK_POSITION} if CheckPosition(Self) then DebugLn('[TControl.SetHeight] ',Name,':',ClassName,' ',dbgs(Value)); {$ENDIF} if csLoading in ComponentState then begin FReadBounds.Bottom := FReadBounds.Top + Value; Include(FControlFlags, cfHeightLoaded); end; if [csDesigning, csDestroying, csLoading] * ComponentState = [csDesigning] then CheckDesignBounds; SetBounds(FLeft, FTop, FWidth, Max(0, Value)); end; {------------------------------------------------------------------------------ procedure TControl.SetHelpContext(const AValue: THelpContext); ------------------------------------------------------------------------------} procedure TControl.SetHelpContext(const AValue: THelpContext); begin if FHelpContext=AValue then exit; if not (csLoading in ComponentState) then FHelpType := htContext; FHelpContext:=AValue; end; {------------------------------------------------------------------------------ procedure TControl.SetHelpKeyword(const AValue: String); ------------------------------------------------------------------------------} procedure TControl.SetHelpKeyword(const AValue: string); begin if FHelpKeyword=AValue then exit; if not (csLoading in ComponentState) then FHelpType := htKeyword; FHelpKeyword:=AValue; end; procedure TControl.SetHostDockSite(const AValue: TWinControl); begin if AValue=FHostDockSite then exit; Dock(AValue, BoundsRect); end; {------------------------------------------------------------------------------ procedure TControl.SetParent(NewParent : TWinControl); ------------------------------------------------------------------------------} procedure TControl.SetParent(NewParent: TWinControl); begin if FParent = NewParent then exit; DisableAutoSizing{$IFDEF DebugDisableAutoSizing}('TControl.SetParent'){$ENDIF}; try CheckNewParent(NewParent); if FParent <> nil then FParent.RemoveControl(Self); if cfBoundsRectForNewParentValid in FControlFlags then begin Exclude(FControlFlags, cfBoundsRectForNewParentValid); BoundsRect := BoundsRectForNewParent; end; if NewParent <> nil then NewParent.InsertControl(Self); finally EnableAutoSizing{$IFDEF DebugDisableAutoSizing}('TControl.SetParent'){$ENDIF}; end; end; procedure TControl.SetParentBackground(const AParentBackground: Boolean); begin if ParentBackground = AParentBackground then Exit; if AParentBackground then ControlStyle := ControlStyle + [csParentBackground] else ControlStyle := ControlStyle - [csParentBackground]; Invalidate; end; {------------------------------------------------------------------------------ TControl SetParentComponent ------------------------------------------------------------------------------} procedure TControl.SetParentComponent(NewParentComponent: TComponent); begin if (NewParentComponent is TWinControl) then SetParent(TWinControl(NewParentComponent)); end; {------------------------------------------------------------------------------ procedure TControl.SetParentColor(Value : Boolean); ------------------------------------------------------------------------------} procedure TControl.SetParentColor(Value : Boolean); begin if FParentColor <> Value then begin FParentColor := Value; if Assigned(FParent) and not (csReading in ComponentState) then Perform(CM_PARENTCOLORCHANGED, 0, 0); end; end; procedure TControl.SetParentFont(Value: Boolean); begin if FParentFont <> Value then begin FParentFont := Value; if Assigned(FParent) and not (csReading in ComponentState) then Perform(CM_PARENTFONTCHANGED, 0, 0); end; end; {------------------------------------------------------------------------------ TControl SetParentShowHint ------------------------------------------------------------------------------} procedure TControl.SetParentShowHint(Value : Boolean); begin if FParentShowHint <> Value then begin FParentShowHint := Value; if Assigned(FParent) and not (csReading in ComponentState) then Perform(CM_PARENTSHOWHINTCHANGED, 0, 0); end; end; {------------------------------------------------------------------------------ TControl SetPopupMenu ------------------------------------------------------------------------------} procedure TControl.SetPopupMenu(Value: TPopupMenu); begin FPopupMenu := Value; if FPopupMenu <> nil then FPopupMenu.FreeNotification(Self); end; {------------------------------------------------------------------------------ TControl WMMouseMove ------------------------------------------------------------------------------} procedure TControl.WMMouseMove(var Message: TLMMouseMove); var MP: TPoint; begin {$IFDEF VerboseMouseBugfix} DebugLn(['[TControl.WMMouseMove] ',Name,':',ClassName,' ',Message.XPos,',',Message.YPos]); {$ENDIF} MP := GetMousePosFromMessage(Message.Pos); UpdateMouseCursor(MP.X,MP.Y); if not (csNoStdEvents in ControlStyle) then MouseMove(KeystoShiftState(Word(Message.Keys)), MP.X, MP.Y); end; {------------------------------------------------------------------------------ TControl MouseDown ------------------------------------------------------------------------------} procedure TControl.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); var P: TPoint; Form: TCustomForm; begin if (not (Self is TWinControl)) or (not TWinControl(Self).CanFocus) then begin Form := GetParentForm(Self); if (Form <> nil) and (Form.ActiveControl <> nil) then Form.ActiveControl.EditingDone; end; if (Button in [mbLeft, mbRight]) and DragManager.IsDragging then begin P := ClientToScreen(Point(X,Y)); DragManager.MouseDown(Button, Shift, P.X, P.Y); end; if Assigned(FOnMouseDown) then FOnMouseDown(Self, Button, Shift, X,Y); end; {------------------------------------------------------------------------------ TControl MouseMove ------------------------------------------------------------------------------} procedure TControl.MouseMove(Shift: TShiftState; X, Y: Integer); var P: TPoint; begin if DragManager.IsDragging then begin P := ClientToScreen(Point(X, Y)); DragManager.MouseMove(Shift, P.X, P.Y); end; if Assigned(FOnMouseMove) then FOnMouseMove(Self, Shift, X, Y); end; {------------------------------------------------------------------------------ TControl MouseUp ------------------------------------------------------------------------------} procedure TControl.MouseUp(Button: TMouseButton; Shift:TShiftState; X, Y: Integer); begin if Assigned(FOnMouseUp) then FOnMouseUp(Self, Button, Shift, X,Y); end; procedure TControl.MouseEnter; begin //DebugLn('TControl.MouseEnter ',Name,':',ClassName,' ',Assigned(FOnMouseEnter)); if Assigned(FOnMouseEnter) then FOnMouseEnter(Self); end; procedure TControl.MouseLeave; begin //DebugLn('TControl.MouseLeave ',Name,':',ClassName,' ',Assigned(FOnMouseLeave)); if Assigned(FOnMouseLeave) then FOnMouseLeave(Self); end; {------------------------------------------------------------------------------ procedure TControl.CaptureChanged; ------------------------------------------------------------------------------} procedure TControl.CaptureChanged; begin if DragManager.IsDragging then DragManager.CaptureChanged(Self); end; {------------------------------------------------------------------------------ TControl SetShowHint ------------------------------------------------------------------------------} procedure TControl.SetShowHint(Value : Boolean); begin if FShowHint <> Value then begin FShowHint := Value; FParentShowHint := False; Perform(CM_SHOWHINTCHANGED, 0, 0); end; end; {------------------------------------------------------------------------------ TControl SetVisible ------------------------------------------------------------------------------} procedure TControl.SetVisible(Value : Boolean); var AsWincontrol: TWinControl; begin if FVisible <> Value then begin //DebugLn(['TControl.SetVisible ',DbgSName(Self),' NewVisible=',Value]); DisableAutoSizing{$IFDEF DebugDisableAutoSizing}('TControl.SetVisible'){$ENDIF}; try VisibleChanging; FVisible := Value; try // create/destroy handle Perform(CM_VISIBLECHANGED, WParam(Ord(Value)), 0);// see TWinControl.CMVisibleChanged if (Self is TWinControl) then AsWincontrol := TWinControl(Self) else AsWincontrol := nil; InvalidatePreferredSize; if Assigned(AsWincontrol) then AsWincontrol.InvalidatePreferredChildSizes; AdjustSize; if (not Visible) and Assigned(Parent) then begin // control became invisible, so AdjustSize was not propagated to Parent // => propagate now Parent.InvalidatePreferredSize; Parent.AdjustSize; end; finally VisibleChanged; end; finally EnableAutoSizing{$IFDEF DebugDisableAutoSizing}('TControl.SetVisible'){$ENDIF}; end; end; if (csLoading in ComponentState) then ControlState := ControlState + [csVisibleSetInLoading]; end; procedure TControl.DoOnParentHandleDestruction; begin // nothing, implement in descendats end; {------------------------------------------------------------------------------ TControl.SetZOrder ------------------------------------------------------------------------------} procedure TControl.SetZOrder(TopMost: Boolean); const POSITION: array[Boolean] of Integer = (0, MaxInt); begin if FParent = nil then exit; FParent.SetChildZPosition(Self, POSITION[TopMost]); end; {------------------------------------------------------------------------------ function TControl.HandleObjectShouldBeVisible ------------------------------------------------------------------------------} function TControl.HandleObjectShouldBeVisible: Boolean; begin Result := not ((csDestroying in ComponentState) or (csDestroyingHandle in FControlState)) and IsControlVisible; if Result and Assigned(Parent) then Result := Parent.HandleObjectShouldBeVisible; //DebugLn(['TControl.HandleObjectShouldBeVisible ',DbgSName(Self),' ',Result]); end; {------------------------------------------------------------------------------ procedure TControl Hide ------------------------------------------------------------------------------} procedure TControl.Hide; begin Visible := False; end; {------------------------------------------------------------------------------ function TControl.ParentDestroyingHandle: boolean; Returns whether any parent is destroying it's handle (and its children's) ------------------------------------------------------------------------------} function TControl.ParentDestroyingHandle: Boolean; var CurControl: TControl; begin Result:=true; CurControl:=Self; while CurControl<>nil do begin if csDestroyingHandle in CurControl.ControlState then exit; CurControl:=CurControl.Parent; end; Result:=false; end; {------------------------------------------------------------------------------ function TControl.ParentHandlesAllocated: boolean; ------------------------------------------------------------------------------} function TControl.ParentHandlesAllocated: Boolean; begin Result:=(Parent<>nil) and (Parent.ParentHandlesAllocated); end; {------------------------------------------------------------------------------ procedure TControl.InitiateAction; ------------------------------------------------------------------------------} procedure TControl.InitiateAction; begin if ActionLink <> nil then ActionLink.Update; end; procedure TControl.ShowHelp; begin {$IFDEF VerboseLCLHelp} debugln(['TControl.ShowHelp ',DbgSName(Self)]); {$ENDIF} if HelpType = htContext then begin if HelpContext <> 0 then begin Application.HelpContext(HelpContext); Exit; end; end else begin if HelpKeyword <> '' then begin Application.HelpKeyword(HelpKeyword); Exit; end; end; if Parent <> nil then Parent.ShowHelp; end; function TControl.HasHelp: Boolean; begin if HelpType = htContext then Result := HelpContext <> 0 else Result := HelpKeyword <> ''; end; {------------------------------------------------------------------------------ procedure TControl.Dock(NewDockSite: TWinControl; ARect: TRect); Docks this control into NewDockSite at ARect. ------------------------------------------------------------------------------} procedure TControl.Dock(NewDockSite: TWinControl; ARect: TRect); procedure RaiseAlreadyDocking; begin RaiseGDBException('TControl.Dock '+Name+':'+ClassName+' csDocking in FControlState'); end; var OldHostDockSite: TWinControl; begin if (csDocking in FControlState) then RaiseAlreadyDocking; // dock DisableAutoSizing{$IFDEF DebugDisableAutoSizing}('TControl.Dock'){$ENDIF}; Include(FControlState, csDocking); try OldHostDockSite:=HostDockSite; if OldHostDockSite<>NewDockSite then begin // HostDockSite will change -> prepare if (OldHostDockSite<>nil) and (OldHostDockSite.FDockClients<>nil) then OldHostDockSite.FDockClients.Remove(Self); if (NewDockSite<>nil) and (NewDockSite.FDockClients<>nil) then NewDockSite.FDockClients.Add(Self); end; //debugln(['TControl.Dock A ',DbgSName(Self),' NewDockSite=',DbgSName(NewDockSite),' ',NewDockSite.Visible]); DoDock(NewDockSite,ARect); if FHostDockSite<>NewDockSite then begin // HostDockSite has changed -> commit OldHostDockSite := FHostDockSite; FHostDockSite := NewDockSite; if NewDockSite<>nil then NewDockSite.DoAddDockClient(Self,ARect); if OldHostDockSite<>nil then OldHostDockSite.DoRemoveDockClient(Self); end; finally if (FHostDockSite<>NewDockSite) and (NewDockSite<>nil) and (NewDockSite.FDockClients<>nil) then NewDockSite.FDockClients.Remove(Self); Exclude(FControlState, csDocking); end; EnableAutoSizing{$IFDEF DebugDisableAutoSizing}('TControl.Dock'){$ENDIF}; //DebugLn(['TControl.Dock END ',DbgSName(Self),' ',DbgSName(HostDockSite)]); end; {------------------------------------------------------------------------------ function TControl.ManualDock(NewDockSite: TWinControl; DropControl: TControl; ControlSide: TAlign): Boolean; Docks this control to DropControl or on NewDockSite. If DropControl is not nil, ControlSide defines on which side of DropControl this control is docked. (alNone,alClient for stacked in pages). DropControl will become part of a TDockManager. If DropControl is nil, then DropControl becomes a normal child of NewDockSite and ControlSide is ignored. ------------------------------------------------------------------------------} function TControl.ManualDock(NewDockSite: TWinControl; DropControl: TControl; ControlSide: TAlign; KeepDockSiteSize: Boolean): Boolean; var NewBounds: TRect; DockObject: TDragDockObject; NewPosition: TPoint; begin if DropControl<>nil then DropControl.DisableAutoSizing{$IFDEF DebugDisableAutoSizing}('TControl.ManualDock DropControl'){$ENDIF}; if NewDockSite<>nil then NewDockSite.DisableAutoSizing{$IFDEF DebugDisableAutoSizing}('TControl.ManualDock NewDockSite'){$ENDIF}; if (NewDockSite=nil) then begin // undock / float this control // float the control at the same screen position if HostDockSiteManagerAvailable(HostDockSite) then begin HostDockSite.DockManager.GetControlBounds(Self,NewBounds); NewBounds.TopLeft:=HostDockSite.ClientToScreen(NewBounds.TopLeft); end else begin NewBounds.TopLeft:=ControlOrigin; end; NewBounds := Bounds(NewBounds.Left,NewBounds.Top,UndockWidth,UndockHeight); //DebugLn('TControl.ManualDock ',Name,' NewDockSite=nil HostDockSiteManagerAvailable=',dbgs(HostDockSiteManagerAvailable(HostDockSite)),' NewBounds=',dbgs(NewBounds)); Result := ManualFloat(NewBounds); end else begin // dock / unfloat this control CalculateDockSizes; Result := (HostDockSite=nil); if not Result then begin // undock from old HostSite // - this only undocks from the DockManager // - this control still uses the DockSite as parent control // Note: This can *not* be combined with ManualFloat, because that would // create a new HostDockSite //DebugLn('TControl.ManualDock UNDOCKING ',Name); Result:=HostDockSite.DoUndock(NewDockSite,Self); end; if Result then begin //DebugLn('TControl.ManualDock DOCKING ',Name); // create TDragDockObject for docking parameters DockObject := TDragDockObject.Create(Self); try // get current screen coordinates NewPosition:=ControlOrigin; // initialize DockObject with DockObject do begin FDragTarget := NewDockSite; FDropAlign := ControlSide; FDropOnControl := DropControl; FIncreaseDockArea := not KeepDockSiteSize; DockRect := Bounds(NewPosition.X,NewPosition.Y,Width,Height); end; // map from screen coordinates to new HostSite coordinates NewPosition:=NewDockSite.ScreenToClient(NewPosition); // DockDrop //DebugLn('TControl.ManualDock DOCKDROP ',Name,' DockRect=',dbgs(DockObject.DockRect),' NewPos=',dbgs(NewPosition)); NewDockSite.DockDrop(DockObject,NewPosition.X,NewPosition.Y); finally DockObject.Free; end; end; end; if NewDockSite<>nil then NewDockSite.EnableAutoSizing{$IFDEF DebugDisableAutoSizing}('TControl.ManualDock NewDockSite'){$ENDIF}; if DropControl<>nil then DropControl.EnableAutoSizing{$IFDEF DebugDisableAutoSizing}('TControl.ManualDock DropControl'){$ENDIF}; end; {------------------------------------------------------------------------------ function TControl.ManualFloat(TheScreenRect: TRect; KeepDockSiteSize: Boolean = true): Boolean; Undock and float. Float means here: create the floating dock site and dock this control into it. Exception: Forms do not need float dock sites and float on their own. ------------------------------------------------------------------------------} function TControl.ManualFloat(TheScreenRect: TRect; KeepDockSiteSize: Boolean): Boolean; var FloatHost: TWinControl; begin DebugLn(['TControl.ManualFloat ',DbgSName(Self)]); DisableAutoSizing{$IFDEF DebugDisableAutoSizing}('TControl.ManualFloat'){$ENDIF}; // undock from old host dock site if HostDockSite = nil then begin Result := True; if Parent <> nil then Parent.DoUndockClientMsg(nil, Self); end else begin Result := HostDockSite.DoUndock(nil, Self, KeepDockSiteSize); end; // create new float dock site and dock this control into it. if Result then begin FloatHost := CreateFloatingDockSite(TheScreenRect); //debugln('TControl.ManualFloat A '+Name,':',ClassName,' ',dbgs(TheScreenRect),' FloatHost=',dbgs(FloatHost<>nil)); if FloatHost <> nil then begin // => dock this control into it. FloatHost.Caption := FloatHost.GetDockCaption(Self); FloatHost.Visible := True; Dock(FloatHost,Rect(0, 0, FloatHost.ClientWidth, FloatHost.ClientHeight)) end else Dock(nil, TheScreenRect); end; EnableAutoSizing{$IFDEF DebugDisableAutoSizing}('TControl.ManualFloat'){$ENDIF}; end; {------------------------------------------------------------------------------ function TControl.ReplaceDockedControl(Control: TControl; NewDockSite: TWinControl; DropControl: TControl; ControlSide: TAlign ): Boolean; ------------------------------------------------------------------------------} function TControl.ReplaceDockedControl(Control: TControl; NewDockSite: TWinControl; DropControl: TControl; ControlSide: TAlign ): Boolean; var OldDockSite: TWinControl; begin Result := False; DisableAutoSizing{$IFDEF DebugDisableAutoSizing}('TControl.ReplaceDockedControl'){$ENDIF}; OldDockSite := Control.HostDockSite; if (OldDockSite<>nil) and (not HostDockSiteManagerAvailable(OldDockSite)) then exit; if OldDockSite <> nil then OldDockSite.DockManager.SetReplacingControl(Control); try ManualDock(OldDockSite,nil,alTop); finally if OldDockSite <> nil then OldDockSite.DockManager.SetReplacingControl(nil); end; Result:=Control.ManualDock(NewDockSite,DropControl,ControlSide); EnableAutoSizing{$IFDEF DebugDisableAutoSizing}('TControl.ReplaceDockedControl'){$ENDIF}; end; function TControl.Docked: Boolean; begin Result := Assigned(Parent) and (Parent = HostDockSite) and (GetParentForm(Parent) <> Parent); end; procedure TControl.AddHandlerOnResize(const OnResizeEvent: TNotifyEvent; AsFirst: Boolean); begin AddHandler(chtOnResize,TMethod(OnResizeEvent),AsFirst); end; procedure TControl.RemoveHandlerOnResize(const OnResizeEvent: TNotifyEvent); begin RemoveHandler(chtOnResize,TMethod(OnResizeEvent)); end; procedure TControl.AddHandlerOnChangeBounds(const OnChangeBoundsEvent: TNotifyEvent; AsFirst: Boolean); begin AddHandler(chtOnChangeBounds,TMethod(OnChangeBoundsEvent),AsFirst); end; procedure TControl.RemoveHandlerOnChangeBounds( const OnChangeBoundsEvent: TNotifyEvent); begin RemoveHandler(chtOnChangeBounds,TMethod(OnChangeBoundsEvent)); end; procedure TControl.AddHandlerOnVisibleChanging(const OnVisibleChangingEvent: TNotifyEvent; AsFirst: Boolean); begin AddHandler(chtOnVisibleChanging,TMethod(OnVisibleChangingEvent),AsFirst); end; procedure TControl.RemoveHandlerOnVisibleChanging( const OnVisibleChangingEvent: TNotifyEvent); begin RemoveHandler(chtOnVisibleChanging,TMethod(OnVisibleChangingEvent)); end; procedure TControl.AddHandlerOnVisibleChanged(const OnVisibleChangedEvent: TNotifyEvent; AsFirst: Boolean); begin AddHandler(chtOnVisibleChanged,TMethod(OnVisibleChangedEvent),AsFirst); end; procedure TControl.RemoveHandlerOnVisibleChanged( const OnVisibleChangedEvent: TNotifyEvent); begin RemoveHandler(chtOnVisibleChanged,TMethod(OnVisibleChangedEvent)); end; procedure TControl.AddHandlerOnEnabledChanging( const OnEnabledChangingEvent: TNotifyEvent; AsFirst: Boolean); begin AddHandler(chtOnEnabledChanging,TMethod(OnEnabledChangingEvent),AsFirst); end; procedure TControl.RemoveHandlerOnEnabledChanging( const OnEnabledChangingEvent: TNotifyEvent); begin RemoveHandler(chtOnEnabledChanging,TMethod(OnEnabledChangingEvent)); end; procedure TControl.AddHandlerOnEnabledChanged(const OnEnabledChangedEvent: TNotifyEvent; AsFirst: Boolean); begin AddHandler(chtOnEnabledChanged,TMethod(OnEnabledChangedEvent),AsFirst); end; procedure TControl.RemoveHandlerOnEnabledChanged( const OnEnabledChangedEvent: TNotifyEvent); begin RemoveHandler(chtOnEnabledChanged,TMethod(OnEnabledChangedEvent)); end; procedure TControl.AddHandlerOnKeyDown(const OnKeyDownEvent: TKeyEvent; AsFirst: Boolean); begin AddHandler(chtOnKeyDown,TMethod(OnKeyDownEvent),AsFirst); end; procedure TControl.RemoveHandlerOnKeyDown(const OnKeyDownEvent: TKeyEvent); begin RemoveHandler(chtOnKeyDown,TMethod(OnKeyDownEvent)); end; procedure TControl.AddHandlerOnBeforeDestruction(const OnBeforeDestructionEvent: TNotifyEvent; AsFirst: Boolean); begin AddHandler(chtOnBeforeDestruction,TMethod(OnBeforeDestructionEvent)); end; procedure TControl.RemoveHandlerOnBeforeDestruction( const OnBeforeDestructionEvent: TNotifyEvent); begin RemoveHandler(chtOnBeforeDestruction,TMethod(OnBeforeDestructionEvent)); end; procedure TControl.AddHandlerOnMouseWheel(const OnMouseWheelEvent: TMouseWheelEvent; AsFirst: Boolean); begin AddHandler(chtOnMouseWheel,TMethod(OnMouseWheelEvent),AsFirst); end; procedure TControl.RemoveHandlerOnMouseWheel( const OnMouseWheelEvent: TMouseWheelEvent); begin RemoveHandler(chtOnMouseWheel,TMethod(OnMouseWheelEvent)); end; procedure TControl.RemoveAllHandlersOfObject(AnObject: TObject); var HandlerType: TControlHandlerType; begin inherited RemoveAllHandlersOfObject(AnObject); for HandlerType:=Low(TControlHandlerType) to High(TControlHandlerType) do FControlHandlers[HandlerType].RemoveAllMethodsOfObject(AnObject); end; {------------------------------------------------------------------------------ Method: TControl.GetTextBuf Params: None Returns: Nothing Copies max bufsize-1 chars to buffer ------------------------------------------------------------------------------} function TControl.GetTextBuf(Buffer: PChar; BufSize: Integer): Integer; var S: string; begin if BufSize <= 0 then Exit(0); S := RealGetText; if Length(S) >= BufSize then begin StrPLCopy(Buffer, S, BufSize - 1); Result := BufSize - 1; end else begin StrPCopy(Buffer, S); Result := length(S); end; end; {------------------------------------------------------------------------------ Method: TControl.SetTextBuf Params: None Returns: Nothing ------------------------------------------------------------------------------} procedure TControl.SetTextBuf(Buffer: PChar); begin RealSetText(Buffer); end; {------------------------------------------------------------------------------ TControl RealSetText ------------------------------------------------------------------------------} procedure TControl.RealSetText(const Value: TCaption); begin if RealGetText = Value then Exit; FCaption := Value; Perform(CM_TEXTCHANGED, 0, 0); end; procedure TControl.TextChanged; begin end; function TControl.GetCachedText(var CachedText: TCaption): Boolean; begin CachedText := FCaption; Result:= true; end; {------------------------------------------------------------------------------ TControl SetText ------------------------------------------------------------------------------} procedure TControl.SetText(const Value: TCaption); begin //if CompareText(Name,'MainForm')=0 then debugln('TControl.SetText A ',DbgSName(Self),' GetText="',GetText,'" Value="',Value,'" FCaption="',FCaption,'"'); if GetText = Value then Exit; // Check if SetTextBuf is overridden, otherwise // we can call RealSetText directly if TMethod(@Self.SetTextBuf).Code = Pointer(@TControl.SetTextBuf) then begin RealSetText(Value); end else begin // Bummer, we have to do it the compatible way. DebugLn('Note: SetTextBuf is overridden for: ', Classname); SetTextBuf(PChar(Value)); end; //if CompareText(ClassName,'TMEMO')=0 then // debugln('TControl.SetText END ',DbgSName(Self),' FCaption="',FCaption,'"'); if HostDockSite <> nil then HostDockSite.UpdateDockCaption(nil); end; {------------------------------------------------------------------------------ TControl Update ------------------------------------------------------------------------------} procedure TControl.Update; begin if Parent<>nil then Parent.Update; end; {------------------------------------------------------------------------------ Method: TControl.Destroy Params: None Returns: Nothing Destructor for the class. ------------------------------------------------------------------------------} destructor TControl.Destroy; var HandlerType: TControlHandlerType; Side: TAnchorKind; i: Integer; CurAnchorSide: TAnchorSide; begin //DebugLn('[TControl.Destroy] A ',Name,':',ClassName); // make sure the capture is released MouseCapture := False; // explicit notification about component destruction. this can be a drag target DragManager.Notification(Self, opRemove); Application.ControlDestroyed(Self); if (FHostDockSite <> nil) and not (csDestroying in FHostDockSite.ComponentState) then begin FHostDockSite.DoUndockClientMsg(nil, Self); SetParent(nil); Dock(nil, BoundsRect); FHostDockSite := nil; end else begin if Assigned(FHostDockSite) and Assigned(FHostDockSite.FDockClients) then begin FHostDockSite.FDockClients.Remove(Self); FHostDockSite := nil; end; SetParent(nil); end; if FAnchoredControls <> nil then begin for i := 0 to FAnchoredControls.Count - 1 do for Side := Low(TAnchorKind) to High(TAnchorKind) do begin CurAnchorSide := AnchoredControls[i].AnchorSide[Side]; if (CurAnchorSide<>nil) and (CurAnchorSide.FControl = Self) then CurAnchorSide.FControl := nil; end; FreeThenNil(FAnchoredControls); end; FreeThenNil(FActionLink); for Side := Low(FAnchorSides) to High(FAnchorSides) do FreeThenNil(FAnchorSides[Side]); FreeThenNil(FBorderSpacing); FreeThenNil(FConstraints); FreeThenNil(FFont); FreeThenNil(FAccessibleObject); //DebugLn('[TControl.Destroy] B ',DbgSName(Self)); inherited Destroy; //DebugLn('[TControl.Destroy] END ',DbgSName(Self)); for HandlerType := Low(TControlHandlerType) to High(TControlHandlerType) do FreeThenNil(FControlHandlers[HandlerType]); {$IFDEF DebugDisableAutoSizing} FreeAndNil(FAutoSizingLockReasons); {$ENDIF} end; procedure TControl.BeforeDestruction; begin inherited BeforeDestruction; DoCallNotifyHandler(chtOnBeforeDestruction); end; {------------------------------------------------------------------------------ Method: TControl.Create Params: None Returns: Nothing Constructor for the class. ------------------------------------------------------------------------------} constructor TControl.Create(TheOwner: TComponent); var Side: TAnchorKind; begin DisableAutoSizing{$IFDEF DebugDisableAutoSizing}('TControl.Create'){$ENDIF}; try //if AnsiCompareText(ClassName,'TSpeedButton')=0 then // DebugLn('TControl.Create START ',Name,':',ClassName); inherited Create(TheOwner); // no csOpaque: delphi compatible, win32 themes notebook depend on it // csOpaque means entire client area will be drawn // (most controls are semi-transparent) FAccessibleObject := CreateAccessibleObject(); FControlStyle := FControlStyle +[csCaptureMouse, csClickEvents, csSetCaption, csDoubleClicks]; FConstraints:= TSizeConstraints.Create(Self); FBorderSpacing := CreateControlBorderSpacing; for Side:=Low(FAnchorSides) to High(FAnchorSides) do FAnchorSides[Side]:=TAnchorSide.Create(Self,Side); FBaseBounds.Right := -1; FAnchors := [akLeft,akTop]; FAlign := alNone; FCaptureMouseButtons := [mbLeft]; FColor := {$ifdef UseCLDefault}clDefault{$else}clWindow{$endif}; FVisible := True; FParentBidiMode := True; FParentColor := True; FParentFont := True; FDesktopFont := True; FParentShowHint := True; FWindowProc := @WndProc; FCursor := crDefault; FFont := TFont.Create; FFont.OnChange := @FontChanged; FIsControl := False; FEnabled := True; FHelpType := htContext; FDragCursor := crDrag; FFloatingDockSiteClass := TCustomDockForm; //DebugLn('TControl.Create END ',Name,':',ClassName); finally EnableAutoSizing{$IFDEF DebugDisableAutoSizing}('TControl.Create'){$ENDIF}; end; end; {------------------------------------------------------------------------------ Method: TControl.CreateControlBorderSpacing Params: None Returns: ControlBorderSpacing instance Creates the default ControlBorderSpacing. Allowes descendant controls to overide this. ------------------------------------------------------------------------------} function TControl.CreateControlBorderSpacing: TControlBorderSpacing; begin Result := TControlBorderSpacing.Create(Self); end; {------------------------------------------------------------------------------ Method: TControl.GetDeviceContext Params: WindowHandle: the windowhandle of this control Returns: a Devicecontext Get the devicecontext of the parent Wincontrol for this Control. ------------------------------------------------------------------------------} function TControl.GetDeviceContext(var WindowHandle: HWND): HDC; begin if Parent = nil then raise EInvalidOperation.CreateFmt(sParentRequired, [Name]); Result := Parent.GetDeviceContext(WindowHandle); MoveWindowOrgEx(Result, Left, Top); IntersectClipRect(Result, 0, 0, Width, Height); end; {------------------------------------------------------------------------------ Method: TControl.HasParent Params: Returns: True - the item has a parent responsible for streaming This function will be called during streaming to decide if a component has to be streamed by it's owner or parent. ------------------------------------------------------------------------------} function TControl.HasParent : Boolean; begin Result := (FParent <> nil); end; function TControl.GetParentComponent: TComponent; begin Result := Parent; end; function TControl.IsParentOf(AControl: TControl): Boolean; begin Result := False; while Assigned(AControl) do begin AControl := AControl.Parent; if Self = AControl then Exit(True); end; end; function TControl.GetTopParent: TControl; begin Result := Self; while Assigned(Result.Parent) do Result := Result.Parent; end; function TControl.FindSubComponent(AName: string): TComponent; // Like TComponent.FindComponent but finds also a subcomponent which name is // separated by a dot. For example 'LabeledEdit1.SubLabel'. var i: Integer; SubName: String; begin i := Pos('.', AName); if i > 0 then begin SubName := Copy(AName, i+1, Length(AName)); Delete(AName, i, Length(AName)); end else SubName := ''; Result := FindComponent(AName); if Assigned(Result) and (SubName<>'') then Result := Result.FindComponent(SubName); end; {------------------------------------------------------------------------------ Method: TControl.SendToBack Params: None Returns: Nothing Puts a control back in Z-order behind all other controls ------------------------------------------------------------------------------} procedure TControl.SendToBack; begin SetZOrder(false); end; {------------------------------------------------------------------------------ procedure TControl.AnchorToNeighbour(Side: TAnchorKind; Space: integer; Sibling: TControl); Setup AnchorSide to anchor one side to the side of a neighbour sibling. For example Right side to Left side, or Top side to Bottom. ------------------------------------------------------------------------------} procedure TControl.AnchorToNeighbour(Side: TAnchorKind; Space: TSpacingSize; Sibling: TControl); begin if Parent<>nil then Parent.DisableAlign; try case Side of akLeft: BorderSpacing.Left:=Space; akTop: BorderSpacing.Top:=Space; akRight: BorderSpacing.Right:=Space; akBottom: BorderSpacing.Bottom:=Space; end; AnchorSide[Side].Side:=DefaultSideForAnchorKind[Side]; AnchorSide[Side].Control:=Sibling; Anchors:=Anchors+[Side]; finally if Parent<>nil then Parent.EnableAlign; end; end; procedure TControl.AnchorParallel(Side: TAnchorKind; Space: TSpacingSize; Sibling: TControl); begin if Parent<>nil then Parent.DisableAlign; try case Side of akLeft: BorderSpacing.Left:=Space; akTop: BorderSpacing.Top:=Space; akRight: BorderSpacing.Right:=Space; akBottom: BorderSpacing.Bottom:=Space; end; case Side of akLeft: AnchorSide[Side].Side:=asrLeft; akTop: AnchorSide[Side].Side:=asrTop; akRight: AnchorSide[Side].Side:=asrRight; akBottom: AnchorSide[Side].Side:=asrBottom; end; AnchorSide[Side].Control:=Sibling; Anchors:=Anchors+[Side]; finally if Parent<>nil then Parent.EnableAlign; end; end; {------------------------------------------------------------------------------ procedure TControl.AnchorHorizontalCenterTo(Sibling: TControl); Setup AnchorSide to center the control horizontally relative to a sibling. ------------------------------------------------------------------------------} procedure TControl.AnchorHorizontalCenterTo(Sibling: TControl); begin if Parent<>nil then Parent.DisableAlign; try AnchorSide[akLeft].Side:=asrCenter; AnchorSide[akLeft].Control:=Sibling; Anchors:=Anchors+[akLeft]-[akRight]; finally if Parent<>nil then Parent.EnableAlign; end; end; {------------------------------------------------------------------------------ procedure TControl.AnchorVerticalCenterTo(Sibling: TControl); Setup AnchorSide to center the control vertically relative to a sibling. ------------------------------------------------------------------------------} procedure TControl.AnchorVerticalCenterTo(Sibling: TControl); begin if Parent<>nil then Parent.DisableAlign; try AnchorSide[akTop].Side:=asrCenter; AnchorSide[akTop].Control:=Sibling; Anchors:=Anchors+[akTop]-[akBottom]; finally if Parent<>nil then Parent.EnableAlign; end; end; procedure TControl.AnchorToCompanion(Side: TAnchorKind; Space: TSpacingSize; Sibling: TControl; FreeCompositeSide: Boolean); procedure AnchorCompanionSides( ResizeSide,// the side of this control, where Sibling is touched and moved OppositeResizeSide, // opposite of ResizeSide FixedSide1,// the first non moving side FixedSide2:// the second non moving side TAnchorKind); begin if not (OppositeAnchor[Side] in Anchors) then AnchorSide[OppositeResizeSide].Control:=nil; AnchorToNeighbour(ResizeSide,Space,Sibling); AnchorParallel(FixedSide1,0,Sibling); AnchorParallel(FixedSide2,0,Sibling); end; var NewAnchors: TAnchors; begin if Parent<>nil then Parent.DisableAlign; try // anchor all. Except for the opposite side. NewAnchors:=[akLeft,akTop,akRight,akBottom]; if FreeCompositeSide or (not (OppositeAnchor[Side] in Anchors)) then Exclude(NewAnchors,OppositeAnchor[Side]); Anchors:=NewAnchors; case Side of akLeft: AnchorCompanionSides(akLeft,akRight,akTop,akBottom); akRight: AnchorCompanionSides(akRight,akLeft,akTop,akBottom); akTop: AnchorCompanionSides(akTop,akBottom,akLeft,akRight); akBottom: AnchorCompanionSides(akBottom,akTop,akLeft,akRight); end; finally if Parent<>nil then Parent.EnableAlign; end; end; procedure TControl.AnchorSame(Side: TAnchorKind; Sibling: TControl); begin if Parent<>nil then Parent.DisableAlign; try if Side in Sibling.Anchors then Anchors:=Anchors+[Side] else Anchors:=Anchors-[Side]; AnchorSide[Side].Assign(Sibling.AnchorSide[Side]); finally if Parent<>nil then Parent.EnableAlign; end; end; procedure TControl.AnchorAsAlign(TheAlign: TAlign; Space: TSpacingSize); begin Parent.DisableAlign; try if akLeft in AnchorAlign[TheAlign] then begin BorderSpacing.Left:=Space; AnchorSide[akLeft].Side:=asrLeft; AnchorSide[akLeft].Control:=Parent; end; if akTop in AnchorAlign[TheAlign] then begin BorderSpacing.Top:=Space; AnchorSide[akTop].Side:=asrTop; AnchorSide[akTop].Control:=Parent; end; if akRight in AnchorAlign[TheAlign] then begin BorderSpacing.Right:=Space; AnchorSide[akRight].Side:=asrRight; AnchorSide[akRight].Control:=Parent; end; if akBottom in AnchorAlign[TheAlign] then begin BorderSpacing.Bottom:=Space; AnchorSide[akBottom].Side:=asrBottom; AnchorSide[akBottom].Control:=Parent; end; Anchors:=Anchors+AnchorAlign[TheAlign]; finally Parent.EnableAlign; end; end; procedure TControl.AnchorClient(Space: TSpacingSize); begin AnchorAsAlign(alClient,Space); end; function TControl.AnchoredControlCount: Integer; begin if FAnchoredControls = nil then Result := 0 else Result := FAnchoredControls.Count; end; procedure TControl.SetInitialBounds(aLeft, aTop, aWidth, aHeight: Integer); begin //DebugLn('TControl.SetInitialBounds A ',Name,':',ClassName,' ',aLeft,',',aTop,',',aWidth,',',aHeight); if (csLoading in ComponentState) or ((Owner<>nil) and (csLoading in Owner.ComponentState)) then exit; //DebugLn('TControl.SetInitialBounds B ',Name,':',ClassName,' ',aLeft,',',aTop,',',aWidth,',',aHeight); SetBounds(aLeft,aTop,aWidth,aHeight); end; procedure TControl.SetBoundsKeepBase(aLeft, aTop, aWidth, aHeight: Integer); begin ChangeBounds(aLeft, aTop, aWidth, aHeight, true); end; {------------------------------------------------------------------------------ procedure TControl.GetPreferredSize( var PreferredWidth, PreferredHeight: integer; Raw: boolean; WithThemeSpace: Boolean); Returns the default/preferred width and height for a control, which is used by the LCL autosizing algorithms as default size. Only positive values are valid. Negative or 0 are treated as undefined and the LCL uses other sizes instead. Raw: If not Raw then the values will be adjusted by the constraints and undefined values will be replaced by GetDefaultWidth/GetDefaultHeight. WithThemeSpace: If true, adds space for stacking. For example: TRadioButton has a minimum size. But for stacking multiple TRadioButtons there should be some space around. This space is theme dependent, so it passed parameter to the widgetset. TWinControl overrides this and asks the interface for theme dependent values. See TWinControl.GetPreferredSize for more information. ------------------------------------------------------------------------------} procedure TControl.GetPreferredSize(var PreferredWidth, PreferredHeight: Integer; Raw: Boolean; WithThemeSpace: Boolean); begin if WithThemeSpace then begin if not (cfPreferredSizeValid in FControlFlags) then begin CalculatePreferredSize(FPreferredWidth,FPreferredHeight,true); Include(FControlFlags,cfPreferredSizeValid); end; PreferredWidth:=FPreferredWidth; PreferredHeight:=FPreferredHeight; end else begin if not (cfPreferredMinSizeValid in FControlFlags) then begin CalculatePreferredSize(FPreferredMinWidth,FPreferredMinHeight,false); Include(FControlFlags,cfPreferredMinSizeValid); end; PreferredWidth:=FPreferredMinWidth; PreferredHeight:=FPreferredMinHeight; end; if not Raw then begin // use defaults for undefined preferred size if (PreferredWidth<0) or ((PreferredWidth=0) and (not (csAutoSize0x0 in ControlStyle))) then begin if AutoSize or WidthIsAnchored then PreferredWidth:=GetDefaultWidth else PreferredWidth:=Width; end; if (PreferredHeight<0) or ((PreferredHeight=0) and (not (csAutoSize0x0 in ControlStyle))) then begin if AutoSize or HeightIsAnchored then PreferredHeight:=GetDefaultHeight else PreferredHeight:=Height; end; // apply constraints PreferredWidth:=Constraints.MinMaxWidth(PreferredWidth); PreferredHeight:=Constraints.MinMaxHeight(PreferredHeight); end; end; function TControl.GetCanvasScaleFactor: Double; begin Result := TWSControlClass(WidgetSetClass).GetCanvasScaleFactor(Self); end; {------------------------------------------------------------------------------ function TControl.GetDefaultWidth: integer; The default width for this control independent of any calculated values like Width and GetPreferredSize. ------------------------------------------------------------------------------} function TControl.GetDefaultWidth: Integer; begin if WidthIsAnchored then // if width is anchored the read and base bounds were changed at designtime Result := Scale96ToFont(GetControlClassDefaultSize.cx) else if cfBaseBoundsValid in FControlFlags then Result := FBaseBounds.Right - FBaseBounds.Left else if cfWidthLoaded in FControlFlags then Result := FReadBounds.Right - FReadBounds.Left else Result := Scale96ToFont(GetControlClassDefaultSize.cx); end; {------------------------------------------------------------------------------ function TControl.GetDefaultHeight: integer; The default height for this control independent of any calculated values like Height and GetPreferredSize. ------------------------------------------------------------------------------} function TControl.GetDefaultHeight: Integer; begin if HeightIsAnchored then // if height is anchored the read and base bounds were changed at designtime Result := Scale96ToFont(GetControlClassDefaultSize.cy) else if cfBaseBoundsValid in FControlFlags then Result := BaseBounds.Bottom - BaseBounds.Top else if cfHeightLoaded in FControlFlags then Result := FReadBounds.Bottom - FReadBounds.Top else Result := Scale96ToFont(GetControlClassDefaultSize.cy); end; {------------------------------------------------------------------------------ class function TControl.GetControlClassDefaultSize: TPoint; The default size of this type of controls. Used by GetDefaultWidth and GetDefaultHeight. ------------------------------------------------------------------------------} class function TControl.GetControlClassDefaultSize: TSize; begin Result.CX := 75; Result.CY := 50; end; {------------------------------------------------------------------------------ procedure TControl.GetSidePosition; Utility function to retrieve Left,Top,Right and Bottom. ------------------------------------------------------------------------------} function TControl.GetSidePosition(Side: TAnchorKind): Integer; begin case Side of akLeft: Result := Left; akTop: Result := Top; akRight: Result := Left + Width; akBottom: Result := Top + Height; end; end; {------------------------------------------------------------------------------ procedure TControl.CNPreferredSizeChanged; Called by the LCL interface, when something changed that effects the result of the interface values for GetPreferredSize. ------------------------------------------------------------------------------} procedure TControl.CNPreferredSizeChanged; begin InvalidatePreferredSize; end; {------------------------------------------------------------------------------ procedure TControl.InvalidatePreferredSize; Invalidate the cache of the preferred size of this and all parent controls. ------------------------------------------------------------------------------} procedure TControl.InvalidatePreferredSize; procedure RaiseLoop; begin raise ELayoutException.Create('TControl.InvalidatePreferredSize loop detected '+DbgSName(Self)+' Bounds='+dbgs(BoundsRect)); end; var AControl: TControl; begin AControl:=Self; while AControl<>nil do begin Exclude(AControl.FControlFlags,cfPreferredSizeValid); Exclude(AControl.FControlFlags,cfPreferredMinSizeValid); if AControl is TWinControl then Exclude(TWinControl(AControl).FWinControlFlags,wcfAdjustedLogicalClientRectValid); if not AControl.IsControlVisible then break; if (AControl.Parent=nil) and (cfKillInvalidatePreferredSize in AControl.FControlFlags) then RaiseLoop; AControl:=AControl.Parent; end; end; function TControl.GetAnchorsDependingOnParent(WithNormalAnchors: Boolean ): TAnchors; var a: TAnchorKind; begin Result:=[]; if Parent=nil then exit; if (Anchors*[akLeft,akRight]=[]) then begin // center horizontally Result:=Result+[akLeft,akRight]; end; if (Anchors*[akTop,akBottom]=[]) then begin // center vertically Result:=Result+[akTop,akBottom]; end; for a:=Low(TAnchorKind) to High(TAnchorKind) do begin if (a in (Anchors+AnchorAlign[Align])) then begin if WithNormalAnchors or (AnchorSide[a].Control=Parent) or ((AnchorSide[a].Control=nil) and (a in [akRight,akBottom])) then begin // side anchored Include(Result,a); end; end; end; end; procedure TControl.DisableAutoSizing {$IFDEF DebugDisableAutoSizing}(const Reason: string){$ENDIF}; begin inc(FAutoSizingLockCount); {$IFDEF DebugDisableAutoSizing} if FAutoSizingLockReasons=nil then FAutoSizingLockReasons:=TStringList.Create; FAutoSizingLockReasons.Add(Reason); {$ENDIF} //DebugLn([Space(FAutoSizingLockCount*2),'TControl.DisableAutoSizing ',DbgSName(Self),' ',FAutoSizingLockCount]); if FAutoSizingLockCount=1 then begin if Parent<>nil then begin //DebugLn([Space(FAutoSizingLockCount*2),'TControl.DisableAutoSizing ',DbgSName(Self),' disable Parent=',DbgSName(Parent)]); Parent.DisableAutoSizing{$IFDEF DebugDisableAutoSizing}('TControl.DisableAutoSizing'){$ENDIF}; end; end; end; procedure TControl.EnableAutoSizing {$IFDEF DebugDisableAutoSizing}(const Reason: string){$ENDIF}; {$IFDEF DebugDisableAutoSizing} procedure CheckReason; var i: Integer; s: String; begin i:=FAutoSizingLockReasons.Count-1; while i>=0 do begin if FAutoSizingLockReasons[i]=Reason then begin FAutoSizingLockReasons.Delete(i); exit; end; dec(i); end; s:='TControl.EnableAutoSizing '+DbgSName(Self)+' never disabled with reason "'+Reason+'"'; for i:=0 to FAutoSizingLockReasons.Count-1 do s+=','+LineEnding+'reason['+IntToStr(i)+']="'+FAutoSizingLockReasons[i]+'"'; RaiseGDBException(s); end; {$ENDIF} begin {$IFDEF DebugDisableAutoSizing} CheckReason; {$ENDIF} if FAutoSizingLockCount<=0 then raise ELayoutException.CreateFmt('TControl.EnableAutoSizing %s: missing DisableAutoSizing', [DbgSName(Self)]); dec(FAutoSizingLockCount); //DebugLn([Space(FAutoSizingLockCount*2),'TControl.EnableAutoSizing ',DbgSName(Self),' ',FAutoSizingLockCount]); if (FAutoSizingLockCount=0) then begin if (Parent<>nil) then begin //DebugLn([Space(FAutoSizingLockCount*2),'TControl.EnableAutoSizing ',DbgSName(Self),' enable Parent ',DbgSName(Parent)]); Parent.EnableAutoSizing{$IFDEF DebugDisableAutoSizing}('TControl.DisableAutoSizing'){$ENDIF}; end else DoAllAutoSize; end; end; {$IFDEF DebugDisableAutoSizing} procedure TControl.WriteAutoSizeReasons(NotIfEmpty: boolean); begin if NotIfEmpty and (FAutoSizingLockReasons.Count=0) then exit; DebugLn(['TControl.WriteAutoSizeReasons ',DbgSName(Self)]); debugln(FAutoSizingLockReasons.Text); end; {$ENDIF} procedure TControl.EndAutoSizing; procedure Error; begin RaiseGDBException('TControl.EndAutoSizing'); end; begin if not FAutoSizingSelf then Error; FAutoSizingSelf := False; end; {------------------------------------------------------------------------------ Method: TControl.WMWindowPosChanged Params: Msg: The message Returns: nothing event handler. ------------------------------------------------------------------------------} procedure TControl.WMWindowPosChanged(var Message: TLMWindowPosChanged); begin // Do not handle this message and leave it to WMSize and WMMove Message.Result := 0; end; {------------------------------------------------------------------------------ Method: TControl.WMSize Params: Message : TLMSize Returns: nothing Event handler for LMSize messages. Overriden by TWinControl.WMSize. ------------------------------------------------------------------------------} procedure TControl.WMSize(var Message : TLMSize); begin {$IFDEF CHECK_POSITION} if CheckPosition(Self) then DebugLn('[TControl.WMSize] Name=',Name,':',ClassName,' Message.Width=',DbgS(Message.Width),' Message.Height=',DbgS(Message.Height),' Width=',DbgS(Width),' Height=',DbgS(Height)); {$ENDIF} //DebugLn(Format('Trace:[TWinControl.WMSize] %s', [ClassName])); if Assigned(Parent) then SetBoundsKeepBase(Left,Top,Message.Width,Message.Height) else SetBounds(Left,Top,Message.Width,Message.Height); end; {------------------------------------------------------------------------------ Method: TControl.WMMove Params: Msg: The message Returns: nothing event handler. Message.MoveType=0 is the default, all other values will force a RequestAlign. ------------------------------------------------------------------------------} procedure TControl.WMMove(var Message: TLMMove); begin {$IFDEF CHECK_POSITION} if CheckPosition(Self) then DebugLn('[TControl.WMMove] Name=',Name,':',ClassName,' Message.XPos=',DbgS(Message.XPos),' Message.YPos=',DbgS(Message.YPos),' OldLeft=',DbgS(Left),' OldTop=',DbgS(Top)); {$ENDIF} // Just sync the coordinates if Assigned(Parent) then SetBoundsKeepBase(Message.XPos, Message.YPos, Width, Height) else SetBounds(Message.XPos, Message.YPos, Width, Height); end; {------------------------------------------------------------------------------ Method: TControl.SetBiDiMode ------------------------------------------------------------------------------} procedure TControl.SetBiDiMode(AValue: TBiDiMode); begin if FBiDiMode=AValue then exit; FBiDiMode:=AValue; FParentBiDiMode := False; DisableAutoSizing{$IFDEF DebugDisableAutoSizing}('TControl.SetBiDiMode'){$ENDIF}; try Perform(CM_BIDIMODECHANGED, 0, 0); // see TWinControl.CMBiDiModeChanged finally EnableAutoSizing{$IFDEF DebugDisableAutoSizing}('TControl.SetBiDiMode'){$ENDIF}; end; end; {------------------------------------------------------------------------------ Method: TControl.SetParentBiDiMode ------------------------------------------------------------------------------} procedure TControl.SetParentBiDiMode(AValue: Boolean); begin if FParentBiDiMode = AValue then Exit; FParentBiDiMode := AValue; if (FParent <> nil) and not (csReading in ComponentState) then Perform(CM_PARENTBIDIMODECHANGED, 0, 0); end; {------------------------------------------------------------------------------ Method: TControl.CMBiDiModeChanged ------------------------------------------------------------------------------} procedure TControl.CMBiDiModeChanged(var Message: TLMessage); begin if (Message.wParam = 0) then Invalidate; end; procedure TControl.CMChanged(var Message: TLMessage); begin if FParent<>nil then FParent.WindowProc(Message); end; procedure TControl.CMSysFontChanged(var Message: TLMessage); begin if FDesktopFont then begin Font := Screen.SystemFont; FDesktopFont := True; end; end; {------------------------------------------------------------------------------ TControl.CMParentBidiModeChanged assumes: FParent <> nil ------------------------------------------------------------------------------} procedure TControl.CMParentBiDiModeChanged(var Message: TLMessage); begin if csLoading in ComponentState then exit; if ParentBidiMode then begin BidiMode := FParent.BidiMode; FParentBiDiMode := True; end; end; {------------------------------------------------------------------------------ TControl.IsBiDiModeStored ------------------------------------------------------------------------------} function TControl.IsBiDiModeStored: Boolean; begin Result := not ParentBidiMode; end; {------------------------------------------------------------------------------ TControl.IsRightToLeft ------------------------------------------------------------------------------} function TControl.IsRightToLeft: Boolean; begin Result := UseRightToLeftReading; end; {------------------------------------------------------------------------------ TControl.UseRightToLeftAlignment ------------------------------------------------------------------------------} function TControl.UseRightToLeftAlignment: Boolean; begin Result := (BiDiMode = bdRightToLeft); end; {------------------------------------------------------------------------------ TControl.UseRightToLeftReading ------------------------------------------------------------------------------} function TControl.UseRightToLeftReading: Boolean; begin Result := (BiDiMode <> bdLeftToRight); end; {------------------------------------------------------------------------------ TControl.UseRightToLeftScrollBar ------------------------------------------------------------------------------} function TControl.UseRightToLeftScrollBar: Boolean; begin Result := (BiDiMode in [bdRightToLeft, bdRightToLeftNoAlign]); end; {$IFDEF ASSERT_IS_ON} {$UNDEF ASSERT_IS_ON} {$C-} {$ENDIF} // included by controls.pp