PowerBuilder – Next Key Service

Posted on Tuesday, January 11th, 2011 at 8:20 pm in

As an extension of the next key stored procedure article, here is a pretty easy to implement a next surrogate key service to use with the PFC. If you don’t use the PFC it is still fairly easy to use with some modification. You can use this service anytime you want to get a sequential number, such as a document number or a sequence number. These services use a DataStore to call the spcmn_getNextKeyResultSet stored procedure, which returns the next key for the specified table.

Next Key Class (n_cst_nextKey)

This is the base class that adds “Next Key” functionality to a PFC based PowerBuilder application. This class can be called directly by the developer but is mainly used by the “Next Key” Application, DataWindow, and DataStore services.

Example usage

// reference to the next key object
n_cst_nextKey lnv_nextKey

// create the next key object
lnv_nextKey = create n_cst_nextKey

// set the next key's trans object; note: this step is not required as SQLCA is the default
lnv_nextKey.of_setTransObject (SQLCA)

// example: getting next key for "Table"
long ll_key
ll_key = lnv_nextKey.of_getNextKey ("Table")

// example: getting next 10 keys for "Table"
long ll_key
ll_key = lnv_nextKey.of_getNextKey ("Table", 10) 

// destroy the object
destroy (lnv_nextKey)

Next Key Application Service (n_cst_nextKey instance variable added to n_cst_appmanager)

This service adds the Next Key class to the PFC application manager class, n_cst_appmanager. When activated, this service adds next key functionality across an entire application. One benefit is that it is instantiated only once and persists the life of the application; therefore, there is very little overhead involved with using this service.

Example usage

In the constructor event of the application manager (typically a descendant of n_cst_appmanager).

// start the next key service
of_setNextKey (true)

After a connection to the database is established, call of_setTransObject( ) to set the trans object. This method need only be called for trans objects other than SQLCA since SQLCA is the default.

// set the next key's trans object; note: this step is not required as SQLCA is the default
gnv_app.inv_nextKey.of_setTransObject (ltr_object)

From here on out, the next key service can be called from anywhere in the application with a single line.

// example: getting next key for "Table"
long ll_nextKey
ll_nextKey = gnv_app.inv_nextKey.of_getNextKey ("Table")

// example: getting next 10 keys for "Table"
long ll_nextKey
ll_nextKey = gnv_app.inv_nextKey.of_getNextKey ("Table", 10)

Next Key DataWindow Service (n_cst_dwsrv_nextKey added to u_dw)

This service adds the Next Key class to the PFC DataWindow control class, u_dw. When activated, this service adds next key functionality for a specific DataWindow control. The main benefit with this service is that key columns in a dataobject can be registered to a table in the constructor event of a DataWindow control, saving the developer from writing the same code for each DataWindow in the application.

There are two ways to register columns with tables: automatic and manual.

Automatic registering allows the writing of generic objects. Registering is done through an assignment in the tag value of a column (e.g., “nextkey=Table”). With automatic registering, of_register( ) is called with no arguments.

Manual registering requires the calling of of_register( ) with explicit table names and column names. There is less overhead with this method because the Next Key class does not have to hunt through tag values columns and tables to register.

Example usage

In the constructor event of the DataWindow control.

// start the next key service
of_setNextKey(true)

// set the next key's trans object; note: this step is not required as SQLCA is the default
inv_nextKey.of_setTransObject (ltr_object)

Manual registering

// manually register columns to tables
inv_nextKey.of_register ("Table", "Column")
inv_nextKey.of_register ("Table2", "Column2")

Automatic registering

// automatically register columns to tables based on dataobject tag values
// "Column" has tag = 'nextkey=Table'
// "Column2" has tag = 'nextkey=Table2'
inv_nextKey.of_register( )

Some advanced examples of getting keys:

// get next key and apply to all rows in a DataWindow
dw_test.inv_nextKey.of_getNextKey("Table", "Column")

// get next key and apply to selected rows in a DataWindow based on an expression
dw_test.inv_nextKey.of_getNextKey("Table", "Column", "isNull(Column)")

