home *** CD-ROM | disk | FTP | other *** search
- {================================================================================
- Copyright (C) 1997-2001 Mills Enterprise
-
- Unit : rmSplit
- Purpose : Fix for original Delphi Bug. Further enhancements included....
- Date : 02-18-1999
- Author : Ryan J. Mills
- Version : 1.80
- ================================================================================}
-
- unit rmSplit;
-
- interface
-
- {$I CompilerDefines.INC}
-
- uses Messages, Windows, SysUtils, Classes, Controls, Forms, Menus, Graphics,
- StdCtrls;
-
- type
- NaturalNumber = 1..High(Integer);
-
- TCanResizeEvent = procedure(Sender: TObject; var NewSize: Integer;
- var Accept: Boolean) of object;
-
- TResizeStyle = (rsNone, rsLine, rsUpdate, rsPattern);
-
- TrmSplitter = class(TGraphicControl)
- private
- FActiveControl: TWinControl;
- FBeveled: Boolean;
- FBrush: TBrush;
- FControl: TControl;
- FDownPos: TPoint;
- FLineDC: HDC;
- FLineVisible: Boolean;
- FMinSize: NaturalNumber;
- FMaxSize: Integer;
- FNewSize: Integer;
- FOldKeyDown: TKeyEvent;
- FOldSize: Integer;
- FPrevBrush: HBrush;
- FResizeStyle: TResizeStyle;
- FSplit: Integer;
- FOnCanResize: TCanResizeEvent;
- FOnMoved: TNotifyEvent;
- FOnPaint: TNotifyEvent;
- procedure AllocateLineDC;
- procedure CalcSplitSize(X, Y: Integer; var NewSize, Split: Integer);
- procedure DrawLine;
- function FindControl: TControl;
- procedure FocusKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
- procedure ReleaseLineDC;
- procedure SetBeveled(Value: Boolean);
- procedure UpdateControlSize;
- procedure UpdateSize(X, Y: Integer);
- protected
- function CanResize(var NewSize: Integer): Boolean; {$ifdef D4_OR_HIGHER} reintroduce; {$endif} virtual;
-
- function DoCanResize(var NewSize: Integer): Boolean; virtual;
- 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 Paint; override;
- procedure RequestAlign; override;
- procedure StopSizing; dynamic;
- public
- constructor Create(AOwner: TComponent); override;
- destructor Destroy; override;
- published
- property Align default alLeft;
- property Beveled: Boolean read FBeveled write SetBeveled default False;
- property Color;
- {$ifdef D4_OR_HIGHER}
- property Constraints;
- {$endif}
- property MinSize: NaturalNumber read FMinSize write FMinSize default 30;
- property ParentColor;
- property ResizeStyle: TResizeStyle read FResizeStyle write FResizeStyle
- default rsPattern;
- property Visible;
- property OnCanResize: TCanResizeEvent read FOnCanResize write FOnCanResize;
- property OnMoved: TNotifyEvent read FOnMoved write FOnMoved;
- property OnPaint: TNotifyEvent read FOnPaint write FOnPaint;
- end;
-
- implementation
-
- { TrmSplitter }
-
- type
- TWinControlAccess = class(TWinControl);
-
- constructor TrmSplitter.Create(AOwner: TComponent);
- begin
- inherited Create(AOwner);
- Align := alLeft;
- Width := 3;
- Cursor := crHSplit;
- FMinSize := 30;
- FResizeStyle := rsPattern;
- FOldSize := -1;
- end;
-
- destructor TrmSplitter.Destroy;
- begin
- FBrush.Free;
- inherited Destroy;
- end;
-
- procedure TrmSplitter.AllocateLineDC;
- begin
- FLineDC := GetDCEx(Parent.Handle, 0, DCX_CACHE or DCX_CLIPSIBLINGS
- or DCX_LOCKWINDOWUPDATE);
- if ResizeStyle = rsPattern then
- begin
- if FBrush = nil then
- begin
- FBrush := TBrush.Create;
- FBrush.Bitmap := AllocPatternBitmap(clBlack, clWhite);
- end;
- FPrevBrush := SelectObject(FLineDC, FBrush.Handle);
- end;
- end;
-
- procedure TrmSplitter.DrawLine;
- var
- P: TPoint;
- begin
- FLineVisible := not FLineVisible;
- P := Point(Left, Top);
- if Align in [alLeft, alRight] then
- P.X := Left + FSplit else
- P.Y := Top + FSplit;
- with P do PatBlt(FLineDC, X, Y, Width, Height, PATINVERT);
- end;
-
- procedure TrmSplitter.ReleaseLineDC;
- begin
- if FPrevBrush <> 0 then
- SelectObject(FLineDC, FPrevBrush);
- ReleaseDC(Parent.Handle, FLineDC);
- if FBrush <> nil then
- begin
- FBrush.Free;
- FBrush := nil;
- end;
- end;
-
- function TrmSplitter.FindControl: TControl;
- var
- P: TPoint;
- I: Integer;
- R: TRect;
- 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 Result.Visible and Result.Enabled then
- begin
- R := Result.BoundsRect;
- if (R.Right - R.Left) = 0 then
- if Align in [alTop, alLeft] then
- Dec(R.Left)
- else
- Inc(R.Right);
- if (R.Bottom - R.Top) = 0 then
- if Align in [alTop, alLeft] then
- Dec(R.Top)
- else
- Inc(R.Bottom);
- if PtInRect(R, P) then Exit;
- end;
- end;
- Result := nil;
- end;
-
- procedure TrmSplitter.RequestAlign;
- begin
- inherited RequestAlign;
- if (Cursor <> crVSplit) and (Cursor <> crHSplit) then Exit;
- if Align in [alBottom, alTop] then
- Cursor := crVSplit
- else
- Cursor := crHSplit;
- end;
-
- procedure TrmSplitter.Paint;
- const
- XorColor = $00FFD8CE;
- var
- FrameBrush: HBRUSH;
- R: TRect;
- begin
- R := ClientRect;
- Canvas.Brush.Color := Color;
- Canvas.FillRect(ClientRect);
- if Beveled then
- begin
- if Align in [alLeft, alRight] then
- InflateRect(R, -1, 2) else
- InflateRect(R, 2, -1);
- OffsetRect(R, 1, 1);
- FrameBrush := CreateSolidBrush(ColorToRGB(clBtnHighlight));
- FrameRect(Canvas.Handle, R, FrameBrush);
- DeleteObject(FrameBrush);
- OffsetRect(R, -2, -2);
- FrameBrush := CreateSolidBrush(ColorToRGB(clBtnShadow));
- FrameRect(Canvas.Handle, R, FrameBrush);
- DeleteObject(FrameBrush);
- end;
- if csDesigning in ComponentState then
- { Draw outline }
- with Canvas do
- begin
- Pen.Style := psDot;
- Pen.Mode := pmXor;
- Pen.Color := XorColor;
- Brush.Style := bsClear;
- Rectangle(0, 0, ClientWidth, ClientHeight);
- end;
- if Assigned(FOnPaint) then FOnPaint(Self);
- end;
-
- function TrmSplitter.DoCanResize(var NewSize: Integer): Boolean;
- begin
- Result := CanResize(NewSize);
- if Result and (NewSize <= MinSize) then
- NewSize := MinSize;
- end;
-
- function TrmSplitter.CanResize(var NewSize: Integer): Boolean;
- begin
- Result := True;
- if Assigned(FOnCanResize) then FOnCanResize(Self, NewSize, Result);
- end;
-
- procedure TrmSplitter.MouseDown(Button: TMouseButton; Shift: TShiftState;
- X, Y: Integer);
- var
- I: Integer;
- begin
- inherited MouseDown(Button, Shift, X, Y);
- if Button = mbLeft then
- begin
- FControl := FindControl;
- FDownPos := Point(X, Y);
- if Assigned(FControl) then
- begin
- if Align in [alLeft, alRight] then
- begin
- FMaxSize := Parent.ClientWidth - FMinSize;
- for I := 0 to Parent.ControlCount - 1 do
- with Parent.Controls[I] do
- if Align in [alLeft, alRight] then Dec(FMaxSize, Width);
- Inc(FMaxSize, FControl.Width);
- end
- else
- begin
- FMaxSize := Parent.ClientHeight - FMinSize;
- for I := 0 to Parent.ControlCount - 1 do
- with Parent.Controls[I] do
- if Align in [alTop, alBottom] then Dec(FMaxSize, Height);
- Inc(FMaxSize, FControl.Height);
- end;
- UpdateSize(X, Y);
- AllocateLineDC;
- with ValidParentForm(Self) do
- if ActiveControl <> nil then
- begin
- FActiveControl := ActiveControl;
- FOldKeyDown := TWinControlAccess(FActiveControl).OnKeyDown;
- TWinControlAccess(FActiveControl).OnKeyDown := FocusKeyDown;
- end;
- if ResizeStyle in [rsLine, rsPattern] then DrawLine;
- end;
- end;
- end;
-
- procedure TrmSplitter.UpdateControlSize;
- begin
- if FNewSize <> FOldSize then
- begin
- case Align of
- alLeft: FControl.Width := FNewSize;
- alTop: FControl.Height := FNewSize;
- alRight:
- begin
- Parent.DisableAlign;
- try
- FControl.Left := FControl.Left + (FControl.Width - FNewSize);
- FControl.Width := FNewSize;
- finally
- Parent.EnableAlign;
- end;
- end;
- alBottom:
- begin
- Parent.DisableAlign;
- try
- FControl.Top := FControl.Top + (FControl.Height - FNewSize);
- FControl.Height := FNewSize;
- finally
- Parent.EnableAlign;
- end;
- end;
- end;
- Update;
- if Assigned(FOnMoved) then FOnMoved(Self);
- FOldSize := FNewSize;
- end;
- end;
-
- procedure TrmSplitter.CalcSplitSize(X, Y: Integer; var NewSize, Split: Integer);
- var
- S: Integer;
- begin
- if Align in [alLeft, alRight] then
- Split := X - FDownPos.X
- else
- Split := Y - FDownPos.Y;
- S := 0;
- case Align of
- alLeft: S := FControl.Width + Split;
- alRight: S := FControl.Width - Split;
- alTop: S := FControl.Height + Split;
- alBottom: S := FControl.Height - Split;
- end;
- NewSize := S;
- if S < FMinSize then
- NewSize := FMinSize
- else if S > FMaxSize then
- NewSize := FMaxSize;
- if S <> NewSize then
- begin
- if Align in [alRight, alBottom] then
- S := S - NewSize else
- S := NewSize - S;
- Inc(Split, S);
- end;
- end;
-
- procedure TrmSplitter.UpdateSize(X, Y: Integer);
- begin
- CalcSplitSize(X, Y, FNewSize, FSplit);
- end;
-
- procedure TrmSplitter.MouseMove(Shift: TShiftState; X, Y: Integer);
- var
- NewSize, Split: Integer;
- begin
- inherited;
- if (ssLeft in Shift) and Assigned(FControl) then
- begin
- CalcSplitSize(X, Y, NewSize, Split);
- if DoCanResize(NewSize) then
- begin
- if ResizeStyle in [rsLine, rsPattern] then DrawLine;
- FNewSize := NewSize;
- FSplit := Split;
- if ResizeStyle = rsUpdate then UpdateControlSize;
- if ResizeStyle in [rsLine, rsPattern] then DrawLine;
- end;
- end;
- end;
-
- procedure TrmSplitter.MouseUp(Button: TMouseButton; Shift: TShiftState;
- X, Y: Integer);
- begin
- inherited;
- if Assigned(FControl) then
- begin
- if ResizeStyle in [rsLine, rsPattern] then DrawLine;
- UpdateControlSize;
- StopSizing;
- end;
- end;
-
- procedure TrmSplitter.FocusKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
- begin
- if Key = VK_ESCAPE then
- StopSizing
- else if Assigned(FOldKeyDown) then
- FOldKeyDown(Sender, Key, Shift);
- end;
-
- procedure TrmSplitter.SetBeveled(Value: Boolean);
- begin
- FBeveled := Value;
- Repaint;
- end;
-
- procedure TrmSplitter.StopSizing;
- begin
- if Assigned(FControl) then
- begin
- if FLineVisible then DrawLine;
- FControl := nil;
- ReleaseLineDC;
- if Assigned(FActiveControl) then
- begin
- TWinControlAccess(FActiveControl).OnKeyDown := FOldKeyDown;
- FActiveControl := nil;
- end;
- end;
- if Assigned(FOnMoved) then
- FOnMoved(Self);
- end;
-
-
- end.
-