unit Multigrd;

interface

uses
  SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
  Forms, Dialogs, Grids;

type
  TMultiGrid = class(TStringGrid)
  private
    SelectedRows  : TList;
    Anchor        : Longint;
    LastMoveOn    : Longint;
    LastTopRow    : Longint;
    MouseIsDown   : Boolean;
    procedure ToggleRow( ARow : Longint );
    procedure SelectRow( ARow : Longint; Select : Boolean );
    procedure SelectRows( ARow , BRow : Longint ; Select : Boolean );
    procedure InvalidateRow( ARow : Longint );
    function GetSelected( Index : Longint ) : Boolean;
    procedure SetSelected( Index : Longint; Select : Boolean );
    function GetSelectedCount : Longint;
  protected
    procedure MouseDown( Button: TMouseButton; Shift: TShiftState; X, Y: Integer );override;
    procedure MouseUp( Button: TMouseButton; Shift: TShiftState; X, Y: Integer );override;
    procedure MouseMove( Shift: TShiftState; X, Y: Integer );override;
    procedure DrawCell( ACol, ARow: Longint; Rect: TRect; State: TGridDrawState );override;
    procedure TopLeftChanged; override;
  public
    constructor Create( AOwner : TComponent );override;
    destructor Destroy; override;
    property Selected[ Index : Longint ] : Boolean read GetSelected write SetSelected;
    property SelectedCount : Longint read GetSelectedCount;
    procedure ClearSelection;
  published
  end;

procedure Register;

implementation

procedure Register;
begin
  RegisterComponents('Samples', [TMultiGrid]);
end;

constructor TMultiGrid.Create( AOwner : TComponent );
begin
  inherited Create( AOwner );
  SelectedRows := TList.Create;
  SelectedRows.Add( Pointer( FixedRows ) );
  Anchor := FixedRows;
  MouseIsDown := False;
  LastTopRow := TopRow;
end;

destructor TMultiGrid.Destroy;
begin
  SelectedRows.Free;
  inherited Destroy;
end;

procedure TMultiGrid.ToggleRow( ARow : Longint );
var
  ACol : Longint;
  Index : Integer;
begin
  Index := SelectedRows.IndexOf( Pointer( ARow ) );
  if Index <> -1 then
    SelectedRows.Delete( Index )
  else
    SelectedRows.Add( Pointer( ARow ) );
  for ACol := FixedCols to Pred( ColCount ) do
    InvalidateCell( ACol , ARow );
end;

procedure TMultiGrid.InvalidateRow( ARow : Longint );
var
  ACol : Longint;
begin
  for ACol := FixedCols to Pred( ColCount ) do
    InvalidateCell( ACol , ARow );
end;

procedure TMultiGrid.SelectRow( ARow : Longint; Select : Boolean );
var
  ListIndex : Integer;
begin
  ListIndex := SelectedRows.IndexOf( Pointer( ARow ) );
  if ( ListIndex = -1 ) and ( Select ) then
    begin
      SelectedRows.Add( Pointer( ARow ) );
      InvalidateRow( ARow );
    end
  else if ( ListIndex <> -1 ) and ( not Select ) then
    begin
      SelectedRows.Delete( ListIndex );
      InvalidateRow( ARow );
    end;
end;

procedure TMultiGrid.SelectRows( ARow , BRow : Longint ; Select : Boolean );
var
  Index , StartRow , EndRow : Longint;
begin
  if ARow > BRow then
    begin
      StartRow := BRow;
      EndRow := ARow;
    end
  else
    begin
      StartRow := ARow;
      EndRow := BRow;
    end;
  for Index := StartRow to EndRow do
    SelectRow( Index , Select );
end;


