MSTRGRID

Version 2.2c,  20.2.1997,  PostCardWare, Albrecht Mengel, mengel@stat-econ.uni-kiel.de

If you have problems/ideas with mStrGrid or mStrList, please feel free to email me.

New: Method ReorderCompleteRows(KeyCol:integer; OrderValues:TStrings);
         Just call with the wished order of the KeyColumn as StringList.
         This sets all columns into that same order.
         Known Problems: No duplicate values allowed in OrderValues nor in the key column
                         OderValues must be a true permutation of the key column
         If UseFixed=hfSmart (default), the fixed rows are excluded from reordering and so
         the number of entries in OrderValues must be the same as the non-fixed entries in
         the grid column.

New: Multi-Column sorting is possible now with SortCompletRows and SortCompleteColumns:
     First sort the least important column(s), then sort the most important column(s).
     (The order of equal-valued rows is kept).
     As result, the whole table is sorted after the most important column. If any values there
     are equal, the table is sorted by the next less important value, and so on.
     (There is an expample supplied to test this property)

**** Bugfix: When ampm is in DateTimeFormat, an error occured, if am/pm was missing in data.
             I intended to allow missing informations.
**** Has anyone a good program (or tip) for easy creating a help file?

New Properties for sorting dates:

DateTimeFormat:string;
     (Reasons for:
      1. I former used the internal DateTime format, but there are simply too much crashes,
         when the date encountered is not valid
      2. The setting of the global ShortDateFormat has no influence on the interpretation,
         so mStrList interprets selv and is sensible to the own DateTimeFormat.)
     When sorting dates, they are interpreted in the kind as DateTimeFormat is set to.
     (see above: KeyType / Date sorting)
ShortYearExpand:boolean;
     if true (default), expands 2-digit-years to 4-digit-years.
ShortYearBorder:(0..99);
     lower values of 2-digit-years are converted to 2000's years and
     greater values of 2-digit-years are converted to 2000's years.
     Default is 10, that means: 0..9 -> 200x and 10..99 -> 19xx

Tip for coloring mStrGrid: see at the end
Tip for sorting by clicking at a title cell: see at the end

Bugfix in 2.1: Method SortCol

New in 2.0: Sort Date

New in 1.3: Properties ReverseSearch, SearchExists, SortIndex
            Method UndoSort

New in 1.2: Click Event is called when clicking on fixed rows/cols
            AND property ClickFixed is true.
            SortDescending: new property

New in 1.1: Cut/Copy/Paste with Clipboard (Works with Excel).
            EnableClipboardShortcuts and PasteToCursor properties

New Tip: You can easy use the LoadFromFile/SaveToFile methods with #9 as delimiter.
         This is useful with Excel.

This is a component descending from TStringGrid with some new sorting and searching properties & methods:

************************************
* Inserting / Deleting / Modifying *
************************************

procedure InsertCols(where,howmuch:Integer);
procedure InsertRows(where,howmuch:Integer);
procedure DeleteCols(where,howmuch:Integer);
procedure DeleteRows(where,howmuch:Integer);
   It does not matter if there are less rows/cols than you want to delete.
procedure AddCol(contents:String;  delimiter:Char);
procedure AddRow(contents:String;  delimiter:Char);
   The contents are splitted into the single cells by the delimiter
function ModifyRow(which:integer; contents,delimiter:string):integer;
   Results in the number of columns
function ModifyCol(which:integer; contents,delimiter:string):integer;
   Results in the number of rows

***********************
* Sorting / Searching *
***********************

The following properties control sorting and searching:

property KeyType:(soString,soStringCaseSensitive,soNumeric,soDate)
   This is the kind, how the keys (and cells) are compared.
   If you work with soNumeric, all non numbers get the same value 0.
      As these zero values would flip in random order a (case insensitive) string sort
      is performed after. So, first come the negatives, then the strings, and thereafter
      the positives.
   Date sorting works now with an own Date-Time comparison. The format of the date/time
      is controlled by the property DateTimeFormat, which is by default ShortDateFormat+' '+LongTimeFormat.
      ( ShortDateFormat and LongTimeFormat are global in Delphi and are set anywhere in
      the options or in the system). The format is similar to the format of ShortDateFormat
      and LongTimeFormat: Use D M Y for day, month, year, and H M (or N) S T for hours, minutes,
      seconds, and 1/100 seconds. Some examples:
      'mm/dd/yy hh:mm' or 'dd.mm.yyyy hh:mm:ss'
      mStrList does not expect the count of digits you have in the format, just the order.
      Watch: Illegal formats raises errors! If you edit with OnChange, uncheck in Tools/Options/...
      the 'stop on Exception' (or so. I don't know the english menu). Sample:
         procedure TForm1.formatChange(Sender:TObject);
           begin try mStrGrid1.DateTimeFormat:=format.text except end
           end;
   property ShortYearExpand:boolean; (default: true)
     if set, 2-digit-years are expanded 4-digit-years, dependend of the seeting of:
   property ShortYearBorder:(0..99);
     lower values of 2-digit-years are converted to 2000's years and
     greater values of 2-digit-years are converted to 2000's years.
     Default is 10, that means: 0..9 -> 200x and 10..99 -> 19xx
property KeyPos:Integer;
property KeyLen:Integer;
   Here you may define, which substring of the cells is used to comparision.
   (Default is KeyPos=1 & KeyLen=MaxInt)
property UseFixed:(hfNot,hfSmart,hfYes);
   hfNot: The fixed rows/columns remain as they are (and are not used in searching)
   hfSmart: When sorting complete rows, the fixed columns are part of the rows and
            change their contents in the same manner as the rows are exchanged.
            The top row (fixed rows) remain intact.
            When sorting complete columns, the fixed rows change and the fixed columns not.
            When sorting (single) row/column, hfSmart protects the fixed part of the row/column.
   hfYes: All sorting includes the fixed columns and rows (as if they were set to 0)
property SortDescending:Boolean;
   false (default) : Ascending sorting order
   true            : Descending sorting order

***********
* Sorting *
***********

procedure SortCompleteColumns(KeyRow:integer);
procedure SortCompleteRows(KeyCol:integer);
procedure SortRow(ThisRow:integer);
procedure SortCol(ThisCol:integer);
procedure SortAllRows;
   The rows are sorted independand
procedure SortAllCols;
   The columns are sorted independand

procedure UndoSort;
  This re-sorts the last sorting.
  Multiple calls of UndoSort have only effect on the last sorting scheme:
   UndoSort;UndoSort -> the sorted order remains.
property SortIndex:TmStrList
  Here you see the actual index. That are the positions, the contents have to be moved
  from, when the undo actions should be made.


*************
* Searching *
*************

Searching can be done with the fixed substrings of cells
   (set SearchSubstring=true and use KeyPos and KeyLen) or
   anywhere in the cells (SearchSubstring=false)
property SearchSubstring:Boolean;

function FindFirst(Key:String; RowWise:Boolean; VAR ResultCol,ResultRow:Integer):Boolean;
    Searches all cells, if UseFixed=hfYes or excludes the fixed cells, if UseFixed=hfNo.
    If UseFixed=hfSmart, then the fixed cols are used if searching rowwise or
                              the fixed rows are used if not searching rowwise.
    All finding methods result in true, when a cell was found.
    If nothing found then ResultCol and ResultRow are set to -1.
function FindFirstInRow(Key:String; searchRow:Integer; VAR ResultCol,ResultRow:Integer):Boolean;
    The same as FindFirst, but only one row is used for searching.
    UseFixed=hfSmart is here the same as hfNo.
function FindFirstInCol(Key:String; searchCol:Integer; VAR ResultCol,ResultRow:Integer):Boolean;
function FindNext(VAR ResultCol,ResultRow:Integer):Boolean;

One can invert the searching direction by the property ReverseSearch. If set to true, then the search
starts at the end. The property may be changed while a search is not ready.
The readonly property SearchExists is set true, if any search was started (and then remains true).
If a search is ready (results in -1,-1) you may invert ReverseSearch. Then the search is started
again at the actual end in the opposite direction and you can get the first result by a FindNext.

A sample searching program:
 if FindFirst('steven',true{rowwise},c,r) then
 repeat memo1.lines.add('Found in cells['+inttostr(c)+','+inttostr(r)+'] = "'+cells[c,r]+'"');
 until not FindNext(c,r);


*******************
* Import / Export *
*******************

File can be read to or written from the table as ASCII file.
The second parameter is the column delimiter in every row.
    procedure LoadFromFile(FileName,ColSep:string);
    procedure SaveToFile(FileName,ColSep:String);

The contents of the selected area can be handled with the clipboard:
    procedure CopyToClipboard;
    procedure CutToClipboard;
              Like CopyToClipboard, but empties then selected cells
    procedure PasteFromClipboard;
              Replaces the selected cell rectangle.
              When PasteToCursor is false (default setting),
              not the cursor position is relevant, but the upper left
              corner of the selected area.
    property  PasteToCursor: controls the PasteFromClipboard method:
              When true, the cells are inserted at cursor position.
 The internal format is Tab Delimited Text.

If the property EnableClipboardShortcuts is set to true, and when NOT editing,
you can perform CopyToClipboard by Ctrl-C, CutToClipboard by Ctrl-X,
and PasteFromClipboard by Ctrl-V.

If the property ClickFixed is true the Click Event is called when clicking on fixed rows/cols.
  Col and Row are set to the proper values.
  The implementation is difficult and not yet flickerfree. (Description in the heading of mStrGid.pas)
  As a result of the implementation the OnClick Procedure reads the FixedCols and FixedRows
  values always as zero (They are temporarily set so). This may give some strange behaviour
  in your program - just use SavedFixedCols and SavedFixedRows in the OnClick procedure.
  Also, if clicked the second time at exact the same mouse position in a short time,
  it does not call the click routine. Reason: Your Doubleclick is set too long!
  Remember: property ClickFixed MUST be true.

********
* Demo *
********

The demo program has one odd property: if found something in the fixed parts of the grid,
the fixed rows/cols are duplicated. (Because it is not allowed to set the cursor there to)

The file mStrList is required for compilation.
It contains a 'sister' of TStringList, which can sort in the above manner and is a complete copy
of TStringList, which can be found in \source\vcl\GRIDS.PAS.
But I had a problem with compiling it completely.
If you are good in programming OOP / VCL then please have a look in mStrList.pas and
help me fixing the problem. Until now all seems to work.

To install mStrGrid:

1. Copy mStrGrid.* and mStrList.* to a directory for your special delphi components.
2. Select the Component: Install menu.
3. Click the Add button.
4. Click the Browse button.
5. Locate and select the mStrGrid.pas file
6. Click the Ok buttons to close all the dialogs.
The Delphi component library DLL will be recompiled with the new mStrGrid component
added to the Additional page of the component palette.

******************
* Coloring cells *
******************

Sample for coloring mStrGrid (or StringGrid): Just add the OnDrawCell event and
replace the conditions for coloring in the TForm1.mStrGrid1DrawCell procedure at the end:

procedure TForm1.mStrGrid1DrawCell(Sender:TObject; Col,Row:Longint; Rect:TRect; State:TGridDrawState);
var OldColor:TColor;
begin with mStrGrid1.Canvas do
      {NOT mStrGrid1,Canvas! Watch: I tried with many WITH's,
      but I could not get the mStrGrid1 references away. If you got it: tell me}
      begin OldColor:=Font.Color;
            {If cell is fixed}
            if gdFixed in state
               then Font.Color:=clBlack else
            {If cell is not fixed}
            if strtoint(mStrGrid1.cells[col,row])>500
              then Font.Color:=clGreen
              else Font.Color:=clRed;
            TextOut(Rect.Left+2,Rect.Top+2,mStrGrid1.Cells[Col,Row]);
            Font.Color:=OldColor;
end   end;

You can even give different colors to single characters or any other font property.
Tip: use TextWidth to calculate a new x position, if you call multiple TextOut's for drawing
the contents of a cell. (The only problem I had is that I cannot EDIT the cell with this display)

***********************************************
* Sorting a table by clicking on a title cell *
***********************************************

procedure TForm1.mStrGrid1Click(Sender: TObject);
{This procedure checks if you clicked on a fixed cell. If so, (and ClickFixed = TRUE)
 then the sorting direction is changed and this col or row is sorted.
 The direction of sorting is here hidden in the objects property. Normally it is a
 pointer to further information, but it is good enough to hold a 4 byte value.
 (This works without memory allocation!)
 When using first, the initial value (whatever it is) is set to 1 and the whole table
 is sorted ascending. The next click finds the 1, sorts descending and set a 2}
begin with mStrGrid1 do
      if (row<SavedFixedRows) and (col>=SavedFixedCols) then
        if Integer(objects[col,row])=1  {The pointer is converted to an integer}
         then begin {ascending -> descending}
                    SortDescending:=true;
                    SortCompleteRows(col);
                    objects[col,row]:=TObject(2); {The integer is converted to the pointer}
              end
         else begin { -> ascending}
                    SortDescending:=false;
                    SortCompleteRows(col);
                    objects[col,row]:=TObject(1)
              end else
      if (col<SavedFixedCols) and (row>=SavedFixedRows) then
        if Integer(objects[col,row])=1
         then begin {ascending -> descending}
                    SortDescending:=true;
                    SortCompleteColumns(row);
                    objects[col,row]:=TObject(2);
              end
         else begin { -> ascending}
                    SortDescending:=false;
                    SortCompleteColumns(row);
                    objects[col,row]:=TObject(1)
end           end;

************************
* Software development *
************************

Programming consumes time, and programmed components save time.
If you like my components feel free to send me some acknowledgment.
I accept post cards of your town, money or cheques (2$ up to 20$),
if you use mStrGrid for private.
For commercial use I give this code free for $20 (or more).

This is a motivation for me to continue developing for you.

If you have some ideas to improve mStrList, mStrGrid or any other component
send me a message.

The component is copyright (C) 1996, by Albrecht Mengel. You may give copies to
others by copying the original, unmodified zip file. You may use this component
in your own projects free of charge as long as those projects are public domain,
freeware or shareware project.

The author of mStrList and mStrGrid (A. Mengel) makes no warranty of any kind,
expressed or implied, including without limitation any warranties of merchantability
and/or fitness for a particular purpose. In no event will the author be liable to you
for any additional damages, including any lost profits, lost savings, or other
incidental or consequential damages arising from the use of, or inability to use,
this software and its accompanying documentation, even if the author has been advised
of the possibility of such damages.

Albrecht Mengel, University of Kiel, Germany
Institute for Statistics & Economics
Olshausenstrasse 40-60,
D-24098 Kiel
Tel. +49-431-880-2424
Fax. +49-431-880-2673
Email: mengel@stat-econ.uni-kiel.de
http://www.stat-econ.uni-kiel.de/pers/mengel.htm