// get next key and apply to all rows in a DataWindow, without remembering the row status
dw_test.inv_nextKey.of_getNextKey("Table", "Column", false)

// get next key and apply to selected rows in a DataWindow based on an expression, without remembering the row status
dw_test.inv_nextKey.of_getNextKey("Table", "Column", "isNull(Column)", false)

Next Key DataStore Service (n_cst_dssrv_nextKey instance variable added to n_ds)

This service adds the Next Key class to the PFC DataStore class, n_ds. When activated, this service adds next key functionality for a specific DataStore object. Due to the nature of this object, registering of tables and columns is not available.

Example usage

// start the next key service
ids_object.of_setNextKey (true)

// set the next key's trans object; note: this step is not required as SQLCA is the default
ids_object.inv_nextKey.of_setTransObject (ltr_object)

// get next key and apply to all rows in a DataStore
ids_test.inv_nextKey.of_getNextKey("Table", "Column")

// get next key and apply to selected rows in a DataStore based on an expression
ids_test.inv_nextKey.of_getNextKey("Table", "Column", "isNull(Column)")

// get next key and apply to all rows in a DataStore, without remembering the row status
ids_test.inv_nextKey.of_getNextKey("Table", "Column", false)

// get next key and apply to selected rows in a DataStore based on an expression, without remembering the row status
ids_test.inv_nextKey.of_getNextKey("Table", "Column", "isNull(Column)", false)

Export of n_cst_dwsrv_nextkey object for Datawindows

$PBExportHeader$n_cst_dwsrv_nextkey.sru
$PBExportComments$Next Key DataWindow Service
forward
global type n_cst_dwsrv_nextkey from n_cst_dwsrv
end type
end forward

global type n_cst_dwsrv_nextkey from n_cst_dwsrv
event type long ue_insertrow ( long al_row )
end type
global n_cst_dwsrv_nextkey n_cst_dwsrv_nextkey

type variables

protected:
n_cst_nextKeyAttrib inv_nextKeyTables
n_cst_nextKey inv_nextKey
n_tr itr_object
end variables

forward prototypes
public function integer of_settransobject (n_tr atr_object)
public function integer of_register (string as_tablename, string as_columnname)
public function integer of_getnextkey (string as_tablename, string as_columnname, string as_expression)
public function integer of_getnextkey (string as_tablename, string as_columnname, boolean ab_rememberrowstatus)
public function integer of_getnextkey (string as_tablename, string as_columnname, string as_expression, boolean ab_rememberrowstatus)
public function integer of_getnextkey (string as_tablename, string as_columnname)
public function integer of_register ()
end prototypes

event ue_insertrow;
// 
integer	li_i
integer	li_numKeys
long     ll_nextKey
string   ls_tableName, ls_columnName

dwItemStatus le_rowstatus

// validate the next key application service is in use
if not isValid(inv_nextKey) then return FAILURE

// Loop thru all the tables.
li_numKeys = upperBound (inv_nextKeyTables.is_tableName) 
For li_i = 1 to li_numKeys

	// get table and column names
	ls_tableName  = inv_nextKeyTables.is_tableName[li_i]
	ls_columnName = inv_nextKeyTables.is_columnName[li_i]

	// get the next key for the table
	ll_nextKey = inv_nextKey.of_getNextKey(ls_tableName)

	if ll_nextKey = 0 then return FAILURE

	// set the key
	if idw_requestor.setItem(al_row, ls_columnName, ll_nextKey) <> 1 then 
		return FAILURE
	end if

next 

// convert row back from NewModified! to New!.
le_rowstatus = idw_Requestor.GetItemStatus(al_row, 0, Primary!)
If le_rowstatus = NewModified! Then
	idw_requestor.setItemStatus(al_row, 0, Primary!, NotModified!)
end if

return al_row
end event

public function integer of_settransobject (n_tr atr_object);
// 
integer li_rc

// validate
if IsNull (atr_object) or not IsValid (atr_object) then return FAILURE

// set the trans object
li_rc = inv_nextKey.of_setTransObject (atr_object)

