* DATAUPDATE.SCX.SRC
* Form "dataupdate.scx"
* C:\PROGRAM FILES (X86)\ABAQUE\FICSAMPLES\FICTUTO\PROGS\FORMS\DATAUPDATE.SCX.SRC (included in c:\program files (x86)\abaque\ficsamples\fictuto\fictutobs.exe)
* (1,374 lines before localization)
* ========================================================================================================================

PUBLIC odataupdate

SET CLASSLIB TO ab\aw\samples\fic\classe\ficsample.vcx ADDITIVE

odataupdate=NEWOBJECT("dataupdate")
odataupdate.Show
RETURN

  **************************************************
*-- Form: dataupdate (ab\aw\samples\fic\fictuto\progs\forms\dataupdate.scx)
*-- ParentClass: ficfrm (ab\aw\samples\fic\classe\ficsample.vcx)
*-- BaseClass: form
*-- Time Stamp: 12/05/20 09:50:14 PM

#INCLUDE "ab\ab.h"
DEFINE CLASS dataupdate AS ficfrm

  Height = 450
  Width = 811
  DoCreate = .T.
  Caption = "Data Update"
  MaxWidth = 1000
  MinWidth = 780
  BackColor = RGB(240,240,240)
  _memberdata = [<VFPData><memberdata name="sourcetype" display="sourceType"/><memberdata name="grctxtvalid" display="grcTxtValid"/><memberdata name="cursorrefresh" display="cursorRefresh"/><memberdata name="fetchiscomplete" display="FetchIsComplete"/><memberdata name="grdrecordsourceset" display="grdRecordSourceSet"/></VFPData>]
  wbswidth = 2
  Name = "DataUpdate"
  cntOK.opgStyle.optClassic.Value = 1
  cntOK.opgStyle.optClassic.Left = 0
  cntOK.opgStyle.optClassic.Top = 0
  cntOK.opgStyle.optClassic.Name = "optClassic"
  cntOK.opgStyle.optBS.Left = 0
  cntOK.opgStyle.optBS.Top = 30
  cntOK.opgStyle.optBS.Name = "optBS"
  cntOK.opgStyle.Left = 10
  cntOK.opgStyle.Top = 10
  cntOK.opgStyle.ZOrderSet = 0
  cntOK.opgStyle.Name = "opgStyle"
  cntOK.cmdOK.Top = 6
  cntOK.cmdOK.Left = 141
  cntOK.cmdOK.ZOrderSet = 2
  cntOK.cmdOK.Name = "cmdOK"
  cntOK.imgSrce.Left = 105
  cntOK.imgSrce.Top = 45
  cntOK.imgSrce.ZOrderSet = 1
  cntOK.imgSrce.Name = "imgSrce"
  cntOK.imgHelp.Left = 152
  cntOK.imgHelp.Top = 49
  cntOK.imgHelp.ZOrderSet = 3
  cntOK.imgHelp.Name = "imgHelp"
  cntOK.Anchor = 9
  cntOK.Top = 5
  cntOK.Left = 615
  cntOK.Width = 185
  cntOK.Height = 85
  cntOK.ZOrderSet = 3
  cntOK.Name = "cntOK"
  waitpic.ZOrderSet = 5
  waitpic.Name = "waitpic"
  lblFiC.Left = 15
  lblFiC.Top = 430
  lblFiC.ZOrderSet = 4
  lblFiC.Name = "lblFiC"
  lblTime.Height = 15
  lblTime.Left = 375
  lblTime.Top = 430
  lblTime.Width = 423
  lblTime.ZOrderSet = 15
  lblTime.Name = "lblTime"

  ADD OBJECT lblresult AS ficlbl WITH ;
    FontBold = .T., ;
    FontName = "Consolas", ;
    Anchor = 11, ;
    WordWrap = .T., ;
    Caption = "Data update result", ;
    Height = 30, ;
    Left = 473, ;
    Top = 105, ;
    Width = 322, ;
    ZOrderSet = 0, ;
    wcpropsave = "Caption,ToolTipText, ForeColor, Visible", ;
    wbsllinked = .NULL., ;
    wbsctag = "h4", ;
    wcssclassadd = "text-info", ;
    Name = "lblResult"

  ADD OBJECT opgbuffering AS ficopg WITH ;
    ButtonCount = 3, ;
    Anchor = 41, ;
    BorderStyle = 0, ;
    Height = 34, ;
    Left = 325, ;
    Top = 22, ;
    Width = 259, ;
    ZOrderSet = 1, ;
    ToolTipText = ['CursorGetProp("Buffering", thisForm.Grid.RecordSource)'], ;
    Name = "opgBuffering", ;
    Ficopt1.FontName = "Consolas", ;
    Ficopt1.FontSize = 11, ;
    Ficopt1.Anchor = 130, ;
    Ficopt1.Caption = "1-None", ;
    Ficopt1.Value = 1, ;
    Ficopt1.Height = 30, ;
    Ficopt1.Left = 0, ;
    Ficopt1.Style = 1, ;
    Ficopt1.Top = 0, ;
    Ficopt1.Width = 83, ;
    Ficopt1.Name = "optNone", ;
    Ficopt2.FontName = "Consolas", ;
    Ficopt2.FontSize = 11, ;
    Ficopt2.Anchor = 160, ;
    Ficopt2.Caption = "3-Row", ;
    Ficopt2.Height = 30, ;
    Ficopt2.Left = 82, ;
    Ficopt2.Style = 1, ;
    Ficopt2.Top = 0, ;
    Ficopt2.Width = 86, ;
    Ficopt2.Name = "optRow", ;
    Ficopt3.FontName = "Consolas", ;
    Ficopt3.FontSize = 11, ;
    Ficopt3.Anchor = 40, ;
    Ficopt3.Caption = "5-Table", ;
    Ficopt3.Value = 0, ;
    Ficopt3.Height = 30, ;
    Ficopt3.Left = 170, ;
    Ficopt3.Style = 1, ;
    Ficopt3.Top = 0, ;
    Ficopt3.Width = 88, ;
    Ficopt3.Name = "optTable"

  ADD OBJECT lblcsp_bf AS ficlbl WITH ;
    FontSize = 8, ;
    Anchor = 41, ;
    Caption = "grid.RecordSource Buffering", ;
    Height = 12, ;
    Left = 325, ;
    Top = 7, ;
    Width = 252, ;
    ForeColor = RGB(64,128,128), ;
    ZOrderSet = 2, ;
    wbsctag = "p", ;
    Name = "lblCSP_BF"

  ADD OBJECT tutoinfoboxfic AS ficebxsrcecode WITH ;
    Anchor = 15, ;
    Height = 247, ;
    Left = 470, ;
    Top = 175, ;
    Width = 327, ;
    ZOrderSet = 6, ;
    Name = "tutoInfoBoxFIC"

  ADD OBJECT grd AS ficgrd WITH ;
    FontName = "Consolas", ;
    FontSize = 9, ;
    Anchor = 7, ;
    AllowRowSizing = .F., ;
    DeleteMark = .F., ;
    Height = 328, ;
    Left = 10, ;
    RowHeight = 20, ;
    Top = 95, ;
    Width = 446, ;
    ZOrderSet = 7, ;
    wlcontentdynamic = .T., ;
    Name = "grd"

  ADD OBJECT opgsourcetype AS ficopg WITH ;
    ButtonCount = 4, ;
    Anchor = 131, ;
    BorderStyle = 0, ;
    Height = 34, ;
    Left = 7, ;
    Top = 22, ;
    Width = 287, ;
    ZOrderSet = 8, ;
    ToolTipText = "thisForm.Grid.RecordSource", ;
    wbsclbllinked = "lblCSP_ST", ;
    Name = "opgSourceType", ;
    Ficopt1.FontName = "Consolas", ;
    Ficopt1.FontSize = 11, ;
    Ficopt1.Anchor = 130, ;
    Ficopt1.Caption = "Table", ;
    Ficopt1.Value = 1, ;
    Ficopt1.Height = 30, ;
    Ficopt1.Left = 1, ;
    Ficopt1.Style = 1, ;
    Ficopt1.ToolTipText = "Local VFP Table: tastrade!customers", ;
    Ficopt1.Top = 0, ;
    Ficopt1.Width = 67, ;
    Ficopt1.Name = "optTable", ;
    Ficopt2.FontName = "Consolas", ;
    Ficopt2.FontSize = 11, ;
    Ficopt2.Anchor = 160, ;
    Ficopt2.Caption = "L View", ;
    Ficopt2.Value = 0, ;
    Ficopt2.Height = 30, ;
    Ficopt2.Left = 69, ;
    Ficopt2.Style = 1, ;
    Ficopt2.ToolTipText = "Local View tastrade!v_customers", ;
    Ficopt2.Top = 0, ;
    Ficopt2.Width = 73, ;
    Ficopt2.Name = "optLView", ;
    Ficopt3.FontName = "Consolas", ;
    Ficopt3.FontSize = 11, ;
    Ficopt3.Anchor = 160, ;
    Ficopt3.Caption = "L CAD", ;
    Ficopt3.Value = 0, ;
    Ficopt3.Height = 30, ;
    Ficopt3.Left = 143, ;
    Ficopt3.Style = 1, ;
    Ficopt3.ToolTipText = "Local cursorAdapter on tastrade!customers", ;
    Ficopt3.Top = 0, ;
    Ficopt3.Width = 67, ;
    Ficopt3.Name = "optLCAD", ;
    Ficopt4.FontName = "Consolas", ;
    Ficopt4.FontSize = 11, ;
    Ficopt4.Anchor = 40, ;
    Ficopt4.Caption = "R View", ;
    Ficopt4.Value = 0, ;
    Ficopt4.Height = 30, ;
    Ficopt4.Left = 211, ;
    Ficopt4.Style = 1, ;
    Ficopt4.ToolTipText = "Remote View on SQLserver Northwind!customers", ;
    Ficopt4.Top = 0, ;
    Ficopt4.Width = 71, ;
    Ficopt4.Name = "optRView"

  ADD OBJECT cmdsave AS ficcmd WITH ;
    Top = 140, ;
    Left = 722, ;
    Height = 30, ;
    Width = 76, ;
    FontName = "Consolas", ;
    FontSize = 11, ;
    Anchor = 40, ;
    Caption = "Save", ;
    ZOrderSet = 9, ;
    Name = "cmdSave"

  ADD OBJECT cmddelete AS ficcmd WITH ;
    Top = 140, ;
    Left = 540, ;
    Height = 30, ;
    Width = 77, ;
    FontName = "Consolas", ;
    FontSize = 11, ;
    Anchor = 160, ;
    Caption = "Delete", ;
    ZOrderSet = 10, ;
    Name = "cmdDelete"

  ADD OBJECT cmdcancel AS ficcmd WITH ;
    Top = 140, ;
    Left = 645, ;
    Height = 30, ;
    Width = 76, ;
    FontName = "Consolas", ;
    FontSize = 11, ;
    Anchor = 160, ;
    Caption = "Cancel", ;
    ZOrderSet = 11, ;
    Name = "cmdCancel"

  ADD OBJECT cmdnew AS ficcmd WITH ;
    Top = 140, ;
    Left = 470, ;
    Height = 30, ;
    Width = 70, ;
    FontName = "Consolas", ;
    FontSize = 11, ;
    Anchor = 130, ;
    Caption = "New", ;
    ZOrderSet = 12, ;
    Name = "cmdNew"

  ADD OBJECT lblrs AS ficlbl WITH ;
    FontBold = .T., ;
    FontName = "Consolas", ;
    Anchor = 3, ;
    Alignment = 2, ;
    Caption = "grid.RecordSource = ...", ;
    Height = 23, ;
    Left = 10, ;
    Top = 65, ;
    Width = 441, ;
    ForeColor = RGB(83,83,255), ;
    ZOrderSet = 13, ;
    wcssclassadd = "text-info", ;
    wbsctag = "", ;
    wbsllinked = .NULL., ;
    Name = "lblRS"

  ADD OBJECT lblcsp_st AS ficlbl WITH ;
    FontSize = 8, ;
    Anchor = 131, ;
    Caption = "grid.RecordSource SourceType", ;
    Height = 12, ;
    Left = 10, ;
    Top = 8, ;
    Width = 277, ;
    ForeColor = RGB(64,128,128), ;
    ZOrderSet = 14, ;
    wbsctag = "p", ;
    Name = "lblCSP_ST"

  ADD OBJECT cmdcboenable AS ficcmd WITH ;
    Top = 385, ;
    Left = 355, ;
    Height = 30, ;
    Width = 85, ;
    FontName = "Consolas", ;
    FontSize = 10, ;
    Anchor = 6, ;
    Caption = "Disable", ;
    ZOrderSet = 16, ;
    wcpropsave = "Enabled,Caption", ;
    Name = "cmdCboEnable"

  *-- CursorGetProp("SourceType", thisForm.grd.RecordSource) as clear text
  PROCEDURE sourcetype
    local sourceType

    sourceType = Iif(Used(thisForm.grd.RecordSource);
      , CursorGetProp("SourceType", thisForm.grd.RecordSource);
      , .null.;
      )

    return ICase(;
      IsNull(m.sourceType),;
        '?',;
      m.sourceType > 100,;
        'LCursorAdapter',;
      m.sourceType % 100 = 1,; && local SQL view
        'LView',;
      m.sourceType % 100 = 2,; && remote SQL view
        'RView',;
      m.sourceType % 100 = 3,;
        'Table',;
        '?';
      )
  ENDPROC

  *-- thisForm.grd.grc*.txt*.Valid() - delegate method
  PROCEDURE grctxtvalid
    return AddProperty(;
       m.thisForm;
      ,'nBufferDirty';
      , nBufferDirty(m.thisForm.grd.recordSource); && nBufferDirty(): modify command abData
      )
  ENDPROC

  *-- refreshes View or CursorAdapter contents
  PROCEDURE cursorrefresh
    LOCAL success as Boolean;
    , sourceType as String;
    , oCAD as ficCAD of ficSample;
    , aa[1];

    sourceType = Lower(thisForm.SourceType())

    do case

    case m.sourceType == Lower('LView')
      success = 1 = thisForm.wViewParmSet(; && FoxInCloud substitute for Requery()
         thisForm.grd.RecordSource; && View Alias
        , 'cCusID'; && parameter name
        , ''; && parameter value
        , .T.; && requery (useful for multiple parameters)
        ) or Empty(AError(aa))

    case m.sourceType == Lower('LCursorAdapter')
      oCAD = GetCursorAdapter(thisForm.grd.RecordSource)
      success = oCAD.CursorRefresh() or Empty(AError(aa))

    otherwise
      success = .T.

    endcase

    if !m.success
      AddProperty(m.thisForm, 'cDataOpe', 'cursor refresh')
      AddProperty(m.thisForm, 'cDataError', m.aa[2])
    endif

    return m.success
  ENDPROC

  *-- grid.RecordSource is not a remote cursor or FetchIsComplete
  PROCEDURE fetchiscomplete
    return !thisForm.sourceType() == 'RView' or CursorGetProp("FetchIsComplete", thisForm.grd.RecordSource)
  ENDPROC

  *-- Set grid.recordSource based on opgSourceType.Value
  PROCEDURE grdrecordsourceset
    lparameters formRefreshNot as Boolean && [.F.] do not refresh form

    LOCAL success as Boolean;
    , iValue as Integer;
    , cAlias_ as String;
    , cAlias as String;
    , lAlias as Boolean;
    , customer_ID as String;
    , lTable as Boolean; && .T.: view > table, .F.: table > view, .null.: no change

    store thisForm.grd.RecordSource to cAlias, cAlias_
    lAlias = Used(m.cAlias)
    customer_ID = Iif(m.lAlias, Evaluate(m.cAlias + '.customer_ID'), '')
    lTable = Upper(m.cAlias) == Upper('Customer_Table') or !m.lAlias
    iValue = thisForm.opgSourceType.Value

    * set new grid.RecordSource
    do case
    case m.iValue = 1 && 'Table'
      cAlias = 'Customer_Table'
      lTable = Iif(m.lTable, .null., .T.)

    case m.iValue = 2 && 'Local View'
      cAlias = 'Customer_LView'
      lTable = Iif(m.lTable, .F., .null.)

    case m.iValue = 3 && 'Local CursorAdapter'
      cAlias = 'Customer_LCAD'
      lTable = Iif(m.lTable, .F., .null.)

    case m.iValue = 4 && 'Remote View'
      cAlias = 'Customer_RView'
      lTable = Iif(m.lTable, .F., .null.)
    endcase

    #IFNDEF PREVIOUS_FIC_VERSION && 2015-10-13 thn testing aw.vcx!wColView support of USE IN views during a user event
    do case
    case Used(m.cAlias)
    case m.cAlias = 'Customer_Table'
      use tastrade!customer in 0 again alias Customer_Table
    case m.cAlias = 'Customer_LView'
      cCusID = ''
      use tastrade!v_customer in 0 again alias Customer_LView
      select Customer_LView
      index on customer_ID tag cust_id candidate
      set order to
    case m.cAlias = 'Customer_LCAD'
      thisForm.dataEnvironment.cursor6.cursorFill
      select Customer_LCAD
      index on customer_ID tag cust_id candidate
      set order to
    case m.cAlias = 'Customer_RView'
      cCusID = ''
      use tastrade!Customer_RView in 0 again alias Customer_RView
    endcase
    #ENDIF

    with thisForm.grd as awgrd of aw.vcx
      .RecordSource = m.cAlias
      for iGrc = 1 to .ColumnCount
        with .Columns(m.iGrc) as ficGrc of tutoSets.prg
          .ControlSource = m.cAlias + '.' + ICase(;
            m.iGrc = 1,;
              'customer_id',;
            m.iGrc = 2,;
              'company_name',;
            m.iGrc = 3,;
              'region_id',;
            m.iGrc = 4,;
              'active',;
            m.iGrc = 5,;
              'contact_title',;
            m.iGrc = 6,;
              'contact_name',;
              '';
            )
          assert !Empty(.ControlSource)

          .Alignment = .Alignment && in desktop mode, with thisForm.grd.recordSource = 'Customer_LCAD', some column align right instead of default automatic

          .DynamicBackColor = Iif(.ReadOnly;
            , '';
            , Textmerge('';
              + [Iif(];
              +   [CursorGetProp('Buffering', '<<m.cAlias>>') = 1]; && no buffering
              +   [ or ];
              +   [<<.ControlSource>> == Oldval('<<JustField(.ControlSource)>>', '<<m.cAlias>>')]; && <<.ControlSource>> == Oldval('<<JustField(.ControlSource)>>', '<<m.cAlias>>') && GetFldState('<<JustField(.ControlSource)>>', '<<m.cAlias>>') = 1
              + [, <<.BackColor>>];
              + [, <<Rgb(255,128,128)>>]; && light red && Rgb(0,255,64) light green
              + [)]);
            )
        endwith
      endfor
    endwith

    * refresh cursor if applicable
    success = thisForm.cursorRefresh()

    * if new grid.RecordSource is not the table, clear buffering on table
    do case
    case !m.success or IsNull(m.lTable)
    case m.lTable
       CursorSetProp("Buffering", m.thisForm.customerTableBuffering, 'Customer_Table') && m.this.customerTableBuffering: see this.Init()
    otherwise
      thisForm.customerTableBuffering = CursorGetProp("Buffering", 'Customer_Table')
      CursorSetProp("Buffering", 1, 'Customer_Table')
    endcase

    * restore Recno(grid.RecordSource)
    select (m.cAlias)
    if m.lAlias
      locate for customer_ID = m.customer_ID
      lAlias = Found()
    endif
    if !m.lAlias
      go top
    endif

    #IF .F. && 2015-10-13 thn testing aw.vcx!wColView support of USE IN views during a user event
    if Upper(m.cAlias) # Upper(m.cAlias_) and Used(m.cAlias_)
      if CursorGetProp("Buffering", m.cAlias_) > 1
        TableRevert(.T., m.cAlias_)
      endif
      use in (m.cAlias_) && alias previously USEd
    endif
    #ENDIF

    if !lTrue(m.formRefreshNot)
      thisForm.Refresh
    endif
  ENDPROC

  PROCEDURE Init
    lparameters tiRS, tiBuf

    DoDefault()
    if m.this.wlInitFirst
      && WEB MODE: form's initial instantiation; code below this block will later execute for each user of this form
      &&
      local aa[1], oCmd as Commandbutton
      if aoClassCont(@m.aa, this, 'Commandbutton') > 0
        for each oCmd in m.aa
          m.oCmd.ResetToDefault('fontSize')
        endfor
      endif
      if thisForm.wBSlHTMLgen
        thisForm.lblCSP_ST.Left = thisForm.opgSourceType.Left
        thisForm.lblCSP_ST.Width = thisForm.opgSourceType.Width
        thisForm.lblCSP_BF.Left = thisForm.opgBuffering.Left
        thisForm.lblCSP_BF.Width = thisForm.opgBuffering.Width
      endif
      this.grdRecordSourceSet()

      return
    endif

    this.opgSourceType.Value = Evl(m.tiRS, 3)
    this.opgSourceType.Valid
    this.opgBuffering.Value = Evl(m.tiBuf, 3)
    this.opgBuffering.Valid

    this.grdRecordSourceSet(.T.) && sets thisForm.grd.RecordSource
  ENDPROC

  PROCEDURE Load
    local result

    result = DoDefault() && call_DoDefault_after_opening_all_views_and_cursorAdapters

    set ansi off && Makes sure that '' matches all customer records
    set deleted on

    && FoxInCloud recommends that grid.RecordSource has a primary or candidate index
    &&

    select Customer_LCAD
    index on customer_ID tag cust_id candidate
    set order to

    select Customer_LView
    index on customer_ID tag cust_id candidate
    set order to

    this.AddProperty('nSQLhandle', SQLconnect('NorthWind')) && , .T.
    wcPropSaveNotEdit(m.this, 'nSQLhandle')
    if m.this.nSQLhandle > -1
      select 0
      use tastrade!customer_RView connstring m.this.nSQLhandle nodata
    *!*    index on customer_ID tag cust_id candidate
    *!*    set order to
      Requery() && pulls all records from the table because of the above index
    endif

    && props for this.lblResult.Refresh_()
    AddProperty(m.thisForm, 'cDataOpe', '')
    AddProperty(m.thisForm, 'nDataRec', 0)
    AddProperty(m.thisForm, 'cDataError', '')
    AddProperty(m.thisForm, 'nBufferDirty', 0)

    && you use AddProperty() or .AddProperty()
    thisForm.AddProperty('customerTableBuffering', CursorGetProp("Buffering", 'customer_table'))

    select 0
    return m.result
  ENDPROC

  PROCEDURE wreadme
    lparameters cLangUser
    cLangUser = Evl(Evl(m.cLangUser, m.this.wcLangUser), cLangUser())

    local result as String

    * remove '.F. && ' whenever a translation is available

    do case
    case m.cLangUser = 'fr' && copy-paste this block to add another language
      text to result noshow flags 1 && pretext 3
    Cet écran illustre les multiples possibilités de mise à jour des données avec FoxInCloud:
    - tous les types de sources de données
    - tous les types de tampons (buffering)
    - saisie des données dans une grille

    Chaque fois que vous changez d'option de données, la grille est rechargée avec les nouvelles données.

    Pour simuler plusieurs utilisateurs simultanée, affichez ce formulaire dans plusieurs navigateurs simultanément (par ex. firefox et chrome) ; vous verrez comment les modifications de données par un utilisateur sont visibles par le ou les autre(s) utilisateur().

    L'
