Customizing a Data Source

The behavior of a data source can be customized using callbacks. A callback is a C++ function that will be called when a given event occurs. A callback can be defined by calling one of the ::setCallback or IlvGraphic::addCallback member functions.

Here is an example:

void ILVCALLBACK MyEnterRow(IlvGraphic* g, IlAny) {

IliDataSource* ds = (IliDataSource*)g;

IlvPrint(“Enter Row %ld in data source ‘%s’”,

(long)ds->getCurrentRow(),

ds->getName());

}

 

int main(int argc, char** argv) {

IliDataSource* ds;

...

ds->setCallback(IliDataSource::EnterRowSymbol(),

MyEnterRow);

...
}

This example defines the EnterRow callback of a data source. This callback will be called each time a new row becomes the current row of the data source.

For each callback type (such as EnterRow), the IliDataSource class provides:

  • A static member function that returns the name of the callback type in the form of an IlSymbol* (for example, EnterRowSymbol).

  • A virtual member function (such as onEnterRow) that is called by the data source when the corresponding event occurs. This virtual member function calls in turn the corresponding callback (if any).

Monitoring the Selected Row

The following callback types can be used to monitor the selected row:

  • EnterRow

  • QuitRow

Update Validation

The following callback types can be used to customize the way rows are updated:

  • EnterUpdateMode

  • ValidateRow

  • PrepareUpdate

  • QuitUpdateMode

  • CancelEdits

These callbacks are called in a particular order.

The first time the end user starts to modify a row (by typing a key on the keyboard), the EnterUpdateMode callback is called. At this point, the isInputModified member function returns true.

When the end user has finished editing the row, validation will usually be triggered. Validation proceeds as follows:

  • The ValidateRow callback is called.

  • The PrepareUpdate callback is called.

These two callbacks are designed to allow you to code custom checks that depend on the application logic, and make on-the-fly adjustments to the row being updated.

Both of these callbacks have the same purpose. However, the ValidateRow callback is also called when a row is inserted (as you will see in the next section), therefore enabling you to specify a single function that is called in both cases.

The following example shows how custom checks can be coded:

void ILVCALLBACK MyValidateRowCallback(IlvGraphic* g, IlAny) {

IliDataSource* ds = (IliDataSource*)g;

if (ds->getValue(“Qty”).asInteger() > 15) {

ds->dontValidateRow();

ds->addErrorMessage(“Invalid quantity”);

}

}

...

IliDataSource* ds = ...;

ds->setCallback(IliDataSource::ValidateRowSymbol(),

MyValidateRowCallback);

If the check criterion is not satisfied, the callback calls the dontValidateRow member function to stop validation and it calls the addErrorMessage member function to provide an appropriate error message.

If both of these callbacks agree on validation (that is, they do not call the dontValidateRow member function), the row updates are transmitted to the underlying table through the IliTable::updateRow member function. If this call succeeds, the QuitUpdateMode callback is called.

The CancelEdits callback is called if the end user cancels the modifications instead of validating.

Insert Validation

The following callback types can be used to customize the way rows are inserted:

  • EnterInsertMode

  • ValidateRow

  • PrepareInsert

  • QuitInsertMode

  • CancelEdits

These callbacks work in the same way as the update callbacks (see the previous section).

The following example shows how a PrepareInsert callback can be defined to compute a unique identifier:

void ILVCALLBACK MyInsertRowCallback(IlvGraphic* g, IlAny) {

IliSQLDataSource* ds = (IliSQLDataSource*)g;

IliSQLTable* tbl = ds->getSQLTable();

if (ds->getValue(“ID”).isNull()) {

IliSQLSession* session = tbl->getEffectiveSQLSession();

IliSQLCursor* curs = session->newCursor();

if (curs->execute(“SELECT NEXTID FROM COUNTER FOR UPDATE”)

&& curs->fetchNext()) {

IlInt id = curs->getIntegerValue(0);

curs->execute(“UPDATE COUNTER SET NEXTID = NEXTID + 1”));

ds->setValue(“ID”, IliValue(id));

}

else {

ds->dontValidateRow();

ds->addErrorMessage(curs->getErrorMessage());

}

session->releaseCursor(curs);

}

}

...

IliSQLDataSource* ds = ...;

ds->setCallback(IliDataSource::PrepareInsertSymbol(),

MyInsertRowCallback);

This example assumes that the data source is tied to a table with an “ID” column. When the end user inserts a row through the data source, the value of the “ID” column is computed by incrementing a value found in the COUNTER database table.

Computed Columns

The FetchRow callback can be used to compute the value of one or more columns. This callback, which applies only to two-tier tables, is called each time a row is retrieved from the remote system and stored in the local row cache.

The following example illustrates the use of the FetchRow callback. Assume you have a database table, named HEATER, with a MAXTEMP column that describes the maximum temperature in degrees Celsius.

To display this table and show the MAXTEMP column in degrees Fahrenheit, do the following:

  • Ensure that the SQL data source has, at least, the following two columns:

  • Define a FetchRow callback in the following way:

void ILVCALLBACK MyFetchRowCallback(IlvGraphic* g, IlAny) {

IliSQLDataSource* ds = (IliSQLDataSource*)g;

IliSQLTable* tbl = ds->getSQLTable();

IlInt rowno = ds->getFetchedRow();

IliTableBuffer* buf = tbl->getBuffer();

buf->rowToBuffer(rowno);

IlDouble celsius = buf->at(“MAXTEMP”).asInteger();

IlDouble fahrenheit = CelsiusToFahrenheit(celsius);

buf->at(“FAHRENHEIT”) = IliValue(fahrenheit);

tbl->updateRowInCache(rowno, buf);

tbl->releaseBuffer(buf);

}

...

IliSQLDataSource* ds = ...;

ds->setCallback(IliDataSource::FetchRowSymbol(),

MyFetchRowCallback);

Note that the updateRowInCache member function is called instead of updateRow because only the local row cache needs to be changed.

Deleted Rows

The following callback types can be used to customize the way rows are deleted:

  • PrepareDeleteRow

  • DeleteRow

The PrepareDeleteRow callback can be used to prohibit row deletions through a given data source. If the callback calls dontDeleteRow, the user will not be able to delete the current row. As with the ValidateRow callback, the addErrorMessage member function may be called to provide an error message.

The DeleteRow callback can be used to monitor row deletion events. Here is an example:

void ILVCALLBACK MyRowDeleted(IlvGraphic* g, IlAny) {

IliDataSource* ds = (IliDataSource*)g;

IlvPrint(“Row %ld in data source ‘%s’ has been deleted”,

(long)ds->getDeletedRow(),

ds->getName());

}

 

int main(int argc, char** argv) {

IliDataSource* ds;

...

ds->setCallback(IliDataSource::DeleteRowSymbol(),

MyRowDeleted);

...

}