// remember the trans object
if li_rc = 1 then
	itr_object = atr_object
end if

return li_rc

end function

public function integer of_register (string as_tablename, string as_columnname);
// 
Integer	li_newUpper
String	ls_id

// Verify required reference.
If isNull(idw_requestor) Or Not isValid(idw_requestor) Then Return FAILURE

// Trim the arguments.
as_tableName = Trim(as_tableName)
as_columnName = Trim(as_columnName)

// Verify passed arguments.
If isNull(as_tableName) or len(as_tableName) = 0 Then Return FAILURE
If isNull(as_columnName) or len(as_columnName) = 0 Then Return FAILURE

// validate column is in requestor
ls_id = idw_requestor.describe (as_columnName + ".ID")
if not isNumber(ls_id) then return FAILURE

// Establish the boundaries of the array.
li_newUpper = UpperBound (inv_nextKeyTables.is_tableName) + 1

// Set the columns.
inv_nextKeyTables.is_tableName[li_newUpper] = as_tableName
inv_nextKeyTables.is_columnName[li_newUpper] = as_columnName

Return SUCCESS

end function

public function integer of_getnextkey (string as_tablename, string as_columnname, string as_expression);
// 
// pass on event with defaults
return this.of_getNextKey(as_tableName, as_columnName, as_expression, true)
end function

public function integer of_getnextkey (string as_tablename, string as_columnname, boolean ab_rememberrowstatus);
// 
// apply keys to a column for all rows in this datawindow
long ll_row, ll_rowCount, ll_numKeys, ll_nextKey
dwItemStatus le_rowStatus

// get total number of rows
ll_rowCount = idw_requestor.rowCount()
if ll_rowCount < 1 then return ll_rowCount

// number of keys = all rows	
ll_numKeys = ll_rowCount
	
// get block of keys
ll_nextKey = inv_nextKey.of_getNextKey(as_tableName, ll_numKeys)

// apply keys to all rows
ll_row = 1
do while ll_row > 0 and ll_row <= ll_rowCount

	// remember row status
	if ab_rememberRowStatus then
		le_rowStatus = idw_Requestor.getItemStatus(ll_row, 0, primary!)
	end if

	// set the column with the next key
	idw_requestor.setItem(ll_row, as_columnName, ll_nextKey)
	
	// reset row status to the remembered row status
	if ab_rememberRowStatus then
		
		// if the row status was originally new! or notModified!, then changing
		// it to notModified! will reset the row status. if the row status
		// was either newModified! or dataModified!, then the row should
		// already have its original row status.
		
		if le_rowStatus = new! or le_rowStatus = notModified! then
			idw_requestor.setItemStatus(ll_row, 0, primary!, NotModified!)
		end if
		
	end if

	ll_nextKey++
	ll_row++
	
loop

return 1

end function

public function integer of_getnextkey (string as_tablename, string as_columnname, string as_expression, boolean ab_rememberrowstatus);
// 
// apply keys to a column for selected rows in a datawindow based
// based on an expression
long ll_row, ll_rowCount, ll_numKeys, ll_nextKey
dwItemStatus le_rowStatus

// get total number of rows
ll_rowCount = idw_requestor.rowCount()
if ll_rowCount < 1 then return ll_rowCount

// count matching rows
ll_row = idw_requestor.find(as_expression, 1, ll_rowCount)
do while ll_row > 0 and ll_row <= ll_rowCount
	ll_numKeys++
	ll_row++
	ll_row = idw_requestor.find(as_expression, ll_row, ll_rowCount)
loop

// get out if no matches
if ll_numKeys < 1 then return ll_numKeys

// get block of keys
ll_nextKey = inv_nextKey.of_getNextKey(as_tableName, ll_numKeys)