option 'R View' (vue distante) illustre le support du chargement progressif des données avec CursorGetProp("FetchAsNeeded") ; initialement la grille est chargée avec CursorGetProp("FetchSize") enregistrements ; chaque fois que vous déplacez le curseur vers la dernière ligne de la grille, un autre jeu de CursorGetProp("FetchSize") enregistrements se charge jusqu'à CursorGetProp("FetchIsComplete").
    Notez que, le curseur ne pouvant être modifié qu'
une fois tous les enregistrements chargés (CursorGetProp("FetchIsComplete")), la grille reste en lecture seule jusque là.
      endtext

    case .F. && m.cLangUser = 'de' && copy-paste this block to add another language
      text to result noshow flags 1 && pretext 3
      endtext

    case .F. && m.cLangUser = 'es' && copy-paste this block to add another language
      text to result noshow flags 1 && pretext 3
      endtext

    case .F. && m.cLangUser = 'it' && copy-paste this block to add another language
      text to result noshow flags 1 && pretext 3
      endtext

    case .F. && m.cLangUser = 'pt' && copy-paste this block to add another language
      text to result noshow flags 1 && pretext 3
      endtext

    otherwise && default: English
      text to result noshow flags 1 && pretext 3
    This screen shows the various possibilities of updating data with FoxInCloud:
    - All types of data sources
    - All types of buffering methods
    - Updating data in a grid

    Every time you select another data option, the grid gets reloaded with new data.

    To simulate multiple simultaneous users, display this form simultaneously in multiple browsers (eg Firefox and Chrome); you will see how a user data changes are visible to other user(s).

    The 'R View' option (remote view) demonstrates progressive fetching using CursorGetProp("FetchAsNeeded"): initially CursorGetProp("FetchSize") records are loaded, whenever you move to the last visible row in the grid, another set of CursorGetProp("FetchSize") records loads in until CursorGetProp("FetchIsComplete").
    Please note that, as the cursor can be modified only when all records are fetched (CursorGetProp("FetchIsComplete")), grid remains read-only until then.
    endtext
    endcase

    return m.result
  ENDPROC

  PROCEDURE lblresult.refresh_
    && .Refresh_() is the FoxInCloud replacement for .Refresh() code
    && Your code should still call .Refresh(), and your .Refresh() code should move into .Refresh_(), leaving .refresh() EMPTY
    && If your .Refresh() code has parameters, .Refresh_() will accept the same.
    && To get an idea how this works, just take a look at the code in aw.vcx!aw*.Refresh()

    &&
    &&
    &&

    DoDefault()

    this.Visible = CursorGetProp("Buffering", m.thisForm.grd.RecordSource) > 1
    if !m.this.Visible
      return
    endif

    this.ToolTipText = ICase(;
      thisForm.wcLangUser == 'fr', [Résultat de la dernière opération sur les données],; && copy-paste this line to add another language support
       [Result of the last data-related operation]; && default: English
      )

    local nBufferDirty as Integer
    nBufferDirty = nBufferDirty(m.thisForm.grd.RecordSource) && number of modified records in an alias buffer && modify command abData

    DO CASE

    case !thisForm.FetchIsComplete()
      this.Caption = '';
          + cL(m.thisForm.grd.RecordSource);
          + ' ' + ICase(;
            thisForm.wcLangUser == 'fr', [est en lecture seule car son chargement est incomplet],; && copy-paste this line to add another language support
             [is read-only because fetch is incomplete]; && default: English
            );
          + '.'
      this.ForeColor = RGB(64,128,128) && grey

    case m.thisForm.nDataRec > 0

    * setStepOn(lView(m.thisForm.grd.RecordSource))

      store '';
        + cL(m.thisForm.grd.RecordSource);
        + [, ] + m.thisForm.cDataOpe;
        + [ ] + Transform(m.thisForm.nDataRec) + [ rec] + Iif(m.thisForm.nDataRec > 1, 's', '') + [.];
        + [: ] + Iif(Empty(m.thisForm.cDataError), 'success', 'error: ' + m.thisForm.cDataError);
        to this.Caption, this.ToolTipText

      this.ForeColor = Iif(Empty(m.thisForm.cDataError);
        , Rgb(0, 200, 0); && success
        , Rgb(255, 0, 0); && error
        )

      AddProperty(m.thisForm, 'cDataOpe', '')
      AddProperty(m.thisForm, 'nDataRec', 0)
      AddProperty(m.thisForm, 'cDataError', '')

    case m.nBufferDirty > 0

      this.Caption = '';
          + cL(m.thisForm.grd.RecordSource);
          + ' ' + ICase(;
            thisForm.wcLangUser == 'fr', [a],; && copy-paste this line to add another language support
             [has]; && default: English
            );
          + ' ' + Transform(m.nBufferDirty);
          + ' ' + ICase(;
            thisForm.wcLangUser == 'fr', [enr. tamponné],; && copy-paste this line to add another language support
             [buff. record]; && default: English
            ) + Iif(m.nBufferDirty > 1, 's', '');
          + ' ' + ICase(;
            thisForm.wcLangUser == 'fr', [modifié] + Iif(m.nBufferDirty > 1, 's', ''),; && copy-paste this line to add another language support
             [modified]; && default: English
            );
          + [.];

      this.ForeColor = Rgb(255, 64, 64) && light red

    otherwise

      this.Caption = '';
          + cL(m.thisForm.grd.RecordSource);
          + ' ' + ICase(;
            thisForm.wcLangUser == 'fr', [n'a aucun enr. modifié],; && copy-paste this line to add another language support
             [has no record modified]; && default: English
            );
          + [.];

      this.ForeColor = Rgb(0,128,255) && blue

    endcase
  ENDPROC

  PROCEDURE opgbuffering.refresh_
    && .Refresh_() is the FoxInCloud replacement for .Refresh() code
    && Your code should still call .Refresh(), and your .Refresh() code should move into .Refresh_(), leaving .refresh() EMPTY
    && If your .Refresh() code has parameters, .Refresh_() will accept the same.
    && To get an idea how this works, just take a look at the code in aw.vcx!aw*.Refresh()
    &&
    &&
    &&

    local iBuffering

    this.Enabled = thisForm.FetchIsComplete() and !lBufferDirty(thisForm.grd.RecordSource) && modify command abData
    this.SetAll('Enabled', this.Enabled)

    iBuffering = Iif(Used(thisForm.grd.RecordSource);
      , CursorGetProp("Buffering", thisForm.grd.RecordSource);
      , .null.;
      )

    this.Value = ICase(;
      IsNull(m.iBuffering),;
        '?',;
      m.iBuffering = 1,;
        1,;
      m.iBuffering = 3,;
        2,;
      m.iBuffering = 5,;
        3,;
        0;
        )
  ENDPROC

  PROCEDURE opgbuffering.Valid
    if m.thisForm.wlHTMLgen && FoxInCloud Automated Adaptation
      return .T. && Execute this VFP event code on FoxInCloud server
    endif

    local success as Boolean;
    , oException as Exception;

    try
      success = CursorSetProp(;
         "Buffering";
        , ICase(;
          m.this.Value = 1,;
            1,;
          m.this.Value = 2,;
            3,;
          m.this.Value = 3,;
            5,;
            0;
          );
        , m.thisForm.grd.RecordSource;
        )

    catch to oException
      thisForm.wMessageBox(cException(m.oException)) && modify command abDev
    endtry

    thisForm.Refresh

    return m.success
  ENDPROC

  PROCEDURE opgbuffering.optNone.refresh_
    && .Refresh_() is the FoxInCloud replacement for .Refresh() code
    && Your code should still call .Refresh(), and your .Refresh() code should move into .Refresh_(), leaving .refresh() EMPTY
    && If your .Refresh() code has parameters, .Refresh_() will accept the same.
    && To get an idea how this works, just take a look at the code in aw.vcx!aw*.Refresh()
    &&
    &&
    &&

    this.Enabled = Lower(thisForm.SourceType()) == Lower('Table')
  ENDPROC

  PROCEDURE grd.refresh_
    this.ReadOnly = !thisForm.FetchIsComplete()
    this.columns(1).ReadOnly = .T. && customer_ID should remain .ReadOnly
    store this.ReadOnly or thisForm.cmdCboEnable.lReadOnly to;
       this.columns(3).ReadOnly;
      , this.columns(3).ficTxt.ReadOnly;
      , thisForm.cmdCboEnable.lReadOnly
  ENDPROC

  PROCEDURE grd.waftercolchange
    LPARAMETERS nColIndex && see documentation in awGrd.wAfterColwChange()

    do case
    case m.thisForm.wlHTMLgen && FoxInCloud Automated Adaptation
      return .T. && Execute this VFP event code on FoxInCloud server
    case !DoDefault(@m.nColIndex) && very important
      return .F.
    endcase

    thisForm.Refresh
  ENDPROC

  PROCEDURE grd.wafterrowchange
    LPARAMETERS tuRow && see documentation in awGrd.wAfterRowChange()

    do case
    case m.thisForm.wlHTMLgen && FoxInCloud Automated Adaptation
      return .T. && Execute this VFP event code on FoxInCloud server

    case !DoDefault(@m.tuRow) && important
      return .F.
    endcase

    if m.thisForm.nBufferDirty > 0 and Empty(nBufferDirty(m.this.recordSource))
      AddProperty(m.thisForm, 'cDataOpe', 'auto save')
      AddProperty(m.thisForm, 'nDataRec', m.thisForm.nBufferDirty)
      AddProperty(m.thisForm, 'cDataError', '')
      thisForm.nBufferDirty = 0
    endif

    thisForm.Refresh
  ENDPROC

  PROCEDURE grd.Init
    local iGrc as Integer, cGrc as String, cField as String

    with m.this as ficGrd of ficSample

      .ColumnCount = 6
      FOR iGrc = 1 TO .ColumnCount
        with .Columns(m.iGrc) as Column

          #define column_region  3
          #define column_active  4
          #define column_readOnly  6

          cField = 'customer.' + ICase(; && see thisform.opgSourceType.Valid()
            m.iGrc = 1,;
              'customer_ID',;
            m.iGrc = 2,;
              'Company_name',;
            m.iGrc = 3,;
              'Region_ID',;
            m.iGrc = 4,;
              'active',;
            m.iGrc = 5,;
              'Contact_Title',;
            m.iGrc = 6,;
              'Contact_name',;
              '';
            )

          cGrc = ICase(;
            m.iGrc = 1,;
              'ID',;
            m.iGrc = 2,;
              'Company',;
            m.iGrc = 3,;
              'Region',;
            m.iGrc = 4,;
              'active?',;
            m.iGrc = 5,;
              'Title',;
            m.iGrc = 6,;
              'Contact',;
              '';
            )

          .Width = ICase(;
            m.iGrc = 1,;
              060,;
            m.iGrc = 2,;
              220,;
            m.iGrc = 3,;
              100,;
            m.iGrc = 4,;
              50,;
            m.iGrc = 5,;
              180,;
            m.iGrc = 6,;
              150,;
              0;
            )

          * Replace native members by instances of adapted classes
          .Name = 'grc' + cVFPName(m.cGrc)
          .RemoveObject('Text1')
          .AddObject('ficTxt', ICase(;
            m.iGrc = column_region,;
              'ficCbo',; && of ficSample.vcx as Combobox
            m.iGrc = column_readOnly,;
              'ficTxtGrc',; && of ficSample.vcx as Textbox
            m.iGrc = column_active,;
              'ficChk',;
              'ficTxt'; && of ficSample.vcx as Textbox
            ))
          .Sparse = !InList(m.iGrc, column_region, column_active)
          .Alignment = Iif(m.iGrc = column_active, 2, 3)
          if m.iGrc = column_region
            with .ficTxt as combobox
              select region, Cast(region_id as C(5)) from region into array .waRowSource
              for i = 1 to _Tally*2
                .waRowSource[m.i] = Alltrim(.waRowSource[m.i])
              endfor
              .Margin = 1
              .Style = 2 && Drop-down list box
              .DisplayCount = 10
              .BoundColumn = 2
              .BoundTo = .T.
              .RowSourceType = 5 && Array
              .RowSource = 'this.waRowSource'

              store rgb(216, 091, 220); && lilac
                to .BackColor, .Parent.BackColor, .ItemBackColor
              store rgb(255, 255, 255); && white
                to .ForeColor, .ItemForeColor
            endwith
          endif
          .CurrentControl = 'ficTxt'

          .wlContentDynamic = .T. && Instruct FoxInCloud that the contents of this column may change

          .ReadOnly = InList(m.iGrc, 1, column_region) && for test purpose

          .BackColor = ICase(; && m.iGrc = column_region : see above
            .ReadOnly,;
              Rgb(200, 200, 200),; && grayish
            m.iGrc = 2,;
              Rgb(255, 255, 128),; && light yellow
              .BackColor;
            )

          BindEvent(.ficTxt, 'Valid', m.thisForm, 'grcTxtValid') && Iif(m.iGrc = column_active, 'Click', 'Valid')

          .SetAll('FontName', .FontName)
          .SetAll('FontSize', .FontSize)
          .SetAll('Visible', .T.)
          .SetAll('FontBold', .T., .HeaderClass)
          .SetAll('Caption', Evl(DBGetProp(m.cField, 'Field', 'Caption'), m.cGrc), .HeaderClass)

          store Evl(DBGetProp(m.cField, 'Field', 'Comment'), m.cField);
            + CRLF + ICase(;
              m.iGrc = column_region,;
                'Fixed now in Web mode! see dataUpdate.js :-)',;
              m.iGrc = column_readOnly,;
                'read-only if .RecordSource is a table (just for fun and test)',;
              .ReadOnly,;
                'read-only',;
                'editable';
                );
            to .ToolTipText, .ficTxt.ToolTipText

        endwith
      endfor
    endwith
  ENDPROC

  PROCEDURE opgsourcetype.refresh_
    && .Refresh_() is the FoxInCloud replacement for .Refresh() code
    && Your code should still call .Refresh(), and your .Refresh() code should move into .Refresh_(), leaving .refresh() EMPTY
    && If your .Refresh() code has parameters, .Refresh_() will accept the same.
    && To get an idea how this works, just take a look at the code in aw.vcx!aw*.Refresh()
    &&
    &&
    &&

    this.Enabled = !lBufferDirty(thisForm.grd.RecordSource) && modify command abData
  ENDPROC

  PROCEDURE opgsourcetype.Valid
    if m.thisForm.wlHTMLgen && FoxInCloud Automated Adaptation
      return .T. && Execute this VFP event code on FoxInCloud server
    endif

    thisForm.grdRecordSourceSet && set thisForm.grd.RecordSource
  ENDPROC

  PROCEDURE opgsourcetype.Init
    this.Value = 1 && Iif(lDevMode(), 4, 1)
    return DoDefault()
  ENDPROC

  PROCEDURE opgsourcetype.optRView.refresh_
    && .Refresh_() is the FoxInCloud replacement for .Refresh() code
    && Your code should still call .Refresh(), and your .Refresh() code should move into .Refresh_(), leaving .refresh() EMPTY
    && If your .Refresh() code has parameters, .Refresh_() will accept the same.
    && To get an idea how this works, just take a look at the code in aw.vcx!aw*.Refresh()
    &&
    &&
    &&

    this.Enabled = m.thisForm.nSQLhandle > -1
  ENDPROC

  PROCEDURE cmdsave.refresh_
    && .Refresh_() is the FoxInCloud replacement for .Refresh() code
    && Your code should still call .Refresh(), and your .Refresh() code should move into .Refresh_(), leaving .refresh() EMPTY
    && If your .Refresh() code has parameters, .Refresh_() will accept the same.
    && To get an idea how this works, just take a look at the code in aw.vcx!aw*.Refresh()
    &&
    &&
    &&

    this.Enabled = lBufferDirty(thisForm.grd.RecordSource) && modify command abData
    return DoDefault()
  ENDPROC

  PROCEDURE cmdsave.Click
    if m.thisForm.wlHTMLgen && FoxInCloud Automated Adaptation
      return .T. && Execute this VFP event code on FoxInCloud server
    endif

    LOCAL success as Boolean;
    , cAlias as String;
    , customer_ID as String;
    , nBufferDirty as Integer;
    , aa[1];

    cAlias = thisForm.grd.RecordSource
    customer_ID = Evaluate(m.cAlias + '.customer_ID')
    nBufferDirty = nBufferDirty(m.cAlias) && modify command abData

    * ======================================
    success = TableUpdate(2, .T., m.cAlias, aa) && Specifies the name of an array created when nRows = 2 and changes to a record cannot be committed. The array contains a single column containing the record numbers of the records for which changes could not be committed. If you include an array name, you must include either a table or cursor alias cTableAlias or a work area number nWorkArea.
    * ======================================

    if !m.success and luEqual(m.aa, -1) && If an error other than a simple commit error occurs while updating records, the first element of cErrorArray will contain –1, and you can then use AERROR( ) to determine the why the changes could not be committed.
      AError(aa)
    endif

    AddProperty(m.thisForm, 'cDataOpe', 'man. save')
    AddProperty(m.thisForm, 'nDataRec', m.nBufferDirty)
    AddProperty(m.thisForm, 'cDataError', Iif(m.success, '', m.aa[2]))

    if m.success
      success = thisForm.cursorRefresh()
    endif

    if m.success
      select (m.cAlias)
      locate for customer_ID = m.customer_ID && If table buffering is used and multiple records are updated, TableUpdate() moves the record pointer to the last record updated.
      if !Found()
        go top
      endif
    endif

    thisForm.Refresh

    return m.success
  ENDPROC

  PROCEDURE cmddelete.Click
    if m.thisForm.wlHTMLgen && FoxInCloud Automated Adaptation
      return .T. && Execute this VFP event code on FoxInCloud server
    endif

    local customer, customer_ID
    customer = Trim(Evaluate(thisForm.grd.RecordSource + '.company_name'))
    customer_ID = Trim(Evaluate(thisForm.grd.RecordSource + '.customer_ID'))

    thisForm.wMessageBox(;
       'wFormCallBack'; && call-back method &&
      , Textmerge([Delete customer '<<m.customer>>'? (ID <<m.customer_ID>>)]); && message
      , 4+32; && yes-no + question mark
      , 'Attention'; && MessageBox() title &&
      )
  ENDPROC

  PROCEDURE cmddelete.wformcallback
    LPARAMETERS tuUserChoice && User's choice

    if m.tuUserChoice = IDYES && see foxpro.h

      * =====================================
      delete in (m.thisForm.grd.RecordSource)
      * =====================================

      if thisForm.cmdSave.Enabled;
       and InList(CursorGetProp("Buffering", m.thisForm.grd.RecordSource), 2, 3) && row

        thisForm.cmdSave.Click
        go top in (m.thisForm.grd.RecordSource)
      else
        go top in (m.thisForm.grd.RecordSource)
        thisForm.Refresh
      endif
    endif
  ENDPROC

  PROCEDURE cmddelete.refresh_
    this.Enabled = thisForm.FetchIsComplete()
    return DoDefault()
  ENDPROC

  PROCEDURE cmdcancel.refresh_
    && .Refresh_() is the FoxInCloud replacement for .Refresh() code
    && Your code should still call .Refresh(), and your .Refresh() code should move into .Refresh_(), leaving .refresh() EMPTY
    && If your .Refresh() code has parameters, .Refresh_() will accept the same.
    && To get an idea how this works, just take a look at the code in aw.vcx!aw*.Refresh()
    &&
    &&
    &&

    this.Enabled = lBufferDirty(thisForm.grd.RecordSource) && modify command abData

    return DoDefault()
  ENDPROC

  PROCEDURE cmdcancel.Click
    if m.thisForm.wlHTMLgen && FoxInCloud Automated Adaptation
      return .T. && Execute this VFP event code on FoxInCloud server
    endif

    LOCAL success as Boolean;
    , cAlias as String;
    , customer_ID as String;
    , sourceType as String;
    , nBufferDirty as Integer;
    , aa[1];

    cAlias = thisForm.grd.RecordSource
    customer_ID = Evaluate(m.cAlias + '.customer_ID')
    sourceType = thisForm.SourceType()

    * ======================================
    nBufferDirty = TableRevert(.T., m.cAlias)
    success = m.nBufferDirty > 0
    * ======================================

    if !m.success
      AError(aa)
    endif

    AddProperty(m.thisForm, 'cDataOpe', 'cancel')
    AddProperty(m.thisForm, 'nDataRec', m.nBufferDirty)
    AddProperty(m.thisForm, 'cDataError', Iif(m.success, '', m.aa[2]))

    if m.success
      success = thisForm.cursorRefresh()
    endif

    select (m.cAlias)
    locate for customer_ID = m.customer_ID && TableRevert() does not return the record pointer to its original position.
    if !Found()
      go top
    endif

    thisForm.Refresh
  ENDPROC

  PROCEDURE cmdnew.refresh_
    && .Refresh_() is the FoxInCloud replacement for .Refresh() code
    && Your code should still call .Refresh(), and your .Refresh() code should move into .Refresh_(), leaving .refresh() EMPTY
    && If your .Refresh() code has parameters, .Refresh_() will accept the same.
    && To get an idea how this works, just take a look at the code in aw.vcx!aw*.Refresh()
    &&
    &&
    &&

    this.Enabled = !(.T.;
     and m.thisForm.SourceType() == 'Table';
     and CursorGetProp("Buffering", m.thisForm.grd.RecordSource) = 1;
     ) and thisForm.FetchIsComplete()

    return DoDefault()
  ENDPROC

  PROCEDURE cmdnew.Click
    if m.thisForm.wlHTMLgen && FoxInCloud Automated Adaptation
      return .T. && Execute this VFP event code on FoxInCloud server
    endif

    append blank in (thisForm.grd.RecordSource)

    replace in (thisForm.grd.RecordSource);
      customer_ID with Right(Sys(2015), 5);
      company_Name with ICase(;
        thisForm.wcLangUser == 'fr', [z Société],; && copy-paste this line to add another language support
         [z Company]; && default: English
        );

    thisform.Refresh
  ENDPROC

  PROCEDURE lblrs.refresh_
    && .Refresh_() is the FoxInCloud replacement for .Refresh() code
    && Your code should still call .Refresh(), and your .Refresh() code should move into .Refresh_(), leaving .refresh() EMPTY
    && If your .Refresh() code has parameters, .Refresh_() will accept the same.
    && To get an idea how this works, just take a look at the code in aw.vcx!aw*.Refresh()
    &&
    &&
    &&

    DoDefault()

    local iBuffering

    iBuffering = Iif(Used(thisForm.grd.RecordSource);
      , CursorGetProp("Buffering", thisForm.grd.RecordSource);
      , .null.;
      )

    this.Caption = '';
      + [grid.RecordSource = '];
      + thisForm.grd.RecordSource;
      + [': ];
      + Ltrim(Str(RecCount_(thisForm.grd.RecordSource)));
      + [ ] + ICase(;
        thisForm.wcLangUser == 'fr', [enrs],; && copy-paste this line to add another language support
         [recs]; && default: English
        );
      + [, ] + Iif(thisForm.FetchIsComplete();
        , ICase(;
          IsNull(m.iBuffering),;
            '?',;
          m.iBuffering >=4,;
            'table',;
          m.iBuffering >= 2,;
            'row',;
            'no';
            );
          + ' buffering';
        , ICase(;
          thisForm.wcLangUser == 'fr', [lecture seule],; && copy-paste this line to add another language support
           [read-only]; && default: English
          );
        );
      + []
  ENDPROC

  PROCEDURE cmdcboenable.refresh_
    && .Refresh_() is the FoxInCloud replacement for .Refresh() code
    && Your code should still call .Refresh(), and your .Refresh() code should move into .Refresh_(), leaving .refresh() EMPTY
    && If your .Refresh() code has parameters, .Refresh_() will accept the same.
    && To get an idea how this works, just take a look at the code in aw.vcx!aw*.Refresh()
    &&
    &&
    &&

    this.Enabled = !this.Parent.grd.ReadOnly
    this.Caption = 'Read ' + Proper(Iif(thisForm.grd.Columns(3).ReadOnly, 'only', 'write'))

    return DoDefault()
  ENDPROC

  PROCEDURE cmdcboenable.Click
    if thisForm.wlHTMLgen
      return && or return .T.
    endif

    with thisForm.grd.Columns(3) as Column
      store !.ReadOnly to .ReadOnly, .ficTxt.ReadOnly, this.lReadOnly
    endwith

    this.Refresh
  ENDPROC

  PROCEDURE cmdcboenable.Init
    this.Visible = !thisForm.wBSlHTMLgen and .F. && 2018-06-19 thn -- {FiC V 2.27.0-beta.6} added
    AddProperty(this, 'lReadOnly', .T.)
    return DoDefault()
  ENDPROC

ENDDEFINE
*-- EndDefine: dataupdate
**************************************************