Module cDbCJGridDataSource.pkg

     1Use ui
     2Use cCJGridDataSource.pkg
     3Use Datadict.pkg
     4
     5Enum_List
     6    Define ufUpdateToDD
     7    Define ufUpdateToBuffer
     8End_Enum_List
     9
    10
    11Class cCJGridCachedDataSource is a cCJGridDataSource
    12
    13    Procedure Construct_Object
    14        Forward Send Construct_Object
    15
    16        { Visibility=Private }
    17        Property Integer piRowCount 0    // Normalized Rows in cache
    18        { Visibility=Private }
    19        Property Integer piRowOffset 0   // Unnormalized off set to rows (if not zero)
    20        { Visibility=Private }
    21        Property Boolean pbAtTop False   // has cache discovered first record
    22        { Visibility=Private }
    23        Property Boolean pbAtEnd False   // has cache discovered last record
    24        { Visibility=Private }
    25        Property Integer piMinPageSize 50 // minimum number of records to page whenever a page is performed
    26        { Visibility=Private }
    27        Property Integer piMaxCacheSizeAllowed 150 // max size of cache, 0 - unlimited
    28        { Visibility=Private }
    29        Property Integer piMaxCacheRange -1 // internal - used to keep track of the largest range the grid has requested.
    30        { Visibility=Private }
    31        Property Boolean pbLoadAll False    // if true, load all data with first page event
    32        { Visibility=Private }
    33        Property Boolean pbStaticData False // determines if datasource is static - once loaded it does not change
    34        { Visibility=Private }
    35        Property Handle  Server             // The DD server
    36        { Visibility=Private }
    37        Property Integer piGridOrdering -1  // Ordering from Grid. See Ordering for actual order which may differ
    38        { Visibility=Private }
    39        Property Boolean pbDDReadMode False   // if true we never actually find the record in the dd - just read
    40        { Visibility=Private }
    41        Property Boolean pbReverseOrdering False // is data source reversed?
    42    End_Procedure
    43
    44    { NoDoc=True }
    45    Function DataIsStatic Returns Boolean
    46        Boolean bStatic
    47        Get pbStaticData to bStatic
    48        Function_Return bStatic
    49    End_Function
    50    
    51    // ordering is calculated. If the server has a value, we use that. If not, use the grid's ordering
    52    { Visibility=Private }
    53    Function Ordering Returns Integer
    54        Handle hoSever
    55        Integer iOrdering
    56        Get Server to hoSever
    57        Get Ordering of hoSever to iOrdering
    58        If (iOrdering=-1) Begin
    59            Get piGridOrdering to iOrdering
    60        End
    61        Function_Return iOrdering
    62    End_Function
    63
    64    { NoDoc=True }
    65    Function AllDataIsLoaded Returns Boolean
    66        Boolean bAtTop bAtEnd
    67        Get pbAtTop to bAtTop
    68        Get pbAtEnd to bAtEnd
    69        Function_Return (bAtTop and bAtEnd)
    70    End_Function
    71    
    72    { NoDoc=True }
    73    Function RowOffset Returns Integer
    74        Integer iTop
    75        Get piRowOffset to iTop
    76        Function_Return iTop
    77    End_Function
    78
    79    { NoDoc=True }
    80    Function RowCount Returns Integer
    81        Integer iRowCount
    82        Get piRowCount to iRowCount
    83        Function_Return iRowCount
    84    End_Function
    85    
    86    // return the maximum cache size needed for this grid.
    87    // This is tied to the logic in PageInRows and how it caches records. It uses
    88    // the current maximum range passed in.
    89    { NoDoc=True }
    90    Function CurrentMaxCache Returns Integer
    91        Integer iMax iPage iMaxC
    92        Get piMaxCacheRange to iMax
    93        Get piMaxCacheSizeAllowed to iMaxC
    94        If (iMax=-1 or iMaxC=0) Begin
    95            Function_Return iMaxC 
    96        End
    97        // cache must be big enough for 3X the range so we can cache a page before and after
    98        // plus it must be big enough handle the min-page, which basically says if you need 
    99        // even one row, you should page in this minimum number of extra rows
   100        Get piMinPageSize to iPage
   101        Move ((3 * iMax) + (2*iPage))  to iMax
   102        // use the required iMax size or if we've set a larger maximum cache, use that
   103        Function_Return (iMax max iMaxC)
   104    End_Function
   105
   106    // currently I adjust first and last row but I don't do anything with the information
   107    // First row is the current top row (first displayable row)
   108    // Last row is at-least the last displayable. Last - first gives us the range
   109    { NoDoc=True }
   110    Procedure PageInRows Integer ByRef iFirstRow Integer ByRef iLastRow
   111        tDataSourceRow[] DataSource
   112        Boolean bAtTop bAtEnd bDirty bLoadAll
   113        Integer iSize iRowCount iNewRowCount iRowOffset iCacheSize iMinPage
   114        Integer iNewTop iNewRows iRowsToAdd iMaxCacheSizeAllowed iMaxRange iCrntMaxRange iCrntMaxCache
   115        Integer iMain
   116        Handle hoDD
   117        RowID riCurrentRow
   118        
   119        // We need to make sure our cache size is big enough to handle the range of records
   120        // requested. To do that we must keep track of the range of records requested and always
   121        // track the largest range.
   122        Get piMaxCacheRange to iMaxRange
   123        Move (iLastRow - iFirstRow +1) to iCrntMaxRange
   124        If (iMaxRange<iCrntMaxRange) Begin
   125            Set piMaxCacheRange to iCrntMaxRange
   126        End
   127        // adjust what we are looking for in a first and last row
   128        Move (iFirstRow-iCrntMaxRange) to iFirstRow // allow a page before the first row
   129        Move (iLastRow+iCrntMaxRange) to iLastRow   // allow a fill page after the last row
   130        
   131        
   132        Get pDataSource to DataSource
   133        Get pbAtTop to bAtTop
   134        Get pbAtEnd to bAtEnd
   135        Get pbLoadAll to bLoadAll
   136        Get piRowOffset to iRowOffset
   137        Move (SizeOfArray(DataSource)) to iCacheSize
   138        
   139        // note that piRowOffset can only be 0 or negative. If negative it means that rows have been added to the top
   140        // but the grid has not been normalized. When normalized, the offset is set back to 0.
   141
   142        // within range. nothing to do
   143        If ( (iFirstRow>=iRowOffset or bAtTop) and ((iLastRow<iCacheSize+iRowOffset) or bAtEnd)) Begin
   144            Procedure_Return
   145        End
   146
   147        If bLoadAll Begin
   148            Move 0 to iMaxCacheSizeAllowed
   149            Move 0 to iMinPage
   150        End
   151        Else Begin 
   152            Get CurrentMaxCache to iMaxCacheSizeAllowed
   153            Get piMinPageSize to iMinPage
   154        End
   155        
   156        Get Server to hoDD
   157        Get Main_File of hoDD to iMain
   158        Move (GetRowID(iMain)) to riCurrentRow
   159        
   160        
   161        // See if we need to page above, add to top
   162        If (iFirstRow<iRowOffset and not(bAtTop) ) Begin
   163            
   164            // Ok to pass negative rows. means find up
   165            If (iMinPage=0) Begin
   166                Move -1 to iRowsToAdd
   167            End
   168            Else Begin
   169                // if we are adding any rows, we make sure we add at least a minimum number
   170                Move ( (iRowOffset-iFirstRow) max iMinPage) to iRowsToAdd
   171            End
   172
   173            Send OnPageData True iRowsToAdd (&DataSource) (&bAtTop) (&bAtEnd)
   174            Set pDataSource to DataSource
   175            Set pbAtEnd to bAtEnd
   176            Set pbAtTop to bAtTop
   177
   178            Get RowCount to iRowCount // this is the old rowcount and will not change
   179            Move (SizeOfArray(DataSource)) to iSize   // new size of cache 
   180            Move (iSize - iCacheSize) to iNewRows     // number of rows added
   181            Move (iRowOffset - iNewRows ) to iNewTop  // reduce cache top by that amount. This can only be negative
   182            
   183            // if we could not add all the rows we needed adjust first-row
   184            If (iNewRows<iRowsToAdd) Begin
   185                Move (iFirstRow + (iRowsToAdd - iNewRows)) to iFirstRow 
   186            End
   187            
   188            // a synch is needed if:
   189            // 1) rowoffset is not zero (can only be negative or 0), if negative, we've added rows to the top.
   190            // 2) the max cache size is exceeded 
   191            If ( (iNewTop<0) or (iMaxCacheSizeAllowed<>0 and iMaxCacheSizeAllowed<iSize) ) Begin
   192                Set pbDataSourceSynchRequired to True
   193            End
   194            
   195            Set piRowOffset to iNewTop
   196            
   197            Move iSize to iCacheSize
   198            Move iNewTop to iRowOffset
   199            
   200        End
   201
   202        // if the last row requires we find down 
   203               
   204        If (iLastRow>=iRowOffset+iCacheSize and not(bAtEnd)) Begin
   205
   206            If (iMinPage=0) Begin
   207                Move -1 to iRowsToAdd
   208            End
   209            Else Begin
   210                // if we are adding any rows, we make sure we add at least a minimum number
   211                Move ( (iLastrow+1 - (iRowOffset+iCacheSize)) max iMinPage) to iRowsToAdd
   212            End
   213
   214            Send OnPageData False iRowsToAdd (&DataSource) (&bAtTop) (&bAtEnd)
   215            Set pDataSource to DataSource
   216            Set pbAtEnd to bAtEnd
   217            Set pbAtTop to bAtTop
   218
   219            Get RowCount to iRowCount // this is the old rowcount and will not change
   220            Move (SizeOfArray(DataSource)) to iSize   // new size of cache 
   221            Move (iSize - iCacheSize) to iNewRows     // number of rows added
   222            
   223            // if we could add all the rows we needed adjust lastrow
   224            If (iNewRows<iRowsToAdd) Begin
   225                Move (iLastRow - (iRowsToAdd - iNewRows)) to iLastRow 
   226            End
   227            
   228            // a synch is needed if:
   229            // 1) rowoffset is not zero (can only be negative or 0),
   230            // 2) at End and the old rowcount and the new size don't agree
   231            // 3) the max cache size is exceeded 
   232            If ( (iRowOffset<0) or (bAtEnd and (iSize<>iRowCount)) or (iSize>iRowCount )  or (iMaxCacheSizeAllowed<>0 and iMaxCacheSizeAllowed<iSize)) Begin
   233                Set pbDataSourceSynchRequired to True
   234            End
   235        End
   236        
   237        If (not(IsSameRowID(riCurrentRow,GetRowID(iMain)))) Begin
   238            Send ReadByRowId of hoDD iMain riCurrentRow
   239        End
   240        
   241    End_Procedure
   242    
   243    // Adjust RowCount and RowOffset to best fit the cache data.
   244    { NoDoc=True }
   245    Function NormalizeDataSource Integer iTopRow Returns Integer
   246        
   247        tDataSourceRow[] DataSource
   248        Boolean bAtTop bAtEnd bLoadAll bDataSourceSynchRequired
   249        Integer iSize iRowCount iNewRowCount iRowOffset iCacheSize iMinPage iNewOffset
   250        Integer  iNewRows iRowsToAdd iSelectedRow iMaxCacheSizeAllowed iOffsetChange
   251        Integer iNewStart iNewEnd
   252        Move 0 to iOffsetChange
   253        
   254        Get pDataSource to DataSource
   255        Get pbAtTop to bAtTop
   256        Get pbAtEnd to bAtEnd
   257        Get piRowOffset to iRowOffset
   258        Get piRowCount to iRowCount
   259
   260        Get pbLoadAll to bLoadAll
   261        
   262        If (not(bLoadAll)) Begin
   263            Get CurrentMaxCache to iMaxCacheSizeAllowed
   264        End
   265        
   266        Move (SizeOfArray(DataSource)) to iCacheSize
   267
   268        Move iRowCount to iNewRowCount
   269        Move iRowOffset to iNewOffset
   270        
   271        // if at top adjust rowcount and RowOffset
   272        If (bAtTop) Begin
   273            Move (-iRowOffset) to iRowsToAdd
   274            Move (iRowCount + iRowsToAdd) to iNewRowCount
   275            Move (iRowOffset + iRowsToAdd) to iNewOffset
   276        End
   277        // else if new top is negative, must normalize to 0
   278        Else If (iNewOffset<0) Begin
   279            Move (-iRowOffset) to iRowsToAdd
   280            Move (iRowCount + iRowsToAdd) to iNewRowCount
   281            Move (iRowOffset + iRowsToAdd) to iNewOffset
   282        End
   283
   284        If bAtEnd Begin
   285            Move (iNewOffset + iCacheSize) to iNewRowCount
   286        End
   287        Else If (iNewRowCount < (iNewOffset +iCacheSize)) Begin
   288            Move ((iNewOffset + iCacheSize) - iNewRowCount ) to iRowsToAdd
   289            Move (iNewRowCount + iRowsToAdd) to iNewRowCount
   290        End
   291
   292        Move (iNewOffset - iRowOffset) to iOffsetChange
   293
   294        // adjust selected row by offset change
   295        
   296        Get piSelectedRow to iSelectedRow
   297        If (iSelectedRow<>-1) Begin
   298            Move (iSelectedRow + iOffsetChange) to iSelectedRow
   299            
   300            If (iSelectedRow<0 or ((iSelectedRow - iNewOffset) >=iCacheSize)) Begin
   301                Error DFERR_PROGRAM "Assert: Invalid SelectedRow in NormalizeDataSource"
   302            End
   303            
   304        End
   305        Move False to bDataSourceSynchRequired
   306        
   307        // Handle shrinking the cache if needed. 
   308        // Before shrinking the cache we make sure that the selected row will not
   309        // be cached out.  If it is, we don't shrink the cache
   310        If (iMaxCacheSizeAllowed<>0 and iCacheSize>iMaxCacheSizeAllowed) Begin
   311            
   312            // shrink cache. Shrink part that is furthest away from the displayed rows
   313            If (iTopRow > (iMaxCacheSizeAllowed/2)) Begin
   314                
   315                // remove from top. 
   316                Move (iCacheSize-iMaxCacheSizeAllowed) to iNewStart
   317                Move (iCacheSize-1) to iNewEnd
   318                If (iSelectedRow<>-1 and iSelectedRow>=iNewStart) Begin
   319                    Move (iSelectedRow-iNewStart) to iSelectedRow
   320                    Move (CopyArray(DataSource,iNewStart,iNewEnd)) to DataSource
   321                    Move (iOffsetChange-iNewStart) to iOffsetChange
   322                    Move False to bAtTop
   323                    Move iMaxCacheSizeAllowed to iNewRowCount
   324                    Set pDataSource to DataSource
   325                End
   326                Else Begin
   327// if we set this it checks too often. If here, our cache is too big because the user is hanging onto a selected
   328// record that's out of range. Once a selection is made and the user does something to change the cache size, this will be adjusted anyway
   329//                    Move True to bDataSourceSynchRequired
   330                End
   331            End
   332            Else Begin
   333                
   334                // Remove from bottom
   335                Move 0 to iNewStart
   336                Move (iMaxCacheSizeAllowed-1) to iNewEnd
   337                If (iSelectedRow<>-1 and iSelectedRow<=iNewEnd) Begin
   338                    Move (CopyArray(DataSource,iNewStart,iNewEnd)) to DataSource
   339                    Move False to bAtEnd
   340                    Move iMaxCacheSizeAllowed to iNewRowCount
   341                    Set pDataSource to DataSource
   342                End
   343                Else Begin
   344// see above note
   345//                    Move True to bDataSourceSynchRequired
   346                End
   347            End
   348        End
   349        
   350        Set piSelectedRow to iSelectedRow
   351        Set piRowCount to iNewRowCount
   352        Set piRowOffset to 0 // iNewOffset
   353        Set pbAtTop to bAtTop
   354        Set pbAtEnd to bAtEnd
   355        Set pbDataSourceSynchRequired to bDataSourceSynchRequired
   356        
   357        Function_Return iOffsetChange
   358
   359    End_Function
   360    
   361    { Visibility=Private }
   362    Function AdjustRowForOffset Integer iRow Returns Integer
   363        Integer iOffset
   364        // if not normalized (dirty) adjust Row so it points to correct datasource array index
   365        Get piRowOffset to iOffset
   366        Function_Return (iRow - iOffset) 
   367    End_Function
   368
   369   { NoDoc=True }
   370   Function PageInMatchingRow RowID riId Returns Integer
   371        tDataSourceRow[] DataSource
   372        Boolean bAtTop bAtBottom bStaticData
   373        Integer  iSize iIndex iRowCount iCacheIndex iNewOffset
   374        Integer iOffsetChange
   375        Integer iFirst iLast 
   376        
   377        Move 0 to iCacheIndex
   378        Get pbAtEnd to bAtBottom
   379        Get pbAtTop to bAtTop
   380        Get pbStaticData to bStaticData
   381        
   382        If (IsNullRowID(riId)) Begin
   383            Function_Return -1
   384        End
   385        
   386        Get FindRowIdInCache riId to iIndex
   387        
   388        // first attempt to find in the cache
   389        If (iIndex<>-1) Begin
   390            Get piRowOffset to iNewOffset
   391            Function_Return (iIndex + iNewOffset)    
   392        End
   393        
   394        // if data is all loaded and it is static, it doesn't exist
   395        If (bStaticData and bAtTop and bAtBottom) Begin
   396            Function_Return -1
   397        End
   398//        If (bAtTop and bAtBottom) Begin
   399//            Function_Return -1 // the entire grid is loaded and this record does not exist
   400//        End
   401        
   402        Send SelectRow -1 False
   403        
   404        // this finds the one matching record
   405        Send OnPageInMatchingRow riId (&DataSource)
   406        If (SizeOfArray(DataSource)=0) Begin
   407            Function_Return -1 // not found nothing changes
   408        End
   409        
   410        Move 0 to iIndex
   411        Set pDataSource to DataSource
   412        Set pbAtTop to False
   413        Set pbAtEnd to False
   414        Set piRowCount to (SizeOfArray(DataSource))
   415        Set piRowOffset to 0
   416
   417        // now page in a set or data around the matching records
   418        // pass in a good range, which we can get from piMaxCacheRange
   419        Move 0 to iFirst
   420        Get piMaxCacheRange to iLast // will be -1 if it has never been set
   421        If (iLast=-1) Begin
   422            Get piMinPageSize to iLast
   423            Move (iLast/2) to iLast
   424        End
   425        Decrement iLast
   426        Send PageInRows (&iFirst) (&iLast)
   427        Get NormalizeDataSource 0 to iNewOffset
   428        Get FindRowIdInCache riId to iIndex
   429
   430        Set pbDataSourceSynchRequired to True 
   431        
   432        Function_Return iIndex
   433
   434     End_Function
   435   
   436    { NoDoc=True }
   437    Procedure PageInLastRow
   438        Integer iIndex
   439        RowID riId
   440        Boolean bAtEnd bStaticData
   441        Get pbAtEnd to bAtEnd
   442        Get pbStaticData to bStaticData
   443        If (not(bStaticData and bAtEnd)) Begin
   444            Send Reset
   445            Get OnReadLastRecord to riID
   446            Get PageInMatchingRow riId to iIndex
   447        End
   448    End_Procedure
   449
   450    { NoDoc=True }
   451    Procedure PageInFirstRow
   452        Integer iIndex
   453        RowID riId
   454        Boolean bAtTop bStaticData
   455        Get pbAtTop to bAtTop
   456        Get pbStaticData to bStaticData
   457        If (not(bStaticData and bAtTop)) Begin
   458            Send Reset
   459            Get OnReadFirstRecord to riID
   460            Get PageInMatchingRow riId to iIndex
   461        End
   462    End_Procedure
   463        
   464    { Visibility=Private }
   465    Function FindRowIdInCache RowID rId Returns Integer
   466        tDataSourceRow[] DataSource
   467        tDataSourceRow DataSourceRow
   468        Integer iIndex
   469        Move rId to DataSourceRow.riID
   470        Get pDataSource to DataSource
   471        Move (SearchArray(DataSourceRow,DataSource,Desktop,RefFunc(ComparetDataSourceRowId))) to iIndex
   472        Function_Return iIndex
   473    End_Function
   474    
   475    Function FindExternalDataInCache Returns Integer
   476        Handle hoServer
   477        Integer iFile iIndex iNewOffset
   478        RowID riID
   479        Get Server to hoServer
   480        Get Main_File of hoServer to iFile
   481        Move (GetRowID(iFile)) to riID
   482        Get FindRowIdInCache riID to iIndex
   483        If (iIndex<>-1) Begin
   484            // if not normalized (dirty) adjust index
   485            Get piRowOffset to iNewOffset
   486            Move (iIndex + iNewOffset) to iIndex    
   487        End
   488        Function_Return iIndex
   489    End_Function
   490    
   491    
   492    { NoDoc=True }
   493    Procedure Reset
   494        tDataSourceRow[] DataSource
   495        Send SelectRow -1 False
   496        Set piRowOffset to 0
   497        Set piRowCount to 0
   498        Set pbAtTop to False
   499        Set pbAtEnd to False
   500        Set pDataSource to DataSource
   501        Set pbDataSourceSynchRequired to True
   502    End_Procedure
   503                
   504    // This loads all data passed and assumes that this is all of the data.
   505    { NoDoc=True }
   506    Procedure InitializeDataSource tDataSourceRow[] DataSource
   507        Integer iSize
   508        Integer iOffsetChange
   509        
   510        Send Reset
   511        Move (SizeOfArray(DataSource)) to iSize
   512        Set pDataSource to DataSource
   513        Set pbAtTop to True
   514        Set pbAtEnd to True
   515        Set pbStaticData to True
   516        Set piRowCount to iSize
   517        Set piRowOffset to 0
   518        Get NormalizeDataSource 0 to iOffsetChange
   519    End_Procedure
   520
   521    { NoDoc=True }
   522    Function CreateDataSourceRow  Returns tDataSourceRow
   523        tDataSourceRow DataSourceRow
   524        Handle[] DataSourceColumnObjects
   525        Integer iColumn iColumns
   526        String sValue
   527        
   528        Get pDataSourceColumnObjects to DataSourceColumnObjects
   529        Get ColumnCount to iColumns
   530        Send OnSetTag (&DataSourceRow)
   531        For iColumn from 0 to (iColumns-1)
   532            Get InitialValue of DataSourceColumnObjects[iColumn] to sValue
   533            Move sValue to DataSourceRow.sValue[iColumn]
   534        Loop
   535        Function_Return DataSourceRow
   536    End_Function
   537    
   538    // This handles translation of find modes to make reverse ordering
   539    // work. Pass the mode and return the mode, translated if reverse ordering is needed
   540    { Visibility=Private }
   541    Function FindMode Integer eFindMode Returns Integer
   542        Boolean bReverseOrdering
   543        Get pbReverseOrdering to bReverseOrdering
   544        If (bReverseOrdering) Begin
   545            Case Begin
   546                Case (eFindMode=GE) Move (LE) to eFindMode
   547                Case (eFindMode=GT) Move (LT) to eFindMode
   548                Case (eFindMode=LT) Move (GT) to eFindMode
   549                Case (eFindMode=LE) Move (GE) to eFindMode
   550                Case (eFindMode=FIRST_RECORD) Move (LAST_RECORD) to eFindMode
   551                Case (eFindMode=LAST_RECORD) Move (FIRST_RECORD) to eFindMode
   552            Case End
   553        End
   554        Function_Return eFindMode
   555    End_Function
   556
   557    { Visibility=Private }
   558    Procedure Establish_Find_Mode Integer eFindMode
   559        Handle hoServer
   560        Integer iFile iOrdering
   561        Get Server to hoServer
   562        Get FindMode eFindMode to eFindMode // mode might change if reverse ordering
   563        Get Main_File of hoServer to iFile
   564        Get Ordering to iOrdering
   565        Send Establish_Find_Direction of hoServer eFindMode iFile iOrdering
   566    End_Procedure
   567    
   568    { Visibility=Private }
   569    Procedure Read_Next_Record
   570        Handle hoServer
   571        Get Server to hoServer
   572        If hoServer Begin
   573            Send Locate_Next of hoServer
   574        End
   575    End_Procedure
   576
   577    { Visibility=Private }
   578    Function ReadRecordInBuffer Returns Boolean
   579        Boolean bFound
   580        RowID riId
   581        Handle hoServer
   582        Integer iFile
   583        Send Establish_Find_mode Ge
   584        Send Read_Next_record
   585        Move (Found) to bFound
   586        If not bFound Begin
   587            Get OnReadLastRecord to riId
   588        End
   589        Else Begin
   590            Get Server to hoServer
   591            Get Main_File of hoServer to iFile
   592            Move (GetRowID(iFile)) to riId
   593        End
   594        Function_Return (not(IsNullRowID(riId)))
   595    End_Function
   596
   597
   598    { Visibility=Private }
   599    Procedure OnPageInMatchingRow RowID riId tDataSourceRow[] ByRef DataSource 
   600        tDataSourceRow DataSourceRow
   601        Boolean bFound 
   602        Handle  hoDD
   603        Integer iIndex iFile
   604        Get Server to hoDD
   605        Get Main_File of hoDD to iFile
   606        Get Ordering to iIndex
   607        Send ReadByRowId of hoDD iFile riId
   608        Move (Found) to bFound
   609        If bFound Begin
   610            Get CreateDataSourceRow to DataSourceRow
   611            Move DataSourceRow to DataSource[0]
   612        End
   613    End_Procedure
   614    
   615    // if iRowstoAdd is minus, add to top, else add to bottom
   616    { Visibility=Private }
   617    Procedure OnPageData Boolean bFindUp Integer iRowsToAdd tDataSourceRow[] ByRef DataSource Boolean ByRef bAtTop Boolean ByRef bAtEnd
   618        tDataSourceRow DataSourceRow
   619        Boolean bFound bFindAll
   620        Integer iRow_Count iSize iNew 
   621        Handle  hoDD
   622        Integer iFile iIndex iMax
   623                     
   624        Move (SizeOfArray(DataSource)) to iRow_Count
   625        Move (iRowsToAdd=-1) to bFindAll
   626        
   627        Get Server to hoDD
   628        Get Main_File of hoDD to iFile
   629        Get  Ordering to iIndex
   630        
   631        // find down    
   632        If (not(bFindUp)) Begin
   633
   634            If (iRow_Count>0) Begin
   635                Send ReadByRowId of hoDD iFile DataSource[iRow_Count-1].riID 
   636            End
   637            Else Begin
   638                Move True to bAtTop
   639            End
   640        
   641            Move True to bAtEnd
   642            Move (iRowsToAdd + iRow_Count) to iMax
   643    
   644           Send Establish_Find_Mode (GT)
   645           Send Read_Next_Record  
   646            Move (Found) to bFound
   647            Move True to Found
   648            While (bFound)
   649                Get CreateDataSourceRow to DataSourceRow
   650                Move DataSourceRow to DataSource[iRow_Count]   
   651                Increment iRow_Count
   652                Send Read_Next_Record  
   653                Move (Found) to bFound
   654                
   655                If ( not(bFindAll) and (bFound) and (iMax <iRow_Count)) Begin
   656                    Move False to bFound
   657                    Move False to bAtEnd
   658                End
   659            Loop
   660
   661        End
   662        // find up
   663        Else Begin
   664
   665            If (iRow_Count>0) Begin
   666                // if the top row is null we assume we are at the top
   667                If (IsNullRowID(DataSource[0].riID)) Begin
   668                    Move True to bAtTop
   669                    Procedure_Return
   670                End
   671                Send ReadByRowId of hoDD iFile DataSource[0].riID 
   672            End
   673            
   674            Move True to bAtTop
   675            Move (iRowsToAdd) to iMax
   676    
   677           Send Establish_Find_Mode (LT)
   678           Send Read_Next_Record  
   679           Move (Found) to bFound
   680            While (bFound)
   681    
   682                Get CreateDataSourceRow to DataSourceRow
   683                Move (InsertInArray(DataSource,0,DataSourceRow)) to DataSource
   684                Decrement iMax
   685                Increment iRow_Count
   686                Send Read_Next_Record
   687                Move (Found) to bFound
   688                
   689                If (not(bFindAll) and (bFound) and (iMax<=0 )) Begin
   690                    Move False to bFound
   691                    Move False to bAtTop
   692                End
   693            Loop
   694
   695        End
   696    End_Procedure
   697            
   698    { Visibility=Private }
   699    Function OnReadFirstRecord Returns RowID
   700        Handle hoDD
   701        Integer iFile eFindMode  iOrder
   702        Get Server to hoDD
   703        Get Ordering to iOrder
   704        Get Main_File of hoDD to iFile
   705        Get FindMode FIRST_RECORD to eFindMode
   706        Send Request_Read of hoDD eFindMode iFile  iOrder
   707        Function_Return (GetRowID(iFile))
   708    End_Function
   709    
   710    
   711    { Visibility=Private }
   712    Function OnReadLastRecord Returns RowID
   713        Handle hoDD
   714        Integer iFile eFindMode iOrder
   715        Get Server to hoDD
   716        Get Ordering to iOrder
   717        Get Main_File of hoDD to iFile
   718        Get FindMode LAST_RECORD to eFindMode
   719        Send Request_Read of hoDD eFindMode iFile iOrder
   720        Function_Return (GetRowID(iFile))
   721    End_Function
   722    
   723    { Visibility=Private }
   724    Procedure OnSetTag tDataSourceRow ByRef DataSourceRow
   725        Handle hoDD
   726        Integer iMain
   727        Get Server to hoDD
   728        Get Main_File of hoDD to iMain
   729        Move (GetRowID(iMain)) to DataSourceRow.riID
   730    End_Procedure
   731                
   732    
   733End_Class
   734
   735Class cDbCJGridDataSource is a cCJGridCachedDataSource
   736
   737    Procedure Construct_Object
   738        Forward Send Construct_Object
   739        { Visibility=Private }
   740        Property Boolean pbNotifyingExternalDD False
   741    End_Procedure
   742    
   743    { NoDoc=True }
   744    Function IsSelectedRowNew Returns Boolean
   745        Handle hoServer
   746        Boolean bHasRecord bDDReadMode 
   747        Integer iSel
   748        tDataSourceRow[] Datasource
   749        
   750        Get SelectedRow to iSel
   751        If (iSel=-1) Begin
   752            Function_Return False 
   753        End
   754        Get pbDDReadMode to bDDReadMode
   755        If bDDReadMode Begin
   756            Get AdjustRowForOffset iSel to iSel
   757            Get pDataSource to Datasource
   758            Move (not(Datasource[iSel].bNewRow)) to bHasRecord
   759        End
   760        Else Begin
   761            Get Server to hoServer
   762            Get HasRecord of hoServer to bHasRecord
   763        End
   764        Function_Return (not(bHasRecord))
   765    End_Function
   766
   767    { NoDoc=True }
   768    Function IsSelectedRowChanged Returns Boolean
   769        Handle hoServer
   770        Boolean bChanged bDDReadMode
   771        Get pbDDReadMode to bDDReadMode
   772        If bDDReadMode Begin
   773            // if deferred, we have no way of knowing if the row is changed so we just assum
   774            // it's not. That means that changes are accepted as they are
   775            Function_Return False
   776        End
   777        Get Server to hoServer
   778        Get Should_Save_Row of hoServer to bChanged
   779        Function_Return bChanged
   780    End_Function
   781    
   782    // only can be saved if there is a change
   783    { NoDoc=True }
   784    Function ShouldSaveSelectedRow Returns Boolean
   785        Boolean bChanged
   786        Get IsSelectedRowChanged to bChanged
   787        Function_Return (bChanged)
   788    End_Function
   789
   790    { NoDoc=True }
   791    Function CanSaveRow Returns Boolean
   792        Boolean bReadOnly bDDReadMode
   793        Handle hoServer
   794        Get pbDDReadMode to bDDReadMode
   795        Get Server to hoServer
   796        Get Read_Only_State of hoServer to bReadOnly
   797        Function_Return (not(bDDReadMode) and not(bReadOnly))
   798    End_Function
   799
   800    { NoDoc=True }
   801    Function CanDeleteRow Returns Boolean
   802        Handle hoServer 
   803        Boolean bCanDelete bDDReadMode
   804        Get pbDDReadMode to bDDReadMode
   805        Get Server to hoServer
   806        Get Can_Delete of hoServer to bCanDelete
   807        Function_Return (not(bDDReadMode) and bCanDelete)
   808    End_Function
   809    
   810    { NoDoc=True }
   811    Function CanAddRow Returns Boolean
   812        Boolean bReadOnly //bIsNew
   813        Handle hoServer
   814        Get Server to hoServer
   815        Get Read_Only_State of hoServer to bReadOnly
   816        Function_Return (not(bReadOnly))
   817    End_Function
   818    
   819    { NoDoc=True }
   820    Function SaveSelectedRow Returns Boolean
   821        Handle hoServer
   822        Boolean bCancel bDDReadMode
   823        Get pbDDReadMode to bDDReadMode
   824        If bDDReadMode Begin
   825            // should never get called. Perhaps should be an error
   826            Function_Return True
   827        End
   828        Get Server to hoServer
   829        Send Request_Save of hoServer
   830        Move (Err) to bCancel
   831        Function_Return bCancel
   832    End_Function
   833    
   834    { NoDoc=True }
   835    Function DeleteSelectedRow Returns Boolean
   836        Handle hoServer
   837        Integer iMain iSel
   838        RowID riID
   839        Boolean bCancel bDDReadMode bNotifyingExternalDDOld
   840        Get pbDDReadMode to bDDReadMode
   841        If bDDReadMode Begin
   842            // should never get called. Perhaps should be an error
   843            Function_Return True
   844        End
   845        
   846        Get SelectedRow to iSel 
   847        Get RowTag iSel to riID 
   848        If (IsNullRowID(riID)) Begin
   849            Error DFERR_PROGRAM "DeleteSelectedRow has no selectedrow"
   850            Function_Return True
   851        End
   852        Get Server to hoServer
   853        Get Main_File of hoServer to iMain
   854        
   855        // This find is required to set the internal Done_Array file bits.
   856        // This will trigger a DD refresh and we only want the find to update the row and not the grid.
   857        Get pbNotifyingExternalDD to bNotifyingExternalDDOld
   858        Set pbNotifyingExternalDD to True
   859        Send FindByRowId of hoServer iMain riID
   860        Set pbNotifyingExternalDD to bNotifyingExternalDDOld
   861        
   862        Send Request_Delete of hoServer
   863        Move (Err) to bCancel
   864        Function_Return bCancel
   865    End_Function
   866
   867    { NoDoc=True }
   868    Function DataForCell Integer iRow Integer iColumn Returns String
   869        tDataSourceRow[] DataSource
   870        Integer iSize
   871        String sValue
   872        Get pDataSource to DataSource
   873        Get AdjustRowForOffset iRow to iRow
   874        Move (SizeOfArray(DataSource)) to iSize
   875        If (iRow>=0 and iRow<iSize and (SizeOfArray(DataSource[iRow].sValue)>iColumn)) Begin
   876            Move DataSource[iRow].sValue[iColumn] to sValue
   877        End
   878        Function_Return sValue
   879    End_Function
   880
   881    { NoDoc=True }
   882    Function ValidateSelectedRow Returns Handle
   883        Handle hoServer hoCol
   884        Boolean bError bDDReadMode
   885        Get pbDDReadMode to bDDReadMode
   886        
   887        If bDDReadMode Begin
   888            // should never get called. Perhaps should be an error
   889            Function_Return -1
   890        End       
   891        
   892        Forward Get ValidateSelectedRow to hoCol
   893        If hoCol Begin
   894            Function_Return hoCol
   895        End
   896        
   897        Get Server to hoServer
   898        Get Request_Validate of hoServer to bError
   899        Function_Return (If(bError,-1,0))
   900
   901    End_Function
   902
   903    { NoDoc=True }
   904    Procedure UpdateDataForSelectedRow Integer iColumn String sValue Boolean bUpdateExternalData
   905        tDataSourceRow[] DataSource
   906        Handle[] DataSourceColumnObjects
   907        Integer iRow
   908        Boolean bDDReadMode
   909        String sOldValue
   910        
   911        // never update the DD when deferred
   912        Get pbDDReadMode to bDDReadMode
   913        Get SelectedRow to iRow
   914        If (iRow=-1) Begin
   915            Procedure_Return
   916        End
   917        If (iRow>=0) Begin
   918            Get AdjustRowForOffset iRow to iRow
   919            Get pDataSource to DataSource
   920            Move DataSource[iRow].sValue[iColumn] to sOldValue
   921            Move sValue to DataSource[iRow].sValue[iColumn] 
   922            Set pDataSource to DataSource
   923            Get pDataSourceColumnObjects to DataSourceColumnObjects
   924            
   925            // update DD
   926            If (bUpdateExternalData and not(bDDReadMode)) Begin
   927                Send UpdateDataToExternalSource of DataSourceColumnObjects[iColumn] sValue ufUpdateToDD 0
   928            End
   929            Send NotifySelectedRowChanged of DataSourceColumnObjects[iColumn] sOldValue sValue
   930        End
   931    End_Procedure        
   932        
   933    // this can get called with the Row out of range for the cache
   934    // because rowcount can exceed the cache size
   935    { NoDoc=True }
   936    Procedure SelectRow Integer iRow Boolean bUpdateExternalData
   937        tDataSourceRow[] DataSource
   938        Boolean bOk bNotify
   939        Integer iRowCount iMain
   940        Handle hoDD
   941        
   942        Get RowCount to iRowCount
   943        If (iRow=-1 or (iRow<0) or (iRow>=iRowCount)) Begin
   944            Set piSelectedRow to -1
   945        End
   946        Else Begin
   947            Get pDataSource to DataSource
   948            Set piSelectedRow to iRow
   949            If (bUpdateExternalData) Begin
   950                Get pbNotifyingExternalDD to bNotify
   951                Set pbNotifyingExternalDD to True
   952                Get AdjustRowForOffset iRow to iRow
   953                Send OnNotifyExternalDDofSelect DataSource[iRow]
   954                Set pbNotifyingExternalDD to bNotify
   955            End
   956        End
   957    End_Procedure
   958    
   959    { NoDoc=True }
   960    Procedure ResetSelectedRow
   961        Integer iRow
   962        Get SelectedRow to iRow
   963        Send SelectRow iRow True
   964    End_Procedure
   965
   966    { Visibility=Private }
   967    Procedure OnNotifyExternalDDofSelect tDataSourceRow DataSourceRow
   968        Handle hoDD
   969        Integer iMain
   970        Boolean bDDReadMode
   971        RowID riCurrentDD
   972        Get Server to hoDD
   973        Get Main_File of hoDD to iMain
   974        Get pbDDReadMode to bDDReadMode
   975        If bDDReadMode Begin
   976            // if deferred, we will not read the record. There is no need since we are
   977            // are not displaying the latest changes anyway (refresh would do that and it doesn't).
   978            //Send ReadByRowId of hoDD iMain DataSourceRow.riID    
   979        End
   980        Else Begin
   981            Send FindByRowId of hoDD iMain DataSourceRow.riID    
   982        End
   983    End_Procedure
   984
   985
   986
   987    { NoDoc=True }
   988    Procedure SynchronizeDataForSelectedRow Boolean bPushData Integer iFlags
   989        If bPushData Begin
   990            Send UpdateExternalSourceFromDataSource iFlags
   991        End
   992        Else Begin
   993            Send UpdateDataSourceFromExternalSource iFlags
   994        End
   995    End_Procedure
   996    
   997    // move data from datasource to the DD's global file buffer. 
   998    { Visibility=Private }
   999    Procedure UpdateExternalSourceFromDataSource Integer iFlags
  1000        Handle[] DataSourceColumnObjects
  1001        tDataSourceRow[] DataSource
  1002        Integer iRow  iCol iCols
  1003        String sValue
  1004        Get SelectedRow to iRow
  1005        If (iRow<>-1) Begin
  1006            Get AdjustRowForOffset iRow to iRow
  1007            Get pDataSource to DataSource
  1008            Get pDataSourceColumnObjects to DataSourceColumnObjects
  1009            Move (SizeOfArray(DataSourceColumnObjects)) to iCols
  1010            For iCol from 0 to (iCols-1)
  1011                If (SizeOfArray(DataSource[iRow].sValue)>iCol) Begin
  1012                    Move DataSource[iRow].sValue[iCol] to sValue
  1013                End
  1014                Else Begin
  1015                    Move "" to sValue
  1016                End
  1017                // iFlags contains the file number of the entry update.
  1018                Send UpdateDataToExternalSource of DataSourceColumnObjects[iCol] sValue ufUpdateToBuffer iFlags
  1019            Loop
  1020        End
  1021    End_Procedure
  1022    
  1023    // move data from the DD's global file buffer to the datasource
  1024    // iFlags contains the refresh mode. Done and Cleared array should be set.
  1025    // called as part of a DD refresh 
  1026    { Visibility=Private }
  1027    Procedure UpdateDataSourceFromExternalSource Integer iFlags
  1028        Boolean bChanged
  1029        Integer iRow
  1030        tDataSourceRow[] DataSource
  1031        tDataSourceRow DataSourceRow
  1032        Get SelectedRow to iRow
  1033        If (iRow<>-1) Begin
  1034            Get pDataSource to DataSource
  1035            Get AdjustRowForOffset iRow to iRow
  1036            Move DataSource[iRow] to DataSourceRow
  1037
  1038            Get UpdateDataSourceRowFromExternalSource iFlags (&DataSourceRow) to bChanged
  1039            // only update the array if there is an actual change
  1040            If (bChanged)  Begin
  1041                Move DataSourceRow to DataSource[iRow]
  1042                Set pDataSource to DataSource
  1043            End
  1044        End
  1045    End_Procedure
  1046    
  1047    // update this row from the DDs, the file buffers and the done arrays. Called during a DD operation
  1048    // returns new datasourcerow (byref) and True if there was a change the change test can be used to
  1049    // optimize updating of the datasource array 
  1050    {Visibility=Private }
  1051    Function UpdateDataSourceRowFromExternalSource Integer iFlags tDataSourceRow ByRef DataSourceRow Returns Boolean
  1052        Handle[] DataSourceColumnObjects
  1053        Handle hoServer
  1054        Integer iBindingTable iBindingCol iMain
  1055        Boolean bFound bFoundClear bUseDDDefault bError bIsEQ bChanged
  1056        String sValue sOldValue
  1057        Integer iCol iCols
  1058        RowID riOld
  1059
  1060        Get Server to hoServer
  1061        Get Main_File of hoServer to iMain
  1062        
  1063        is_file_included iMain 1 to bFound // see if main file is in find done array
  1064        is_file_included iMain 0 to bFoundClear // see if main file is in clear done array
  1065        Move (bFound or bFoundClear) to bFound
  1066        If bFound Begin
  1067            // only set rowid if this is a main file update
  1068            Move DataSourceRow.riID to riOld
  1069            Send OnSetTag (&DataSourceRow)
  1070            Move (not(IsSameRowID(riOld,DataSourceRow.riID))) to bChanged
  1071        End
  1072
  1073        If (iFlags=MODE_SAVE and DataSourceRow.bNewRow) Begin
  1074           Move False to DataSourceRow.bNewRow
  1075           Move True to bChanged
  1076        End
  1077
  1078        Get pDataSourceColumnObjects to DataSourceColumnObjects
  1079        Move (SizeOfArray(DataSourceColumnObjects)) to iCols
  1080        For iCol from 0 to (iCols-1)
  1081            
  1082            Move False to bFound
  1083            Move False to bFoundClear
  1084            Move False to bUseDDDefault
  1085            
  1086            // if we have a binding table we only update the column if it 
  1087            // is part of the refresh (cleared or found). If no binding table
  1088            // we assume it is calcuated and we always update
  1089            Get piBindingTable of DataSourceColumnObjects[iCol] to iBindingTable
  1090            Get piBindingColumn of DataSourceColumnObjects[iCol] to iBindingCol
  1091            If iBindingTable Begin
  1092                is_file_included iBindingTable 1 to bFound // see if main file is in find done array
  1093                is_file_included iBindingTable 0 to bFoundClear // see if main file is in clear done array
  1094                Move (bFound or bFoundClear) to bFound
  1095            End
  1096            Else Begin
  1097                Move True to bFound
  1098            End
  1099            
  1100            If (bFound) Begin
  1101                If (bFoundClear) Begin
  1102                    // if the DD's field changed state is true, we must have a default value to use
  1103                    Get File_field_Changed_state of hoServer iBindingTable iBindingCol to bUseDDDefault
  1104                End
  1105                
  1106                If bUseDDDefault Begin
  1107                    Get File_Field_Current_Value of hoServer iBindingTable iBindingCol to sValue
  1108                End
  1109                Else Begin 
  1110                    Get InitialValue of DataSourceColumnObjects[iCol] to sValue
  1111                End
  1112                Move DataSourceRow.sValue[iCol]  to sOldValue
  1113                Send DataTypeEQTest of DataSourceColumnObjects[iCol] DataSourceRow.sValue[iCol] sValue (&bError) (&bIsEQ)
  1114                If (not(bError) and not(bIsEQ)) Begin
  1115                    Move sValue to DataSourceRow.sValue[iCol]
  1116                    Send NotifySelectedRowChanged of DataSourceColumnObjects[iCol] sOldValue sValue
  1117                    Move True to bChanged
  1118                End
  1119            End
  1120        Loop
  1121        Function_Return bChanged
  1122    End_Procedure
  1123    
  1124
  1125    // augmented to clear the DD and get DD defaults as needed
  1126    Function CreateClearedDataSourceRow Returns tDataSourceRow
  1127        tDataSourceRow DataSourceRow
  1128        Handle[] DataSourceColumnObjects
  1129        Integer  iColumns
  1130        Handle hoServer
  1131        Boolean bChanged
  1132        
  1133        Get Server to hoServer
  1134        // if operation_mode is set, this was probably called by Refresh in reaction to another
  1135        // DEO performing a clear. We don't need to clear in this case.
  1136        If (OPERATION_MODE=MODE_WAITING) Begin
  1137            Send Clear of hoServer
  1138        End
  1139        Get pDataSourceColumnObjects to DataSourceColumnObjects
  1140        Move (SizeOfArray(DataSourceColumnObjects)) to iColumns
  1141        Move True to DataSourceRow.bNewRow
  1142        Move (ResizeArray(DataSourceRow.sValue,iColumns)) to DataSourceRow.sValue
  1143        Get UpdateDataSourceRowFromExternalSource MODE_CLEAR (&DataSourceRow) to bChanged
  1144        Function_Return DataSourceRow
  1145
  1146    End_Function
  1147    
  1148
  1149End_Class