Function Set_Record(Data, DictColPos, VirtualOnly, SkipSymbolics, Window) /*********************************************************************************************************************** This program is proprietary and is not to be used by or disclosed to others, nor is it to be copied without written permission from SRP Computer Solutions, Inc. Name : Set_Record Description : Updates an OpenInsight window's controls with the data being passed in. Notes : The primary purpose of this routine is to make it easier to update the contents of an OpenInsight window without having to name the controls. This allows the developer to simulate the AREV method which would automatically update the window's fields whenever @RECORD was update. OpenInsight makes this more cumbersome by requiring each control to be updated individually using a property setting. Set_Record provides a level of automation that simulate AREV's @RECORD support. The developer can pass in a specific field position and the data that needs to be updated in that field or the developer can pass in the entire record array. Set_Record will automatically query the window's controls and update them as needed. Even symbolic fields will be updated accordingly. (NOTE: OpenInsight 7.2 introduced a new property - ATRECORD. This works the same as the RECORD property when used in the Get_Property function. However, unlike the RECORD property it will update the window's databound controls, including symbolic fields, when used in the Set_Property function. This also simulates AREV's @RECORD support. However, it cannot be used to update specific fields at a time or to update non-databound controls that need to be associated with database columns such as OLE controls.) Simulating @RECORD = RecordVar ============================== In AREV, the entire data record could be updated at once by setting @RECORD to the new value. As an example, if the developer needed to restore the content of the AREV window to the value of record as it was read from the disk, the following line of code could be called: @RECORD = WC_OREC% ; * Restore the contents of the original record. To do the equivelent using Set_Record: Set_Record(OrigRecord) ; // Restore the contents of the original record. Simulating @RECORD = FieldData ======================================== Individual database columns (aka fields) could also be updated by specifing the column number. In AREV the code might look like this: @RECORD<5> = 1000 ; * Put the customer number in place. To do the equivelent using Set_Record: Set_Record(1000, 5) ; // Put the customer number in place. Value, subvalue, text, and subtext positions can also be updated by separating each delimiter position with a comma. In AREV the code might look like this: @RECORD<10, 2> = "8005552222" ; * Put the contact phone number in the * 2nd value position of field 10. To do the equivalent with this routine: Set_Record("8005552222", "10,2") ; // Put the contact phone number in the // 2nd value position of field 10. Virtual Databound Controls ========================== Sometimes controls that need to display live data are not truly databound to the database column. A good example of this is if an ActiveX control is used to display and manage data. The internal method OpenInsight uses to populate the window has no knowledge that these controls are connected to the database in any way so they just get ignored. Set_Record provides support for this through the use of the user-defined property @POS. This will virtually bind the control to a database column (or columns, see below). Use the CREATE event of the window to set @POS. For single field controls you would do something like: Set_Property(@Window:".COMBO_1", "@POS", 5) For multi-field controls (e.g. AMV fields) like the SRP OLE EditTable, just separate each database column number with @VM. For instance: Set_Property(@Window:".OLE_EDITTABLE1", "@POS", 2:@VM:5:@VM:6:@VM:"":@VM:7) Set_Property(@Window:".OLE_EDITTABLE2", "@POS", 2:@VM:"DESCRIPTION":@VM:6) In the first example, the first command has a null embedded between database columns 6 and 7. This is to illusrate how one would handle an EditTable column that is not associated with a database column. In the case of the SRP OLE EditTable, this could be a column of buttons or hyperlinks which are there for functionality and not data storage. The second example has a word embedded between database columns 2 and 6. This is to illustrate how one would handle an EditTable column that is virtually bound to a symbolic (calculated) column. Just enter then name of the symbolic column and this routine will recalculate the value and put the data into the the appropriate EditTable column. Handling Symbolic (aka Calculated) Columns ========================================== As noted above in the Virtual Databound Controls section, non-databound controls can be virtually bound to a symbolic column by using the @POS user-defined property. Set_Record will compute it using the latest content from the data record. By default, controls that are truly databound to symbolic columns will be recalculated after all the data fields have been updated. However, sometimes this is not necessary or desirable. Once instance would be during the window's WRITE event. Since OpenInsight windows clear all controls during a successful database write, there is no need to go through the additional effort to recalculate symbolic columns. In these situations set the SkipSymbolic parameter to 1: Set_Record(RecordVar, "", Yes$) Another option for handling symbolics using Set_Record is to update them while leaving all data fields alone. This is useful when changes have been made to a virtually databound control and there is a need to update the dependent symbolic controls. This is done by passing "SYM" into the DictColPos parameter: Set_Record("", "SYM") A Word About Data Conversions ============================= Native OpenInsight controls have validation and conversion settings which respond to the INVALUE property. OLE controls, however, do not have validation and conversion settings that can be configured in the Form Designer. Therefore, OLE controls must provide their own way of reporting what validation and/or conversion patterns needed to be used. For the SRP OLE EditTable this is done through the CellConv property. If a CellConv property has been set, the raw data will be converted to this format first before the EditTable is visually updated. Otherwise, only internal data will be displayed in the OLE control. Parameters : Data [in] -- The data that needs to updated into the controls. If DictColPos is not populated then this should be an entire data record. Otherwise, it should only be the field data for the indicated data column position. DictColPos [in] -- The database column position being updated. If unassigned or null then Data is assumed to be the entire data record. Value, subvalue, text, and subtext positions can also be updated using commas to separate each delimiter position. See Notes for details. VirtualOnly [in] -- Flag to determine if only virtually data databound controls should be updated. This would be ideal during a READ event since has already updated the true databound controls. SkipSymbolics [in] -- Flag to determine if symbolic (calculated) columns should be updated. If unassigned or null it will be Yes. This should be set to no if data is being updated during the WRITE event. This will speed up the execution of this routine. Window [in] -- The window being updated. If unassigned or null it will be @Window. Record [out] -- The data record with updated values. History : (Date, Initials, Notes) 01/27/06 dmb Original programmer 02/02/06 dmb Add new parameter, VirtualOnly, to indicate that only virtually databound controls (e.g. OLE controls) should be updated. Modified the logic so that virtually databound controls will be updated even if there are no true databound controls bound to the same data column. 02/03/06 dmb Add support for value, subvalue, text, and subtext updates to a single field. Fix problem where symbolic columns weren't being updated if only a specific field is being updated. Improved support for virtually bound controls so virtually bound symbolic columns will be updated even if there are no controls that are actually bound to the symbolic column. 03/24/06 dmb Change EQ to _EQC when checking for SRP OLE EditTable ProgIDs. 03/24/06 dmb If the entire record is being set then clear SRP OLE EditTables to remove accidental data from a previous record. 02/14/08 dmb New feature: Rows As Columns. This checks the @ROWSASCOLUMNS UDP which populates SRP EditTable rows with database column data. Thus, the format is inverted. 02/15/08 dmb New feature: Cells as Fields. This checks the @CELLSASFIELDS UDP which populates SRP EditTable cells with specific single-valued database columns. 02/17/08 dmb New feature: If DictColPos is set to "SYM" then only symbolic columns will be recalculated and displayed. 02/17/08 dmb Remove Utility("CURSOR") calls. This was causing to many visual distractions. 03/04/08 dmb Don't use Clear method on SRP EditTables if they are also the control with focus. 03/18/08 rch Use @CLEARFILL setting if provided to Clear SRP EditTables. 12/09/10 rch Move 'While Flag' statements after Remove statements to just before Repeat statements so the last value in each array gets processed by loop's logic. 12/01/12 krf Implement SRP FastArray processes to improve performance. Also built reliance upon the @EDITTABLES UDP for the current window to store all SRP EditTable controls. Thus, this needs to be populated from within the form's CREATE event or from the promoted CREATE event. 02/26/13 dmb UDPCtrl was not being populated using the SRP_FastArray_Extract so it was getting the handle to UDPCtrls rather than the value stored in the hash table. - [SRPFW-8] 02/28/13 dmb UDP column positions and names were not being properly extracted since the Remove statement is unable to handle the two-dimensional array that tracks these. - [SRPFW-10] 03/02/13 dmb Fix a reference to CellConv by extracting array <2> rather than the whole value. This was left out during the last major enhancement conversion. - [SRPFW-10] 04/01/13 dmb Fix a VNAV issue in Get_Column_Positions gosub. Original code created a For/Next loop setting the variable i. This For/Next loop was replaced with a Remove loop, but the variable i was not being set. - [SRPFW-10] ***********************************************************************************************************************/ $insert Logical $insert OIWin_Equates If Assigned(Window) else Window = @Window If Window EQ "" then Window = @Window WinId = Window $insert OIWin_Comm_Init Declare function Get_property, Utility, OIWin_Compile_Impacts, Calculate, GetTickCount, SRP_FastArray_Create, SRP_FastArray_Extract, SRP_FastArray_GetVariable Declare subroutine Set_Property, Utility, Send_Message, Send_Event, SRP_FastArray_Release, SRP_FastArray_Insert Benchmark = '' Total = '' ProcessCnt = 0 ProcessDone = No$ Loop ProcessCnt += 1 Until ProcessCnt GT 7 OR ProcessDone = Yes$ StartTime = GetTickCount() On ProcessCnt GoSub Initialize_Vars, Set_At_Variables, Get_Column_Positions, Update_Window, Update_Record, Update_Symbolics, Clean_Up EndTime = GetTickCount() Benchmark := "Process ":ProcessCnt:": ":(EndTime - StartTime):"ms|" Total += (EndTime - StartTime) Repeat Benchmark := "|Total ":Total:"ms" * Call Msg(@Window, Benchmark) GoSub Restore_At_Variables Return Record Initialize_Vars: If Assigned(Data) else Data = "" If Assigned(DictColPos) else DictColPos = "" Convert " " to "" in DictColPos Begin Case Case DictColPos EQ "" // DictColPos is null so the whole data record is being updated. WholeRecord = Yes$ Case DictColPos _EQC "SYM" // DictColPos is set to update only symbolics. To do this the WholeRecord flag needs to be set. WholeRecord = Yes$ Case Num(DictColPos[1, 1]) // DictColPos is numerical so only a specific column is being updated. WholeRecord = No$ Case Otherwise$ // An unrecognized value was sent in. Just assume the whole data record is being updated. WholeRecord = Yes$ End Case If Assigned(VirtualOnly) else VirtualOnly = No$ If VirtualOnly EQ "" then VirtualOnly = No$ If Assigned(SkipSymbolics) else SkipSymbolics = No$ If SkipSymbolics EQ "" then SkipSymbolics = No$ Controls = "" ; // List of OpenInsight controls that will be updated. Properties = "" ; // List of OpenInsight properties that will be set. For native OI controls this will be INVALUE. OLE controls will use appropriate properties. Values = "" ; // List of values that will be updated to the controls. EdtCols = "" ; // EditTable column positions. AMV data is mapped to specific column positions within the EditTable control. RowMaps = RowMaps@ ; // @VM/@SVM common array from OIWin_Comm_Init containing a list of all dictionary column names, their column numbers, and formatting information. If RowMaps[-1, 1] = @VM then RowMaps[-1, 1] = "" RMCount = Count(RowMaps, @VM) + (RowMaps NE "") // @FM/@VM Common array of control, control type, and other relevant information. Subvalue 4 in each RowMaps entry has an index to this array. MasterRowMap = SRP_FastArray_Create(MasterRowMap@) MRCount = Count(MasterRowMap@, @FM) + (MasterRowMap@ NE "") FocusControl = Get_Property(Window, "FOCUS") ; // Get the name of the control with focus will need this later. UpdatedOLEEditTables = "" ; // Store the list of OLE EditTables already updated. Refer to this before executing a Clear method. return Set_At_Variables: If Window NE @Window then // Preserve the original values of @ variables in case a different window other than @Window is being updated. Transfer @ID to OrigID Transfer @RECORD to OrigRecord Transfer @DICT to OrigDict end TableName = JoinMap@<1, 1> If TableName NE "" then // Set up @ variables so that symbolic (calculated) columns can be computed after the data record has been // updated. Dictionary = "DICT.":TableName Open Dictionary to @DICT then NumKeyCtrls = Count(KeyMap@, @FM) + (KeyMap@ NE "") @ID = "" For i = 1 to NumKeyCtrls @ID := Get_Property(KeyMap@, "INVALUE"):"*" Next i @ID[-1, 1] = "" Begin Case Case WholeRecord EQ Yes$ AND DictColPos _NEC "SYM" DelimDepth = 1 @RECORD = Data Case WholeRecord EQ No$ GoSub ParseDictColPos If DelimDepth GT 1 then GoSub Update_Data_Array @RECORD = Get_Property(Window, "RECORD") @RECORD = Data Case Otherwise$ // Most likely only updating symbolic columns. Make sure @RECORD is current. @RECORD = Get_Property(Window, "RECORD") End Case end end else @RECORD = "" end return Get_Column_Positions: // Get the actual data column positions and the virtual (@POS) data column positions of the current window // to set up in look up arrays // Start with the actual data column positions. OpenInsight EditTable controls store data column positions // as an @SVM delimited array. This code will flatten this out so each each EditTable column will have its // own lookup reference. OICtrls = SRP_FastArray_Create() OICtrlColPos = SRP_FastArray_Create() OICtrlEdtCol = SRP_FastArray_Create() OICtrlTypes = SRP_FastArray_Create() // These variables will track symbolic (calculated) column information so that the CALCULATE event or CALCULATE // function can be executed as needed. NumSymCtrls = 0 OISymCtrls = SRP_FastArray_Create() OISymDepColPos = SRP_FastArray_Create() OISymCtrlEdtCol = SRP_FastArray_Create() OISymCtrlRowsAsColumns = SRP_FastArray_Create() OISymCtrlCellsAsFields = SRP_FastArray_Create() OISymCtrlSelPos = SRP_FastArray_Create() OISymCtrlTypes = SRP_FastArray_Create() OISymCtrlOrigText = SRP_FastArray_Create() OISymCtrlColName = SRP_FastArray_Create() OICtrlPos = 1 ControlSemanticPos = 1 ControlListItemPos = 1 Flag = "" Unused = "" Loop Remove OICtrl from ControlMap@ at OICtrlPos Setting Flag ControlSemantic = ControlSemantics@[ControlSemanticPos, @FM] ; ControlSemanticPos = Col2() + 1 ControlListItem = ControlList@[ControlListItemPos, @FM] ; ControlListItemPos = Col2() + 1 Unused = ControlSemantic[1, @VM] ColName = ControlSemantic[Col2() + 1, @VM] ColPos = ControlSemantic[Col2() + 1, @VM] OICtrlType = ControlListItem<1, 3> NumEdtCols = Count(ColPos, @SVM) + 1 ; // There is always one even if it is null EdtColPos = 0 ; // Start the column counter at 0. Increase as each column is retrieved. ColPosFlag = "" ColPosPos = 1 Loop Remove CurrColPos from ColPos at ColPosPos setting ColPosFlag EdtColPos += 1 Begin Case Case CurrColPos GT 0 // Regular databound control. SRP_FastArray_Insert(OICtrlColPos, -1, 0, 0, CurrColPos) SRP_FastArray_Insert(OICtrls, -1, 0, 0, OICtrl) SRP_FastArray_Insert(OICtrlEdtCol, -1, 0, 0, EdtColPos) SRP_FastArray_Insert(OICtrlTypes, -1, 0, 0, OICtrlType) Case ColName<0, 0, EdtColPos> NE "" AND CurrColPos EQ "" AND OICtrlType NE "WINDOW" // Must be a symbolic (calculated) control. If symbolics are not being skipped then collect // necessary information for them to be updated later on. If Not(SkipSymbolics) then DepColPos = "" ColNames = ColName<0, 0, EdtColPos> ColNameFlag = "" ColNamePos = 1 Loop Remove ColName from ColNames at ColNamePos setting ColNameFlag If Len(ColName) then DepCols = OIWin_Compile_Impacts(TableName, ColName) DepColsFlag = "" DepColsPos = 1 Loop Remove DepCol from DepCols at DepColsPos Setting DepColsFlag DepCol = Field(DepCol, "*", 2) If Num(DepCol) then // This is a database column position. If it hasn't already been identified then // append the tracking variables. Locate DepCol in DepColPos using @FM setting fPos else DepColPos<-1> = DepCol ; // Track columns already found to avoid duplication // Information is being inserted rather than appended so the most independent // columns appear first in the list. This way the CALCULATE event is sent to // those controls and more dependent columsn will have the most current // information available. NumSymCtrls += 1 SRP_FastArray_Insert(OISymCtrls, 1, 0, 0, OICtrl) SRP_FastArray_Insert(OISymDepColPos, 1, 0, 0, DepCol) SRP_FastArray_Insert(OISymCtrlEdtCol, 1, 0, 0, EdtColPos) SRP_FastArray_Insert(OISymCtrlRowsAsColumns, 1, 0, 0, "") ; // Not needed here, but need to keep values synched for the UDP controls. SRP_FastArray_Insert(OISymCtrlCellsAsFields, 1, 0, 0, "") ; // Not needed here, but need to keep values synched for the UDP controls. SRP_FastArray_Insert(OISymCtrlSelPos, 1, 0, 0, "") ; // Not needed here, but need to keep values synched for the UDP controls. SRP_FastArray_Insert(OISymCtrlTypes, 1, 0, 0, OICtrlType) SRP_FastArray_Insert(OISymCtrlOrigText, 1, 0, 0, "") ; // Not needed here, but need to keep values synched for the UDP controls. SRP_FastArray_Insert(OISymCtrlColName, 1, 0, 0, "") ; // Not needed here, but need to keep values synched for the UDP controls. end end else // This is another symbolic column. Add it to the list so all dependent database // columns are collected. ColNames<-1> = DepCol end While DepColsFlag Repeat end While ColNameFlag Repeat end End Case While ColPosFlag Repeat While Flag Repeat // Now get the virtual (@POS) data column positions. SRP OLE EditTable controls store data column positions // as an @VM delimited array. This code will flatten this out so each each SRP OLE EditTable column will have its // own lookup reference. ControlMap = Get_Property(Window, "@EDITTABLES") Convert @FM to @RM in ControlMap UDPColPos = Get_Property(ControlMap, "@POS") UDPRowsAsColumns = Get_Property(ControlMap, "@ROWSASCOLUMNS") UDPCellsAsFields = Get_Property(ControlMap, "@CELLSASFIELDS") UDPTypes = Get_Property(ControlMap, "TYPE") UDPOrigTexts = Get_Property(ControlMap, "ORIG_TEXT") UDPCtrls = SRP_FastArray_Create() UDPCtrlColPos = SRP_FastArray_Create() UDPCtrlRowsAsColumnsList = SRP_FastArray_Create() UDPCtrlCellsAsFieldsList = SRP_FastArray_Create() UDPCtrlSelPosList = SRP_FastArray_Create() * UDPCtrlTypes = SRP_FastArray_Create() UDPCtrlEdtCol = SRP_FastArray_Create() UDPCtrlOrigText = SRP_FastArray_Create() ControlMapPos = 1 UDPColPosPos = 1 UDPRowsAsColumnsPos = 1 UDPCellsAsFieldsPos = 1 UDPTypesPos = 1 UDPOrigTextsPos = 1 Flag = "" Unused = "" Loop ColPos = UDPColPos[UDPColPosPos, @RM] UDPColPosPos = Col2() + 1 Remove UDPCtrl from ControlMap at ControlMapPos Setting Flag Remove UDPCtrlRowsAsColumns from UDPRowsAsColumns at UDPRowsAsColumnsPos Setting Unused Remove UDPCtrlCellsAsFields from UDPCellsAsFields at UDPCellsAsFieldsPos Setting Unused Remove UDPCtrlType from UDPTypes at UDPTypesPos Setting Unused Remove UDPOrigText from UDPOrigTexts at UDPOrigTextsPos Setting Unused If ColPos NE "" then If UDPCtrlRowsAsColumns EQ "" then UDPCtrlRowsAsColumns = No$ ; // Make sure something is in place so the <-1> append logic will work. If UDPCtrlCellsAsFields EQ "" then UDPCtrlCellsAsFields = No$ ; // Make sure something is in place so the <-1> append logic will work. If UDPOrigText EQ "" then UDPOrigText = UDPCtrlType ; // Make sure something is in place so the <-1> append logic will work. NumEdtCols = Count(ColPos, @VM) + (ColPos NE "") If UDPCtrlCellsAsFields EQ Yes$ then NumEdtRows = Count(ColPos<0, 1>, @SVM) + (ColPos<0, 1> NE "") If NumEdtRows GT 1 then NumEdtCols = NumEdtCols * NumEdtRows end else NumEdtRows = 1 end ColNameFlag = "" ColNamePos = 1 For i = 1 to NumEdtCols For j = 1 to NumEdtRows ColName = ColPos<0, i, j> If ColName NE "" then UDPCtrlSelPos = i:";":j SRP_FastArray_Insert(UDPCtrlColPos, -1, 0, 0, ColName) SRP_FastArray_Insert(UDPCtrls, -1, 0, 0, UDPCtrl) SRP_FastArray_Insert(UDPCtrlEdtCol, -1, 0, 0, i) SRP_FastArray_Insert(UDPCtrlRowsAsColumnsList, -1, 0, 0, UDPCtrlRowsAsColumns) SRP_FastArray_Insert(UDPCtrlCellsAsFieldsList, -1, 0, 0, UDPCtrlCellsAsFields) SRP_FastArray_Insert(UDPCtrlSelPosList, -1, 0, 0, UDPCtrlSelPos) * SRP_FastArray_Insert(UDPCtrlTypes, -1, 0, 0, UDPCtrlType) SRP_FastArray_Insert(UDPCtrlOrigText, -1, 0, 0, UDPOrigText) // In case there are no true databound controls on the form, the data column positions that are // virtually bound will be added to the RowMaps variable. This way the logic that updates the // entire window will catch these virtually bound column positions and update accordingly. If Num(ColName) then // This is a data column so put the column position in the dictionary column name and number // areas. MRIndex = MRCount + 1 RowMaps := @VM:ColName:@SVM:ColName:@SVM:@SVM:MRIndex RMCount += 1 SRP_FastArray_Insert(MasterRowMap, -1, 0, 0, UDPCtrl:@VM:i:@VM:UDPCtrlType) MRCount += 1 end else // This is a symbolic column so only put the column name in the dictionary column name area. RowMaps := @VM:ColName:@SVM RMCount += 1 DepColPos = "" DepCols = OIWin_Compile_Impacts(TableName, ColName) DepColsFlag = "" DepColsPos = 1 Loop Remove DepCol from DepCols at DepColsPos Setting DepColsFlag DepCol = Field(DepCol, "*", 2) If Num(DepCol) then // This is a database column position. If it hasn't already been identified then // append the tracking variables. Locate DepCol in DepColPos using @FM setting fPos else DepColPos<-1> = DepCol ; // Track columns already found to avoid duplication // Information is being inserted rather than appended so the most independent // columns appear first in the list. This way the CALCULATE event is sent to // those controls and more dependent columsn will have the most current // information available. SRP_FastArray_Insert(OISymCtrls, 1, 0, 0, UDPCtrl) ; NumSymCtrls += 1 SRP_FastArray_Insert(OISymDepColPos, 1, 0, 0, DepCol) SRP_FastArray_Insert(OISymCtrlEdtCol, 1, 0, 0, i) SRP_FastArray_Insert(OISymCtrlRowsAsColumns, 1, 0, 0, UDPCtrlRowsAsColumns) SRP_FastArray_Insert(OISymCtrlCellsAsFields, 1, 0, 0, UDPCtrlCellsAsFields) SRP_FastArray_Insert(OISymCtrlSelPos, 1, 0, 0, UDPCtrlSelPos) SRP_FastArray_Insert(OISymCtrlTypes, 1, 0, 0, UDPCtrlType) SRP_FastArray_Insert(OISymCtrlOrigText, 1, 0, 0, UDPOrigText) SRP_FastArray_Insert(OISymCtrlColName, 1, 0, 0, ColName) ; // The name of the symbolic column so it can be calculated later on. end end While DepColsFlag Repeat end end Next j Next i end While Flag Repeat // Check the DictColPos for the special keyword "SYM". This means only the symbolic columns should be updated. // Reset the ProcessCnt counter accordingly. If DictColPos _EQC "SYM" then ProcessCnt += 2 return Update_Window: // Update the control(s) on the window with the data. If WholeRecord EQ No$ then // Only one database column is being updated. Add all the controls that are bound to this database column and // update them. FieldData = Data // Add all the controls that are actually data bound to this database column. If Not(VirtualOnly) then GoSub Add_OI_Control_Information // Add all the controls that are virtually data bound to this database column. GoSub Add_UDP_Control_Information end else // The entire data record is being updated. Use the common variables already set up in the OIWin_Comm_Init // insert to loop through each control, get the database column bound to it, then update it with the data record // column. Loop RowMap = RowMaps[1, @VM] RowMaps[1, Col2()] = "" While RowMap GT "" DictColName = RowMap<1, 1, 1> DictColPos = RowMap<1, 1, 2> Begin Case Case DictColPos GT 0 // This is a data column. FieldData = Data // Add the control indexed to this RowMap. If Not(VirtualOnly) then GoSub Add_Native_Control_Information // Add all the controls that are virtually data bound to this database column. GoSub Add_UDP_Control_Information Case DictColPos EQ "" // This is a symbolic (calculated) column but a non-databound control might be virtually bound to // it. The symbolic will need to be calculated and the value put into FieldData. However, in order // to avoid unnecessary symbolic calculations check first to see if any controls are virtually bound Transfer DictColName to DictColPos GoSub Add_UDP_Control_Information End Case Repeat end // Strip off the preceding @RM Controls[1, 1] = "" Properties[1, 1] = "" Values[1, 1] = "" EdtCols[1, 1] = "" Set_Property(Controls, Properties, Values, EdtCols) ProgID = Get_Property(FocusControl, "OLE.ProgID") ; // Get the ProgID of the focus control If ProgID _EQC "SRP.EditTable.1" then ; // Is the focus control an SRP EditTable? Send_Message(FocusControl, "OLE.AbortCellEdit") ; // Because the SRP EditTable can already be in edit mode before the data Send_Message(FocusControl, "OLE.EditCell", "") ; // is updated, reset the edit mode so the data is visible in the cell end return Update_Record: If TableName NE "" then // Clear out and then update the RECORD property of the window. This will make sure that all related components // to the data changes are in synch so symbolic (calculated) columns and the database write work correctly. Set_Property(Window, "RECORD", "") Set_Property(Window, "RECORD", @RECORD) end return Update_Symbolics: // Symbolics are updated last (and not with the data controls in the Update_Window gosub) because they need to be // recalculated after the data controls have been populated. If Not(SkipSymbolics) then If WholeRecord EQ Yes$ then // Send a CALCULATE event to all controls bound to a symbolic column. For fPos = 1 to NumSymCtrls GoSub Calculate_And_Display Next fPos // No longer need OISymDepColPos array since all controls are being recalculated. SRP_FastArray_Release(OISymDepColPos) end else // Convert from Fast Array back to normal array Handle = OISymDepColPos OISymDepColPos = SRP_FastArray_GetVariable(Handle) SRP_FastArray_Release(Handle) // Send a CALCULATE event to those controls which are dependent upon the changed database column. Match = Yes$ Loop Locate DictColPos in OISymDepColPos using @FM setting fPos then OISymDepColPos = "" ; // Clear field position so it won't be found again. GoSub Calculate_And_Display end else Match = No$ end Until Not(Match) Repeat end end return Clean_Up: SRP_FastArray_Release(MasterRowMap) SRP_FastArray_Release(OICtrls) SRP_FastArray_Release(OICtrlColPos) SRP_FastArray_Release(OICtrlEdtCol) SRP_FastArray_Release(OICtrlTypes) SRP_FastArray_Release(OISymCtrls) SRP_FastArray_Release(OISymCtrlEdtCol) SRP_FastArray_Release(OISymCtrlRowsAsColumns) SRP_FastArray_Release(OISymCtrlCellsAsFields) SRP_FastArray_Release(OISymCtrlSelPos) SRP_FastArray_Release(OISymCtrlTypes) SRP_FastArray_Release(OISymCtrlOrigText) SRP_FastArray_Release(OISymCtrlColName) SRP_FastArray_Release(UDPCtrls) SRP_FastArray_Release(UDPCtrlColPos) SRP_FastArray_Release(UDPCtrlRowsAsColumnsList) SRP_FastArray_Release(UDPCtrlCellsAsFieldsList) SRP_FastArray_Release(UDPCtrlSelPosList) * SRP_FastArray_Release(UDPCtrlTypes) SRP_FastArray_Release(UDPCtrlEdtCol) SRP_FastArray_Release(UDPCtrlOrigText) return Calculate_And_Display: SymCtrl = SRP_FastArray_Extract(OISymCtrls, fPos, 0, 0) SymCtrlEdtCol = SRP_FastArray_Extract(OISymCtrlEdtCol, fPos, 0, 0) SymCtrlRowsAsColumns = SRP_FastArray_Extract(OISymCtrlRowsAsColumns, fPos, 0, 0) SymCtrlCellsAsFields = SRP_FastArray_Extract(OISymCtrlCellsAsFields, fPos, 0, 0) SymCtrlSelPos = SRP_FastArray_Extract(OISymCtrlSelPos, fPos, 0, 0) SymCtrlOrigText = SRP_FastArray_Extract(OISymCtrlOrigText, fPos, 0, 0) SymCtrlColName = SRP_FastArray_Extract(OISymCtrlColName, fPos, 0, 0) If SymCtrlColName EQ "" then // This control is bound to an actual symbolic (calculated) column. Use the CALCULATE event to update it. Send_Event(SymCtrl, "CALCULATE", SymCtrlEdtCol) end else // This control is virtually bound to a symboic (calculated) column. Use the CALCULATE function to compute its // value and update the control's display directly. SymResult = Calculate(SymCtrlColName) If SymCtrlOrigText _EQC "SRP.EditTable.1" then // OLE EditTable controls need to have enough empty rows to display the data being added. Compute the // number of additional rows and use the Dimension property to create them. If WholeRecord EQ Yes$ then Locate SymCtrl in UpdatedOLEEditTables using @FM setting Dummy else // This OLE EditTable might have information from a previous record. Therefore, clear the OLE // EditTable just in case but track the control so it won't get cleared if other columns are updated If DictColPos _NEC "SYM" then // If the control with focus is the control being updated then don't call the Clear method. // First, the changes that are being made in this control will be cleared. Second, if the // current cell is a Combobox type this will crash OpenInsight. If SymCtrlRowsAsColumns OR SymCtrlCellsAsFields then // Clear data but maintain the original rows Send_Message(SymCtrl, "OLE.Clear", 2) end else // Clear data and rows Send_Message(SymCtrl, "OLE.Clear", 0) end end UpdatedOLEEditTables<-1> = SymCtrl ; // Track this OLE EditTable so the Clear method won't be executed again. end end Dimension = Get_Property(SymCtrl, "OLE.Dimension") CurrNumColumns = Dimension[1, @FM] CurrNumRows = Dimension[Col2() + 1, @FM] Begin Case Case SymCtrlRowsAsColumns EQ Yes$ // Data in the SRP EditTable is being stored in a LIST format where rows are associated with field // data. NeededColumns = Count(SymResult, @VM) + (SymResult NE "") If NeededColumns GT CurrNumColumns then CurrNumColumns = NeededColumns Set_Property(SymCtrl, "OLE.Dimension", CurrNumColumns) end Conv = Get_Property(SymCtrl, "OLE.CellConv[1;":SymCtrlEdtCol:"]")<2> If Conv NE "" then SymResult = Oconv(SymResult, Conv) Set_Property(SymCtrl, "OLE.RecordData[":SymCtrlEdtCol:"]", SymResult) Case SymCtrlCellsAsFields EQ Yes$ // Each cell within he SRP EditTable is associated with a specific field data. NeededColumns = Trim(SymCtrlSelPos[1, ";"]) NeededRows = Trim(SymCtrlSelPos[Col2() + 1, ";"]) If (NeededColumns GT CurrNumColumns) OR (NeededRows GT CurrNumRows) then If NeededColumns GT CurrNumColumns then CurrNumColumns = NeededColumns If NeededRows GT CurrNumRows then CurrNumRows = NeededRows Set_Property(SymCtrl, "OLE.Dimension", CurrNumColumns:@FM:CurrNumRows) end Conv = Get_Property(SymCtrl, "OLE.CellConv[":SymCtrlSelPos:"]")<2> If Conv NE "" then SymResult = Oconv(SymResult, Conv) Set_Property(SymCtrl, "OLE.CellText[":SymCtrlSelPos:"]", SymResult) Case Otherwise$ // Data in the SRP EditTable is being stored in the traditional ARRAY format where columns are // associated with field data. NeededRows = Count(SymResult, @VM) + (SymResult NE "") If NeededRows GT CurrNumRows then DCurrNumRows = NeededRows Set_Property(SymCtrl, "OLE.Dimension", @FM:CurrNumRows) end Conv = Get_Property(SymCtrl, "OLE.CellConv[":SymCtrlEdtCol:";1]")<2> If Conv NE "" then SymResult = Oconv(SymResult, Conv) Set_Property(SymCtrl, "OLE.ColumnData[":SymCtrlEdtCol:"]", SymResult) End Case end else // Data is being updated in a standard OpenInsight control. Use the INVALUE property to make sure the data // is being converted correctly. Set_Property(SymCtrl, "INVALUE", SymResult, SymCtrlEdtCol) end end return Add_Native_Control_Information: MRIndex = RowMap<1, 1, 4> Control = SRP_FastArray_Extract(MasterRowMap, MRIndex, 1, 0) EdtCol = SRP_FastArray_Extract(MasterRowMap, MRIndex, 2, 0) CtrlType = SRP_FastArray_Extract(MasterRowMap, MRIndex, 3, 0) If CtrlType NE "OLECONTROL" then // Only native OpenInsight controls apply here. If CtrlType EQ "EDITTABLE" then // EditTable controls need to have enough empty rows to display the data being added. Compute the number of // additional rows and use the INSERT method to create them. Invalue = Get_Property(Control, "INVALUE") CurrNumRows = Count(Invalue<1>, @VM) + (Invalue<1> NE "") NeededRows = Count(FieldData, @VM) + (FieldData NE "") For i = CurrNumRows to NeededRows Send_Message(Control, "INSERT", -1, "") Next i end Controls := @RM : Control Properties := @RM : "INVALUE" Values := @RM : FieldData EdtCols := @RM : EdtCol end return Add_OI_Control_Information: TempOICtrlColPos = SRP_FastArray_GetVariable(OICtrlColPos) Match = Yes$ Loop Locate DictColPos in TempOICtrlColPos using @FM setting fPos then Control = SRP_FastArray_Extract(OICtrls, fPos, 0, 0) CtrlType = SRP_FastArray_Extract(OICtrlTypes, fPos, 0, 0) If CtrlType EQ "EDITTABLE" then // EditTable controls need to have enough empty rows to display the data being added. Compute the number // of additional rows and use the INSERT method to create them. Invalue = Get_Property(Control, "INVALUE") CurrNumRows = Count(Invalue<1>, @VM) + (Invalue<1> NE "") NeededRows = Count(FieldData, @VM) + (FieldData NE "") For i = CurrNumRows to NeededRows Send_Message(Control, "INSERT", -1, "") Next i end Controls := @RM : Control Properties := @RM : "INVALUE" Values := @RM : FieldData EdtCol = SRP_FastArray_Extract(OICtrlEdtCol, fPos, 0, 0) EdtCols := @RM : EdtCol TempOICtrlColPos = "" end else Match = No$ end Until Not(Match) Repeat return Add_UDP_Control_Information: TempUDPCtrlColPos = SRP_FastArray_GetVariable(UDPCtrlColPos) Match = Yes$ Loop Locate DictColPos in TempUDPCtrlColPos using @FM setting fPos then Control = SRP_FastArray_Extract(UDPCtrls, fPos, 0, 0) RowsAsColumns = SRP_FastArray_Extract(UDPCtrlRowsAsColumnsList, fPos, 0, 0) CellsAsFields = SRP_FastArray_Extract(UDPCtrlCellsAsFieldsList, fPos, 0, 0) SelPos = SRP_FastArray_Extract(UDPCtrlSelPosList, fPos, 0, 0) OrigText = SRP_FastArray_Extract(UDPCtrlOrigText, fPos, 0, 0) // If DictColPos is not a number then this is a symbolic that needs to be calculated If Not(Num(DictColPos)) then FieldData = Calculate(DictColPos) Controls := @RM : Control Begin Case Case OrigText _EQC "SRP.EditTable.1" // OLE EditTable controls need to have enough empty rows to display the data being added. Compute // the number of additional rows and use the Dimension property to create them. If WholeRecord EQ Yes$ then Locate Control in UpdatedOLEEditTables using @FM setting Dummy else // This OLE EditTable might have information from a previous record. Therefore, clear the // OLE EditTable just in case but track the control so it won't get cleared if other columns // are updated. If Control NE FocusControl then // If the control with focus is the control being updated then don't call the Clear // method. First, the changes that are being made in this control will be cleared. // Second, if the current cell is a Combobox type this will crash OpenInsight. ClearFill = Get_Property(Control, "@CLEARFILL") *If RowsAsColumns OR CellsAsFields then Begin Case Case ClearFill // Clear using fill value defined for EditTable Send_Message(Control, "OLE.Clear", ClearFill) Case RowsAsColumns OR CellsAsFields // Clear data but maintain the original rows Send_Message(Control, "OLE.Clear", 2) Case Otherwise$ // Clear data and rows Send_Message(Control, "OLE.Clear", 0) End Case end UpdatedOLEEditTables<-1> = Control ; // Track this OLE EditTable so the Clear method won't be executed again. end end Dimension = Get_Property(Control, "OLE.Dimension") CurrNumColumns = Dimension<1> CurrNumRows = Dimension<2> Begin Case Case RowsAsColumns EQ Yes$ // Data in the SRP EditTable is being stored in a LIST format where rows are associated with // field data. NeededColumns = Count(FieldData, @VM) + (FieldData NE "") If NeededColumns GT CurrNumColumns then Dimension<1> = NeededColumns Set_Property(Control, "OLE.Dimension", Dimension) end EdtCol = SRP_FastArray_Extract(UDPCtrlEdtCol, fPos, 0, 0) Properties := @RM : "OLE.RecordData[":EdtCol:"]" CellConv = Get_Property(Control, "OLE.CellConv[1;":EdtCol:"]") Case CellsAsFields EQ Yes$ // Each cell within he SRP EditTable is associated with a specific field data. NeededColumns = Trim(Field(SelPos, ";", 1)) NeededRows = Trim(Field(SelPos, ";", 2)) If (NeededColumns GT CurrNumColumns) OR (NeededRows GT CurrNumRows) then If NeededColumns GT CurrNumColumns then Dimension<1> = NeededColumns If NeededRows GT CurrNumRows then Dimension<2> = NeededRows Set_Property(Control, "OLE.Dimension", Dimension) end Properties := @RM : "OLE.CellText[":SelPos:"]" CellConv = Get_Property(Control, "OLE.CellConv[":SelPos:"]") Case Otherwise$ // Data in the SRP EditTable is being stored in the traditional ARRAY format where columns // are associated with field data. NeededRows = Count(FieldData, @VM) + (FieldData NE "") If NeededRows GT CurrNumRows then Dimension<2> = NeededRows Set_Property(Control, "OLE.Dimension", Dimension) end EdtCol = SRP_FastArray_Extract(UDPCtrlEdtCol, fPos, 0, 0) Properties := @RM : "OLE.ColumnData[":EdtCol:"]" CellConv = Get_Property(Control, "OLE.CellConv[":EdtCol:";1]") End Case Conv = CellConv<2> If Conv NE "" then FieldData = Oconv(FieldData, Conv) Values := @RM : FieldData EdtCols := @RM : "" Case Otherwise$ Properties := @RM : "INVALUE" Values := @RM : FieldData EdtCols := @RM : "" End Case TempUDPCtrlColPos = "" end else Match = No$ end Until Not(Match) Repeat return Restore_At_Variables: Record = @RECORD ; // Copy @RECORD before it gets restored (if necessary) so it can be returned. If Window NE @Window then // Restore the original values of @ variables in case a different window other than @Window is being updated. Transfer OrigID to @ID Transfer OrigRecord to @RECORD Transfer OrigDict to @DICT end return ParseDictColPos: // Determine if the data needs to update a value, subvalue, text, or subtext part of a field. DelimDepth = 0 Loop DelimDepth += 1 Pos = Field(DictColPos, ",", DelimDepth) Until Not(Num(Pos)) OR Pos EQ "" OR DelimDepth EQ 6 Begin Case Case DelimDepth EQ 1 ; Transfer Pos to FieldPos Case DelimDepth EQ 2 ; Transfer Pos to ValPos Case DelimDepth EQ 3 ; Transfer Pos to SubValPos Case DelimDepth EQ 4 ; Transfer Pos to TextPos Case DelimDepth EQ 5 ; Transfer Pos to SubTextPos End Case Repeat DictColPos = FieldPos DelimDepth -= 1 return Update_Data_Array: // Drill into the data field as much as necessary and update with the data being passed in. The entire data field // will then be put into each control that is bound to this database column. DataArray = "" DataArray<1> = @RECORD DataArray<2> = Field(DataArray<1>, @VM, ValPos) If DelimDepth GT 2 then DataArray<3> = Field(DataArray<2>, @SVM, SubValPos) If DelimDepth GT 3 then DataArray<4> = Field(DataArray<3>, @TM, TextPos) If DelimDepth GT 4 then Data = FieldStore(DataArray<4>, @STM, SubTextPos, 1, Data) end Data = FieldStore(DataArray<3>, @TM, TextPos, 1, Data) end Data = FieldStore(DataArray<2>, @SVM, SubValPos, 1, Data) end Data = FieldStore(DataArray<1>, @VM, ValPos, 1, Data) return