// apply keys to the only matching rows
ll_row = idw_requestor.find(as_expression, 1, ll_rowCount)
do while ll_row > 0 and ll_row <= ll_rowCount

	// remember row status
	if ab_rememberRowStatus then
		le_rowStatus = idw_Requestor.getItemStatus(ll_row, 0, primary!)
	end if

	// set the column with the next key
	idw_requestor.setItem(ll_row, as_columnName, ll_nextKey)
	
	// reset row status to the remembered row status
	if ab_rememberRowStatus then
		
		// if the row status was originally new! or notModified!, then changing
		// it to notModified! will reset the row status. if the row status
		// was either newModified! or dataModified!, then the row should
		// already have its original row status.
		
		if le_rowStatus = new! or le_rowStatus = notModified! then
			idw_requestor.setItemStatus(ll_row, 0, primary!, NotModified!)
		end if
		
	end if

	ll_nextKey++
	ll_row++
	
	ll_row = idw_requestor.find(as_expression, ll_row, ll_rowCount)
	
loop
	
return 1

end function

public function integer of_getnextkey (string as_tablename, string as_columnname);
// 
// pass on call using defaults
return this.of_getNextKey(as_tableName, as_columnName, true)
end function

public function integer of_register ();
// 
// automatic registering of tables and columns with the next key service
// this method looks for objects on a dataobject with a tag of "nextkey=table"
n_cst_string lnv_string
string ls_objects[], ls_tag, ls_tableName, ls_columnName
integer li_objectCount, li_objectPointer

// make sure base datawindow service is started
if not isValid (idw_requestor.inv_base) then
	idw_requestor.of_setBase(true)
end if

// get dataobject objects into an array
li_objectCount = idw_requestor.inv_base.of_getObjects (ls_objects[])

// loop through objects
for li_objectPointer = 1 to li_objectCount
	
	// get object name
	ls_columnName = ls_objects[li_objectPointer]
	
	// get tag for object
	ls_tag = idw_requestor.describe(ls_columnName + ".tag")

	// get "nextkey" key.
	if not isNull(ls_tag) and len(trim(ls_tag)) > 0 then
		ls_tableName = lnv_string.of_getKeyValue (ls_tag, "nextkey", "|")
	end if
	
	// register table/column with the next key service
	if not isNull(ls_tableName) and len(trim(ls_tableName)) > 0 then
		this.of_register(ls_tableName, ls_columnName)
	end if
	
next

return 1
end function

on n_cst_dwsrv_nextkey.create
call super::create
end on

on n_cst_dwsrv_nextkey.destroy
call super::destroy
end on

event constructor;call super::constructor;
// 
inv_nextKey = create n_cst_nextKey
end event

event destructor;call super::destructor;
// 
if isValid(inv_nextKey) then destroy(inv_nextKey)
end event

Export of n_cst_dssrv_nextkey object for Datastores

$PBExportHeader$n_cst_dssrv_nextkey.sru
$PBExportComments$Next Key DataWindow Service
forward
global type n_cst_dssrv_nextkey from n_cst_dssrv
end type
end forward

global type n_cst_dssrv_nextkey from n_cst_dssrv
end type
global n_cst_dssrv_nextkey n_cst_dssrv_nextkey

type variables

protected:
n_cst_nextKey inv_nextKey
n_tr itr_object
end variables

forward prototypes
public function integer of_settransobject (n_tr atr_object)
public function integer of_getnextkey (string as_tablename, string as_columnname)
public function integer of_getnextkey (string as_tablename, string as_columnname, string as_expression)
public function integer of_getnextkey (string as_tablename, string as_columnname, string as_expression, boolean ab_resetrowstatus)
public function integer of_getnextkey (string as_tablename, string as_columnname, boolean ab_resetrowstatus)
end prototypes

public function integer of_settransobject (n_tr atr_object);
// 
integer li_rc

// validate
if IsNull (atr_object) or not IsValid (atr_object) then return FAILURE

// set the trans object
li_rc = inv_nextKey.of_setTransObject (atr_object)

// remember the trans object
if li_rc = 1 then
	itr_object = atr_object
end if

return li_rc


end function

public function integer of_getnextkey (string as_tablename, string as_columnname);
// 
// pass on call with defaults
return this.of_getNextKey(as_tableName, as_columnName, true)

end function

