Products
> GRID
The Grid Control
Lcc grid
Lcc grid is a control that allows you to quickly display tabular
data. The grid has two modes:
- As a control window within another window.
- As a standalone application.
As a standalone application, the grid will create a standalone
window, open a frame with a menu, where the user can edit the data if
wished, and save it to disk. The command line parameters are
explained below.
The general logical outline of a table looks like this :
| |
Column Titles |
| Row |
Cells
|
| Titles |
| |
| |
| |
| |
The column titles and the row titles are predefined as numbers from 1
to the number of columns or rows.
It is possible to change the column titles to any string, but not the
row titles.
Top of page lcc-win32 main page Products main page
The default user interface
Keyboard:
If there is a selected cell, the arrow keys move the selected cell
up,down, left or right. Page up and page down have their usual
meanings.
Mouse:
Left click selects a cell. If it is in a column title, it will select
a column. If it is in a row title it will select a row.
Double click opens a cell for editing. When editing, Esc means abort
the editing, return means validate entry.
Left click in a column title boundary allows the user to resize a
column.
Right click in a column will open a context menu for sorting a
column.
Top of page
lcc-win32 main page
Products main page
Programming interface
The table structure itself is not public, and it is hidden
behind a void pointer. This shields you from any changes in the
implementation of the grid, preserving your source code. This
interface can change, but the changes will not be done in an
incompatible way. The functions here described will not change. If
new interfaces, or new functions are needed, they will have new
names. The old ones will continue to be as here described.
Creating a grid
void *CreateGrid(HWND hwnd,int
rows,int cols);
This function will create an initially empty table with the given
number of rows and columns. All necessary windows (the table window,
the edit field window, and the tool-tips window are created also. The
resulting pointer identifies the table, and should be used in
subsequent calls. The input parameters are the parent window, and the
desired number of initial rows and columns.
void *LoadCSV(char *fname,HWND
hwnd);
This function will load a table from a Comma Separated Values (CSV)
type of file. The character that is used to delimit the fields is
searched in the file, it can be < , >,
< ; > or < \t >. The same character
is used to write back the file, unless you set another one:
char GetCsvSeparator(void *tab);
char SetCsvSeparator(void *tab, char Separator);
The second function returns the character set. It is 0 if the
character you give is not one of < , >,
< ; > or < \t >.
void *GridOpenDatabase(HWND hDlg, char *DSNname);
int GridSelectTable(HWND hwnd, void *Database);
int GridSelectTableOrView(HWND hwnd, void *Database);
void *LoadODBC(void *Database,HWND hwnd);
The first function GridOpenDatabase without
any DSN name opens a database using the standard Windows interface,
allowing us to open all the databases that are recognised by Windows.
You can also give a complete connexion string, like
DSN=DatabaseName; DBQ=c:\\Directory\\Data.mdb;
DriverId=25; FIL=MS Access; MaxBufferSize=2048; PageTimeout=5;
UID=admin; PWD=password;
The second function GridSelectTable opens a
dialog box that allows us to choose the table of the database we want
to open. The function GridSelectTableOrView
shows all tables and views. The last function
LoadODBC loads the choosen table of the
database.
Alternately, you can give a DSN name or a connexion string to
GridOpenDatabase and use two other
functions:
int GetTablesOfDatabase(void *Database, int
*NbOfTables, char ***TableNames);
int SetSelectedTable(void *Database, int
TableNr);
The function GetTablesOfDatabase gives you
the list of the tables of the opened database, and the function
SetSelectedTable sets the table you want to
open. You have to call LoadODBC to create the
grid and load the data.
If you use the GridOpenDatabase function, and
abort before having created the grid, you should use this function to
close the database:
int GridCloseDatabase(void
*Database);
Here is an example of code, where we choose a database by selecting a
DSN file
void *newtab, *Database;
newtab=0;
if (NULL != (Database = GridOpenDatabase(hwnd, DSNname))) {
if (GridSelectTable(hwnd, Database)) {
newtab = LoadODBC(Database,hwndChildMdi);
}
}
if (newtab == NULL) {
// Close the database if we dont choose a table
// or if it is not readable
GridCloseDatabase(Database);
}
When the grid is destroyed, generally when you close the window
that contains it, all elements are correctly closed. You can explicitly
destroy the table with :
int DestroyTable(void *tab);
Note that all functions use indexes that are zero
based : the first column is always zero and runs until the number of
columns minus one, the first row is always zero and runs until the
number of rows minus one.
Top of page
lcc-win32 main page
Products main page
Setting the data of the grid.
int SetCell(void
*tab,int row, int col,char *data);
This will set the data to display in a given cell. Returns 1 if the
operation is successful, zero otherwise (index error). The data
pointer can be NULL, in this case the information in the cell (if
any) is just discarded.
int GetFromClipboard(void *tab);
This function puts the content of the clipboard in the selected
cell.
int SetColumnTitle(void *tab,int col,char
*data);
This will set the column title for the given column. Returns 1 if
successful, zero otherwise. A previous title is discarded, and
freed.
Top of page lcc-win32 main page Products main page
Just a test
Getting the data from the grid.
You can get a whole
column/row of data with :
int GetColumnData(void *tab,int col,char
***pResult);
int GetRowData(void *tab,int row,char
***pResult);
You pass to these functions a pointer on an empty array of pointers
to character data that the grid fills. The result is the number of
rows/columns that were written to the passed array. You don't have to
allocate the memory for that, but you should free it with :
int FreeBlockData(void *tab, char
***pResult);
Your pointer becomes NULL, avoiding future illegal use.
You can access the data in a single cell with :
char *GetCellData(void *tab,int row,int
col);
Be carefull with the usage of the pointer you receive : it may become
obsolete, and more frequently it will contain a different data than
that you expect. You should copy the data for a future use.
If you are sure that a cell contains numeric data you can use :
double GetCellNumericData(void *tab,int row,int
col);
This is the same as
atof(GetCellData(tab,row,col));
If the substitution is effective on the cell, the data given by these
functions correspond to the original values, before any
substitution.
To get the substituted data, use the function :
int GetSubstitution(void *tab, int col, char *input,
char *output, int len);
The string in data may also be something else that the content of the
cell. If the value is not found in the substitution table, the result
is 0. Otherwise it is set to 1, and the output buffer is written on a
maximum of len bytes.
Top of page lcc-win32 main page Products main page
Saving the data in the grid.
int SaveRtf(void
*tab ,char *filename);
This will output an rtf representation of the table suitable for a
word processor. Returns 1 for success zero otherwise (bad file
name).
int SaveCsv(void *tab,char
*fname);
This will output a CSV (Comma Separated Values) to the given file
name. Returns 1 for success zero otherwise.
int SaveToClipboard(void *tab,int
fSaveAll);
This will copy the selected row or column to the clipboard. If the
flag fSaveAll is true, or nothing is selected, all data will be
copied. Returns 1 for success zero otherwise.
int SaveXml(void *tab,int
fSaveAll);
This will output an XML file with the following layout :
<TABLE>
Number-of-rows="68"
Number-of-columns="10"
<COLUMN-TITLES>
Column0Title="name"
Column1Title="name"
...
</COLUMN-TITLES>
<ROW-TITLES>
Similar layout to column titles: RowX="NAME"
</ROW-TITLES>
<ROW0>
Cell0 "AdjustColumnWidth"
Cell1 "gridresize.c"
Cell2 "37"
...
</ROW0>
Follows the other rows
</TABLE>
Note that a grid opened on a database does not need to be explicitely
saved to set the data in the database. Each time the value of a cell
is set, the database is updated, and then the data is reloaded
because another user may have inserted, deleted or modified several
rows.
However, like all grids, it is still possible to save the data in a
CSV file, for instance.
Top of page lcc-win32 main page Products main page
Special data output.
int GridSubstitutions(HWND
hwnd, void *tab);
This function uses a dialog box to define the data substitutions : a
field containing the values 0 and 1 may be showed, for instance, as
False and True.
For each column of the grid, we can choose couples of character
strings : the value in the grid and the replacement string.
Alternately, you can use another function :
int SetDirectSubstitution(void *tab, int col, int
NbOfSubstitutions, char **OriginalData, char
**SubstitutedData);
The arguments OriginalData and
SubstitutedData must have the length given by
NbOfSubstitutions. To clear the
substitutions, set NbOfSubstitutions to 0 and
the 2 pointers to the values to NULL.
If the gris is opened on a database, the function
GridSubstitutions gives another way to change
the output. All the tables of the database are listed, so you can
choose on of them. Then the columns of this table are displayed, and
you can select two of them : the one where the original value is
found (generally the key corresponding to the data displayed in the
grid), and the one that you want to display instead. Click on the
Validate button to accept each substitution definition.
For instance, when you display a billing table, the product ID is a
column of the billing table, but you want to display the product
name. So you substitute the product number with the table Products,
choosing the ProductID as the column containing the value, and
ProductName as the column to display.
The other way to set this substitution is to use the function :
int SetSQLSubstitution(void *tab, int col, char
*SQLselect1, char *SQLselectall);
You have to define two SQL expressions. The first one is executed
when the system has to found the value of a specific cell. This
expression must be like :
SELECT ProductName FROM Products WHERE
ProductID=#value#
Be sure to replace the value we have to found by #value#, and if this
value is a character string, put it between quotes, like :
SELECT ProductName FROM Products WHERE
ProductID='#value#'
The second SQL expression is executed when the system has to load all
the substitution data. For instance, when we double-click in a cell,
the user does not have to type in the characters of the substituted
data, but the control is a combo box that displays all the values.
The SQL expression has to be like :
SELECT DISTINCT ProductID,ProductName FROM
Products
The first column is the one that contains the value found in the
grid, and the second one the value to display.
To clear the substitution, just call the function with the two SQL
expressions set to NULL.
Note that this function does not do anything, and returns 0, if you
call it when the grid is not opened on a database.
Take in mind that the original data is still stored in the grid, so
the substitution is calculated each time is is needed. This may be a
problem when you have to go through a large number of cells, for
instance if you are searching for a particular value.
The solution is to fix the substitution, so the second SQL expression
is executed, and the result is read to simulate a user-defined
substitution. When the loop through the cells ends, you can unfix the
substitution, so if you modify the substitution table, the new values
are read and can be displayed in the grid.
int FixDatabaseSubstitution(void *tab, int col);
int UnfixDatabaseSubstitution(void *tab, int
col);
These functions use the index of the column you want to fix or unfix.
You can give the value -1 that means all the columns.
Top of page lcc-win32 main page Products main page
The virtual rows.
int
SetGridVirtualScrolling(HWND hwnd, void *, int
NbRows,fnFetchCellValue fnFetch, fnSetCellValueBack
fnBack,fnGetWidthOfCol fnWidth);
The function sets the grid on a virtual data that your program has to
maintain. The virtual rows are used internally when the grid is
opened on a database This function allows you to use the grid to see
a file that you can read and write, even if this file is very
large.
First create a grid with one row, and the number of columns that is
convenient :
Table = CreateGrid(hwnd, 1, NbOfCols);
Then tells the grid that you maintain the data :
SetGridVirtualScrolling(hwnd, Table, NbOfRows, MyFetch,
MyBack, MyWidth);
NbOfRows is the actual number of rows of your
data, you will be able to modify it when necessary. The number of
columns corresponds to the total number of columns of the grid, even
if they are hidden. All the column numbers are relative to the data,
not to the display, so you work with fix numbers, unless you insert
or delete a column.
MyFetch is the function called by the grid to
get the content of a cell :
char *MyFetch(void *tab, int row, int col)
{...}
You have to return the pointer on the string to be displayed in the
specified cell, or NULL if the cell is empty.
MyBack is the function called by the grid to
put back the content of a cell into your file :
char *MyBack(void *tab, int row, int col, char
*data)
{...}
You use the pointer on the content of the cell to put the data back
into your file. If you do not want to modify your file, you can
forget this function, and give NULL as the corresponding argument to
the SetGridVirtualScrolling function. If you
do so, you will not be able to edit any cell, nor add a line within
the grid. However, you can still change your values as convenient,
even add or delete rows.
MyWidth is the function called by the grid to
get the width of the columns, in characters :
int MyWidth(void *tab, int col)
{...}
Be sure to give the maximum width of the column. This value is only
read when you call SetGridVirtualScrolling,
so the width of a column that can grow, like a line number that can
change from one digit to two, has to be estimated, for instance the
number will certainly be less than 99999999, so the width of 8 will
be convenient.
int SetVirtualNbOfRows(void *tab, int
NbRows);
This function sets the grid to a new number of rows. This number may
be 0. Remember that you maintain the data, so setting the number of
rows to 0 cannot delete your data. You can only see, and modify with
the grid, the rows that the grid displays, but the data you want to
hide is still in your hands.
void *SetUserData(void *tab, int index, void
*data);
void *GetUserData(void *tab, int index);
These functions allows you to store and recall up to 10 values in the
grid. They can be pointers on your data, like a buffer containing a
portion of the larger file that you have to read and decrypt for the
grid.
The index value is from 0 to 9. The function
SetUserData returns the old value.
If the access to your file is relatively slow, it may be useful to
read a few lines from it, and put them into a buffer. It is a good
idea to have enough place to put at least 2 pages of data in this
buffer : when the grig has to be painted, the cells from the first
row to the last one of the screen have to get there data. So, when
your function MyFetch is called, the row of
the cell is, in this case, one displayed on the screen. Be sure that
this function will be called for other rows. If your buffer if larger
than 2 times the maximum number of displayed rows, you can read the
buffer with the wanted row at the middle of it, so the first cell of
the sreen and the last one are in the buffer.
Top of page lcc-win32 main page Products main page
Managing the selection.
The user selects a cell by clicking
once in its surface. The selection can be moved with the arrow
keys.
You can set the selection to a specific cell with :
int SelectCell(void *tab,int row,int
col):
This function returns 0 if it fails (bad index) or 1 if the cell was
selected or the selection was set to that cell. The grid will ensure
that the selected cell is visible.
You can use
int SelectRow(void *tab,int row);
int SelectColumn(void *tab,int col);
to select a row or a column. Both return 1 if the row/column was
already selected or the selection was set, zero otherwise (bad
index).
Once the selection is set, you can move it with :
int MoveSelectedCellUp(void *tab);
int MoveSelectedCellDown(void *tab);
int MoveSelectedCellLeft(void *tab);
int MoveSelectedCellRight(void *tab);
This function return 1 if the selected cell was moved, zero if there
was no selected cell, and nothing was moved. Moving up and down will
wrap around the table.
Normally, the arrows can move the selected cell with their usual
meanings. You may cancel any selection with :
int CancelSelection(void *tab);
It returns zero if nothing was selected, 1 otherwise.
You can get the current selected cell with
int GetSelection(void *tab,int *prow,int
*pcol);
The results written into the pointers passed in are :
| Row |
Column |
Meaning |
| -1 |
0 ... Number of table columns-1 |
The column is selected |
| 0 ... Number of table rows-1 |
-1 |
The row is selected |
| 0 ... Number of table rows-1 |
0 ... Number of table columns-1 |
The cell at row, column is selected |
| -1 |
-1 |
Nothing is selected |
If either the pointer to row or to the column is NULL, nothing is
written to them of course. This means that you are not interested in
that information. The result is always one.
You can get notified when the selection changes by establishing a
callback function that is called by the grid when this event
occurs.
typedef int (*GridSelChange)(void *tab,int row,int
col);
GridSelChange SetGridSelChangeCallback(void * tab, GridSelChange
fn);
When the user defined callback returns 1, the selection will not be
changed. If the result is zero, the selection change continues. Note
too that when the user selects an already selected cell, this
callback is not called.
Here is a simple example that updates a status bar when the selection
changes.
int SetStatusLine(void *tab,int row,int col)
{
char buf[128];
if (row == -1)
wsprintf(buf,"col %d",col+1);
else if (col == -1)
wsprintf(buf,"line %d",row+1);
else
wsprintf(buf,"line %d col %d",row+1,col+1);
UpdateStatusBar(buf,0,0);
return 0; // Accept the change!
}
After the table is created, the code calls :
GridSetSelChange(tab,SetStatusLine);
Top of page lcc-win32 main page Products main page
Getting state information from the grid.
You get the window
handle of the grid window with :
HWND GetGridWindow(void *tab);
This window handle allows you to perform all operations you may want
with the grid, like moving it, resizing it, closing it, etc.
int GetDimensions(void *tab,int *prows,int
*pcols);
Returns the total number of rows and columns in *prows and *pcols and
returns 1. If you want only one of these numbers, you can give a NULL
pointer to the other one.
int GridGetFirstVisibleRow(void
*tab);
Returns the index of the first visible row.
int GridGetFirstVisibleColumn(void
*tab);
Returns the index of the first visible column.
int GridGetVisibleColumns(void *tab);
int GridGetVisibleRows(void *tab);
These functions return the number of rows or columns that are
displayed currently in the grid window.
You can scroll a cell into view with
int EnsureCellVisible(void *tab,int row,int
col);
Returns 1 if the cell could be set to visible, zero if the cell was
already visible, -1 if there was an index error.
int IsCellVisible(void *tab,int row,int
col);
Returns 1 if the cell is visible, zero otherwise.
You can query the grid for information about a specific point in it.
You pass to the grid the coordinates in its window coordinates of
course, not in screen coordinates.
int GridHitTest(void *tab,int x,int y,GRID_HITTEST
*result);
The structure GRID_HITTEST is defined like this :
typedef struct {
int AreaCode;
int col;
int row;
int x; // point coordinates
} GRID_HITTEST;
The AreaCode member of the structure is one of the following
values defined in windows.h :
HTNOWHERE : The point is not in the grid.
HTTOPLEFT : The point is in cell 0,0.
HTLEFT : The point is in a row title. The y member contains the row
index.
HTTOP : The point is in a column title. The x member contains the
column index.
HTCLIENT : The point is in the cell area surface. The x and y contain
the column and row respectively for the cell under the point.
This function returns always the area code. The pointer to the
structure can be NULL, if you are just interested in finding out in
which region the point is, and do not care about the specific
coordinates.
Top of page lcc-win32 main page Products main page
Hiding or moving columns.
You can hide a column with :
int HideColumn(void *tab, int
col);
You can move a column to a new position with :
int ShowColumnAt(void *tab, int col, int
pos);
As nearly all the functions, the value of col
refers to what we can see on the screen, and the value of
pos refers to the position where we want to
see the column. This means that if we hide a column, we still access
to the columns from 0 to the number of columns we can see on the
screen.
int ShowColumns(void *tab, int NbOfColumns, int
*SortedColumns);
This function needs the original column numbers. You give the number
of columns to display, and the indexes of theses columns. You cannot
give the same number several times : each column can appear only
once. Use this function to show a hidden column, or, if you want to
restore all the columns :
int ResetColumns(void *tab);
To get the columns that are displayed, you can use
GetDimensions to know the number of displayed
columns, and :
int GetDisplayedColumns(void *tab, int size, int
*columns);
The size argument is to be sure you give a sufficient area to return
the values, and these are the index in the original list of columns.
The value returned is 0 is there is an error, or the number of
displayed columns, which is always at least 1.
int ColDisplay2Data(void *tab, int col);
int ColData2Display(void *tab, int col);
These functions give the translated index of column between the
display and the data. The secund one returns -1 if the column is not
displayed.
Top of page lcc-win32 main page Products main page
Adding or deleting columns or rows.
You can add columns or
rows with :
int InsertRowBefore(void *tab,int idx);
int InsertColumnBefore(void *tab,int idx);
These functions receive an index to the row or column just before the
insertion point. You cannot insert rows in a database grid, but it is
possible to add a row by introducing data in the last row of the
grid. In a virtual rows grid, the lines are depending on the calling
procedure. It can add one blank line at the end of the grid, or
insert a blank line anywhere, but this is its responsability.
int DeleteColumn(void *tab,int idx);
int DeleteRow(void *tab,int idx);
These functions delete a row or column at the specified position. The
second one is not available with a virtual rows grid.
These functions do not update the window. You should update the
window of the table to force a refresh. This is done to optimize a
series of row deletions/insertions without producing screen
flickering. You can use this function :
int RefreshGrid(void *tab);
It just sends a message to the grid asking for a repaint.
NOTE : As you can imagine, all indexes after the inserted row
or column will increase, or decrease.
Top of page lcc-win32 main page Products main page
The undo functionality.
The grid supports one level of undo.
This level may be constituted of any number of elementary
functions.
For instance, if you create a function that replace the content of
all the cells that contain a specific value, it is possible to undo
all the cell changes in one call to the undo function.
The first function to call starts the undo memorization :
int ResetUndoAction(void *tab);
The list of undo actions is now cleared, and all the actions before
the next call to this function will be included in the list of
elementary actions that will be undone if we call :
int UndoLastCommand(void *tab);
This functions execute the undo list of elementary actions, and store
the new actions in the list, so that undoing again restore the grid
like it was before undo, and so on.
Top of page lcc-win32 main page Products main page
Controlling user input.
The first way to control may
sometimes be sufficient.
void StartEditCell(void *tab);
void EndEditCell(void *tab);
void CancelEdit(void *tab);
int GetCellEdit(void *tab, int *prow, int
*pcol);
StartEditCell starts an edit session on the
selected cell. EndEditCell ends the edit
session, reading the data, CancelEditCell
ends but does not read the data. GetCellEdit
gives the coordinates of the cell being edited.
This first way to control the input allows us only to edit the cells
we want, so we have to go further if we want to get the user's input
key by key.
When the user double-clicks in a cell, the grid will show a control
window to allow the user to modify the cell's contents. If you want
another behavior as the default, you can control this process by
establishing a callback function and intervening at three points :
- When the editing process starts the callback is called. If it
returns a non-zero result, the editing process will stop. No edit
window will be shown.
- When each character of text is keyed in by the user the
callback will be notified. If it returns a non-zero result the
character will be discarded.
- When the user presses return or the editing process is
otherwise finished (by pressing the tab key for instance), the
callback is called again. If it returns a non-zero result, the
contents of the cell will be left untouched.
- If the user presses the Esc key, the callback will receive a
notification but the contents of the editing process will be
discarded anyway.
Establishing the
callback.
The callback function receives a pointer to the following structure :
typedef struct {
void *table;
HWND hControl;// Contains Edit or Combo handle when opened
HWND hControlEdit;
HWND hControlCombo;
int row; // Will be always equal to the selected row
int col; // Will be always equal to the selected column
int character;// Character typed in
int flags; // Type of event
} GRID_EDIT_PARAMS;
Note that we find 3 HWND members.
hControlEdit contains the handle of the edit
control, and hControlCombo that of the combobox
control. This one is used when a substitution is defined on a column,
it may be a user-defined one or a database defined one. The list of the
combobox contains all the possible values for this column.
If a cell is being edited, the member
hControl contains the handle of the
corresponding control, otherwise it is NULL.
Since the callback can change the edit callback, it is possible to
build complex editing schemas by simply having a standard procedure
for handling the GRIDEDIT_START event, then changing the character
filter according to the cell that is going to be edited, relying on
the event GRIDEDIT_END to re-establish to first callback
function.
You set the callback using :
typedef int (*GridDrawCallback)(GRID_DRAW_PARAMS
*params);
GridDrawCallback SetGridEditCallback(void * tab, GridEditCallback
fn);
Of course, setting it to NULL disables any callbacks. The result of
this function is the previous filter. It can be NULL if there wasn't
any.
A simple
filter.
Here is an example of a very simple filter that allows only numeric
input :
int numericFilter(GRID_EDIT_PARAMS *params)
{
if (0 == (params->flags & GRIDEDIT_CHAR))
// If the event is not a
// character input event, just go on.
return 0;
// Accept digits
if (params->character >= '0' &&
params->character <= '9')
return 0;
// accept the point
if (params->character == '.')
return 0;
// discard everything else
return 1;
}
Top of page lcc-win32 main page Products main page
Customizing the grid.
You can replace many of the
functionalities of the grid by defining callback functions that
replace the default grid functionality at many steps.
All these functions should return TRUE if they have replaced the
functionality, FALSE to indicate the grid that the default
functionality is desired.
Of course, mixed strategies are possible. For instance, the grid cell
draw callback receives an HDC (handle to the device context), and can
change the text color, the background color, or some other parameter
in this HDC, and then return FALSE. The grid will use the modified
HDC to draw/paint the cell.
Modifying the drawing
of a cell.
You can specify a callback for cell drawing. If your callback returns
TRUE, the grid will not paint anything in the cell, supposing that
the callback has done it already.
The single parameter of your callback is a structure like this :
typedef struct {
void *Table;// The table you are drawing in
int row; // the row
int col; // The column
HDC hDC; // The device context
RECT rc; // The coordinates of the cell's rectangle
int selected;// Whether the cell is selected or not
} GRID_DRAW_PARAMS;
The callback function has the following signature :
typedef int (*GridDrawCallback)(GRID_DRAW_PARAMS
*params);
It should return an integer, and it receives a pointer to a
parameters structure.
You set the callback with:
GridDrawCallback SetCellDrawCallback(void *tab,
GridDrawCallback fn);
Note that you can pass NULL to this function. This will disable any
existing callback. The function returns the value of the old setting.
NULL means there wasn't any callback defined.
Since you receive the value of the old function, you can establish a
whole chain of functions if you wish, each calling the callback that
was in place if it wants to.
Note that the device context that you receive is not clipped. You can
draw anywhere in the surface of the table window... Please try to
restrain yourself to the given rectangle however.
Modifying the aspect of
the grid lines.
You can set your own function for modifying the grid lines with :
GridDrawCallback SetGridLinesCallback(void *tab,
GridDrawCallback fn);
You will receive the same structure as the callback to draw the
cells. The data in the structure will be as follows :
- Table : Will be set to the handle of the table.
- hDC : The device context.
- row : Will be set to the row number of the row just below the
horizontal line you should draw. If you should draw a vertical line
this will be -1.
- col : Will be set to the column number of the column at the
right of the vertical line that you should draw, or -1 if you
should draw an horizontal line.
- selected : Will always be zero.
- The rectangle will contain the coordinates of the starting
point, in the top left members, and the coordinates of the ending
point in the right, bottom members.
Modifying the grid
fonts.
The grid has two types of fonts : a mono-spaced font used for the
text in the cells, and a variable pitch font used for column and row
titles.
You can set your own fonts with :
int SetGridFixedFont(void *tab,HFONT newfont);
int SetGridVarPitchFont(void *tab,HFONT
newfont);
Note that the grid will destroy this object when the grid is
destroyed, or when the font is changed. The result of these functions
is always one.
HFONT GetGridFixedFont(void *tab);
HFONT GetGridVarPitchFont(void *tab);
These functions return the current fixed/variable pitch font
handles.
Modifying the keyboard
interface.
You can establish a callback for all keyboard events. The prototype
is as follows:
typedef int (*GridKeyboardCallback) (void *tab,
unsigned int message, WPARAM wParam, LPARAM
lParam);
The callback function receives the table, the windows message, and
its corresponding parameters. This callback is called when the grid
window receives the message WM_KEYDOWN, WM_KEYUP or WM_CHAR messages.
If the callback function returns a non zero result, no further
processing will be done.
You set the callback by calling :
GridKeyboardCallback SetGridKeyboardCallback(void *tab,
GridKeyboardCallback fn);
The result is the previous callback function, or NULL if there wasn't
any. If the "fn" argument is NULL, no callbacks will be called and
the default grid keyboard behavior is re-established.
Getting all window
messages.
You can receive all messages that windows sends to the grid control
by sub classing it, i.e. putting your own procedure to be called by
windows, instead of the windows procedure. Here is an example :
LRESULT MyGridProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)
{
if (msg == WM_CLOSE) {
int r = MessageBox(hwnd,
"Really quit?" "Grid", MB_OK|MB_CANCEL);
if (r == MB_OK)
return 0;
}
return CallWindowProc(OriginalFn,msg,wParam,lParam);
}
This procedure will receive all messages, and if it is the
WM_CLOSE message, it will put up a confirmation message box. If not it
will call the original function of the window.
You setup this function with the following code :
FARPROC OriginalFn;
HWND GridWindow = GetGridWindow(table1);
OriginalFn = (FARPROC)GetWindowLong(GridWindow,GWL_WNDPROC);
SetWindowLong(GridWindow,GWL_WNDPROC,MyGridProc);
You first obtain a pointer to the old window procedure of the
grid window, then you set your own.
And that's all. Normal windows programming.
Top of page lcc-win32 main page Products main page
Modifying the mouse interface.
As with the keyboard, you can
establish a callback for getting notified when a mouse event occurs.
The prototype of the function you should write is the same as with
the keyboard, and you receive the same parameters : the table, the
windows message and its parameters, lparam and wparam.
You set the callback with :
GridMouseCallback SetGridMouseCallback(void *tab,
GridMouseCallback fn);
If your mouse callback returns a non-zero result, no further
processing is done and the message is ignored.
Top of page lcc-win32 main page Products main page
Managing the tool-tips.
The grid displays a tool-tip window
when the mouse hovers over a cell whose text is longer than the width
of the cell.
You can get the window handle of this window with :
HWND GetTooltipWindow(void *tab);
You can set a text into the tool-tip window with :
int ShowTooltipText(void *tab,POINT *pt,char
*text);
The POINT structure holds the coordinates of the top left corner of
the tool-tip that will resize itself to hold the text. The origin of
those coordinates is the top left corner of the grid window. This
function returns zero if tool-tips are disabled, 1 otherwise.
The tool-tip window can be distracting. You can hide it with :
int HideTooltips(void *tab);
You can turn tool-tips off completely with :
int DisableTooltips(void *tab);
or enable them with :
int EnableTooltips(void *tab);
Both functions return the old state of the flag.
If you use the tool-tip functionality, it is often usefull to call
these last functions to disable it when the window containing the
grid does not have the focus, and to enable it again when it gains
the focus.
Top of page lcc-win32 main page Products main page
Enumerating cells.
You can sweep through all cells in the
table by writing a function that will be called by the grid at each
cell. You receive the row index (zero based), the column index (zero
based) and a pointer to the cell's data. You can pass an argument to
this function.
The function to be called has the following prototype :
int Function(int row,int col, char *data, LPARAM
lParam);
You start the enumeration by calling
typedef int (*GridEnumCallback)(int row, int col, char
*data, LPARAM lParam);
int EnumGrid(void *tab, GridEnumCallback fn, int Reverse, LPARAM
lParam);
Set Reverse to 1 to look from bottom to the
top and from right to left.
The enumeration begins with the first cell of the grid, or the last
one if Reverse is set, if no cell is
selected.
If a cell is selected, the enumeration starts on the next cell.
If the callback function returns zero, the enumeration stops, and the
EnumTable function returns the number of calls it has made. Any other
value different than zero continues the process until all cells have
been visited.
If the callback function adds or deletes cells during the callback
the results are undefined (at best !).
To enumerate a table within a certain range of cells you can use
:
int EnumGridWithBounds(void *tab, RECT *rc,
GridEnumCallback fn, LPARAM lParam);
The rc parameter is a rectangle describing the range of cells.
rc.left and rc.top are the coordinates (row,column) of the top left
cell, the rc.right and rc.bottom members are the coordinates of the
lower right cell in the rectangle within the grid.
If the coordinates of the lower left corner are -1 and -1, this means
that the enumeration goes on until the end of the table.
Top of page lcc-win32 main page Products main page
Using EnumGrid to implement a search function.
To implement a
search function, we can use EnumGrid to compare a given character
string to the data stored in the cells.
First, we write a callback function that will be called by
EnumGrid.
Since we will need that this function returns the coordinates of the
found cell (if any), we define a simple structure where the callback
will store its results.
typedef struct {
char *str; // String to search
int flags; // case sensitive, regular expression, etc
int row; // Here the callback stores its result
int col;
} FINDPARAMETERS;
This structure holds the string to be searched, some flags
(whether case sensitive, etc), and a couple of integers to hold the
result.
Then we will pass a pointer to this structure to EnumGrid, which in
turn, will pass it to the enumeration function using the LPARAM
argument to it.
Our function would look like this :
int findstr(int row,int col,char *data,LPARAM lParam)
{
FINDPARAMETERS *p = (FINDPARAMETERS *)lParam;
// Note that data can be NULL if the cell has never been assigned
if (data && strstr(p->str,data)) {
p->row = row;
p->col = col;
// We stop enumerating at the first cell that matches
return 0;
}
// Continue the enumeration.
return 1;
}
Note that this simple function ignores the flags.
We can now implement our search function like this :
int SearchGrid(void *tab,char *str,int flags)
{
FINDPARAMETERS fp;
fp.str = str;
fp.flags = flags;
fp.row = fp.col = -1;
EnumGrid(tab,findstr,(LPARAM)&fp);
if (fp.row != -1 && fp.col != -1) {
SelectCell(tab,fp.row,fp.col);
return 1;
}
return 0; // Nothing was found
}
Well, that is it.
To begin the search on the first cell, we have to cancel the cell
selection before the first call to EnumGrid.
We select the cell where we have found the desired string, and we are
ready to search from the next cell.
Top of page lcc-win32 main page Products main page
Properties.
int SetReadOnly(void
*tab);
This will disable any editing of the given table.
int SetReadWrite(void *tab);
This will enable editing for this table.
char *SetGridTitle(void *tab,char
*title);
Sets the table title to the given argument. Returns the old title or
NULL if there wasn't any.
char *GetGridTitle(void *tab);
Returns the table title or NULL if the title wasn't set.
int IsGridVirtual(void *tab);
Returns 1 if the grid has user-driven virtual rows.
int IsGridOnDatabase(void *tab);
Return 1 if the grid has been opened on a database.
int GetDatabaseTableName(void *tab, char *output, int
dim);
If the grid is on a database, this function returns 1 and
output receives the name of the opened table.
The dim value is the size of the buffer you
give.
Top of page lcc-win32 main page Products main page
Sorting Columns.
int GridColumnSort(void
*tab,int col,SortFn fn);
This function will call the sorting function "fn" for each cell in
the given column. The "fn" function should return an integer that
represents the order of the data in the cell, which can be NULL if
the cell is empty.
Here is an example of a function that sorts the data in the cells
handling the contents as floating point numbers :
#include <math.h>
static int invertSortFlipFlop;
static int comparenumbers(const void *n1,const void *n2)
{
double delta,d1,d2;
// Note this casts !
char *p1 = *(char **)n1;
char *p2 = *(char **)n2;
//Handle the case where the cell is empty and the
// pointer to its data is NULL.
if (p1 == NULL)
d1 = 0.0;
else
d1 = atof(p1);
if (p2 == NULL)
d2 = 0.0;
else
d2 = atof(p2);
delta = d1 - d2;
if (invertSortFlipFlop) {
delta = -delta;
}
if (delta < 0.0)
return -1;
else if (delta > 0.0)
return 1;
return 0;
}
In this example we have a flip-flop that oscillates between an
ascending and descending sort. To sort a column, we would call :
GridColumnSort(tab,2,comparenumbers);
NOTE : the grid on virtual rows does not accept the sort
function. It's the responsability of the calling procedure. A
database grid accept the sort of the columns, but we use an SQL
expression to read the database sorted by one column. You call the
same function :
GridColumnSort(tab,col,SORTASCENDING);
GridColumnSort(tab,col,SORTDESCENDING);
As you can imagine, the first call sorts the table with the ascending
sort of the column, and the second one with the descending sort.
Top of page lcc-win32 main page Products main page
The programming examples.
To give you a "hands on" approach
for using the grid, an example code for an MDI application is
distributed with the software. There you will find examples of
creating tables, setting callbacks, database grids, virtual rows,
etc.
To build the example, compile "gridExample.c" with grid.lib
Top of page lcc-win32 main page Products main page
Function index
CancelEdit
CancelSelection
ColData2Display
ColDisplay2Data
CreateGrid
DeleteColumn
DeleteRow
DestroyTable
DisableTooltips
EnableTooltips
EndEditCell
EnsureCellVisible
EnumGrid
EnumGridWithBounds
FixDatabaseSubstitution
FreeBlockData
GetCellData
GetCellEdit
GetCellNumericData
GetColumnData
GetCsvSeparator
GetDatabaseTableName
GetDimensions
GetDisplayedColumns
GetFromClipboard
GetGridFixedFont
GetGridTitle
GetGridVarPitchFont
GetGridWindow
GetRowData
GetSelection
GetSubstitution
GetTablesOfDatabase
GetTooltipWindow
GetUserData
GridCloseDatabase
GridColumnSort
GridGetFirstVisibleColumn
GridGetFirstVisibleRow
GridGetVisibleColumns
GridGetVisibleRows
GridHitTest
GridOpenDatabase
GridSelectTable
GridSelectTableOrView
GridSubstitutions
HideColumn
HideTooltips
InsertColumnBefore
InsertRowBefore
IsCellVisible
IsGridOnDatabase
IsGridVirtual
LoadCSV
LoadODBC
MoveSelectedCellDown
MoveSelectedCellLeft
MoveSelectedCellRight
MoveSelectedCellUp
RefreshGrid
ResetColumns
ResetUndoAction
SaveCsv
SaveRtf
SaveToClipboard
SaveXml
SelectCell
SelectColumn
SelectRow
SetCell
SetCellDrawCallback
SetColumnTitle
SetCsvSeparator
SetDirectSubstitution
SetGridEditCallback
SetGridFixedFont
SetGridKeyboardCallback
SetGridLinesCallback
SetGridMouseCallback
SetGridSelChangeCallback
SetGridTitle
SetGridVarPitchFont
SetGridVirtualScrolling
SetReadOnly
SetReadWrite
SetSelectedTable
SetSQLSubstitution
SetUserData
SetVirtualNbOfRows
ShowColumnAt
ShowColumns
ShowTooltipText
StartEditCell
UndoLastCommand
UnfixDatabaseSubstitution
Top of page
lcc-win32 main page
Products main page