Module Table_ds.pkg

     1//************************************************************************
     2//     File Name: table_DS.Pkg
     3// Creation Date: Mon  02-17-1992
     4//     Author(s): John J. Tuohy
     5//
     6// Class: Table_DS <--- Data_List <---Wide_List
     7//
     8// Table_DS class
     9//
    10// Thu  02-06-1992 For now we are not supporting deferred states with tables
    11// Thu  07-02-1992 Changed add row and clear row behavior F5 S+F10
    12// Thu  01-14-1993 reset new-item after delete of row (search for date)
    13// 1/30/93  - Rewritten and brought up to date for DAF style
    14// 01/12/94 - Added No_Create_State (for old table compatability)
    15// 03/18/94 - Modifed Add row and clear to return to column zero after
    16//            a blank row is created.
    17// 07/21/94 - Added Save_row procedure to maintain interface compatability
    18//            with the old DAC tables.
    19// 10/04/94 -JJT Row_Changing is changed to make add mode items insert new
    20//               rows instead of overwriting
    21// 11/07/94 -JJT Row_Changing altered to refind the current_record when
    22//               a find above or below the end of list fails and a blank
    23//               row is not allowed.
    24//05/31/96 - JJT Renamed package and class to table_DS. Table.pkg is now
    25//               used to intelligently apply DD Logic.
    26//************************************************************************/
    27
    28//************************************************************************
    29// Version: 1.0
    30//  02-23-1993 : Created
    31//
    32// Author: John J. Tuohy
    33//
    34// 1. Adds Stop_UI_State Support
    35// 2. Adds Should_Save Property (LS - deleted)
    36// 3. Modifies Changed_State to check Should_save (fixes RT bug) (LS - deleted)
    37// 4. Adds Child_Table_State Property. If TRUE then Child_Entering and
    38//    Child_exiting called during object entry and exit.
    39// 5. Adds Child_Entering Function (null). Intended for augmentation
    40// 6. Add Child_Exiting Function. Attempts to save table line before
    41//    exiting the object.
    42// 7. Modifies request_clear behavior to be more intuitive (02-23-1993)
    43//             F5 = request_Clear     = Undo any changes
    44//      Shift+F10 = add_or_remove_Row = add new row, remove current row
    45// 8. Adds auto-prompt
    46// 9. Adds Validate_all_items_state
    47//10. Adds dependent_item support
    48//11. Add checkbox item support to class
    49//12. Adds movable support (ver 1.1)
    50//13. Added Validate_mode (mixin). Table default to Validate_on_save_Next
    51//14. Added entry_defaults (and better retains) through mixin
    52//
    53// 10-07-1993  Add include file with new exit_function procedure.
    54// 03/11/94    Item 10 and 11 for 1.1
    55// 03/17/94    Item 12 v.1.1
    56// 03/22/94    (LS) send add_row during end_construct_object to prevent
    57//             invalid item number error with dependent_items.
    58// 04/08/94    (LS) v1.1 Added DEO delegation.
    59// 05/02/94    Added Validate_Mode to handle validation (and their errors)
    60//             in a more friendly fashion. Done with mixin class.
    61// 07/18/94    Add Clear_mx mixin for better setting of default values
    62//************************************************************************/
    63//************************************************************************/
    64// 12/22/94 JJT Merge Changes
    65//              Merged the 0 class into entry_form.
    66//              Added DEO delegate logic to request_???? messages.
    67//              Set Deo_delegate_mode to default to DELEGATE_NEVER
    68//
    69// 03/06/95 -JJT 1) Altered Clear, Add_or_remove_row, clear_a_Row and
    70//               request_delete to go to first enterable item (set Item)
    71//               and not column 0. Needed when col1 is displayonly.
    72//               2) Altered Add_or_revmoe_row and Clear_a_row to not remove
    73//               a new unchanged row when it is the only row.
    74//               3) Altered Remove_row so that when the last row is removed
    75//               that the item will attempt to remain in the same
    76//               column if the prior row.
    77// 05/16/95 jjt  Added refresh to set unsorted_state true after a save.
    78//
    79// 05/22/95 JJT Added object_validation to focus support in request_clear_all
    80//              (also see deodlgmx.pkg). Note not added to request_clear
    81//              since this is handled differently in table.
    82// 09/04/95 JJT - Code Clean up (removed dead commented code)
    83//************************************************************************/
    84
    85
    86
    87use Datalist.pkg       // Super class Data_list
    88use Verify.pkg         // Verify Mixin
    89Use FindEdit.pkg       // Find Edit mixin
    90Use Dep_item.pkg       // v1.1 Auto-dependent-item support
    91Use CkBox_Mx.pkg       // v1.1 checkbox mixin support
    92Use DEODlgMx.pkg       // v1.1 DEO delegation support
    93Use Val_MX.pkg         // v1.1 test validate options
    94Use Clear_mx.pkg       // v1.1 new defaults upon clear
    95
    96Register_Function Row_Changed Returns Integer
    97
    98class Table_DS_mixin is a mixin
    99//  New Properties:
   100//
   101//     Allow_Top_Add_State Dflt:False If true then you may cursor up
   102//                                    to the top of the list and an empty
   103//                                    space will open up
   104//
   105//     Allow_Bottom_Add_State Dflt: TRUE If true then you may cursor down
   106//                                    to the end of the list and an empty
   107//                                    space will open up
   108//
   109//     Allow_Insert_Add_State    Dflt: TRUE If true then you may insert a new
   110//                                    row at the current position.
   111//
   112//     No_Create_State Dflt: False    If True you can not add any new row (it
   113//                                    overrides the above three states).
   114
   115//     No_Delete_State Dflt: False    If True deletes are disabled in table
   116//
   117//     Read_Only_State Dflt: False    If true no edits, adds, or deletes
   118//                                    are allowed.
   119//
   120//     Auto_Refresh_State  Dflt:True  If true the the table updates itself
   121//                                    after a save with the exception of
   122//                                    being in add mode.. It will rebuild
   123//                                    after you exit add mode. If False it
   124//                                    will not rebuild until screen is
   125//                                    refreshed
   126//
   127//     Needs_Refresh_State Dflt:False Maintained by system. If true than
   128//                                    a save might have changed the table
   129//                                    order.
   130//
   131  procedure Construct_Object integer img
   132    forward send construct_object img
   133    on_key kADD_MODE send add_or_remove_row    PRIVATE  // same as F5
   134    { Visibility=Private }
   135    { PropertyType=Boolean }
   136    Property Integer Wrapping_State            False
   137    { Category=Behavior }
   138   { PropertyType=Boolean }
   139    Property Integer Allow_Bottom_Add_State    True
   140    { Category=Behavior }
   141    { PropertyType=Boolean }
   142    Property Integer Allow_Top_Add_State       False
   143    { Category=Behavior }
   144    { PropertyType=Boolean }
   145    Property Integer Allow_Insert_Add_State    True
   146    { Category=Data }
   147    { PropertyType=Boolean }
   148    Property Integer No_Create_State           False
   149    { Category=Data }
   150    { PropertyType=Boolean }
   151    Property Integer No_Delete_State           False
   152    { Category=Behavior }
   153    { PropertyType=Boolean }
   154    Property Integer Auto_Regenerate_State     True
   155    { Visibility=Private }
   156    { PropertyType=Boolean }
   157    Property Integer Unsorted_State            False
   158    { Category=Behavior }
   159    { PropertyType=Boolean }
   160    Property Integer Child_Table_State         False
   161    
   162    // used to stop recursive saves
   163    { Visibility=Private }
   164    Property Boolean pbInRowSave              False
   165    
   166    Send define_Verify    //invoke verify support constructor
   167    send define_find_edit //invoke find/edit support constructor
   168    // for grids, begin/end of data do what they always did. Jump to top/botton of data in grid
   169    // If developer wants a key to find first/last record of current file (which may be a parent)
   170    // they should define the key and send find_first/find_last
   171    on_key kBegin_of_Data       SEND Beginning_of_Data  PRIVATE
   172    on_key kEnd_of_Data         SEND End_of_Data        PRIVATE
   173    Send Define_Validate
   174    Send Define_Clear_Defaults
   175    Send Define_CheckBox_Support
   176    Send Define_DEO_Delegate // v1.1
   177
   178    Set Auto_Save_State to True  //tables change default for wrap-around save
   179
   180    // In Tables navigation item validation will only occur on NEXT events
   181    // (tab or enter). Without this, you get all kinds of unwanted item
   182    // validation errors when you navigate - this makes tables much happier
   183    //
   184    Set Validate_Mode to VALIDATE_ON_SAVE_NEXT
   185
   186    // It probably makes most sense to make table NOT delegate any
   187    // of their request_ messages. Tables have a very different behavior
   188    // then item based DEOs. Delegating to a client (most likely case)
   189    // will not do us any good. In rare cases a table might want to
   190    // delegate to another table (with the same server) and in this
   191    // case you can change this property.
   192    //
   193    Set Deo_delegate_mode to DELEGATE_NEVER
   194
   195  end_procedure
   196
   197  IMPORT_CLASS_PROTOCOL VERIFY_Mixin //include Verification support module
   198  IMPORT_CLASS_PROTOCOL FIND_EDIT_Mixin //include finding/editing support module
   199  IMPORT_CLASS_PROTOCOL Validate_Mixin // ver 1.1
   200          // Note: This augments: Next and Validate_Items
   201  IMPORT_CLASS_PROTOCOL DEO_Delegate_Mixin // ver 1.1
   202  IMPORT_CLASS_PROTOCOL DEO_Dependent_item_mixin //v1.1
   203  IMPORT_CLASS_PROTOCOL Clear_Defaults_Mixin // ver 1.1
   204
   205  IMPORT_CLASS_PROTOCOL Entry_CheckBox_Mixin // v1.1
   206  //
   207  // Create the cbox object. This only gets created if required.
   208  // We create this in the class and not the mixin because you seem to
   209  // get odd results if objects are created in mixin procedures
   210  //
   211  { Visibility=Private }
   212  Function Create_Cbox_Object returns Integer // returns ID of object
   213    Integer Obj
   214    Object CBox is a CBox_array   // keep track of list of
   215       Move self to Obj // items which are check_box items
   216    End_Object
   217    Function_return Obj
   218  End_Function
   219
   220
   221  //
   222  // clear inserts a new line and goes to that line
   223  // 03/06/95 JJT - adjust so a clear returns to the first enterable
   224  //                item and not the first column.
   225  { Visibility=Private }
   226  procedure Clear
   227    integer oldDynUpdt base oldChg iRet
   228    if (Line_Display_State(self)) send entry_clear 1
   229    else if (CurrentRowHasRecord(self))  begin
   230      // This has been changed to make sure that an exit and an entry message
   231      // is always sent, even if thee actual item number does not change (which it will not)
   232      if (item_count(self)) ;
   233          get exec_exit (current_item(self)) to iRet
   234      get Dynamic_Update_State to oldDynUpdt
   235      set Dynamic_Update_State to false
   236      get base_item to base  //send insert blank row changed base_item...
   237      send insert_blank_row (current_row(self))
   238      get Changing_State to oldChg
   239      set Changing_State to true
   240      //set current_item to base  // doesn't work if 1st is displayonly
   241      set item to true // Go to first enterable column!
   242      Send Trim_Page
   243      Send entry_display 0 0 // redisplay active (parent) file
   244      set Changing_State to oldChg
   245      set Dynamic_Update_State to oldDynUpdt
   246      get exec_entry (current_item(self)) to iRet
   247    end
   248  end_procedure
   249
   250  // public
   251  procedure Request_Clear
   252    Integer rval
   253    If (Should_delegate_Clear(self)) ;
   254       Delegate send request_clear
   255    Else ;
   256       Get Clear_a_row to rval
   257  end_procedure
   258
   259  // internal
   260  { Visibility=Private }
   261  function validate_range integer from# integer to# returns integer
   262    integer count retval oldcuritem
   263    move 0 to retval
   264    get current_item to oldcuritem
   265    for count from from# to to#
   266      set new_item to count
   267      move (exec_Validate(self,count)) to retval
   268    until retval ne 0
   269    set new_item to oldcuritem
   270    if retval ne 0 function_return count  //return item# that failed
   271    else function_return -1               //-1 means all ok
   272  end_function
   273
   274  // Internal
   275  //  Function: Allow_row_Change - Rets 0 if ok to change
   276  //
   277  //  We want to change row...See if it is allowed. it is allowed if:
   278  //  1. There is no change - no problem
   279  //  2. If changes and auto_save - attempt to make the save
   280  //  3. If changes and not auto_save - verify_data Loss
   281  //
   282  { Visibility=Private }
   283  Function Allow_Row_Change Returns Integer
   284    Integer RetVal
   285    If (Read_Only_State(self)) function_Return // Read only...who cares
   286
   287    if Not (Row_Changed(self)) Function_Return // nothing changed..aok
   288
   289    If (Auto_Save_State(self)) ;  // autosave...attempt to save
   290       Get Row_save to RetVal
   291    Else Begin
   292       Get Verify_Data_Loss to RetVal // not autosave..verify loss
   293       If RetVal eq 0 send Find_RowId (CurrentRowId(self))
   294    End
   295    Function_Return RetVal
   296  End_Function
   297
   298 // internal:
   299 // Remove row. Add another line in the table to replace the missing
   300 //             row. Make sure the current record remains active
   301 //
   302 // 03/06/95 JJT - make sure we stay in the same column when we remove
   303 //                the last row in a table
   304  { Visibility=Private }
   305  Procedure Remove_Row Integer CurRow
   306    Integer rowCount CurItem notAtEnd
   307    RowId riRec
   308    integer eRecordStatus
   309    Get Current_item to CurItem // remember where we started..
   310    send delete_row curRow      // dump the row
   311    //
   312    // if no rows or the last row has a valid record than add a blank row.
   313    // if their already is a blank row we leave it alone
   314    get Row_Count to RowCount
   315    Move (RowCount=0 OR RowHasRecord(self,rowCount-1) ) to NotAtEnd
   316    if (notatend) begin
   317       send add_row
   318       increment rowCount
   319    end
   320    get fill_next_row (rowCount-1) to eRecordStatus // lets add to the table end
   321    // if no record remove that row we added
   322    //If (eRecordStatus<>rsNewAtBottom and notatend) send Delete_Row (rowCount-1)
   323    If ( (eRecordStatus=rsCleared or eRecordStatus=rsNewAtTop or eRecordStatus=rsNewAtBottom) and notatend) send Delete_Row (rowCount-1)
   324    //
   325    // If we removed the last row - move curItem up 1 row.
   326    If (CurItem >= Item_Count(self)) ;
   327       Move (CurItem - Item_limit(self)) to CurItem
   328    Set New_Item to CurItem // reset back to old item
   329    set Row_base_item to (Current_row(self))
   330    Get CurrentRowId to riRec // we will refind the current record
   331    If not (isNullRowId(riRec)) Begin
   332        send Find_RowId riRec
   333    end
   334  End_Procedure
   335
   336  // private. Figure out what the first enterable column is likely to be. We use
   337  // this to figure out if an exit message will need to get sent
   338  { Visibility=Private }
   339  Function FirstEnterableColumn returns integer
   340     integer hProto iCol bShadow
   341     get prototype_object to hProto
   342     for iCol from 0 to (item_count(hProto)-1)
   343        get shadow_state of hProto iCol to bShadow
   344        if not bShadow function_return iCol
   345     loop
   346     function_return -1
   347  end_function
   348
   349
   350  // internal
   351  // Add_or_Remove_row: This is the table clearing handler. If:
   352  //   A. New row / Unchanged:  Delete empty row if this is not the
   353  //                            only row.
   354  //   B. old row / unchanged:  If Allow_Insert_Add_State and not
   355  //                            no_create_state, insert new row and enter.
   356  //   C. New row / changed:    Verify_loss and create emtpy row. Goto 1st
   357  //                            enterable column.
   358  //   D. old row / changed:    Verify_loss and insert row.
   359  //                                                (was: restore to what was)
   360  //
   361  //
   362  // 03/06/95 JJT Altered logic. Case A: If row is new and unchanged
   363  //              and there is only 1 row do nothing.
   364  //              Case C: Goto first enterable column (not col 0)
   365  { Visibility=Private }
   366  procedure add_or_Remove_row
   367    integer dynUpdt Changed iCur iRet
   368    RowId riCurRec
   369    If (read_only_State(self) or no_create_state(self)) Procedure_return
   370    Get Row_Changed to Changed
   371    Get CurrentRowId to riCurRec
   372    Get dynamic_update_State to dynUpdt
   373    Set dynamic_update_State to false
   374    If not Changed begin
   375       If (isNullRowId(riCurRec)) Begin      // A. new row-unchanged
   376          // If we only have 1 blank, unchanged row, do not
   377          // remove it (it already is blank).
   378          If (row_Count(self)>1) Begin
   379              Get Current_item to iCur
   380              get exec_exit iCur to iRet // we always want an exit message.
   381              send Remove_Row (Current_Row(self))
   382              if (Unsorted_State(self)) send Display
   383              // if item did not change (very possible) we still want an entry message
   384              if (current_item(self)=iCur) get exec_entry iCur to iRet
   385          end
   386       end
   387       Else ;                    // B. old row-unchanged
   388          if (Allow_Insert_Add_State(self)) send Clear
   389    End
   390    Else Begin // changed...first do verify data loss
   391       If not (Verify_Data_Loss(self)) Begin
   392          If (isNullRowId(riCurRec)) Begin  // C. new row-changed
   393             // Check to see if we are likely to switch columns when we clear the row. If we are
   394             // not we still want an exit message so we must provide it ourselves
   395             Get Current_item to iCur
   396             If (FirstEnterableColumn(self)=Column(self,iCur)) ;  // if first enterable column is where we
   397                 get exec_exit iCur to iRet                       // are, we still an exit message.
   398             Send Clear_Row (Current_Row(self))
   399             Set Item to TRUE // move to first enterable column!
   400             // if item did not change (very possible) we still want an entry message
   401             if (current_item(self)=iCur) get exec_entry iCur to iRet
   402          End
   403          Else begin            // D. old row-changed
   404             send Find_RowId riCurRec // restore current record
   405             //added 12/30/93 08:45 am
   406             if (Allow_Insert_Add_State(self)) send Clear // and add new
   407          End
   408       End
   409    End
   410    set dynamic_update_State to dynUpdt
   411  end_procedure
   412
   413  // internal
   414  // Clear_a_row: This is the table clearing handler. If:
   415  //   A. New row / Unchanged:  Delete empty row (should it be ignore?)_
   416  //                            Only do this, if this is not the only row.
   417  //   B. old row / unchanged:  ignore
   418  //   C. New row / changed:    verify_loss and create emtpy row, goto 1st
   419  //                            enterable column
   420  //   D. old row / changed:    verify_loss and restore to what was
   421  //
   422  //
   423  // 03/06/95 JJT Altered logic. Case A: If row is new and unchanged
   424  //              and there is  only 1 row do nothing.
   425  //              Case C: Goto first enterable column (not col 0)
   426  { Visibility=Private }
   427  Function Clear_a_row Returns integer
   428    integer dynUpdt  Changed  rVal iCur
   429    RowId riCurRec
   430    If (read_only_State(self)) Procedure_return
   431    Get Row_Changed to Changed
   432    Get CurrentRowId to riCurRec
   433    Get dynamic_update_State to dynUpdt
   434    Set dynamic_update_State to false
   435    If not Changed  begin
   436       If (isNullRowId(riCurRec)) Begin      // A. new row-unchanged
   437          // If we only have 1 blank, unchanged row, do not
   438          // remove it (it already is blank).
   439          if (row_Count(self)>1) Begin
   440             Get Current_item to iCur
   441             get exec_exit iCur to rVal // we always want an exit message.
   442             send Remove_Row (Current_Row(self))
   443             if (Unsorted_State(self)) send Display
   444             // if item did not change (very possible) we still want an entry message
   445             if (current_item(self)=iCur) get exec_entry iCur to rVal
   446          end
   447       end
   448       //Else ;                    // B. old row-unchanged
   449       //   if (Allow_Insert_Add_State(self)) send Clear
   450    End
   451    Else Begin // changed...first do verify data loss
   452       Get Verify_Data_Loss to Rval
   453       If Rval eq 0 begin
   454          If (IsNullRowId(riCurRec)) begin       // C. new row-changed
   455             Get Current_item to iCur
   456             // Check to see if we are likely to switch columns when we clear the row. If we are
   457             // not we still want an exit message so we must provide it ourselves
   458             If (FirstEnterableColumn(self)=Column(self,iCur)) ;  // if first enterable column is where we
   459                 get exec_exit iCur to rVal                       // are, we still an exit message.
   460             Send Clear_Row (Current_Row(self))
   461             set item to TRUE  // go to first enterable column
   462             // if item did not change (very possible) we still want an entry message
   463             if (current_item(self)=iCur) get exec_entry iCur to rVal
   464          end
   465          Else ;                // D. old row-changed
   466             send Find_RowId riCurRec
   467       End
   468    End
   469    If (Row_count(self)=0) Send Append_Blank_row
   470    set dynamic_update_State to dynUpdt
   471    Procedure_Return rVal
   472  end_procedure
   473
   474  //  public message: Add_new_row
   475  //
   476  //  Pass: loc as:  -1=top, 0=current row, 1=bottom
   477  //
   478  //
   479  procedure Add_New_Row Integer iLoc
   480    integer iOpt iCur iCount
   481    If (read_only_State(self)) Procedure_return
   482    if (iLoc=0) Begin // if insert row at current position
   483       // if a new rec with no changes we already have a cleared row..
   484       // so we skip this step. If not add_or_remove_row will do it.
   485       if not ( not(CurrentRowHasrecord(self)) AND ;
   486                not(row_changed(self)) ) send Add_or_Remove_Row
   487    End
   488    Else Begin
   489        If (Allow_Row_Change(self)=0) begin // check is change is legal
   490            // we will attempt to move up or down in the same column. If the current col is noenter
   491            // we will move to first enterable column. If this happens the most likely case is that
   492            // it is item 0 and we've never attempted to find a valid column.
   493            Get Current_item to iCur
   494            Get Item_count   to iCount
   495            If (iCur<iCount) Begin // check in case we have an empty grid.
   496                get item_option iCur NoEnter to iOpt
   497                If iOpt begin           // if noenter
   498                    set item to true    // go to first enterable item
   499                end
   500            End
   501
   502            If (iLoc=-1) begin // add to top
   503                forward send Beginning_of_Data
   504                send up_row
   505            end
   506            else begin    // add to bottom
   507                forward send end_of_Data
   508                send down_row
   509            end
   510        end
   511    End
   512  end_procedure
   513
   514  // Internal
   515  // procedure Child_Wrapping integer direction
   516  //   integer base targetItem
   517  //   send activate
   518  //   get base_item to base
   519  //   if direction ne 0 begin
   520  //     Move (base + item_limit(self)) to targetItem
   521  //     if targetItem ge (item_count(self)) send add_row
   522  //   end
   523  //   else begin
   524  //     Move (base - 1) to targetItem
   525  //     if targetItem lt 0 move 0 to targetItem
   526  //   end
   527  //   set current_item to targetItem
   528  //   procedure_return 1
   529  // end_procedure
   530
   531  { MethodType=Event Visibility=Private Obsolete=True }
   532  procedure Child_Wrapping integer direction integer xorigID
   533    integer targetItem origID
   534
   535    if NUM_ARGUMENTS gt 1 move xorigID to origID
   536    else get focus of desktop to origID
   537    if origID eq 0 move self to origID
   538
   539    send activate
   540
   541    if direction ne 0 begin     //wrapping forward
   542      Move (base_item(self) + item_limit(self)) ;
   543          to targetItem
   544      if targetItem GE (item_count(self)) send add_row
   545
   546      //
   547      // save only when wrapping forward
   548      //
   549      if (auto_Save_State(origID)) send request_Save to origID
   550      else if (auto_save_state(self)) send request_save
   551
   552      set Wrapping_State to true
   553      set current_item to targetItem
   554    end
   555    else set Wrapping_State to true
   556
   557    set item to direction      //sets current_item to first/last enterable item
   558
   559    set Wrapping_State to false
   560    procedure_return 1
   561  end_procedure
   562
   563  // public
   564  // Sun  07-05-1992 Added send Append_Blank_Row to leave an blank line
   565  // 03/06/95 - Move to first enterable item if a blank-row is added.
   566  procedure Request_Delete
   567    integer obj# dynUpdt
   568    If (Should_delegate_delete(self)) ;
   569       Delegate Send Request_Delete
   570    else Begin
   571       if (read_only_State(self)=0 and No_Delete_State(self)=0) Begin
   572          Get Server to Obj#
   573          if (can_Delete(Obj#) AND Verify_Delete(self)=0) Begin
   574             set changed_state to false
   575             Send Row_Delete
   576             [not err] Begin
   577               get dynamic_update_State to dynUpdt
   578               set dynamic_update_State to false
   579               Send Remove_Row (Current_Row(self))
   580               If (Row_count(self)=0) Begin
   581                  Send Append_Blank_row
   582                  Set Item to True // go to first enterable item
   583               End
   584               Set dynamic_update_State to dynUpdt
   585             End
   586         end
   587       end
   588    end
   589  end_procedure
   590
   591  // public
   592  procedure Request_Save
   593    integer curItem retval
   594    If (should_delegate_save(self)) ;
   595       delegate Send Request_Save
   596    else begin
   597       If (Read_Only_State(self)) Procedure_Return
   598       get current_item to curItem
   599       if (exec_validate(self,curItem) <> 0) procedure_return
   600       if (exec_exit(self,curItem) <> 0) procedure_return
   601       if (Row_changed(self)) begin
   602         Get Row_save to Retval
   603         If (Retval=0 AND Auto_regenerate_State(self)) send Display
   604       end
   605    end
   606  end_procedure
   607
   608   // this allows us to save a record without it clearing regardless of
   609   // the Auto_Clear_DEO_State value
   610   //
   611   Procedure Request_Save_No_Clear
   612     integer oldclr
   613     If (should_delegate_save(self)) ;
   614       delegate Send Request_Save_no_Clear
   615     else begin
   616       Get Auto_Clear_DEO_State to OldClr  // whatever it was
   617       Set Auto_Clear_DEO_State to False   // it is now NO!
   618       send request_save                   // do your magic
   619       Set Auto_Clear_DEO_State to OldClr  // back to whatever it was
   620     end
   621   End_procedure
   622
   623  // internal
   624  // Function: Row Changing
   625  //
   626  //   New row / Unchanged:  Delete empty row..then move
   627  //   old row / unchanged:  Move
   628  //   Auto_Save -New row / changed:    Save then move
   629  //   Auto_Save -old row / changed:    Save then move
   630  //  ~Auto_Save -New row / changed:    Verify data loss / empty / move
   631  //  ~Auto_Save -old row / changed:    Verify data loss / restore /move
   632  //
   633  // 10/04/94 -JJT changed to make add mode items insert new rows
   634  //               instead of overwriting
   635  { MethodType=Event Visibility=Private }
   636  function Row_Changing integer from# integer to# returns integer
   637    integer retval lim toRow fromRow rowcount
   638    integer dynUpdt wasNew  ROS NCS
   639    integer bToMoved iOldCurrentItem iOldTo
   640    Integer eRowStatus
   641    Rowid   riRec
   642
   643    get Current_item to iOldCurrentItem
   644
   645    get Item_Limit to lim
   646    get row item from# to fromRow
   647    Move (not(RowHasRecord(self,fromRow))) to wasNew
   648
   649    // check if change allowed....if not return from#
   650    // this might save a new record..so we recheck the rec array
   651    If ( Allow_Row_Change(self) ) Function_Return from#
   652
   653    If (not(RowHasRecord(self,fromRow))) begin
   654      // if a new record..we have approval to remove this row..do so
   655      get dynamic_update_State to dynUpdt
   656      set dynamic_update_State to false
   657      send Remove_row fromRow
   658      set dynamic_update_State to dynUpdt
   659      if to# ge from# Move (to#-lim) to to# // target has moved
   660      // anytime we remove the row, we consider this a new item. We use this to
   661      // determine if an item_entry must be forced. Sometimes the actual item number
   662      // will not change (so entry will not be sent normally). In such a case, we will
   663      // force it.
   664      move 1 to bToMoved
   665    end
   666
   667    get row item to# to toRow
   668    set base_item to (toRow * lim)
   669    set new_item to to#
   670    // this handles auto add. Auto Add=T if Move 1 down and it was a new rec
   671    if ( (toRow - fromRow = 1) AND (wasNew) ) Begin
   672           // 10/04/94 - Changed to make new rows get inserted instead
   673           // of replacing current item. At this point we are in insert mode
   674           // and we've moved to the new row. If this new row has data
   675           // (i.e., its eRowStatus is not -1,-2, or 0) then we need to insert a row,
   676           // else we just need to clear the row. In either case, make sure the
   677           // current_row number does not change (insert changes current row)
   678           If (CurrentRowHasRecord(self)) ;
   679               Send Insert_Blank_row (Current_Row(self)) // existing data
   680           else ;
   681               Send Clear_Row (Current_Row(self)) // row already blank
   682           set base_item to (toRow * lim) // The current row might
   683           set new_item to to#            // change so we reset it here.
   684           Move (NullRowId()) to riRec
   685    End
   686    Else Begin
   687       Get Record_RowId Item toRow to riRec // the new rec number
   688       // if eRowStatus -2 or -1 then we need to go to the database to fill
   689       // in the next record.
   690       If not (IsNullRowId(riRec)) Begin
   691           Send ReadByRowId riRec // read the next record
   692           Move 1 to eRowStatus  // 1 means has a record
   693       End
   694       else Begin
   695           get Fill_next_row toRow to eRowStatus
   696           If (eRowStatus>1) move 1 to eRowStatus // in case fill_next_row still returns a record
   697       end
   698       //
   699       If (eRowStatus=1) Begin
   700          send Display_other_UI // fill out rest of UI
   701          set base_item to (toRow * lim)
   702       End
   703       Else begin
   704         // if top row couldn't add only allow an empty row on top if
   705         // it is allowed
   706         Get Row_Count to rowCount
   707         // if row count is 1 then this is the only row we have and we
   708         // will not delete it.
   709         If (RowCount>1 and eRowStatus=rsNewAtTop AND ;
   710             ( Allow_top_add_State(self)=0 OR  ;
   711               No_Create_State(self) OR ;
   712               Read_Only_State(self) ) ) ;
   713         Begin
   714            Send Delete_Row toRow
   715            Set New_item to to# // added 01-14-1993
   716            // added 11/07/94 - make sure we've got the right record
   717            //                  in the buffer
   718            Send ReadByRowId (CurrentRowId(self))
   719         End
   720         else Begin
   721           // if bottom row couldn't add only allow an empty row on bottom if
   722           // it is allowed
   723           If (RowCount>1 and eRowStatus=rsNewAtBottom AND ;
   724               ( Allow_Bottom_add_State(self)=0 OR ;
   725                 No_Create_State(self) OR ;
   726                 Read_Only_State(self) ) ) ;
   727           begin
   728              Send Delete_Row toRow // (Current_Row(self))
   729              // adjustment stuff since current row no longer exists..
   730              // could probably be optimized
   731              move from# to to#
   732              If (top_item(self)<>0) Set Top_item to 0 // (top-1)
   733              set new_item to to#
   734              // added 11/07/94 - make sure we've got the right record
   735              //                  in the buffer
   736              Send ReadbyRowId (CurrentRowId(self))
   737           end
   738           else Send Clear_Row toRow
   739         End
   740       End
   741    End
   742
   743    get current_item to iOldTo // keep track of current To item, it might change now
   744    If (eRowStatus=1 and Auto_regenerate_State(self) and UnSorted_State(self)) begin
   745        send Display
   746    end
   747    Else Begin
   748       Send Trim_Page
   749    end
   750    get Current_Item to to#
   751    // if trimming changed to#, we mark it (it is a move)
   752    if (to#<>iOldTo) move 1 to bToMoved
   753
   754    // if we are not in column 0 we must validate up to this row
   755    // for all to work properly.
   756    if to# gt (toRow * lim) begin
   757      move (validate_range(self,(toRow * lim),(to# - 1))) to retval
   758      if retval ne -1 function_return retval  //returns item# which failed
   759    end
   760
   761    // This traps cases where moves occur but the final current item ends up
   762    // being in the same position where it started. We still want to force an entry
   763    // message. So if we know a move has occured but it does not look like the item
   764    // actually changed, we will force the message
   765    if (bToMoved and to#=iOldCurrentItem) ;
   766        get exec_entry to# to retval
   767
   768    function_return to#
   769  end_function
   770
   771  // public
   772  procedure Beginning_of_Data
   773    If (Allow_Row_Change(self)=0) ;
   774      forward send beginning_of_data
   775  end_procedure
   776
   777  // public
   778  procedure End_of_Data
   779    If (Allow_Row_Change(self)=0) ;
   780      forward send end_of_data
   781  end_procedure
   782
   783  // public
   784  Procedure Scroll Integer eDirection Integer iNumLines
   785    integer dynUpdt
   786    get dynamic_update_State to dynUpdt
   787    set dynamic_update_State to false
   788    If (Allow_Row_Change(self)=0) begin
   789       // if rec is new and we have more than 1 row..remove the record
   790       If ( Not(CurrentRowHasRecord(self)) AND Row_Count(self)>1 ) begin
   791          // if a new record..we have approval to remove this row..do so
   792          send Remove_row (Current_Row(self))
   793       end
   794       forward send Scroll eDirection iNumLines
   795    end
   796    set dynamic_update_State to dynUpdt
   797  end_procedure
   798
   799
   800  //
   801  // Augment to unset the UnSorted_State Flag
   802  //
   803  { Visibility=Private }
   804  Function Load_Page Integer Row# Returns Integer
   805    Integer Retval
   806    Forward Get Load_Page Row# to Retval
   807    Set UnSorted_State to False
   808    Function_Return Retval
   809  End_Function
   810
   811  // Private:
   812  // Control Mouse down so that it will move into an empty
   813  // row just like down arrow does. Also if pressed anyhwere at the
   814  // bottom of an empty then move to the last valid row.
   815  //
   816  { MethodType=Event  NoDoc=True }
   817  Procedure Mouse_Down Integer iWindowNumber Integer iPosition
   818     integer rCount Row#
   819     If iWindowNumber Gt 0 Begin
   820        Decrement iWindowNumber
   821        Get Row Item iWindowNumber to Row# // zero based
   822        Get Row_Count to rCount     // one based
   823        If Row# GE rCount Begin
   824           If ( rCount=0 OR ;
   825                ( RowHasRecord(self,rcount-1) AND ;
   826                  Allow_Bottom_Add_State(self) AND ;
   827                  No_Create_State(self)=0 ) ) ;
   828           //Begin
   829           //  If (Allow_row_Change(self)) Procedure_Return
   830             Send Add_row
   831           //end
   832           Else Decrement rCount
   833           Move ( rCount*Item_limit(self)+;
   834                     Column(self,iWindowNumber)) to iWindowNumber
   835        End
   836        //
   837        Increment iWindowNumber
   838     End
   839     Forward Send Mouse_down iWindowNumber iPosition
   840  End_Procedure
   841
   842  //-------------------------------------------------------------------
   843  // messages requiring server services
   844  //------------------------------------------------------------------
   845  //
   846
   847  // public
   848  procedure Request_Clear_All
   849    integer srvr# retval foc
   850    If (Should_delegate_Clear(self)) ;
   851       Delegate send request_clear_all
   852    Else Begin
   853        get Server to srvr#
   854        if (Read_Only_State(self)=0 AND ;
   855            Row_Changed(self)) begin
   856          if (Verify_Data_Loss(self) <> 0) ;
   857            procedure_return
   858        end
   859        Get Focus of desktop to Foc
   860        get Object_Item_Validation of Foc to retval
   861        set Object_Item_Validation of Foc to false
   862        if Srvr# ne 0 send clear_all to Srvr#
   863        else send entry_clear_all 0
   864        if (Auto_Top_Panel_State(self)) send beginning_of_panel
   865        set Object_Item_Validation of Foc to retval
   866    end
   867  end_procedure
   868
   869  // Support passing of optinal Item to autofind on. If not passed the
   870  // runtine will figure it out. Always pass the autofind-item. The runtime
   871  // also calls this not passing an item but it figure it out.
   872  { Visibility=Private }
   873  Procedure Entry_Autofind integer eFindMode integer iItem
   874    integer srvr# file# datafile# field# oldDisp oldChg item#
   875    If (Num_Arguments>=2) ;
   876        Move iItem to Item#  // if item passed, use it for autofind
   877    else ;
   878        Get autofind_item to item#
   879    get data_file item item# to datafile#
   880    If DataFile# le 0 Procedure_Return
   881    get data_field item item# to field#
   882    get Server to srvr#
   883    get main_file to file#
   884    get Changing_State to oldChg
   885    set Changing_State to TRUE
   886    if srvr# ne 0 Begin
   887       get line_display_State to oldDisp
   888       if datafile# ne file# set line_display_State to true
   889       //----remove deferred_state support
   890       //send Item_Find to srvr# mode datafile# field# TRUE FALSE ;
   891       //   (Deferred_State(self))
   892       send Item_Find to srvr# eFindMode datafile# field# TRUE FALSE FALSE
   893       set line_display_State to oldDisp
   894    end
   895    Else begin
   896      forward send entry_autofind eFindMode
   897      if datafile# eq file# send display
   898    end
   899    set Changing_State to oldChg
   900  end_procedure
   901
   902  // public
   903  // FindEdit support behavior
   904  //
   905  procedure Request_Find integer mode integer entUpdtFlag
   906    integer dataFile ser# mainfile# dfrdState
   907    If (Should_delegate_find(self)) ;
   908       delegate Send Request_Find mode entUpdtFlag
   909    else begin
   910       get Data_File to dataFile
   911       If (DataFile=0) Procedure_return // if no datafile, skip the find
   912       get Server to ser#
   913       get Main_file to mainfile#
   914       if (Read_Only_State(self) AND mainfile#<>DataFile) ;
   915          Procedure_return
   916       if (ser# <> 0 AND dataFile > 0) begin
   917          get Deferred_State to dfrdState
   918          send Item_Find to ser# mode dataFile ;
   919            (Data_Field(self,CURRENT)) entUpdtFlag TRUE dfrdState
   920          //--Re-add deferred_state support
   921          //send Item_Find to ser# mode dataFile ;
   922          //  (Data_Field(self,CURRENT)) entUpdtFlag TRUE FALSE
   923          [found] if dfrdState begin
   924            if dataFile eq mainfile# send display
   925            else send entry_display 0 0
   926          end
   927       end
   928       else begin
   929         send entry_find mode
   930         [found] Begin
   931           if datafile eq mainfile# send display
   932           else send entry_Display 0 0
   933         end
   934       end
   935    end
   936  end_procedure
   937
   938  // public
   939  // FindEdit support behavior
   940  //
   941  procedure Request_Superfind integer mode
   942    integer obj# file# mainfile#
   943    If (Should_delegate_find(self)) ;
   944       delegate Send Request_SuperFind mode
   945    else begin
   946       get Server to obj#
   947       get data_file to file#
   948       if file# gt 0 begin
   949         get main_file to mainfile#
   950         if obj# ne 0 begin
   951           indicate err false
   952           send Request_SuperFind to obj# mode file# ;
   953               (data_field(self,CURRENT))
   954           [not found] begin
   955             if mode lt 2 error DFERR_FIND_PRIOR_BEG_OF_FILE
   956             else error DFERR_FIND_PAST_END_OF_FILE
   957           end
   958         end
   959         else begin
   960           send entry_superfind mode mainfile#
   961           [found] begin
   962             if file# eq mainfile# send display
   963             else send entry_Display 0 0
   964           end
   965         end
   966       end
   967     End
   968  end_procedure
   969
   970  // internal
   971  { Visibility=Private }
   972  procedure Row_Delete
   973    integer Srvr#
   974    get Server to Srvr#
   975    indicate err false
   976    //----remove deferred_state support
   977    //if (Deferred_State(self)) ;
   978    //   send Request_Assign to Srvr# 0  //0 means main_file of Server
   979    send Request_Delete to Srvr#
   980    // add this because the server does not 0 current_record
   981    // didn't work...sent off refresh Sun  07-05-1992
   982    //send Request_Assign to Srvr# 0  //0 means main_file of Server
   983  end_procedure
   984
   985  // This isbeing marked public because it is the one message that is always sent when a row in a grid is saved
   986  // All other DEOs can use request_save for both sending and augmentation to catch a save. Here we need to
   987  // send request_save but augment row_save (which i scalled by request_save and others).
   988  function Row_save returns integer
   989    Handle hoServer 
   990    Integer iErr iRetVal iMainFile
   991    Boolean bInRowSave
   992    
   993    // 12.1: Added a recursion check for saving. A save validation error could trigger a navigation
   994    // event which could trigger another save. If in a save and we encounter another save, we consider
   995    // this to be a validation error 
   996    Get pbInRowSave to bInRowSave
   997    If bInRowSave Begin
   998        Function_Return 1
   999    End
  1000    
  1001    Set pbInRowSave to True
  1002    Get Server to hoServer
  1003    Get Request_Validate of hoServer to iRetVal
  1004    If (iRetVal<>0) Begin
  1005        Move 1 to iErr
  1006    End
  1007    Else Begin
  1008        If (Verify_Save(Self) = 0) Begin
  1009            Send Save_Row // we do this for backwards interface reasons
  1010            Move (If((Err),3,0)) to iErr
  1011        End
  1012        Else Begin
  1013            Move 2 to iErr
  1014        End
  1015    End
  1016    Set pbInRowSave to False
  1017    Function_Return iErr
  1018  End_Function
  1019
  1020  // internal
  1021  // This exists to maintain compatability with the DAC table
  1022  // interface (people might have augmented this). Note: It is better
  1023  // to augment the row_save function because it returns a status value
  1024  { Visibility=Private }
  1025  Procedure Save_row
  1026    Handle hoDD
  1027    integer iRec iMain
  1028    RowId   riNewRec
  1029    get Server to hoDD          // we just assume that a server exists
  1030    indicate err false          // this would not get called if it did
  1031    send Request_Save of hoDD   // not.
  1032    [not err] begin
  1033      Get Main_File to iMain
  1034      // if recnum based table, we will store the recnum in the status array. This is
  1035      // done for backwards compatibility. Developers should only use the status check
  1036      If (IsRecnumTable(self, iMain)) begin
  1037          Get FileRecord to iRec
  1038          set CurrentRecordStatus to iRec
  1039      end
  1040      else begin
  1041          set CurrentRecordStatus to 1 // >0 means has record
  1042      end
  1043
  1044      get FileRowid      to riNewRec
  1045      set currentRowId   to riNewRec
  1046      Set UnSorted_State to True
  1047    end
  1048  end_Procedure
  1049
  1050  // internal
  1051  { Visibility=Private }
  1052  Function Row_Changed Returns Integer
  1053    Integer Srvr# ch
  1054    Get Server to Srvr#
  1055    get changed_state to Ch
  1056    Function_Return (Ch OR ;
  1057                     (Srvr#<>0 AND Should_Save_Row(Srvr#)) )
  1058  End_Function
  1059
  1060  { MethodType=Property }
  1061  Function Should_Save Returns Integer // added 07-24-1992 - could replace row_changed
  1062    // 02-21-1993 should only check fo change if items exist.
  1063    If (Item_Count(self)) ;
  1064       Function_Return (Row_Changed(self))
  1065    //**Function_Return (Row_Changed(self))
  1066  End_function
  1067
  1068  // Child_Entering and Child_Exiting are only called when Child_Table_State is
  1069  // true. When a table is nested inside another DEO (a header / table
  1070  // condition) you have different needs.
  1071
  1072  //  Function Child_Entering
  1073  //
  1074  //  By default this does nothing. If you return a non-zero value the table
  1075  //  will not be entered. Very useful. You will often use this to make sure
  1076  //  that 1) it is valid to enter the table and 2) that the header is
  1077  //  saved before entering the table.
  1078  //
  1079  { MethodType=Event }
  1080  Function Child_Entering Returns Integer
  1081  End_function
  1082
  1083  // Function Child_Exiting
  1084  //
  1085  // You can stop the exiting by returning a non-zero value. By default this
  1086  // save the current record. If the save fails the exiting is stopped. Note
  1087  // that the object ID you are entering is passed. This can be useful to
  1088  // know. If you are exiting to a form or editor that is part of the table
  1089  // (often a child of the table object) you may not want to save the table
  1090  // line. If the object you are entering is part of the header, you would
  1091  // probably want to save the table. Note that the default errs on the side
  1092  // of caution (it always saves the table).
  1093  //
  1094  { MethodType=Event }
  1095  Function Child_Exiting Integer toObj# Returns Integer
  1096     Integer ret_val
  1097     Boolean bInRowSave
  1098     
  1099     // 12.1: if we are in the middle of saving a row, we do nothing which will most likely
  1100     // allow the exit. This could happen if you have a validation error that requires
  1101     // you to change the focus to the object where the validation failed.
  1102     Get pbInRowSave to bInRowSave
  1103     If bInRowSave Begin
  1104        Function_Return 0 
  1105     End
  1106     
  1107     Get Should_Save to Ret_Val
  1108     if Ret_Val Send Request_Save
  1109     Get Should_Save to Ret_Val
  1110     Function_Return Ret_Val
  1111  end_Function
  1112
  1113  // Entering is augmented to call Child_Entering if Child_Table_State=T
  1114  //
  1115  { MethodType=Event }
  1116  Procedure Entering
  1117     Integer ret_val
  1118     integer bChanged
  1119     Boolean bHasRecord
  1120
  1121     // If we are entering a new table (new record, unchanged) then
  1122     // we should clear the row so we trigger any needed defaults from the DD0
  1123     // This fixes a problem where entering a an empty child table does not show
  1124     // the DD defaults.
  1125     Get Row_Changed to bChanged
  1126     Get CurrentRowHasRecord to  bHasRecord
  1127     // we do this here so that this dflts are set before the item_exit message
  1128     // is sent, which is sent in the forward of entering.
  1129     If ( not(bChanged) and Not(bHasRecord) ) ;
  1130        send clear_row (Current_Row(self))
  1131
  1132     if (Child_Table_State(self)) Get Child_Entering to Ret_val
  1133     If ret_Val eq 0 Forward get MSG_Entering to Ret_Val
  1134     Procedure_Return Ret_Val
  1135  end_procedure
  1136
  1137  // Exiting is augmented to call Child_Exiting if Child_Table_State=T
  1138  //
  1139  { MethodType=Event  NoDoc=True }
  1140    Procedure Exiting Handle hoDestination Returns Integer
  1141     Integer ret_val
  1142     if (Child_Table_State(self)) Get Child_Exiting hoDestination to Ret_val
  1143     If ret_Val eq 0 Forward get MSG_Exiting hoDestination to Ret_Val
  1144     Procedure_Return Ret_Val
  1145  end_procedure
  1146
  1147
  1148  // fix for RT bug in dependent_items that causes invalid item number
  1149  // error if items don't exist when update_dependent_items occurs.
  1150  procedure End_Construct_Object
  1151     forward send end_construct_object
  1152     send add_row
  1153  end_procedure
  1154
  1155  // 05/16/95 -Augment so that refreshes that are part of a save sets the sorted
  1156  // state to false. This probably belongs in data_list but this would require
  1157  // moving all of the unsorted_state logic into that class. Not for now.
  1158  //
  1159  { MethodType=Event  NoDoc=True }
  1160  procedure Refresh integer notifyMode
  1161    if (Batch_State(self)) Procedure_return
  1162    Forward Send refresh notifyMode
  1163    // If save on main-file, we are no longer sorted properly
  1164    if ( notifyMode=MODE_SAVE AND ;
  1165         Main_File(self)=Main_file(Server(self)) ) ;
  1166             Set Unsorted_State to TRUE
  1167  end_procedure
  1168
  1169  // disallow if row cannot be changed or it the record is new
  1170  Procedure Reorder_List Integer iNewCol
  1171    RowId riId
  1172    Get currentRowId to riId
  1173    If ( not(IsNullRowId(riId)) And (Read_Only_State(self) or not(Row_Changed(self)))) begin
  1174        if (NUM_ARGUMENTS<1) ;
  1175            Forward send reorder_list
  1176        else ;
  1177            Forward send reorder_list iNewCol
  1178     end
  1179  End_procedure
  1180
  1181  // disallow if row cannot be changed or it the record is new
  1182  // but still set the new ordering
  1183  { Visibility=Private }
  1184  Procedure DoSetOrderingDirection integer bReverse
  1185    RowId riId
  1186    Get currentRowId to riId
  1187    If ( not(IsNullRowId(riId)) And (Read_Only_State(self) or not(Row_Changed(self)))) ;
  1188        forward send DoSetOrderingDirection bReverse
  1189  End_Procedure
  1190
  1191
  1192end_class
  1193