home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 1998 April A / Pcwk4a98.iso / PROGRAM / DELPHI16 / Calmira / Src / VCL / MULTIGRD.PAS < prev    next >
Pascal/Delphi Source File  |  1997-02-15  |  16KB  |  556 lines

  1. {*********************************************************}
  2. {                                                         }
  3. {    Calmira Visual Component Library 1.0                 }
  4. {    by Li-Hsin Huang,                                    }
  5. {    released into the public domain January 1997         }
  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.     function GetSelected(i : Integer): Boolean;
  103.     procedure SetSelected(i : Integer; Sel : Boolean);
  104.     function GetFocus : Integer;
  105.     procedure SetFocus(i : Integer);
  106.     procedure SetMulti(m: Boolean);
  107.     procedure SetSelColor(value: TColor);
  108.     function GetThumbTrack: Boolean;
  109.     procedure SetThumbTrack(value : Boolean);
  110.     procedure SetDropFocus(value: Integer);
  111.   protected
  112.     { Protected declarations }
  113.     procedure SizeChanged(OldColCount, OldRowCount: Longint); override;
  114.     procedure DrawCell(ACol, ARow: Longint; ARect: TRect;
  115.        AState: TGridDrawState); override;
  116.     procedure CellSelected(i : Integer; IsSelected : Boolean); virtual;
  117.     function SelectCell(ACol, ARow: Longint): Boolean; override;
  118.     procedure TopLeftChanged; override;
  119.     procedure BeginUpdate;
  120.     procedure EndUpdate;
  121.   public
  122.     { Public declarations }
  123.     constructor Create(AOwner : TComponent); override;
  124.     destructor Destroy; override;
  125.     procedure SelectAll;
  126.     procedure DeselectAll;
  127.     function CellIndex(ACol, ARow : Longint) : Integer;
  128.     procedure Reset;
  129.     procedure SetSize(AColCount, ARowCount : Longint);
  130.     procedure SizeGrid;
  131.     procedure Select(Index: Integer);
  132.     function MouseToCell(X, Y: Integer): Integer;
  133.     function CellBounds(i: Integer): TRect;
  134.  
  135.     property SelCount : Integer read FSelCount;
  136.     property Selected[i: Integer] : Boolean read GetSelected write SetSelected;
  137.     property Multi : Boolean read FMulti write SetMulti;
  138.     property DropFocus: Integer read FDropFocus write SetDropFocus;
  139.     property Canvas;
  140.     property TopRow;
  141.     property LeftCol;
  142.     property VisibleRowCount;
  143.     property VisibleColCount;
  144.   published
  145.     { Published declarations }
  146.     property Focus : Integer read GetFocus write SetFocus;
  147.     property OnSelect : TGridSelectEvent read FOnSelect write FOnSelect;
  148.     property OnCellSelected : TCellSelectedEvent read FOnCellSelected write FOnCellSelected;
  149.     property OnDrawCell : TMultiDrawCellEvent read FOnDrawCell write FOnDrawCell;
  150.     property OnSelectCell : TMultiSelectCellEvent read FOnSelectCell write FOnSelectCell;
  151.     property OnTopLeftChange : TNotifyEvent read FOnTopLeftChange write FOnTopLeftChange;
  152.     property AllowMulti: Boolean read FAllowMulti write FAllowMulti;
  153.     property Limit : Integer read FLimit write FLimit;
  154.     property SelColor : TColor read FSelColor write SetSelColor;
  155.     property ThumbTrack : Boolean read GetThumbTrack write SetThumbTrack default False;
  156.     property DefaultColWidth;
  157.     property DefaultRowHeight;
  158.     property RowCount;
  159.     property ColCount;
  160.     property Color;
  161.     property Ctl3D;
  162.     property DefaultDrawing;
  163.     property DragCursor;
  164.     property DragMode;
  165.     property Enabled;
  166.     property Font;
  167.     property GridLineWidth;
  168.     property ParentColor;
  169.     property ParentCtl3D;
  170.     property ParentFont;
  171.     property ParentShowHint;
  172.     property PopupMenu;
  173.     property ShowHint;
  174.     property Scrollbars;
  175.     property TabOrder;
  176.     property Visible;
  177.     property OnClick;
  178.     property OnDblClick;
  179.     property OnDragDrop;
  180.     property OnDragOver;
  181.     property OnEndDrag;
  182.     property OnEnter;
  183.     property OnExit;
  184.     property OnKeyDown;
  185.     property OnKeyPress;
  186.     property OnKeyUp;
  187.     property OnMouseDown;
  188.     property OnMouseMove;
  189.     property OnMouseUp;
  190.   end;
  191.  
  192.  
  193. procedure Register;
  194.  
  195. implementation
  196.  
  197. uses MiscUtil;
  198.  
  199. constructor TMultiGrid.Create(AOwner : TComponent);
  200. begin
  201.   inherited Create(AOwner);
  202.   FixedRows := 0;
  203.   FixedCols := 0;
  204.   DefaultDrawing := True;
  205.   GridLineWidth := 0;
  206.   Options := Options - [goRangeSelect];
  207.   FDropFocus := -1;
  208.   FMulti := False;
  209.   FAllowMulti := True;
  210.   FSelColor := clBtnFace;
  211.   FSelected := AllocMem(RowCount * ColCount);
  212. end;
  213.  
  214.  
  215. destructor TMultiGrid.Destroy;
  216. begin
  217.   FreeMem(FSelected, RowCount * ColCount);
  218.   inherited Destroy;
  219. end;
  220.  
  221.  
  222. function TMultiGrid.GetSelected(i : Integer): Boolean;
  223. begin
  224.   if (i >= 0) and (i < ColCount * RowCount) then Result := FSelected^[i]
  225.   else raise EListError.Create('Index of out range');
  226. end;
  227.  
  228.  
  229. procedure TMultiGrid.SetSelected(i : Integer; Sel : Boolean);
  230. begin
  231.   if (i >= 0) and (i < ColCount * RowCount) then begin
  232.     if FSelected^[i] <> Sel then begin
  233.       FSelected^[i] := Sel;
  234.  
  235.       if Sel then begin
  236.         Inc(FSelCount);
  237.         if not FMulti and (FSelcount > 1) then begin
  238.           FAllowMulti := True;
  239.           FMulti := True;
  240.         end;
  241.       end
  242.       else Dec(FSelCount);
  243.  
  244.       InvalidateCell(i mod ColCount, i div ColCount);
  245.       if Assigned(FOnCellSelected) then FOnCellSelected(self, i, Sel);
  246.     end
  247.   end
  248.   else raise EGridError.Create('Index of out range');
  249. end;
  250.  
  251.  
  252. { BeginUpdate and EndUpdate
  253.  
  254.   These are internal methods used to prevent the grid from redrawing
  255.   when some shuffling of properties is taking place.  When TMultiGrid
  256.   is in an "updating" state, OnSelectCell and OnDrawCell are bypassed }
  257.  
  258. procedure TMultiGrid.BeginUpdate;
  259. begin
  260.   Inc(FUpdates);
  261. end;
  262.  
  263.  
  264. procedure TMultiGrid.EndUpdate;
  265. begin
  266.   if FUpdates > 0 then Dec(FUpdates);
  267. end;
  268.  
  269.  
  270. function TMultiGrid.GetFocus : Integer;
  271. begin
  272.   Result := Row * ColCount + Col;
  273. end;
  274.  
  275.  
  276. procedure TMultiGrid.SetFocus(i : Integer);
  277. begin
  278.   if i < RowCount * ColCount then begin
  279.     BeginUpdate;
  280.     Row := i div ColCount;
  281.     Col := i mod ColCount;
  282.     EndUpdate;
  283.   end;
  284. end;
  285.  
  286.  
  287. procedure TMultiGrid.SetMulti(m: Boolean);
  288. begin
  289.   if FMulti <> m then begin
  290.     if m then begin
  291.       FAllowMulti := True;
  292.       FMulti := True;
  293.     end
  294.     else begin
  295.       if SelCount > 0 then DeselectAll;
  296.       FMulti := False;
  297.     end;
  298.   end;
  299. end;
  300.  
  301.  
  302. function TMultiGrid.CellBounds(i: Integer): TRect;
  303. begin
  304.   Result := CellRect(i mod ColCount, i div ColCount);
  305. end;
  306.  
  307.  
  308. procedure TMultiGrid.SetSelColor(value: TColor);
  309. begin
  310.   if FSelColor <> value then begin
  311.     FSelColor := value;
  312.     if SelCount > 0 then Invalidate;
  313.   end;
  314. end;
  315.  
  316. procedure TMultiGrid.SetSize(AColCount, ARowCount : Longint);
  317. var
  318.   f : Integer;
  319.   p : PBooleanList;
  320.   bufsize : Word;
  321. begin
  322.   if (AColCount = ColCount) and (ARowCount = RowCount) then exit;
  323.   if AColCount = 0 then AColCount := 1;
  324.   if ARowCount = 0 then ARowCount := 1;
  325.  
  326.   { The current selection is copied to a temporary buffer and then
  327.     restored once the inherited sizing is complete }
  328.  
  329.   BeginUpdate;
  330.   f := Focus;
  331.   bufsize := Min(AColCount * ARowCount, ColCount * RowCount);
  332.   p := AllocMem(bufsize);
  333.   try
  334.     Move(FSelected^, p^, bufsize);
  335.     ColCount := AColCount;
  336.     RowCount := ARowCount;
  337.     Move(p^, FSelected^, bufsize);
  338.     Focus := f;
  339.   finally
  340.     EndUpdate;
  341.     FreeMem(p, bufsize);
  342.     Invalidate;
  343.   end;
  344. end;
  345.  
  346.  
  347. procedure TMultiGrid.SizeGrid;
  348. var c, r: Longint;
  349. begin
  350.   { try to display without the scroll bar first }
  351.  
  352.   c := Width div DefaultColWidth;
  353.   if c = 0 then Inc(c);
  354.   r := Limit div c;
  355.   if Limit mod c > 0 then Inc(r);
  356.  
  357.   { if the computed row count exceeds the number of rows that
  358.     can be displayed, take the scroll bar width into account and recalculate }
  359.  
  360.   if (Height - 4) div DefaultRowHeight < r then begin
  361.     c := (Width - GetSystemMetrics(SM_CXVSCROLL)) div DefaultColWidth;
  362.     if c = 0 then Inc(c);
  363.     r := Limit div c;
  364.     if Limit mod c > 0 then Inc(r);
  365.   end;
  366.  
  367.   Setsize(c, r);
  368. end;
  369.  
  370.  
  371.  
  372. procedure TMultiGrid.SizeChanged(OldColCount, OldRowCount: Longint);
  373. begin
  374.   inherited SizeChanged(OldColCount, OldRowCount);
  375.   FreeMem(FSelected, OldColCount * OldRowCount);
  376.   FSelected := AllocMem(ColCount * RowCount);
  377. end;
  378.  
  379.  
  380. procedure TMultiGrid.CellSelected(i : Integer; IsSelected : Boolean);
  381. begin
  382.   Selected[i] := IsSelected;
  383.   InvalidateCell(i mod ColCount, i div ColCount);
  384.   if Assigned(FOnCellSelected) then FOnCellSelected(self, i, IsSelected);
  385. end;
  386.  
  387.  
  388. function TMultiGrid.CellIndex(ACol, ARow : Longint) : Integer;
  389. begin
  390.   Result := ARow * ColCount + ACol;
  391. end;
  392.  
  393.  
  394. procedure TMultiGrid.Reset;
  395. begin
  396.   FillChar(FSelected^, ColCount * RowCount, False);
  397.   FSelcount := 0;
  398.   FMulti := False;
  399.   Invalidate;
  400. end;
  401.  
  402.  
  403.  
  404. function TMultiGrid.SelectCell(ACol, ARow: Longint): Boolean;
  405. var
  406.   i, j, index, lower, upper, temp: Integer;
  407. begin
  408.   if FUpdates > 0 then begin
  409.     Result := True;
  410.     exit;
  411.   end;
  412.  
  413.   index := ARow * ColCount + ACol;
  414.  
  415.   Result := index < Limit;
  416.   if Result and Assigned(FOnSelectCell) then
  417.     FOnSelectCell(self, index, Result);
  418.  
  419.   if Result then begin
  420.  
  421.     if AllowMulti and (GetKeyState(VK_CONTROL) < 0) then begin
  422.       { Ctrl-click.  Invert selection of target cell }
  423.       FMulti := True;
  424.       Selected[index] := not Selected[index];
  425.     end
  426.     else if AllowMulti and (GetKeyState(VK_SHIFT) < 0) then begin
  427.       { Shift-click.  Select range of cells }
  428.       FMulti := True;
  429.       lower := Row * ColCount + Col;
  430.       upper := index;
  431.       if lower > upper then begin
  432.         temp := lower;
  433.         lower := upper;
  434.         upper := temp;
  435.       end;
  436.  
  437.       for i := 0 to Limit-1 do
  438.         Selected[i] := (i >= lower) and (i <= upper);
  439.     end
  440.     else
  441.       { normal click -- no Ctrl or Shift }
  442.       if FMulti then begin
  443.         if not FSelected^[index] then begin
  444.           { turn off multi mode }
  445.           FMulti := False;
  446.           for i := 0 to Limit-1 do Selected[i] := False;
  447.           Selected[index] := True;
  448.         end;
  449.       end
  450.       else begin
  451.         { change highlighted cell }
  452.         i := Row * ColCount + Col;
  453.         Selected[i] := False;
  454.         Selected[index] := True;
  455.       end;
  456.     if Assigned(FOnSelect) then FOnSelect(self, index);
  457.   end;
  458. end;
  459.  
  460.  
  461. procedure TMultiGrid.Select(Index : Integer);
  462. var ACol, ARow, c, r: Longint;
  463. begin
  464.   c := Col; r := Row;
  465.   if SelectCell(Index mod ColCount, Index div ColCount) then begin
  466.     Focus := Index;
  467.     InvalidateCell(c, r);
  468.     Update;
  469.   end;
  470. end;
  471.  
  472.  
  473. procedure TMultiGrid.DrawCell(ACol, ARow: Longint; ARect: TRect;
  474.    AState: TGridDrawState);
  475. var i : Integer;
  476. begin
  477.   if FUpdates > 0 then exit;
  478.   i := ARow * ColCount + ACol;
  479.   with Canvas do begin
  480.     if FSelected^[i] then begin
  481.       Brush.Color := SelColor;
  482.       Include(AState, gdSelected);
  483.     end
  484.     else begin
  485.       Brush.Color := Color;
  486.       Exclude(AState, gdSelected);
  487.     end;
  488.     if DefaultDrawing then begin
  489.       if gdFocused in AState then DrawFocusRect(ARect);
  490.       FillRect(ARect);
  491.     end;
  492.   end;
  493.   Exclude(AState, gdFixed);
  494.   if (i < Limit) and Assigned(FOnDrawCell) then
  495.     FOnDrawCell(self, i, ARect, AState);
  496. end;
  497.  
  498.  
  499. procedure TMultiGrid.DeselectAll;
  500. var i: Integer;
  501. begin
  502.   for i := 0 to Limit-1 do Selected[i] := False;
  503.   if Assigned(FOnSelect) then FOnSelect(self, Focus);
  504. end;
  505.  
  506.  
  507. procedure TMultiGrid.SelectAll;
  508. var i: Integer;
  509. begin
  510.   for i := 0 to Limit-1 do Selected[i] := True;
  511.   if Assigned(FOnSelect) then FOnSelect(self, Focus);
  512. end;
  513.  
  514.  
  515. function TMultiGrid.MouseToCell(X, Y: Integer): Integer;
  516. begin
  517.   with MouseCoord(X, Y) do Result := Y * ColCount + X;
  518. end;
  519.  
  520. procedure TMultiGrid.TopLeftChanged;
  521. begin
  522.   if Assigned(FOnTopLeftChange) then FOnTopLeftChange(self);
  523. end;
  524.  
  525. function TMultiGrid.GetThumbTrack: Boolean;
  526. begin
  527.   Result := goThumbTracking in Options;
  528. end;
  529.  
  530. procedure TMultiGrid.SetThumbTrack(value : Boolean);
  531. begin
  532.   if value then Options := Options + [goThumbTracking]
  533.   else Options := Options - [goThumbTracking];
  534. end;
  535.  
  536.  
  537. procedure TMultiGrid.SetDropFocus(value: Integer);
  538. begin
  539.   if FDropFocus <> Value then begin
  540.     if FDropFocus <> -1 then
  541.       Canvas.DrawFocusRect(CellBounds(FDropFocus));
  542.     if value <> -1 then
  543.       Canvas.DrawFocusRect(CellBounds(value));
  544.  
  545.     FDropFocus := value;
  546.   end;
  547. end;
  548.  
  549.  
  550. procedure Register;
  551. begin
  552.   RegisterComponents('Samples', [TMultiGrid]);
  553. end;
  554.  
  555. end.
  556.