procedure TMultiGrid.MouseDown( Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
var
  ARow : Longint;
  ACol : Longint;
  StartRow : Longint;
  EndRow : Longint;
begin
  inherited MouseDown( Button , Shift , X , Y );
  if ( Button = mbLeft ) then
    begin
      MouseToCell( X , Y , ACol , ARow );
      if ARow < FixedRows then
        Exit;
      MouseIsDown := True;
      LastMoveOn := ARow;

      if ssCtrl in Shift then
        begin
          if ( ARow > -1 ) then
            begin
              ToggleRow( ARow );
              Anchor := ARow;
            end;
        end
      else if ssShift in Shift then
        begin
          SelectedRows.Clear;
          SelectRows( Anchor , ARow , True );
        end
      else
        begin
          if SelectedRows.Count > 0 then
            SelectedRows.Clear;
          if ( ARow > -1 ) then
            begin
              SelectedRows.Add( Pointer( ARow ) );
              Anchor := ARow;
            end;
          Refresh;
        end;
    end;
end;

procedure TMultiGrid.MouseUp( Button: TMouseButton; Shift: TShiftState; X, Y: Integer );
begin
  inherited MouseUp( Button , Shift , X , Y );
  MouseIsDown := False;
end;

procedure TMultiGrid.MouseMove( Shift: TShiftState; X, Y: Integer );
var
  ACol , ARow : Longint;
  StartRow , EndRow , RowIndex : Longint;
begin
  inherited MouseMove( Shift , X , Y );
  if MouseIsDown then
    begin
      MouseToCell( X , Y , ACol , ARow );
      if ( ARow <> LastMoveOn ) then
        begin
          if ( ARow >= Anchor ) and ( ARow < LastMoveOn ) then
            begin
              SelectRows( LastMoveOn , ARow , False );
              if ARow = Anchor then
                SelectRow( Anchor , True );
            end
          else if ( ARow <= Anchor ) and ( ARow > LastMoveOn ) then
            begin
              SelectRows( LastMoveOn , ARow , False );
              if ARow = Anchor then
                SelectRow( Anchor , True );
            end
          else if ( ARow < Anchor ) and ( LastMoveOn > Anchor ) then
            begin
              SelectRows( LastMoveOn , Anchor + 1 , False );
              SelectRows( Anchor , ARow , True );
            end
          else if ( ARow > Anchor ) and ( LastMoveOn < Anchor ) then
            begin
              SelectRows( LastMoveOn , Anchor - 1 , False );
              SelectRows( Anchor , ARow , True );
            end
          else
            SelectRows( Arow , Anchor , True );
          LastMoveOn := ARow;
        end;
    end;
end;


procedure TMultiGrid.TopLeftChanged;
begin
  inherited TopLeftChanged;
  if MouseIsDown then
    begin
      if TopRow > LastTopRow then
        begin
          LastMoveOn := TopRow + VisibleRowCount - 1;
          SelectRow( TopRow + VisibleRowCount - 1 , True );
        end;
    end;
  LastTopRow := TopRow;
end;

procedure TMultiGrid.DrawCell( ACol, ARow: Longint; Rect: TRect; State: TGridDrawState );
var
  NewState : TGridDrawState;
  MyRect : TRect;
begin
  NewState := State;
  if ( SelectedRows.IndexOf( Pointer( ARow ) ) > -1 ) then
    Include( NewState , gdSelected )
  else
    Exclude( NewState , gdSelected );
  if not ( gdFixed in NewState ) then
    begin
      if ( gdSelected in NewState ) then
        begin
          Canvas.Brush.Color := clHighlight;
          Canvas.Font.Color := clHighlightText;
        end
      else
        begin
          Canvas.Brush.Color := clWindow;
          Canvas.Font.Color := clWindowText;
        end;
    end;
  Canvas.FillRect( Rect );
  MyRect := Rect;
  InflateRect( MyRect , -2 , -2 );
  Canvas.TextOut( MyRect.Left , MyRect.Top , Cells[ ACol , ARow ] );
  DefaultDrawing := False;
  inherited DrawCell( ACol , ARow , Rect , NewState );
  DefaultDrawing := True;
end;

function TMultiGrid.GetSelected( Index : Longint ) : Boolean;
begin
  Result := SelectedRows.IndexOf( Pointer( Index ) ) > -1 ;
end;

procedure TMultiGrid.SetSelected( Index : Longint; Select : Boolean );
begin
  SelectRow( Index , Select );
end;

procedure TMultiGrid.ClearSelection;
begin
  SelectedRows.Clear;
  SelectedRows.Add( Pointer( Row ) );
  Refresh;
end;

function TMultiGrid.GetSelectedCount : Longint;
begin
  Result := SelectedRows.Count;
end;

end.