public function integer of_getnextkey (string as_tablename, string as_columnname, string as_expression);
// 
// pass on call with defaults
return this.of_getNextKey(as_tableName, as_columnName, as_expression, true)
end function

public function integer of_getnextkey (string as_tablename, string as_columnname, string as_expression, boolean ab_resetrowstatus);
// 
// apply keys to a column for selected rows in a datawindow based
// based on an expression
long ll_row, ll_rowCount, ll_numKeys, ll_nextKey
dwItemStatus le_rowStatus

// get total number of rows
ll_rowCount = ids_requestor.rowCount()
if ll_rowCount < 1 then return ll_rowCount

// count matching rows
ll_row = ids_requestor.find(as_expression, 1, ll_rowCount)
do while ll_row > 0 and ll_row <= ll_rowCount
	ll_numKeys++
	ll_row++
	ll_row = ids_requestor.find(as_expression, ll_row, ll_rowCount)
loop

// get out if no matches
if ll_numKeys < 1 then return ll_numKeys

// get block of keys
ll_nextKey = inv_nextKey.of_getNextKey(as_tableName, ll_numKeys)

// apply keys to the only matching rows
ll_row = ids_requestor.find(as_expression, 1, ll_rowCount)
do while ll_row > 0 and ll_row <= ll_rowCount

	// remember row status
	if ab_resetRowStatus then
		le_rowStatus = ids_Requestor.getItemStatus(ll_row, 0, primary!)
	end if

	// set the column with the next key
	ids_requestor.setItem(ll_row, as_columnName, ll_nextKey)
	
	// reset row status
	if ab_resetRowStatus then
		
		// if the row status was originally new! or notModified!, then changing
		// it to notModified! will reset the row status. if the row status
		// was either newModified! or dataModified!, then the row should
		// already have its original row status.
		
		if le_rowStatus = new! or le_rowStatus = notModified! then
			ids_requestor.setItemStatus(ll_row, 0, primary!, NotModified!)
		end if
		
	end if

	ll_nextKey++
	ll_row++
	
	ll_row = ids_requestor.find(as_expression, ll_row, ll_rowCount)
	
loop
	
return 1

end function

public function integer of_getnextkey (string as_tablename, string as_columnname, boolean ab_resetrowstatus);
// 
// apply keys to a column for all rows in this datawindow
long ll_row, ll_rowCount, ll_numKeys, ll_nextKey
dwItemStatus le_rowStatus

// get total number of rows
ll_rowCount = ids_requestor.rowCount()
if ll_rowCount < 1 then return ll_rowCount

// number of keys = all rows	
ll_numKeys = ll_rowCount
	
// get block of keys
ll_nextKey = inv_nextKey.of_getNextKey(as_tableName, ll_numKeys)

// apply keys to all rows
ll_row = 1
do while ll_row > 0 and ll_row <= ll_rowCount
	
	// remember row status
	if ab_resetRowStatus then
		le_rowStatus = ids_Requestor.getItemStatus(ll_row, 0, primary!)
	end if

	// set the column with the next key
	ids_requestor.setItem(ll_row, as_columnName, ll_nextKey)
	
	// reset row status
	if ab_resetRowStatus then
		
		// if the row status was originally new! or notModified!, then changing
		// it to notModified! will reset the row status. if the row status
		// was either newModified! or dataModified!, then the row should
		// already have its original row status.
		
		if le_rowStatus = new! or le_rowStatus = notModified! then
			ids_requestor.setItemStatus(ll_row, 0, primary!, NotModified!)
		end if
		
	end if
	
	ll_nextKey++
	ll_row++
	
loop

return 1

end function

event constructor;call super::constructor;
// 
inv_nextKey = create n_cst_nextKey
end event

on n_cst_dssrv_nextkey.create
call super::create
end on

on n_cst_dssrv_nextkey.destroy
call super::destroy
end on

event destructor;call super::destructor;
// 
if isValid(inv_nextKey) then destroy(inv_nextKey)
end event

Datawindow Object Database Information from export

