unit PCScrollBar;

interface

uses Classes, Controls, Messages, Forms, StdCtrls, Windows, ExtCtrls,SysUtils;


type
//== TPCScrollBar =============================================================
	TPCScrollBarInc = 1..2147483647;
	TPCScrollBar = class(TWinControl)
	private
		FKind       : TScrollBarKind;
		FPageSize   : Cardinal;
		FPosition   : Integer;
		FMin        : Integer;
		FMax        : Integer;
		FSmallChange: TPCScrollBarInc;
		FLargeChange: TPCScrollBarInc;
		FOnChange   : TNotifyEvent;
		FOnScroll   : TScrollEvent;
		procedure DoScroll(var Message: TWMScroll);
		procedure SetKind(Value: TScrollBarKind);
		procedure SetMax(Value: Integer);
		procedure SetMin(Value: Integer);
		procedure SetPageSize(Value: Cardinal);
		procedure SetPosition(Value: Integer);
		procedure CNHScroll(var Message: TWMHScroll); message CN_HSCROLL;
    procedure CNVScroll(var Message: TWMVScroll); message CN_VSCROLL;
  protected
		procedure CreateParams(var Params: TCreateParams); override;
		procedure CreateWnd; override;
		procedure Change; dynamic;
		procedure Scroll(ScrollCode: TScrollCode; var Pos: Integer); dynamic;
	public
		constructor Create(AOwner: TComponent); override;
		procedure SetParams(APosition, AMin, AMax: Integer);
		procedure SetParams32(APosition, AMin, AMax: Integer; APage: Cardinal);
	published
		property Ctl3D;
		property DragCursor;
		property DragMode;
		property Enabled;
		property Kind: TScrollBarKind read FKind write SetKind default sbHorizontal;
		property LargeChange: TPCScrollBarInc read FLargeChange write FLargeChange default 1;
		property Max: Integer read FMax write SetMax default 100;
		property Min: Integer read FMin write SetMin default 0;
		property PageSize: Cardinal read FPageSize write SetPageSize default 0;
		property ParentCtl3D;
		property ParentShowHint;
		property PopupMenu;
		property Position: Integer read FPosition write SetPosition default 0;
		property ShowHint;
		property SmallChange: TPCScrollBarInc read FSmallChange write FSmallChange default 1;
		property TabOrder;
		property TabStop default True;
		property Visible;
		property OnChange: TNotifyEvent read FOnChange write FOnChange;
		property OnDragDrop;
		property OnDragOver;
		property OnEndDrag;
		property OnEnter;
		property OnExit;
		property OnKeyDown;
		property OnKeyPress;
		property OnKeyUp;
		property OnScroll: TScrollEvent read FOnScroll write FOnScroll;
		property OnStartDrag;
	end;



//*****************************************************************************
implementation
{$R *.DCR}
uses Consts;

//== TPCScrollBar =============================================================
constructor TPCScrollBar.Create(AOwner: TComponent);
begin
	inherited create(AOwner);
	FKind        := sbHorizontal;
	Width        := 121;
	Height       := GetSystemMetrics(SM_CYHSCROLL);
	ControlStyle := [csFramed, csDoubleClicks];
	TabStop      := True;
	FPosition    := 0;
	FMin         := 0;
	FMax         := 100;
	FSmallChange := 1;
	FLargeChange := 1;
	FPageSize    := 0;    {this gives the classical thumb}
end;

procedure TPCScrollBar.CreateParams(var Params: TCreateParams);
const
  Kinds: array[TScrollBarKind] of LongInt = (SBS_HORZ, SBS_VERT);
begin
  inherited CreateParams(Params);
  CreateSubClass(Params, 'SCROLLBAR');
  with Params do Style := Style or Kinds[FKind];
end;

procedure TPCScrollBar.CreateWnd;
var
	ScrollInfo: TScrollInfo;
begin
	inherited CreateWnd;
	ScrollInfo.cbSize := Sizeof (ScrollInfo);
	ScrollInfo.fMask  := SIF_PAGE or SIF_POS or SIF_RANGE;
	ScrollInfo.nPage  := FPageSize;
	ScrollInfo.nMin   := FMin;
	ScrollInfo.nMax   := FMax;
	ScrollInfo.nPos   := FPosition;
	SetScrollInfo (Handle, SB_CTL, ScrollInfo, True);
end;

