Rxsplit.pas
上传用户:hylc_2004
上传日期:2014-01-23
资源大小:46800k
文件大小:17k
源码类别:

Delphi控件源码

开发平台:

Delphi

  1. {*******************************************************}
  2. {                                                       }
  3. {         Delphi VCL Extensions (RX)                    }
  4. {                                                       }
  5. {         Copyright (c) 1995, 1996 AO ROSNO             }
  6. {         Copyright (c) 1997 Master-Bank                }
  7. {                                                       }
  8. {*******************************************************}
  9. unit RXSplit;
  10. interface
  11. {$I RX.INC}
  12. uses Classes, {$IFDEF WIN32} Windows, {$ELSE} WinTypes, WinProcs, {$ENDIF}
  13.   Controls, ExtCtrls, Forms, Graphics, VCLUtils;
  14. type
  15. { TRxSplitter }
  16.   TSplitterStyle = (spUnknown, spHorizontalFirst, spHorizontalSecond,
  17.     spVerticalFirst, spVerticalSecond);
  18.   TInverseMode = (imNew, imClear, imMove);
  19.   TSplitterMoveEvent = procedure (Sender: TObject; X, Y: Integer;
  20.     var AllowChange: Boolean) of object;
  21.   TRxSplitter = class(TCustomPanel)
  22.   private
  23.     FControlFirst: TControl;
  24.     FControlSecond: TControl;
  25.     FSizing: Boolean;
  26.     FStyle: TSplitterStyle;
  27.     FPrevOrg: TPoint;
  28.     FOffset: TPoint;
  29.     FNoDropCursor: Boolean;
  30.     FLimitRect: TRect;
  31.     FTopLeftLimit: Integer;
  32.     FBottomRightLimit: Integer;
  33.     FForm: TCustomForm;
  34.     FActiveControl: TWinControl;
  35.     FAppShowHint: Boolean;
  36.     FOldKeyDown: TKeyEvent;
  37.     FOnPosChanged: TNotifyEvent;
  38.     FOnPosChanging: TSplitterMoveEvent;
  39.     function FindControl: TControl;
  40.     procedure ControlKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
  41.     procedure StartInverseRect;
  42.     procedure EndInverseRect(X, Y: Integer; AllowChange, Apply: Boolean);
  43.     function GetAlign: TAlign;
  44.     procedure MoveInverseRect(X, Y: Integer; AllowChange: Boolean);
  45.     procedure ShowInverseRect(X, Y: Integer; Mode: TInverseMode);
  46.     procedure DrawSizingLine(Split: TPoint);
  47.     function GetStyle: TSplitterStyle;
  48.     function GetCursor: TCursor;
  49.     procedure SetControlFirst(Value: TControl);
  50.     procedure SetControlSecond(Value: TControl);
  51.     procedure SetAlign(Value: TAlign);
  52.     procedure StopSizing(X, Y: Integer; Apply: Boolean);
  53.     procedure CheckPosition(var X, Y: Integer);
  54.     procedure ReadOffset(Reader: TReader);
  55.     procedure WriteOffset(Writer: TWriter);
  56.   protected
  57.     procedure DefineProperties(Filer: TFiler); override;
  58.     procedure Loaded; override;
  59.     procedure Notification(AComponent: TComponent; AOperation: TOperation); override;
  60.     procedure MouseDown(Button: TMouseButton; Shift: TShiftState;
  61.       X, Y: Integer); override;
  62.     procedure MouseMove(Shift: TShiftState; X, Y: Integer); override;
  63.     procedure MouseUp(Button: TMouseButton; Shift: TShiftState;
  64.       X, Y: Integer); override;
  65.     procedure Changed; dynamic;
  66.     procedure Changing(X, Y: Integer; var AllowChange: Boolean); dynamic;
  67.   public
  68.     constructor Create(AOwner: TComponent); override;
  69.     procedure UpdateState;
  70.   published
  71.     property ControlFirst: TControl read FControlFirst write SetControlFirst;
  72.     property ControlSecond: TControl read FControlSecond write SetControlSecond;
  73.     property Align: TAlign read GetAlign write SetAlign default alNone;
  74. {$IFDEF RX_D4}
  75.     property Constraints;
  76. {$ENDIF}
  77.     property BevelInner;
  78.     property BevelOuter;
  79.     property BevelWidth;
  80.     property BorderStyle;
  81.     property Enabled;
  82.     property Color;
  83.     property Ctl3D {$IFDEF WIN32} default False {$ENDIF};
  84.     property Cursor read GetCursor stored False;
  85.     property TopLeftLimit: Integer read FTopLeftLimit write FTopLeftLimit default 20;
  86.     property BottomRightLimit: Integer read FBottomRightLimit write FBottomRightLimit default 20;
  87.     property ParentColor;
  88.     property ParentCtl3D default False;
  89.     property ParentShowHint;
  90.     property ShowHint;
  91.     property Visible;
  92.     property OnPosChanged: TNotifyEvent read FOnPosChanged write FOnPosChanged;
  93.     property OnPosChanging: TSplitterMoveEvent read FOnPosChanging write FOnPosChanging;
  94.     property OnClick;
  95.     property OnDblClick;
  96.     property OnEnter;
  97.     property OnExit;
  98.     property OnMouseDown;
  99.     property OnMouseMove;
  100.     property OnMouseUp;
  101.     property OnResize;
  102.   end;
  103. implementation
  104. uses SysUtils;
  105. const
  106.   InverseThickness = 2;
  107.   DefWidth = 3;
  108. function CToC(C1, C2: TControl; P: TPoint): TPoint;
  109. begin
  110.   Result := C1.ScreenToClient(C2.ClientToScreen(P));
  111. end;
  112. type
  113.   THack = class(TWinControl);
  114. { TRxSplitter }
  115. constructor TRxSplitter.Create(AOwner: TComponent);
  116. begin
  117.   inherited Create(AOwner);
  118.   ControlStyle := [csAcceptsControls, csCaptureMouse, csClickEvents,
  119.     csOpaque, csDoubleClicks];
  120.   Width := 185;
  121.   Height := DefWidth;
  122.   FSizing := False;
  123.   FTopLeftLimit := 20;
  124.   FBottomRightLimit := 20;
  125.   FControlFirst := nil;
  126.   FControlSecond := nil;
  127.   ParentCtl3D := False;
  128. {$IFDEF WIN32}
  129.   Ctl3D := False;
  130. {$ENDIF}
  131. end;
  132. procedure TRxSplitter.Loaded;
  133. begin
  134.   inherited Loaded;
  135.   UpdateState;
  136. end;
  137. procedure TRxSplitter.DefineProperties(Filer: TFiler); { for backward compatibility }
  138. begin
  139.   inherited DefineProperties(Filer);
  140.   Filer.DefineProperty('LimitOffset', ReadOffset, WriteOffset, False);
  141. end;
  142. procedure TRxSplitter.ReadOffset(Reader: TReader);
  143. var
  144.   I: Integer;
  145. begin
  146.   I := Reader.ReadInteger;
  147.   FTopLeftLimit := I;
  148.   FBottomRightLimit := I;
  149. end;
  150. procedure TRxSplitter.WriteOffset(Writer: TWriter);
  151. begin
  152.   Writer.WriteInteger(FTopLeftLimit);
  153. end;
  154. procedure TRxSplitter.UpdateState;
  155. begin
  156.   inherited Cursor := Cursor;
  157. end;
  158. function TRxSplitter.FindControl: TControl;
  159. var
  160.   P: TPoint;
  161.   I: Integer;
  162. begin
  163.   Result := nil;
  164.   P := Point(Left, Top);
  165.   case Align of
  166.     alLeft: Dec(P.X);
  167.     alRight: Inc(P.X, Width);
  168.     alTop: Dec(P.Y);
  169.     alBottom: Inc(P.Y, Height);
  170.     else Exit;
  171.   end;
  172.   for I := 0 to Parent.ControlCount - 1 do begin
  173.     Result := Parent.Controls[I];
  174.     if PtInRect(Result.BoundsRect, P) then Exit;
  175.   end;
  176.   Result := nil;
  177. end;
  178. procedure TRxSplitter.CheckPosition(var X, Y: Integer);
  179. begin
  180.   if X - FOffset.X < FLimitRect.Left then
  181.     X := FLimitRect.Left + FOffset.X
  182.   else if X - FOffset.X + Width > FLimitRect.Right then
  183.     X := FLimitRect.Right - Width + FOffset.X;
  184.   if Y - FOffset.Y < FLimitRect.Top then
  185.     Y := FLimitRect.Top + FOffset.Y
  186.   else if Y - FOffset.Y + Height > FLimitRect.Bottom then
  187.     Y := FLimitRect.Bottom + FOffset.Y - Height;
  188. end;
  189. procedure TRxSplitter.StartInverseRect;
  190. var
  191.   R: TRect;
  192.   W: Integer;
  193. begin
  194.   if Parent = nil then Exit;
  195.   R := Parent.ClientRect;
  196.   FLimitRect.TopLeft := CToC(Self, Parent, Point(R.Left + FTopLeftLimit,
  197.     R.Top + FTopLeftLimit));
  198.   FLimitRect.BottomRight := CToC(Self, Parent, Point(R.Right - R.Left -
  199.     FBottomRightLimit, R.Bottom - R.Top - FBottomRightLimit));
  200.   FNoDropCursor := False;
  201.   FForm := ValidParentForm(Self);
  202.   FForm.Canvas.Handle := GetDCEx(FForm.Handle, 0, DCX_CACHE or DCX_CLIPSIBLINGS
  203.     or DCX_LOCKWINDOWUPDATE);
  204.   with FForm.Canvas do begin
  205.     Pen.Color := clWhite;
  206.     if FStyle in [spHorizontalFirst, spHorizontalSecond] then W := Height
  207.     else W := Width;
  208.     if W > InverseThickness + 1 then W := W - InverseThickness
  209.     else W := InverseThickness;
  210.     Pen.Width := W;
  211.     Pen.Mode := pmXOR;
  212.   end;
  213.   ShowInverseRect(Width div 2, Height div 2, imNew);
  214. end;
  215. procedure TRxSplitter.EndInverseRect(X, Y: Integer; AllowChange,
  216.   Apply: Boolean);
  217. const
  218.   DecSize = 3;
  219. var
  220.   NewSize: Integer;
  221.   Rect: TRect;
  222.   W, H: Integer;
  223.   DC: HDC;
  224.   P: TPoint;
  225. begin
  226.   if FForm <> nil then begin
  227.     ShowInverseRect(0, 0, imClear);
  228.     with FForm do begin
  229.       DC := Canvas.Handle;
  230.       Canvas.Handle := 0;
  231.       ReleaseDC(Handle, DC);
  232.     end;
  233.     FForm := nil;
  234.   end;
  235.   FNoDropCursor := False;
  236.   if Parent = nil then Exit;
  237.   Rect := Parent.ClientRect;
  238.   H := Rect.Bottom - Rect.Top - Height;
  239.   W := Rect.Right - Rect.Left - Width;
  240.   if not AllowChange then begin
  241.     P := ScreenToClient(FPrevOrg);
  242.     X := P.X + FOffset.X - Width div 2;
  243.     Y := P.Y + FOffset.Y - Height div 2
  244.   end;
  245.   if not Apply then Exit;
  246.   CheckPosition(X, Y);
  247.   if (ControlFirst.Align = alRight) or
  248.     ((ControlSecond <> nil) and (ControlSecond.Align = alRight)) then
  249.   begin
  250.     X := -X;
  251.     FOffset.X := -FOffset.X;
  252.   end;
  253.   if (ControlFirst.Align = alBottom) or
  254.     ((ControlSecond <> nil) and (ControlSecond.Align = alBottom)) then
  255.   begin
  256.     Y := -Y;
  257.     FOffset.Y := -FOffset.Y;
  258.   end;
  259.   Parent.DisableAlign;
  260.   try
  261.     if FStyle = spHorizontalFirst then begin
  262.       NewSize := ControlFirst.Height + Y - FOffset.Y;
  263.       if NewSize <= 0 then NewSize := 1;
  264.       if NewSize >= H then NewSize := H - DecSize;
  265.       ControlFirst.Height := NewSize;
  266.     end
  267.     else if FStyle = spHorizontalSecond then begin
  268.       NewSize := ControlSecond.Height + Y - FOffset.Y;
  269.       if NewSize <= 0 then NewSize := 1;
  270.       if NewSize >= H then NewSize := H - DecSize;
  271.       ControlSecond.Height := NewSize;
  272.     end
  273.     else if FStyle = spVerticalFirst then begin
  274.       NewSize := ControlFirst.Width + X - FOffset.X;
  275.       if NewSize <= 0 then NewSize := 1;
  276.       if NewSize >= W then NewSize := W - DecSize;
  277.       ControlFirst.Width := NewSize;
  278.     end
  279.     else if FStyle = spVerticalSecond then begin
  280.       NewSize := ControlSecond.Width + X - FOffset.X;
  281.       if NewSize <= 0 then NewSize := 1;
  282.       if NewSize >= W then NewSize := W - DecSize;
  283.       ControlSecond.Width := NewSize;
  284.     end;
  285.   finally
  286.     Parent.EnableAlign;
  287.   end;
  288. end;
  289. procedure TRxSplitter.MoveInverseRect(X, Y: Integer; AllowChange: Boolean);
  290. var
  291.   P: TPoint;
  292.   NoDrop: Boolean;
  293. begin
  294.   if not AllowChange then begin
  295.     SetCursor(Screen.Cursors[crNoDrop]);
  296.     Exit;
  297.   end;
  298.   P := Point(X, Y);
  299.   CheckPosition(X, Y);
  300.   NoDrop := not AllowChange or (((X <> P.X) and (FStyle in [spVerticalFirst,
  301.     spVerticalSecond])) or ((Y <> P.Y) and (FStyle in [spHorizontalFirst,
  302.     spHorizontalSecond])));
  303.   if NoDrop <> FNoDropCursor then begin
  304.     FNoDropCursor := NoDrop;
  305.     if NoDrop then SetCursor(Screen.Cursors[crNoDrop])
  306.     else SetCursor(Screen.Cursors[Cursor]);
  307.   end;
  308.   ShowInverseRect(X - FOffset.X + Width div 2, Y - FOffset.Y + Height div 2,
  309.     imMove);
  310. end;
  311. procedure TRxSplitter.ShowInverseRect(X, Y: Integer; Mode: TInverseMode);
  312. var
  313.   P: TPoint;
  314.   MaxRect: TRect;
  315.   Horiz: Boolean;
  316. begin
  317.   P := Point(0, 0);
  318.   if FStyle in [spHorizontalFirst, spHorizontalSecond] then begin
  319.     P.Y := Y;
  320.     Horiz := True;
  321.   end
  322.   else begin
  323.     P.X := X;
  324.     Horiz := False;
  325.   end;
  326.   MaxRect := Parent.ClientRect;
  327.   P := ClientToScreen(P);
  328.   with P, MaxRect do begin
  329.     TopLeft := Parent.ClientToScreen(TopLeft);
  330.     BottomRight := Parent.ClientToScreen(BottomRight);
  331.     if X < Left then X := Left;
  332.     if X > Right then X := Right;
  333.     if Y < Top then Y := Top;
  334.     if Y > Bottom then Y := Bottom;
  335.   end;
  336.   if (Mode = imMove) then
  337.     if ((P.X = FPrevOrg.X) and not Horiz) or
  338.       ((P.Y = FPrevOrg.Y) and Horiz) then Exit;
  339.   if Mode in [imClear, imMove] then
  340.     DrawSizingLine(FPrevOrg);
  341.   if Mode in [imNew, imMove] then begin
  342.     DrawSizingLine(P);
  343.     FPrevOrg := P;
  344.   end;
  345. end;
  346. procedure TRxSplitter.DrawSizingLine(Split: TPoint);
  347. var
  348.   P: TPoint;
  349. begin
  350.   if FForm <> nil then begin
  351.     P := FForm.ScreenToClient(Split);
  352.     with FForm.Canvas do begin
  353.       MoveTo(P.X, P.Y);
  354.       if FStyle in [spHorizontalFirst, spHorizontalSecond] then
  355.         LineTo(CToC(FForm, Self, Point(Width, 0)).X, P.Y)
  356.       else LineTo(P.X, CToC(FForm, Self, Point(0, Height)).Y);
  357.     end;
  358.   end;
  359. end;
  360. function TRxSplitter.GetStyle: TSplitterStyle;
  361. begin
  362.   Result := spUnknown;
  363.   if ControlFirst <> nil then begin
  364.     if ((ControlFirst.Align = alTop) and ((ControlSecond = nil) or
  365.        (ControlSecond.Align = alClient))) or
  366.        ((ControlFirst.Align = alBottom) and ((ControlSecond = nil) or
  367.        (ControlSecond.Align = alClient))) then
  368.       Result := spHorizontalFirst
  369.     else if ((ControlFirst.Align = alClient) and (ControlSecond <> nil) and
  370.        (ControlSecond.Align = alBottom)) or
  371.        ((ControlFirst.Align = alClient) and (ControlSecond <> nil) and
  372.        (ControlSecond.Align = alTop)) then
  373.       Result := spHorizontalSecond
  374.     else if ((ControlFirst.Align = alLeft) and ((ControlSecond = nil) or
  375.        (ControlSecond.Align = alClient))) or
  376.        ((ControlFirst.Align = alRight) and ((ControlSecond = nil) or
  377.        (ControlSecond.Align = alClient))) then
  378.       Result := spVerticalFirst
  379.     else if ((ControlFirst.Align = alClient) and (ControlSecond <> nil) and
  380.        (ControlSecond.Align = alRight)) or
  381.        ((ControlFirst.Align = alClient) and (ControlSecond <> nil) and
  382.        (ControlSecond.Align = alLeft)) then
  383.       Result := spVerticalSecond;
  384.     case Result of
  385.       spHorizontalFirst, spVerticalFirst:
  386.         if Align <> FControlFirst.Align then Result := spUnknown;
  387.       spHorizontalSecond, spVerticalSecond:
  388.         if Align <> FControlSecond.Align then Result := spUnknown;
  389.     end;
  390.   end;
  391. end;
  392. procedure TRxSplitter.SetAlign(Value: TAlign);
  393. begin
  394.   if not (Align in [alTop, alBottom, alLeft, alRight]) then begin
  395.     inherited Align := Value;
  396.     if not (csReading in ComponentState) then begin
  397.       if Value in [alTop, alBottom] then Height := DefWidth
  398.       else if Value in [alLeft, alRight] then Width := DefWidth;
  399.     end;
  400.   end
  401.   else inherited Align := Value;
  402.   if (ControlFirst = nil) and (ControlSecond = nil) then
  403.     ControlFirst := FindControl;
  404. end;
  405. function TRxSplitter.GetAlign: TAlign;
  406. begin
  407.   Result := inherited Align;
  408. end;
  409. function TRxSplitter.GetCursor: TCursor;
  410. begin
  411.   Result := crDefault;
  412.   case GetStyle of
  413.     spHorizontalFirst, spHorizontalSecond: Result := crVSplit;
  414.     spVerticalFirst, spVerticalSecond: Result := crHSplit;
  415.   end;
  416. end;
  417. procedure TRxSplitter.SetControlFirst(Value: TControl);
  418. begin
  419.   if Value <> FControlFirst then begin
  420.     if (Value = Self) or (Value is TForm) then FControlFirst := nil
  421.     else begin
  422.       FControlFirst := Value;
  423. {$IFDEF WIN32}
  424.       if Value <> nil then Value.FreeNotification(Self);
  425. {$ENDIF}
  426.     end;
  427.     UpdateState;
  428.   end;
  429. end;
  430. procedure TRxSplitter.SetControlSecond(Value: TControl);
  431. begin
  432.   if Value <> FControlSecond then begin
  433.     if (Value = Self) or (Value is TForm) then FControlSecond := nil
  434.     else begin
  435.       FControlSecond := Value;
  436. {$IFDEF WIN32}
  437.       if Value <> nil then Value.FreeNotification(Self);
  438. {$ENDIF}
  439.     end;
  440.     UpdateState;
  441.   end;
  442. end;
  443. procedure TRxSplitter.Notification(AComponent: TComponent; AOperation: TOperation);
  444. begin
  445.   inherited Notification(AComponent, AOperation);
  446.   if AOperation = opRemove then begin
  447.     if AComponent = ControlFirst then ControlFirst := nil
  448.     else if AComponent = ControlSecond then ControlSecond := nil;
  449.   end;
  450. end;
  451. procedure TRxSplitter.Changed;
  452. begin
  453.   if Assigned(FOnPosChanged) then FOnPosChanged(Self);
  454. end;
  455. procedure TRxSplitter.Changing(X, Y: Integer; var AllowChange: Boolean);
  456. begin
  457.   if Assigned(FOnPosChanging) then FOnPosChanging(Self, X, Y, AllowChange);
  458. end;
  459. procedure TRxSplitter.StopSizing(X, Y: Integer; Apply: Boolean);
  460. var
  461.   AllowChange: Boolean;
  462. begin
  463.   if FSizing then begin
  464.     ReleaseCapture;
  465.     AllowChange := Apply;
  466.     if Apply then Changing(X, Y, AllowChange);
  467.     EndInverseRect(X, Y, AllowChange, Apply);
  468.     FSizing := False;
  469.     Application.ShowHint := FAppShowHint;
  470.     if Assigned(FActiveControl) then begin
  471.       THack(FActiveControl).OnKeyDown := FOldKeyDown;
  472.       FActiveControl := nil;
  473.     end;
  474.     if Apply then Changed;
  475.   end;
  476. end;
  477. procedure TRxSplitter.ControlKeyDown(Sender: TObject; var Key: Word;
  478.   Shift: TShiftState);
  479. begin
  480.   if Key = VK_ESCAPE then StopSizing(0, 0, False)
  481.   else if Assigned(FOldKeyDown) then FOldKeyDown(Sender, Key, Shift);
  482. end;
  483. procedure TRxSplitter.MouseDown(Button: TMouseButton; Shift: TShiftState;
  484.   X, Y: Integer);
  485. begin
  486.   inherited MouseDown(Button, Shift, X, Y);
  487.   if not (csDesigning in ComponentState) and (Button = mbLeft) then begin
  488.     FStyle := GetStyle;
  489.     if FStyle <> spUnknown then begin
  490.       FSizing := True;
  491.       FAppShowHint := Application.ShowHint;
  492.       SetCapture(Handle);
  493.       with ValidParentForm(Self) do begin
  494.         if ActiveControl <> nil then FActiveControl := ActiveControl
  495.         else FActiveControl := GetParentForm(Self);
  496.         FOldKeyDown := THack(FActiveControl).OnKeyDown;
  497.         THack(FActiveControl).OnKeyDown := ControlKeyDown;
  498.       end;
  499.       Application.ShowHint := False;
  500.       FOffset := Point(X, Y);
  501.       StartInverseRect;
  502.     end;
  503.   end;
  504. end;
  505. procedure TRxSplitter.MouseMove(Shift: TShiftState; X, Y: Integer);
  506. var
  507.   AllowChange: Boolean;
  508. begin
  509.   inherited MouseMove(Shift, X, Y);
  510.   if (GetCapture = Handle) and FSizing then begin
  511.     AllowChange := True;
  512.     Changing(X, Y, AllowChange);
  513.     MoveInverseRect(X, Y, AllowChange);
  514.   end;
  515. end;
  516. procedure TRxSplitter.MouseUp(Button: TMouseButton; Shift: TShiftState;
  517.   X, Y: Integer);
  518. begin
  519.   StopSizing(X, Y, True);
  520.   inherited MouseUp(Button, Shift, X, Y);
  521. end;
  522. end.