Componentes.Terceros.jvcl/official/3.32/run/JvSplit.pas

688 lines
19 KiB
ObjectPascal

{-----------------------------------------------------------------------------
The contents of this file are subject to the Mozilla Public License
Version 1.1 (the "License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.mozilla.org/MPL/MPL-1.1.html
Software distributed under the License is distributed on an "AS IS" basis,
WITHOUT WARRANTY OF ANY KIND, either expressed or implied. See the License for
the specific language governing rights and limitations under the License.
The Original Code is: JvSplit.PAS, released on 2002-07-04.
The Initial Developers of the Original Code are: Fedor Koshevnikov, Igor Pavluk and Serge Korolev
Copyright (c) 1997, 1998 Fedor Koshevnikov, Igor Pavluk and Serge Korolev
Copyright (c) 2001,2002 SGB Software
All Rights Reserved.
You may retrieve the latest version of this file at the Project JEDI's JVCL home page,
located at http://jvcl.sourceforge.net
Known Issues:
-----------------------------------------------------------------------------}
// $Id: JvSplit.pas 10688 2006-06-09 13:26:54Z obones $
unit JvSplit;
{$I jvcl.inc}
interface
uses
{$IFDEF UNITVERSIONING}
JclUnitVersioning,
{$ENDIF UNITVERSIONING}
Windows, Controls, ExtCtrls, Forms, Graphics, SysUtils, Classes,
JvExtComponent;
type
TSplitterStyle = (spUnknown, spHorizontalFirst, spHorizontalSecond,
spVerticalFirst, spVerticalSecond);
TInverseMode = (imNew, imClear, imMove);
TSplitterMoveEvent = procedure(Sender: TObject; X, Y: Integer;
var AllowChange: Boolean) of object;
TJvxSplitter = class(TJvCustomPanel)
private
FControlFirst: TControl;
FControlSecond: TControl;
FSizing: Boolean;
FStyle: TSplitterStyle;
FPrevOrg: TPoint;
FOffset: TPoint;
FNoDropCursor: Boolean;
FLimitRect: TRect;
FTopLeftLimit: Integer;
FBottomRightLimit: Integer;
FForm: TCustomForm;
FActiveControl: TWinControl;
FAppShowHint: Boolean;
FOldKeyDown: TKeyEvent;
FOnPosChanged: TNotifyEvent;
FOnPosChanging: TSplitterMoveEvent;
function FindControl: TControl;
procedure ControlKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
procedure StartInverseRect;
procedure EndInverseRect(X, Y: Integer; AllowChange, Apply: Boolean);
function GetAlign: TAlign;
procedure MoveInverseRect(X, Y: Integer; AllowChange: Boolean);
procedure ShowInverseRect(X, Y: Integer; Mode: TInverseMode);
procedure DrawSizingLine(Split: TPoint);
function GetStyle: TSplitterStyle;
function GetCursor: TCursor;
procedure SetControlFirst(Value: TControl);
procedure SetControlSecond(Value: TControl);
procedure SetAlign(Value: TAlign);
procedure StopSizing(X, Y: Integer; Apply: Boolean);
procedure CheckPosition(var X, Y: Integer);
procedure ReadOffset(Reader: TReader);
procedure WriteOffset(Writer: TWriter);
protected
procedure DefineProperties(Filer: TFiler); override;
procedure Loaded; override;
procedure Notification(AComponent: TComponent; AOperation: TOperation); override;
procedure MouseDown(Button: TMouseButton; Shift: TShiftState;
X, Y: Integer); override;
procedure MouseMove(Shift: TShiftState; X, Y: Integer); override;
procedure MouseUp(Button: TMouseButton; Shift: TShiftState;
X, Y: Integer); override;
procedure Changed; dynamic;
procedure Changing(X, Y: Integer; var AllowChange: Boolean); dynamic;
public
constructor Create(AOwner: TComponent); override;
procedure UpdateState;
published
property ControlFirst: TControl read FControlFirst write SetControlFirst;
property ControlSecond: TControl read FControlSecond write SetControlSecond;
property Align: TAlign read GetAlign write SetAlign default alNone;
property Constraints;
property BevelInner;
property BevelOuter;
property BevelWidth;
property BorderStyle;
property Enabled;
property Color;
{$IFDEF VCL}
property Flat default True;
property ParentFlat default False;
{$ENDIF VCL}
property Cursor read GetCursor stored False;
property TopLeftLimit: Integer read FTopLeftLimit write FTopLeftLimit default 20;
property BottomRightLimit: Integer read FBottomRightLimit write FBottomRightLimit default 20;
property ParentColor;
property ParentShowHint;
property ShowHint;
property Visible;
property OnPosChanged: TNotifyEvent read FOnPosChanged write FOnPosChanged;
property OnPosChanging: TSplitterMoveEvent read FOnPosChanging write FOnPosChanging;
property OnClick;
property OnDblClick;
property OnEnter;
property OnExit;
property OnMouseDown;
property OnMouseMove;
property OnMouseUp;
property OnResize;
end;
{$IFDEF UNITVERSIONING}
const
UnitVersioning: TUnitVersionInfo = (
RCSfile: '$URL: https://jvcl.svn.sourceforge.net/svnroot/jvcl/tags/JVCL3_32/run/JvSplit.pas $';
Revision: '$Revision: 10688 $';
Date: '$Date: 2006-06-09 15:26:54 +0200 (ven., 09 juin 2006) $';
LogPath: 'JVCL\run'
);
{$ENDIF UNITVERSIONING}
implementation
const
InverseThickness = 2;
DefWidth = 3;
type
TWinControlAccessProtected = class(TWinControl);
function CToC(C1, C2: TControl; P: TPoint): TPoint;
begin
Result := C1.ScreenToClient(C2.ClientToScreen(P));
end;
constructor TJvxSplitter.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
ControlStyle := [csCaptureMouse, csClickEvents,
csOpaque, csDoubleClicks]; // csAcceptsControls
Width := 185;
Height := DefWidth;
FSizing := False;
FTopLeftLimit := 20;
FBottomRightLimit := 20;
FControlFirst := nil;
FControlSecond := nil;
{$IFDEF VCL}
ParentFlat := False;
Flat := True;
{$ENDIF VCL}
end;
procedure TJvxSplitter.Loaded;
begin
inherited Loaded;
UpdateState;
end;
procedure TJvxSplitter.DefineProperties(Filer: TFiler); { for backward compatibility }
begin
inherited DefineProperties(Filer);
Filer.DefineProperty('LimitOffset', ReadOffset, WriteOffset, False);
end;
procedure TJvxSplitter.ReadOffset(Reader: TReader);
var
I: Integer;
begin
I := Reader.ReadInteger;
FTopLeftLimit := I;
FBottomRightLimit := I;
end;
procedure TJvxSplitter.WriteOffset(Writer: TWriter);
begin
Writer.WriteInteger(FTopLeftLimit);
end;
procedure TJvxSplitter.UpdateState;
begin
inherited Cursor := Cursor;
end;
function TJvxSplitter.FindControl: TControl;
var
P: TPoint;
I: Integer;
begin
Result := nil;
P := Point(Left, Top);
case Align of
alLeft:
Dec(P.X);
alRight:
Inc(P.X, Width);
alTop:
Dec(P.Y);
alBottom:
Inc(P.Y, Height);
else
Exit;
end;
for I := 0 to Parent.ControlCount - 1 do
begin
Result := Parent.Controls[I];
if PtInRect(Result.BoundsRect, P) then
Exit;
end;
Result := nil;
end;
procedure TJvxSplitter.CheckPosition(var X, Y: Integer);
begin
if X - FOffset.X < FLimitRect.Left then
X := FLimitRect.Left + FOffset.X
else
if X - FOffset.X + Width > FLimitRect.Right then
X := FLimitRect.Right - Width + FOffset.X;
if Y - FOffset.Y < FLimitRect.Top then
Y := FLimitRect.Top + FOffset.Y
else
if Y - FOffset.Y + Height > FLimitRect.Bottom then
Y := FLimitRect.Bottom + FOffset.Y - Height;
end;
procedure TJvxSplitter.StartInverseRect;
var
R: TRect;
W: Integer;
begin
if Parent = nil then
Exit;
R := Parent.ClientRect;
FLimitRect.TopLeft := CToC(Self, Parent, Point(R.Left + FTopLeftLimit,
R.Top + FTopLeftLimit));
FLimitRect.BottomRight := CToC(Self, Parent, Point(R.Right - R.Left -
FBottomRightLimit, R.Bottom - R.Top - FBottomRightLimit));
FNoDropCursor := False;
FForm := ValidParentForm(Self);
{$IFDEF VCL}
FForm.Canvas.Handle := GetDCEx(FForm.Handle, 0,
DCX_CACHE or DCX_CLIPSIBLINGS or DCX_LOCKWINDOWUPDATE);
{$ENDIF VCL}
with FForm.Canvas do
begin
Pen.Color := clWhite;
if FStyle in [spHorizontalFirst, spHorizontalSecond] then
W := Height
else
W := Width;
if W > InverseThickness + 1 then
W := W - InverseThickness
else
W := InverseThickness;
Pen.Width := W;
Pen.Mode := pmXOR;
end;
ShowInverseRect(Width div 2, Height div 2, imNew);
end;
procedure TJvxSplitter.EndInverseRect(X, Y: Integer; AllowChange, Apply: Boolean);
const
DecSize = 3;
var
NewSize: Integer;
Rect: TRect;
W, H: Integer;
{$IFDEF VCL}
DC: HDC;
{$ENDIF VCL}
P: TPoint;
begin
if FForm <> nil then
begin
ShowInverseRect(0, 0, imClear);
{$IFDEF VCL}
with FForm do
begin
DC := Canvas.Handle;
Canvas.Handle := 0;
ReleaseDC(Handle, DC);
end;
{$ENDIF VCL}
FForm := nil;
end;
FNoDropCursor := False;
if Parent = nil then
Exit;
Rect := Parent.ClientRect;
H := Rect.Bottom - Rect.Top - Height;
W := Rect.Right - Rect.Left - Width;
if not AllowChange then
begin
P := ScreenToClient(FPrevOrg);
X := P.X + FOffset.X - Width div 2;
Y := P.Y + FOffset.Y - Height div 2
end;
if not Apply then
Exit;
CheckPosition(X, Y);
if (ControlFirst.Align = alRight) or
((ControlSecond <> nil) and (ControlSecond.Align = alRight)) then
begin
X := -X;
FOffset.X := -FOffset.X;
end;
if (ControlFirst.Align = alBottom) or
((ControlSecond <> nil) and (ControlSecond.Align = alBottom)) then
begin
Y := -Y;
FOffset.Y := -FOffset.Y;
end;
Parent.DisableAlign;
try
if FStyle = spHorizontalFirst then
begin
NewSize := ControlFirst.Height + Y - FOffset.Y;
if NewSize <= 0 then
NewSize := 1;
if NewSize >= H then
NewSize := H - DecSize;
ControlFirst.Height := NewSize;
end
else
if FStyle = spHorizontalSecond then
begin
NewSize := ControlSecond.Height + Y - FOffset.Y;
if NewSize <= 0 then
NewSize := 1;
if NewSize >= H then
NewSize := H - DecSize;
ControlSecond.Height := NewSize;
end
else
if FStyle = spVerticalFirst then
begin
NewSize := ControlFirst.Width + X - FOffset.X;
if NewSize <= 0 then
NewSize := 1;
if NewSize >= W then
NewSize := W - DecSize;
ControlFirst.Width := NewSize;
end
else
if FStyle = spVerticalSecond then
begin
NewSize := ControlSecond.Width + X - FOffset.X;
if NewSize <= 0 then
NewSize := 1;
if NewSize >= W then
NewSize := W - DecSize;
ControlSecond.Width := NewSize;
end;
finally
Parent.EnableAlign;
end;
end;
procedure TJvxSplitter.MoveInverseRect(X, Y: Integer; AllowChange: Boolean);
var
P: TPoint;
NoDrop: Boolean;
begin
if not AllowChange then
begin
SetCursor(Screen.Cursors[crNoDrop]);
Exit;
end;
P := Point(X, Y);
CheckPosition(X, Y);
NoDrop := not AllowChange or (((X <> P.X) and (FStyle in [spVerticalFirst,
spVerticalSecond])) or ((Y <> P.Y) and (FStyle in [spHorizontalFirst,
spHorizontalSecond])));
if NoDrop <> FNoDropCursor then
begin
FNoDropCursor := NoDrop;
if NoDrop then
SetCursor(Screen.Cursors[crNoDrop])
else
SetCursor(Screen.Cursors[Cursor]);
end;
ShowInverseRect(X - FOffset.X + Width div 2, Y - FOffset.Y + Height div 2,
imMove);
end;
procedure TJvxSplitter.ShowInverseRect(X, Y: Integer; Mode: TInverseMode);
var
P: TPoint;
MaxRect: TRect;
Horiz: Boolean;
begin
P := Point(0, 0);
if FStyle in [spHorizontalFirst, spHorizontalSecond] then
begin
P.Y := Y;
Horiz := True;
end
else
begin
P.X := X;
Horiz := False;
end;
MaxRect := Parent.ClientRect;
P := ClientToScreen(P);
with P, MaxRect do
begin
TopLeft := Parent.ClientToScreen(TopLeft);
BottomRight := Parent.ClientToScreen(BottomRight);
if X < Left then
X := Left;
if X > Right then
X := Right;
if Y < Top then
Y := Top;
if Y > Bottom then
Y := Bottom;
end;
if Mode = imMove then
if ((P.X = FPrevOrg.X) and not Horiz) or
((P.Y = FPrevOrg.Y) and Horiz) then
Exit;
if Mode in [imClear, imMove] then
DrawSizingLine(FPrevOrg);
if Mode in [imNew, imMove] then
begin
DrawSizingLine(P);
FPrevOrg := P;
end;
end;
procedure TJvxSplitter.DrawSizingLine(Split: TPoint);
var
P: TPoint;
begin
if FForm <> nil then
begin
P := FForm.ScreenToClient(Split);
with FForm.Canvas do
begin
MoveTo(P.X, P.Y);
if FStyle in [spHorizontalFirst, spHorizontalSecond] then
LineTo(CToC(FForm, Self, Point(Width, 0)).X, P.Y)
else
LineTo(P.X, CToC(FForm, Self, Point(0, Height)).Y);
end;
end;
end;
function TJvxSplitter.GetStyle: TSplitterStyle;
begin
Result := spUnknown;
if ControlFirst <> nil then
begin
if ((ControlFirst.Align = alTop) and ((ControlSecond = nil) or
(ControlSecond.Align = alClient))) or
((ControlFirst.Align = alBottom) and ((ControlSecond = nil) or
(ControlSecond.Align = alClient))) then
Result := spHorizontalFirst
else
if ((ControlFirst.Align = alClient) and (ControlSecond <> nil) and
(ControlSecond.Align = alBottom)) or
((ControlFirst.Align = alClient) and (ControlSecond <> nil) and
(ControlSecond.Align = alTop)) then
Result := spHorizontalSecond
else
if ((ControlFirst.Align = alLeft) and ((ControlSecond = nil) or
(ControlSecond.Align = alClient))) or
((ControlFirst.Align = alRight) and ((ControlSecond = nil) or
(ControlSecond.Align = alClient))) then
Result := spVerticalFirst
else
if ((ControlFirst.Align = alClient) and (ControlSecond <> nil) and
(ControlSecond.Align = alRight)) or
((ControlFirst.Align = alClient) and (ControlSecond <> nil) and
(ControlSecond.Align = alLeft)) then
Result := spVerticalSecond;
case Result of
spHorizontalFirst, spVerticalFirst:
if Align <> FControlFirst.Align then
Result := spUnknown;
spHorizontalSecond, spVerticalSecond:
if Align <> FControlSecond.Align then
Result := spUnknown;
end;
end;
end;
procedure TJvxSplitter.SetAlign(Value: TAlign);
begin
if not (Align in [alTop, alBottom, alLeft, alRight]) then
begin
inherited Align := Value;
if not (csReading in ComponentState) then
begin
if Value in [alTop, alBottom] then
Height := DefWidth
else
if Value in [alLeft, alRight] then
Width := DefWidth;
end;
end
else
inherited Align := Value;
if (ControlFirst = nil) and (ControlSecond = nil) then
ControlFirst := FindControl;
end;
function TJvxSplitter.GetAlign: TAlign;
begin
Result := inherited Align;
end;
function TJvxSplitter.GetCursor: TCursor;
begin
Result := crDefault;
case GetStyle of
spHorizontalFirst, spHorizontalSecond:
Result := crVSplit;
spVerticalFirst, spVerticalSecond:
Result := crHSplit;
end;
end;
procedure TJvxSplitter.SetControlFirst(Value: TControl);
begin
if Value <> FControlFirst then
begin
if (Value = Self) or (Value is TForm) then
FControlFirst := nil
else
begin
FControlFirst := Value;
if Value <> nil then
Value.FreeNotification(Self);
end;
UpdateState;
end;
end;
procedure TJvxSplitter.SetControlSecond(Value: TControl);
begin
if Value <> FControlSecond then
begin
if (Value = Self) or (Value is TForm) then
FControlSecond := nil
else
begin
FControlSecond := Value;
if Value <> nil then
Value.FreeNotification(Self);
end;
UpdateState;
end;
end;
procedure TJvxSplitter.Notification(AComponent: TComponent; AOperation: TOperation);
begin
inherited Notification(AComponent, AOperation);
if AOperation = opRemove then
begin
if AComponent = ControlFirst then
ControlFirst := nil
else
if AComponent = ControlSecond then
ControlSecond := nil;
end;
end;
procedure TJvxSplitter.Changed;
begin
if Assigned(FOnPosChanged) then
FOnPosChanged(Self);
end;
procedure TJvxSplitter.Changing(X, Y: Integer; var AllowChange: Boolean);
begin
if Assigned(FOnPosChanging) then
FOnPosChanging(Self, X, Y, AllowChange);
end;
procedure TJvxSplitter.StopSizing(X, Y: Integer; Apply: Boolean);
var
AllowChange: Boolean;
begin
if FSizing then
begin
ReleaseCapture;
AllowChange := Apply;
if Apply then
Changing(X, Y, AllowChange);
EndInverseRect(X, Y, AllowChange, Apply);
FSizing := False;
Application.ShowHint := FAppShowHint;
if Assigned(FActiveControl) then
begin
TWinControlAccessProtected(FActiveControl).OnKeyDown := FOldKeyDown;
FActiveControl := nil;
end;
if Apply then
Changed;
end;
end;
procedure TJvxSplitter.ControlKeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
begin
if Assigned(FOldKeyDown) then
FOldKeyDown(Sender, Key, Shift);
StopSizing(0, 0, False);
end;
procedure TJvxSplitter.MouseDown(Button: TMouseButton; Shift: TShiftState;
X, Y: Integer);
begin
inherited MouseDown(Button, Shift, X, Y);
if not (csDesigning in ComponentState) and (Button = mbLeft) then
begin
FStyle := GetStyle;
if FStyle <> spUnknown then
begin
FSizing := True;
FAppShowHint := Application.ShowHint;
SetCapture(Handle);
with ValidParentForm(Self) do
begin
if ActiveControl <> nil then
FActiveControl := ActiveControl
else
FActiveControl := GetParentForm(Self);
FOldKeyDown := TWinControlAccessProtected(FActiveControl).OnKeyDown;
TWinControlAccessProtected(FActiveControl).OnKeyDown := ControlKeyDown;
end;
Application.ShowHint := False;
FOffset := Point(X, Y);
StartInverseRect;
end;
end;
end;
procedure TJvxSplitter.MouseMove(Shift: TShiftState; X, Y: Integer);
var
AllowChange: Boolean;
begin
inherited MouseMove(Shift, X, Y);
if (GetCapture = Handle) and FSizing then
begin
AllowChange := True;
Changing(X, Y, AllowChange);
MoveInverseRect(X, Y, AllowChange);
end;
end;
procedure TJvxSplitter.MouseUp(Button: TMouseButton; Shift: TShiftState;
X, Y: Integer);
begin
StopSizing(X, Y, True);
inherited MouseUp(Button, Shift, X, Y);
end;
{$IFDEF UNITVERSIONING}
initialization
RegisterUnitVersion(HInstance, UnitVersioning);
finalization
UnregisterUnitVersion(HInstance);
{$ENDIF UNITVERSIONING}
end.