home *** CD-ROM | disk | FTP | other *** search
/ PC Open 19 / pcopen19.iso / Win31 / Calmira / SOURCE.ZIP / VCL / MULTIGRD.PAS < prev    next >
Encoding:
Pascal/Delphi Source File  |  1998-02-20  |  15.8 KB  |  562 lines

  1. {*********************************************************}
  2. {                                                         }
  3. {    Calmira Visual Component Library 2.1                 }
  4. {    by Li-Hsin Huang,                                    }
  5. {    released into the public domain January 1998         }
  6. {                                                         }
  7. {*********************************************************}
  8.  
  9. unit Multigrd;
  10.  
  11. { TMultiGrid component
  12.  
  13.   Properties
  14.  
  15.   Selected - determines if a given cell is highlighted
  16.   SelCount - determines how many cells are highlighted in the grid
  17.   Multi - true if the grid is in "multi-select" mode
  18.   AllowMulti - enables or disables multiple selections
  19.   Limit - the valid range for the grid.  Cells with an index outside
  20.     of Limit will not be painted and cannot be selected with the mouse.
  21.   Focus - determines which cell has the dotted box draw around it
  22.   ThumbTrack - controls the goThumbTracking element of TCustomGrid
  23.     (the inherited Options property is not made public)
  24.   DropFocus - determines which cell has a focus rect drawn around it
  25.     during drag and drop.  Set to -1 to hide the drop focus.
  26.  
  27.   Methods
  28.  
  29.   SelectAll and DeselectAll - highlights and unhighlights all cells in
  30.     the grid, up to Limit
  31.   CellIndex - returns the linear index of a given row and column
  32.   Reset - deselects all cells without generating events and redraws
  33.     the control Use this to initialize between different phases of use.
  34.   Select - moves the focus to the given cell and selects it
  35.   MouseToCell - returns the index of the cell at the given pixel position
  36.   SetSize - changes the number of columns and rows while preserving the
  37.     current selection.  If you modify the ColCount and RowCount
  38.     properties directly, all selections are lost.
  39.   SizeGrid - automatically adjusts the number of columns and rows to
  40.     fit the current grid size
  41.  
  42.   Events
  43.  
  44.   OnSelectCell - occurs just before a cell is selected (like TDrawGrid's
  45.      OnSelectCell event).  You have the chance to cancel this operation.
  46.  
  47.   OnSelect - occurs after the user has selected a cell by left clicking
  48.      with the mouse (or moving the cursor keys).  Typically you would
  49.      use this event to respond to a single or multiple selection.  This
  50.      event occurs only once for each mouse click.
  51.  
  52.   OnCellSelected - occurs after the highlight of a cell is turned on or
  53.      off, either by the user or by the program assigning a value to the
  54.      Selected property.  If the user selects a range of cells by using
  55.      the Shift key, this event occurs once for every cell that has its
  56.      highlight changed.
  57.  
  58.   OnDrawCell - same as OnDrawCell for a TDrawGrid except that an integer
  59.      cell index is used
  60. }
  61.  
  62. interface
  63.  
  64. uses
  65.   SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
  66.   Forms, Dialogs, Grids, Menus, StdCtrls;
  67.  
  68. type
  69.   TBooleanList = array[0..65528] of Boolean;
  70.   PBooleanList = ^TBooleanList;
  71.  
  72.   EGridError = class(Exception);
  73.  
  74.   TGridSelectEvent = procedure (Sender : TObject; Index : Integer) of object;
  75.  
  76.   TCellSelectedEvent = procedure (Sender : TObject; Index : Integer;
  77.      IsSelected : Boolean) of object;
  78.  
  79.   TMultiDrawCellEvent = procedure (Sender : TObject; Index: Integer; Rect : TRect;
  80.      State : TGridDrawState) of object;
  81.  
  82.   TMultiSelectCellEvent = procedure (Sender : TObject; Index: Integer;
  83.      var CanSelect: Boolean) of object;
  84.  
  85.  
  86.   TMultiGrid = class(TCustomGrid)
  87.   private
  88.     { Private declarations }
  89.     FSelected       : PBooleanList;
  90.     FSelCount       : Integer;
  91.     FSelColor       : TColor;
  92.     FMulti          : Boolean;
  93.     FAllowMulti     : Boolean;
  94.     FOnSelect       : TGridSelectEvent;
  95.     FOnCellSelected : TCellSelectedEvent;
  96.     FOnDrawCell     : TMultiDrawCellEvent;
  97.     FOnSelectCell   : TMultiSelectCellEvent;
  98.     FOnTopLeftChange: TNotifyEvent;
  99.     FUpdates        : Integer;
  100.     FLimit          : Integer;
  101.     FDropFocus      : Integer;
  102.     FSelAnchor      : Integer;
  103.     function GetSelected(i : Integer): Boolean;
  104.     procedure SetSelected(i : Integer; Sel : Boolean);
  105.     function GetFocus : Integer;
  106.     procedure SetFocus(i : Integer);
  107.     procedure SetMulti(m: Boolean);
  108.     procedure SetSelColor(value: TColor);
  109.     function GetThumbTrack: Boolean;
  110.     procedure SetThumbTrack(value : Boolean);
  111.     procedure SetDropFocus(value: Integer);
  112.   protected
  113.     { Protected declarations }
  114.     procedure SizeChanged(OldColCount, OldRowCount: Longint); override;
  115.     procedure DrawCell(ACol, ARow: Longint; ARect: TRect;
  116.        AState: TGridDrawState); override;
  117.     procedure CellSelected(i : Integer; IsSelected : Boolean); virtual;
  118.     function SelectCell(ACol, ARow: Longint): Boolean; override;
  119.     procedure TopLeftChanged; override;
  120.     procedure BeginUpdate;
  121.     procedure EndUpdate;
  122.   public
  123.     { Public declarations }
  124.     constructor Create(AOwner : TComponent); override;
  125.     destructor Destroy; override;
  126.     procedure SelectAll;
  127.     procedure DeselectAll;
  128.     function CellIndex(ACol, ARow : Longint) : Integer;
  129.     procedure Reset;
  130.     procedure SetSize(AColCount, ARowCount : Longint);
  131.     procedure SizeGrid;
  132.     procedure Select(Index: Integer);
  133.     function MouseToCell(X, Y: Integer): Integer;
  134.     function CellBounds(i: Integer): TRect;
  135.  
  136.     property SelCount : Integer read FSelCount;
  137.     property Selected[i: Integer] : Boolean read GetSelected write SetSelected;
  138.     property Multi : Boolean read FMulti write SetMulti;
  139.     property DropFocus: Integer read FDropFocus write SetDropFocus;
  140.     property Canvas;
  141.     property TopRow;
  142.     property LeftCol;
  143.     property VisibleRowCount;
  144.     property VisibleColCount;
  145.   published
  146.     { Published declarations }
  147.     property Focus : Integer read GetFocus write SetFocus;
  148.     property OnSelect : TGridSelectEvent read FOnSelect write FOnSelect;
  149.     property OnCellSelected : TCellSelectedEvent read FOnCellSelected write FOnCellSelected;
  150.     property OnDrawCell : TMultiDrawCellEvent read FOnDrawCell write FOnDrawCell;
  151.     property OnSelectCell : TMultiSelectCellEvent read FOnSelectCell write FOnSelectCell;
  152.     property OnTopLeftChange : TNotifyEvent read FOnTopLeftChange write FOnTopLeftChange;
  153.     property AllowMulti: Boolean read FAllowMulti write FAllowMulti;
  154.     property Limit : Integer read FLimit write FLimit;
  155.     property SelColor : TColor read FSelColor write SetSelColor;
  156.     property ThumbTrack : Boolean read GetThumbTrack write SetThumbTrack default False;
  157.     property DefaultColWidth;
  158.     property DefaultRowHeight;
  159.     property RowCount;
  160.     property ColCount;
  161.     property Color;
  162.     property Ctl3D;
  163.     property DefaultDrawing;
  164.     property DragCursor;
  165.     property DragMode;
  166.     property Enabled;
  167.     property Font;
  168.     property GridLineWidth;
  169.     property ParentColor;
  170.     property ParentCtl3D;
  171.     property ParentFont;
  172.     property ParentShowHint;
  173.     property PopupMenu;
  174.     property ShowHint;
  175.     property Scrollbars;
  176.     property TabOrder;
  177.     property Visible;
  178.     property OnClick;
  179.     property OnDblClick;
  180.     property OnDragDrop;
  181.     property OnDragOver;
  182.     property OnEndDrag;
  183.     property OnEnter;
  184.     property OnExit;
  185.     property OnKeyDown;
  186.     property OnKeyPress;
  187.     property OnKeyUp;
  188.     property OnMouseDown;
  189.     property OnMouseMove;
  190.     property OnMouseUp;
  191.   end;
  192.  
  193.  
  194. procedure Register;
  195.  
  196. implementation
  197.  
  198. uses MiscUtil;
  199.  
  200. constructor TMultiGrid.Create(AOwner : TComponent);
  201. begin
  202.   inherited Create(AOwner);
  203.   FixedRows := 0;
  204.   FixedCols := 0;
  205.   DefaultDrawing := True;
  206.   GridLineWidth := 0;
  207.   Options := Options - [goRangeSelect];
  208.   FDropFocus := -1;
  209.   FMulti := False;
  210.   FAllowMulti := True;
  211.   FSelColor := clBtnFace;
  212.   FSelected := AllocMem(RowCount * ColCount);
  213.   FSelAnchor := -1;
  214. end;
  215.  
  216.  
  217. destructor TMultiGrid.Destroy;
  218. begin
  219.   FreeMem(FSelected, RowCount * ColCount);
  220.   inherited Destroy;
  221. end;
  222.  
  223.  
  224. function TMultiGrid.GetSelected(i : Integer): Boolean;
  225. begin
  226.   if (i >= 0) and (i < ColCount * RowCount) then Result := FSelected^[i]
  227.   else raise EListError.Create('Index of out range');
  228. end;
  229.  
  230.  
  231. procedure TMultiGrid.SetSelected(i : Integer; Sel : Boolean);
  232. begin
  233.   if (i >= 0) and (i < ColCount * RowCount) then begin
  234.     if FSelected^[i] <> Sel then begin
  235.       FSelected^[i] := Sel;
  236.  
  237.       if Sel then begin
  238.         Inc(FSelCount);
  239.         if not FMulti and (FSelcount > 1) then begin
  240.           FAllowMulti := True;
  241.           FMulti := True;
  242.         end;
  243.       end
  244.       else Dec(FSelCount);
  245.  
  246.       InvalidateCell(i mod ColCount, i div ColCount);
  247.       if Assigned(FOnCellSelected) then FOnCellSelected(self, i, Sel);
  248.     end
  249.   end
  250.   else raise EGridError.Create('Index of out range');
  251. end;
  252.  
  253.  
  254. { BeginUpdate and EndUpdate
  255.  
  256.   These are internal methods used to prevent the grid from redrawing
  257.   when some shuffling of properties is taking place.  When TMultiGrid
  258.   is in an "updating" state, OnSelectCell and OnDrawCell are bypassed }
  259.  
  260. procedure TMultiGrid.BeginUpdate;
  261. begin
  262.   Inc(FUpdates);
  263. end;
  264.  
  265.  
  266. procedure TMultiGrid.EndUpdate;
  267. begin
  268.   if FUpdates > 0 then Dec(FUpdates);
  269. end;
  270.  
  271.  
  272. function TMultiGrid.GetFocus : Integer;
  273. begin
  274.   Result := Row * ColCount + Col;
  275. end;
  276.  
  277.  
  278. procedure TMultiGrid.SetFocus(i : Integer);
  279. begin
  280.   if i < RowCount * ColCount then begin
  281.     BeginUpdate;
  282.     Row := i div ColCount;
  283.     Col := i mod ColCount;
  284.     EndUpdate;
  285.   end;
  286. end;
  287.  
  288.  
  289. procedure TMultiGrid.SetMulti(m: Boolean);
  290. begin
  291.   if FMulti <> m then begin
  292.     if m then begin
  293.       FAllowMulti := True;
  294.       FMulti := True;
  295.     end
  296.     else begin
  297.       if SelCount > 0 then DeselectAll;
  298.       FMulti := False;
  299.     end;
  300.   end;
  301. end;
  302.  
  303.  
  304. function TMultiGrid.CellBounds(i: Integer): TRect;
  305. begin
  306.   Result := CellRect(i mod ColCount, i div ColCount);
  307. end;
  308.  
  309.  
  310. procedure TMultiGrid.SetSelColor(value: TColor);
  311. begin
  312.   if FSelColor <> value then begin
  313.     FSelColor := value;
  314.     if SelCount > 0 then Invalidate;
  315.   end;
  316. end;
  317.  
  318. procedure TMultiGrid.SetSize(AColCount, ARowCount : Longint);
  319. var
  320.   f : Integer;
  321.   p : PBooleanList;
  322.   bufsize : Word;
  323. begin
  324.   if (AColCount = ColCount) and (ARowCount = RowCount) then exit;
  325.   if AColCount = 0 then AColCount := 1;
  326.   if ARowCount = 0 then ARowCount := 1;
  327.  
  328.   { The current selection is copied to a temporary buffer and then
  329.     restored once the inherited sizing is complete }
  330.  
  331.   BeginUpdate;
  332.   f := Focus;
  333.   bufsize := Min(AColCount * ARowCount, ColCount * RowCount);
  334.   p := AllocMem(bufsize);
  335.   try
  336.     Move(FSelected^, p^, bufsize);
  337.     ColCount := AColCount;
  338.     RowCount := ARowCount;
  339.     Move(p^, FSelected^, bufsize);
  340.     Focus := f;
  341.   finally
  342.     EndUpdate;
  343.     FreeMem(p, bufsize);
  344.     Invalidate;
  345.   end;
  346. end;
  347.  
  348.  
  349. procedure TMultiGrid.SizeGrid;
  350. var c, r: Longint;
  351. begin
  352.   { try to display without the scroll bar first }
  353.  
  354.   c := Width div DefaultColWidth;
  355.   if c = 0 then Inc(c);
  356.   r := Limit div c;
  357.   if Limit mod c > 0 then Inc(r);
  358.  
  359.   { if the computed row count exceeds the number of rows that
  360.     can be displayed, take the scroll bar width into account and recalculate }
  361.  
  362.   if (Height - 4) div DefaultRowHeight < r then begin
  363.     c := (Width - GetSystemMetrics(SM_CXVSCROLL)) div DefaultColWidth;
  364.     if c = 0 then Inc(c);
  365.     r := Limit div c;
  366.     if Limit mod c > 0 then Inc(r);
  367.   end;
  368.  
  369.   Setsize(c, r);
  370. end;
  371.  
  372.  
  373.  
  374. procedure TMultiGrid.SizeChanged(OldColCount, OldRowCount: Longint);
  375. begin
  376.   inherited SizeChanged(OldColCount, OldRowCount);
  377.   FreeMem(FSelected, OldColCount * OldRowCount);
  378.   FSelected := AllocMem(ColCount * RowCount);
  379. end;
  380.  
  381.  
  382. procedure TMultiGrid.CellSelected(i : Integer; IsSelected : Boolean);
  383. begin
  384.   Selected[i] := IsSelected;
  385.   InvalidateCell(i mod ColCount, i div ColCount);
  386.   if Assigned(FOnCellSelected) then FOnCellSelected(self, i, IsSelected);
  387. end;
  388.  
  389.  
  390. function TMultiGrid.CellIndex(ACol, ARow : Longint) : Integer;
  391. begin
  392.   Result := ARow * ColCount + ACol;
  393. end;
  394.  
  395.  
  396. procedure TMultiGrid.Reset;
  397. begin
  398.   FillChar(FSelected^, ColCount * RowCount, False);
  399.   FSelcount := 0;
  400.   FMulti := False;
  401.   Invalidate;
  402. end;
  403.  
  404.  
  405.  
  406. function TMultiGrid.SelectCell(ACol, ARow: Longint): Boolean;
  407. var
  408.   i, j, index, lower, upper, temp: Integer;
  409. begin
  410.   if FUpdates > 0 then begin
  411.     Result := True;
  412.     exit;
  413.   end;
  414.  
  415.   index := ARow * ColCount + ACol;
  416.  
  417.   Result := index < Limit;
  418.   if Result and Assigned(FOnSelectCell) then
  419.     FOnSelectCell(self, index, Result);
  420.  
  421.   if Result then begin
  422.  
  423.     if AllowMulti and (Focused and (GetAsyncKeyState(VK_CONTROL) < 0)) then begin
  424.       { Ctrl-click.  Invert selection of target cell }
  425.       FMulti := True;
  426.       Selected[index] := not Selected[index];
  427.       FSelAnchor := -1;
  428.     end
  429.     else if AllowMulti and (Focused and (GetKeyState(VK_SHIFT) < 0)) then begin
  430.       { Shift-click.  Select range of cells }
  431.       FMulti := True;
  432.       if FSelAnchor = -1 then FSelAnchor := Row * ColCount + Col;
  433.       lower := FSelAnchor;
  434.       upper := index;
  435.       if lower > upper then begin
  436.         temp := lower;
  437.         lower := upper;
  438.         upper := temp;
  439.       end;
  440.  
  441.       for i := 0 to Limit-1 do
  442.         Selected[i] := (i >= lower) and (i <= upper);
  443.     end
  444.     else begin
  445.       FSelAnchor := -1;
  446.       { normal click -- no Ctrl or Shift }
  447.       if FMulti then begin
  448.         if not FSelected^[index] then begin
  449.           { turn off multi mode }
  450.           FMulti := False;
  451.           for i := 0 to Limit-1 do Selected[i] := False;
  452.           Selected[index] := True;
  453.         end;
  454.       end
  455.       else begin
  456.         { change highlighted cell }
  457.         i := Row * ColCount + Col;
  458.         Selected[i] := False;
  459.         Selected[index] := True;
  460.       end;
  461.     end;
  462.     if Assigned(FOnSelect) then FOnSelect(self, index);
  463.   end;
  464. end;
  465.  
  466.  
  467. procedure TMultiGrid.Select(Index : Integer);
  468. var ACol, ARow, c, r: Longint;
  469. begin
  470.   c := Col; r := Row;
  471.   if SelectCell(Index mod ColCount, Index div ColCount) then begin
  472.     Focus := Index;
  473.     InvalidateCell(c, r);
  474.     Update;
  475.   end;
  476. end;
  477.  
  478.  
  479. procedure TMultiGrid.DrawCell(ACol, ARow: Longint; ARect: TRect;
  480.    AState: TGridDrawState);
  481. var i : Integer;
  482. begin
  483.   if FUpdates > 0 then exit;
  484.   i := ARow * ColCount + ACol;
  485.   with Canvas do begin
  486.     if FSelected^[i] then begin
  487.       Brush.Color := SelColor;
  488.       Include(AState, gdSelected);
  489.     end
  490.     else begin
  491.       Brush.Color := Color;
  492.       Exclude(AState, gdSelected);
  493.     end;
  494.     if DefaultDrawing then begin
  495.       if gdFocused in AState then DrawFocusRect(ARect);
  496.       FillRect(ARect);
  497.     end;
  498.   end;
  499.   Exclude(AState, gdFixed);
  500.   if (i < Limit) and Assigned(FOnDrawCell) then
  501.     FOnDrawCell(self, i, ARect, AState);
  502. end;
  503.  
  504.  
  505. procedure TMultiGrid.DeselectAll;
  506. var i: Integer;
  507. begin
  508.   for i := 0 to Limit-1 do Selected[i] := False;
  509.   if Assigned(FOnSelect) then FOnSelect(self, Focus);
  510. end;
  511.  
  512.  
  513. procedure TMultiGrid.SelectAll;
  514. var i: Integer;
  515. begin
  516.   for i := 0 to Limit-1 do Selected[i] := True;
  517.   if Assigned(FOnSelect) then FOnSelect(self, Focus);
  518. end;
  519.  
  520.  
  521. function TMultiGrid.MouseToCell(X, Y: Integer): Integer;
  522. begin
  523.   with MouseCoord(X, Y) do Result := Y * ColCount + X;
  524. end;
  525.  
  526. procedure TMultiGrid.TopLeftChanged;
  527. begin
  528.   if Assigned(FOnTopLeftChange) then FOnTopLeftChange(self);
  529. end;
  530.  
  531. function TMultiGrid.GetThumbTrack: Boolean;
  532. begin
  533.   Result := goThumbTracking in Options;
  534. end;
  535.  
  536. procedure TMultiGrid.SetThumbTrack(value : Boolean);
  537. begin
  538.   if value then Options := Options + [goThumbTracking]
  539.   else Options := Options - [goThumbTracking];
  540. end;
  541.  
  542.  
  543. procedure TMultiGrid.SetDropFocus(value: Integer);
  544. begin
  545.   if FDropFocus <> Value then begin
  546.     if FDropFocus <> -1 then
  547.       Canvas.DrawFocusRect(CellBounds(FDropFocus));
  548.     if value <> -1 then
  549.       Canvas.DrawFocusRect(CellBounds(value));
  550.  
  551.     FDropFocus := value;
  552.   end;
  553. end;
  554.  
  555.  
  556. procedure Register;
  557. begin
  558.   RegisterComponents('Samples', [TMultiGrid]);
  559. end;
  560.  
  561. end.
  562.