table(column=(type=decimal(0) updatewhereclause=yes name=nextkey dbname="nextKey" )
 procedure="1 execute dbo.spcmn_getNextKeyResultSet;1 @isTableName = :isTableName, @iiKeys = :iiKeys" arguments=(("isTableName", string),("iiKeys", number)) )

Once you have the stored procedures in your database, create a datawindow object with the spcmn_getNextKeyResultSet procedure as its datasource.

Stored Procedure for the NextKey datawindow object

This calls the spcmn_getNextKey procedure detailed in the earlier post.

/****** Object: Procedure [dbo].[spcmn_getNextKeyResultSet]   Script Date: 12/30/2010 10:17:41 AM ******/
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[spcmn_getNextKeyResultSet]') AND type in (N'P', N'PC'))
BEGIN
DROP PROCEDURE [dbo].[spcmn_getNextKeyResultSet];
END
GO

SET ANSI_NULLS ON;
GO
SET QUOTED_IDENTIFIER OFF;
GO

CREATE PROCEDURE [dbo].[spcmn_getNextKeyResultSet]
    @isTableName varchar(128)  = null -- table to get next key for
   ,@iiKeys      decimal(18,0) = null -- number of keys to get; null = 1
AS
   set nocount on
   -- ----------------------------------------------------------
   -- Procedure: spcmn_getNextKeyResultSet
   -- Get next technical key(s) for the table name passed in.
   -- ----------------------------------------------------------

   declare @lsERRORTEXT varchar(255)
          ,@liERROR     int
          ,@liROWCOUNT  int
          ,@liTRANCOUNT int

   declare @liNextKey decimal(18,0)

   -- work around for the PowerBuilder IDE
   if @iiKeys = 0
   begin
      set @liNextKey = 0
      select @liNextKey 'nextKey'   
      return
   end
   -- initialize error handling
   select @liERROR = @@ERROR
   if @liERROR <> 0 goto ErrorHandler

   -- get next key
   exec spcmn_getNextKey @isTableName, @liNextKey output, @iiKeys

   select @liERROR = @@ERROR
   IF @liERROR <> 0 goto ErrorHandler

   select @liNextKey 'nextKey'

   return

ErrorHandler:
   if @@TRANCOUNT > @liTRANCOUNT --0
      rollback transaction

   select @lsERRORTEXT = isNull(@lsERRORTEXT, 'spcmn_getNextKeyResultSet: Unexpected Error Occurred!')
   raiserror(@lsERRORTEXT, 0, 1) WITH SETERROR

GO
GRANT EXECUTE ON [dbo].[spcmn_getNextKeyResultSet] TO [public];
GO

Datastore (n_ds) object export

$PBExportHeader$n_ds.sru
$PBExportComments$Extension Datastore class
forward
global type n_ds from pfc_n_ds
end type
end forward

global type n_ds from pfc_n_ds
end type
global n_ds n_ds

type variables

public:
n_cst_dssrv_nextKey inv_nextKey
end variables

forward prototypes
public function integer of_setnextkey (boolean ab_switch)
end prototypes

public function integer of_setnextkey (boolean ab_switch);
//Check arguments
If IsNull(ab_switch) Then
	Return -1
End If

IF ab_Switch THEN
	IF IsNull(inv_nextKey) Or Not IsValid (inv_nextKey) THEN
		inv_nextKey = Create n_cst_dssrv_nextKey
		inv_nextKey.of_setRequestor ( this )
		Return 1
	END IF
ELSE 
	IF IsValid (inv_nextKey) THEN
		Destroy inv_nextKey
		Return 1
	END IF	
END IF

Return 0
end function

on n_ds.create
call super::create
end on

on n_ds.destroy
call super::destroy
end on

Datawindow (u_dw) object export

Nextkey service used in pfc_addrow and pfc_insertrow events

$PBExportHeader$u_dw.sru
$PBExportComments$Extension DataWindow class
forward
global type u_dw from pfc_u_dw
end type
end forward

global type u_dw from pfc_u_dw
end type
global u_dw u_dw

type variables

public:
n_cst_dwsrv_nextKey inv_nextKey
end variables