procedure TPCScrollBar.SetKind(Value: TScrollBarKind);
begin
	if FKind <> Value then begin
    FKind := Value;
		if not (csLoading in ComponentState) then SetBounds(Left, Top, Height, Width);
    RecreateWnd;
  end;
end;

procedure TPCScrollBar.SetParams(APosition, AMin, AMax: Integer);
begin
	SetParams32(APosition, AMin, AMax, FPageSize);
end;

procedure TPCScrollBar.SetParams32(APosition, AMin, AMax: Integer; APage: Cardinal);
var
	ScrollInfo: TScrollInfo;
begin
	{clip values to valid ranges}
	if AMax < AMin then raise EInvalidOperation.CreateRes(0);
	if APage > AMax-AMin+1 then APage := AMax-AMin+1;
	if APosition < AMin then APosition := AMin;
	if APage > 1 then begin
		if APosition > AMax-APage+1 then APosition := AMax-APage+1;
	end else
		if APosition > AMax then APosition := AMax;
	{set mask for parameters to be changed}
	ScrollInfo.fMask := 0;
	if (FMin <> AMin) or (FMax <> AMax) then
		ScrollInfo.fMask := ScrollInfo.fMask or SIF_RANGE;
	if FPosition <> APosition then
		ScrollInfo.fMask := ScrollInfo.fMask or SIF_POS;
	if FPageSize <> APage then
		ScrollInfo.fMask := ScrollInfo.fMask or SIF_PAGE;
	{change parameters}
	ScrollInfo.cbSize := Sizeof(ScrollInfo);
	ScrollInfo.nPos   := APosition;
	ScrollInfo.nMin   := AMin;
	ScrollInfo.nMax   := AMax;
	ScrollInfo.nPage  := APage;
	if HandleAllocated then SetScrollInfo (Handle, SB_CTL, ScrollInfo, True);
	{remember changes}
	FMin      := AMin;
	FMax      := AMax;
	FPageSize := APage;
	if FPosition <> APosition then begin
		FPosition := APosition;
		Change;
	end;
end;

procedure TPCScrollBar.SetPosition(Value: Integer);
begin
	SetParams32(Value, FMin, FMax, FPageSize);
end;

procedure TPCScrollBar.SetMin(Value: Integer);
begin
	SetParams32(FPosition, Value, FMax, FPageSize);
end;

procedure TPCScrollBar.SetMax(Value: Integer);
begin
	SetParams32(FPosition, FMin, Value, FPageSize);
end;

procedure TPCScrollBar.SetPageSize (Value: Cardinal);
begin
	SetParams32(FPosition, FMin, FMax, Value);
end;

procedure TPCScrollBar.Change;
begin
  if Assigned(FOnChange) then FOnChange(Self);
end;

procedure TPCScrollBar.Scroll(ScrollCode: TScrollCode; var Pos: Integer);
begin
  if Assigned(FOnScroll) then FOnScroll(Self, ScrollCode, Pos);
end;

procedure TPCScrollBar.DoScroll(var Message: TWMScroll);
var
	ScrollInfo: TScrollInfo;
	NewPos: Integer;
begin
	with Message do
  begin
		NewPos := FPosition;
		case TScrollCode(ScrollCode) of
			scLineUp  : Dec(NewPos, FSmallChange);
			scLineDown: Inc(NewPos, FSmallChange);
			scPageUp  : Dec(NewPos, FLargeChange);
			scPageDown: Inc(NewPos, FLargeChange);
			scTrack   : begin
            				ScrollInfo.cbSize := Sizeof (ScrollInfo);
                		ScrollInfo.fMask := SIF_TRACKPOS;
            				GetScrollInfo (Handle, SB_CTL, ScrollInfo);
            				NewPos := ScrollInfo.nTrackPos;
            			end;
			scTop   : NewPos := FMin;
			scBottom: NewPos := FMax;
		end;
		if NewPos < FMin then
      NewPos := FMin;
		if FPageSize > 1 then
    begin
			if NewPos > FMax - FPageSize + 1 then
        NewPos := FMax - FPageSize + 1;
		end
    else
		if NewPos > FMax then
      NewPos := FMax;
		Scroll(TScrollCode(ScrollCode), NewPos);
		SetPosition(NewPos);
	end;
end;

procedure TPCScrollBar.CNHScroll(var Message: TWMHScroll);
begin
  DoScroll(Message);
end;

procedure TPCScrollBar.CNVScroll(var Message: TWMVScroll);
begin
  DoScroll(Message);
end;


end.