forward prototypes
public function integer of_setnextkey (boolean ab_switch)
end prototypes

public function integer of_setnextkey (boolean ab_switch);
if IsNull(ab_switch) then return FAILURE

if ab_Switch then
	if IsNull(inv_nextKey) or not IsValid (inv_nextKey) then
		inv_nextKey = Create n_cst_dwsrv_nextKey
		inv_nextKey.of_SetRequestor (this)
		return SUCCESS
	end if
else 
	if IsValid (inv_nextKey) then
		Destroy inv_nextKey
		return SUCCESS
	end if	
end if

return NO_ACTION
end function


on u_dw.create
call super::create
end on

on u_dw.destroy
call super::destroy
end on

event destructor;call super::destructor;
of_setNextKey(false)
end event

event pfc_addrow;// override of pfc event!

//////////////////////////////////////////////////////////////////////////////
//	Event:			pfc_addrow
//	Arguments:		None
//	Returns:			long - number of the new row that was inserted
//	 					0 = No row was added.
//						-1 = error
//	Description:	Adds a new row to the end of the DW
//////////////////////////////////////////////////////////////////////////////
long	ll_rc
boolean lb_disablelinkage

// Allow for pre functionality.
if this.Event pfc_preinsertrow() <= 0 then return NO_ACTION

// Is Querymode enabled?
if IsValid(inv_QueryMode) then lb_disablelinkage = inv_QueryMode.of_GetEnabled()

if not lb_disablelinkage then
	// Notify that a new row is about to be added.
	if IsValid ( inv_Linkage ) then inv_Linkage.Event pfc_InsertRow (0) 
end if

// Insert row.
if IsValid (inv_RowManager) then
	ll_rc = inv_RowManager.event pfc_addrow ()
else
	ll_rc = this.InsertRow (0) 
end if

// pass on event to the next key service
// added here so linkage service can know about keys
if isValid (inv_nextKey) then	inv_nextKey.event ue_insertRow (ll_rc) 

if not lb_disablelinkage then
	// Notify that a new row has been added.
	if IsValid ( inv_Linkage ) then inv_Linkage.Event pfc_InsertRow (ll_rc) 
end if

// Allow for post functionality.
this.Post Event pfc_postinsertrow(ll_rc)

return ll_rc
end event

event pfc_insertrow;// -- override of pfc event

//////////////////////////////////////////////////////////////////////////////
//	Event:			pfc_insertrow
//	Arguments:		None
//	Returns:			long - number of the new row that was inserted
//	 					0 = No row was added.
//						-1 = error
//	Description:	Inserts a new row into the DataWindow before the current row
//////////////////////////////////////////////////////////////////////////////
long	ll_currow
long	ll_rc
boolean lb_disablelinkage

// Allow for pre functionality.
if this.Event pfc_preinsertrow() <= PREVENT_ACTION then return NO_ACTION

// Get current row
ll_currow = this.GetRow()
if ll_currow < 0 then ll_currow = 0

// Is Querymode enabled?
if IsValid(inv_QueryMode) then lb_disablelinkage = inv_QueryMode.of_GetEnabled()
		
if not lb_disablelinkage then		
	// Notify that a new row is about to be added.
	if IsValid ( inv_Linkage ) then inv_Linkage.Event pfc_InsertRow (0) 
end if

// Insert row.
if IsValid (inv_RowManager) then
	ll_rc = inv_RowManager.event pfc_insertrow (ll_currow)
else
	ll_rc = this.InsertRow (ll_currow) 
end if

// pass on event to the next key service
// added here so linkage service can know about keys
if isValid (inv_nextKey) then	inv_nextKey.event ue_insertRow (ll_rc) 

if not lb_disablelinkage then		
	// Notify that a new row has been added.
	if IsValid ( inv_Linkage ) then inv_Linkage.Event pfc_InsertRow (ll_rc) 
end if
// Allow for post functionality.
this.Post Event pfc_postinsertrow(ll_rc)

return ll_rc
end event

Special thanks to A.J. Schroeder.

Top