Module Dftable.pkg

     1//************************************************************************
     2//
     3// Confidential Trade Secret.
     4// Copyright 1987-1997 Data Access Corporation, Miami FL, USA
     5// All Rights reserved
     6// DataFlex is a registered trademark of Data Access Corporation.
     7//
     8//************************************************************************
     9//
    10// $File name  : DFTable.pkg
    11// $File title : Table (dbGrid) support for VDF
    12// Notice      :
    13// $Author(s)  : John Tuohy
    14//
    15// $Rev History
    16//
    17// 11/6/98  JJT - Fixed but in Assign_DD_Label
    18//  8/13/97 JJT - Set up support for Prompt_button_State
    19// 07/30/97 JJT - Added set current_item check for batch_state
    20// 6/24/97  JJT - Added Auto-label DD support: Auto_label_State. Assign_DD_Label
    21// 01/31/97 JJT - default highlight_row_state to T
    22// 07/23/96 JJT - New Class names
    23// 06/05/96 JJT - Fixed new_item and current_item to check if current_item
    24//                really exists before calling update_focus_field
    25// 06/03/96 JJT - Fixed new_item and current_item to check for extended dso
    26//                before proceeding.
    27// 05/28/96 JJT - Added code list exported description support.
    28// 04/26/96 JJT - Augmented Set Current_item/Item to refresh DD Buffer
    29//                (we need a better way).
    30// 05/01/95 JJT - Modified to support Cell and row highlighting
    31// 03/28/95 JJT - Removed 0 Layer class
    32//                Changed name to dftable.pkg
    33// 08/19/94 JJT - Blended Classes from CB and DAF
    34//************************************************************************
    35
    36
    37// not defined in windows, the aspect for a multi row FORM
    38#REPLACE BASPECT_MULTIFORM |CI135
    39
    40Use Windows.pkg
    41Use Table_ds.pkg
    42Use DFData.pkg
    43Use dfConfrm.pkg
    44Use PrmpBtMx.pkg // need the defines for prompt button modes
    45Use DfDeoFt.pkg
    46
    47
    48
    49// batch_state should never be used in dbGrids, only dbLists.
    50
    51Class dbGrid_ is a DataList
    52    Import_Class_Protocol Table_ds_mixin
    53End_Class
    54
    55//
    56//  dfGrid
    57//
    58
    59
    60
    61
    62Class dbGridDS is a dbGrid_
    63
    64  Procedure Construct_Object
    65     Forward Send Construct_Object No_Image
    66
    67     Set Floating_Menu_Object to Default_dbFloating_Menu_ID
    68
    69     // set default verify messages for table. The most sensible is to
    70     // use no save verify, a line delete verify and parent's data-loss
    71     // Note: these messsages are defined in dfconfrm.pkg
    72     Set Verify_Delete_msg to (RefFunc(Line_Delete_Confirmation))
    73     Set Verify_Save_msg   to (RefFunc(No_Confirmation))
    74
    75     on_key key_F11 send add_or_remove_row      PRIVATE  // same as F5
    76
    77     
    78     Property String Prompt_Button_Value      '>>'
    79     
    80     
    81     Property Integer Prompt_Button_Mode      PB_PromptAuto
    82     Set HighLight_Row_State to True
    83  End_Procedure // Construct_Object
    84
    85  // These two messages are maintained for backwards compatibility. Avoid
    86  // using these. Use Prompt_Button_Mode instead
    87  
    88  Procedure Set Auto_Create_Prompt_Button integer bState
    89        Set Prompt_Button_Mode to (if(bstate,PB_PromptAuto,PB_PromptOff))
    90  End_Procedure
    91
    92  
    93  Function Auto_Create_Prompt_Button Returns integer
    94        Function_Return (Prompt_Button_Mode(self)=PB_PromptAuto)
    95  End_Function
    96
    97  // If any items are checkboxes we also need to set their aspect.
    98  // We can't set these for the protoobject
    99  // because they won't get copied in to the real rows. However, setting
   100  // aspect for the actual table items (even if there are none) works.
   101  // Also, you must set this for every item because setting zero sets all other items.
   102  //
   103  Procedure End_Construct_Object
   104     integer ele i ic
   105     Forward Send END_CONSTRUCT_OBJECT // as you were.
   106     Get prototype_object to ele // get prototype row object
   107     get item_count of ele to ic // number of items in this object
   108     decrement ic                //
   109     For i from 0 to ic
   110         if (checkbox_item_state(ele,i)) Begin  // if item is check
   111             Set Button_aspect item i TO BASPECT_CHECKLIST   // set check look
   112             Set Form_Datatype item i to ASCII_WINDOW
   113         end
   114         else Set Button_aspect item i TO BASPECT_MULTIFORM // set form look
   115     Loop
   116     Send Create_Prompt_Button (Prompt_Button_Mode(self)) // add prompt symbol to headers is needed
   117     //Send Set_up_rows
   118  End_Procedure // END_CONSTRUCT_OBJECT
   119
   120  
   121  Procedure Header_Mouse_Click Integer Item#
   122     Integer iRVal
   123     Boolean bUsePrompt
   124     // if prompt not used, do normal header stuff which supports reversing indexes.
   125     If (prompt_button_mode(self)=PB_PromptOff) ;
   126         Forward Send Header_mouse_Click Item#
   127     else Begin
   128        Get Msg_Activate_Column item# to iRval
   129        // with VDF8 if prompt_button_mode (for headers) is OFF we will not send prompt.
   130        If (not(iRval)) Send Prompt
   131     end
   132  End_Procedure
   133
   134  
   135  Procedure Set Current_Item integer item#
   136    Integer i 
   137    Integer iOldItem iNewItem
   138    If (item# eq Current_item(Self) or batch_state(Self)) ;
   139       Forward Set Current_Item to item#
   140    Else Begin
   141       get dynamic_update_State to i
   142       Set dynamic_update_State to 0
   143       
   144       Get Current_Item to iOldItem
   145       Forward Set Current_Item to item#
   146       Get Current_Item to iNewItem
   147       If (iOldItem<>iNewItem) Begin
   148            // Bug fix that makes sure we did not land in a skipfound field
   149            Send SkipFoundAdjust // see comments in SkipFoundAdjust about this fix
   150       End
   151       
   152       If i eq 1 ;
   153          set dynamic_update_State to 2
   154       Else ;
   155          if i eq 0 Set dynamic_update_State to 0
   156    End
   157  end_procedure
   158
   159  // find the prompt object for the passed prototype obj and item
   160  // this augmented in DD to be a bit more fancy
   161  
   162  Function Ele_Prompt_Object integer ele integer item# returns integer
   163     Function_Return (Prompt_Object(ele,item#))
   164  End_Function
   165
   166  // Create a prompt Buttons. This was created so the process of creating
   167  // a prompt button object can be augmented.
   168  // Although a param is passed, it is ignored here (used in forms). This
   169  // checks all labels if auto-prompt is true, is a trailer prompt marker
   170  // exists (...) and if the column a prompt. If so, it sets the trailer
   171  // prompt marker (if it is not already there).
   172  // To disable this entire process set Prompt_Button_Mode to PB_PromptOff
   173  Procedure Create_Prompt_Button Integer CreateMode // 0=auto, 1=make, 2=remove
   174     integer ele i ic
   175     String sPrVal sLbl
   176     integer PrValLen
   177     Get Prompt_Button_Value to sPrVal
   178//     If (Auto_Create_Prompt_Button(self)=0 OR sPrVal='') ;
   179//        Procedure_Return
   180     If (CreateMode=PB_PromptOff OR sPrVal='') ;
   181         Procedure_Return
   182     Length sPrVal to PrValLen
   183     Get prototype_object to ele // get prototype row object
   184     get item_count of ele to ic // number of items in this object
   185     decrement ic
   186     For i from 0 to ic
   187         If (CreateMode=PB_PromptOn OR ;
   188              (Ele_Prompt_Object(self,ele,i) AND ;
   189               Shadow_State(ele,i)=0 ) ) Begin
   190            Get Header_Label Item i to sLbl
   191            If (sLbl<>'' AND right(sLbl,PrValLen)<>sPrVal) ;
   192               Set Header_Label item i to (sLbl * sPrVal)
   193         End
   194     Loop
   195  End_Procedure // Create_Prompt_Button
   196
   197    // used to create automatic embedded prompt buttons. Called by end_construct_object
   198    
   199    Procedure CreateEmbeddedPrompts
   200        handle  hEle
   201        integer i ic
   202        Get prototype_object to hEle // get prototype row object
   203        get item_count of hEle to ic // number of items in this object
   204        decrement ic
   205        For i from 0 to ic
   206            // only create automatic buttons if there is no button predefined already and the
   207            // column has a prompt object associated with it.
   208            If ( (Form_Button(self,i)=FORM_BUTTON_NONE) AND Ele_Prompt_Object(self,hEle,i) ) Begin
   209                Set Form_Button_Value i To (psEmbeddedButtonValue(self))
   210                Set Form_Button i To FORM_BUTTON_PROMPT
   211            End
   212        Loop
   213    End_Procedure
   214
   215    // changes to improve autofind logic.
   216    
   217    procedure DoAutoFind
   218        Integer bChanged bOn iCur
   219        Get Current_item to iCur
   220        // both changed_state and item_changed_state must be set for an autofind. If a default
   221        // value is set item_changed_state will be true and changed_state will be false. We want to
   222        // ignore these conditions
   223        get item_changed_State item iCur to bChanged
   224        if (bChanged and changed_state(self)) begin
   225            get item_option item iCur AUTOFIND_BIT to bOn
   226            if bOn begin
   227                get item_option item iCur AUTOFIND_GE_BIT to bOn
   228                Send entry_autofind (if(bOn,GE,EQ)) iCur
   229            end
   230        end
   231    end_procedure
   232
   233     // changes to improve autofind logic. Before save do an autofind check for all items in row
   234     
   235     function Row_save  returns integer
   236        Integer iFrom iTo iCrnt iOld iErr
   237        Integer iOldDyn
   238        Boolean bInRowSave
   239
   240        Get pbInRowSave to bInRowSave
   241        If bInRowSave Begin
   242            Function_Return 1
   243        End
   244
   245        Get Current_item to iOld
   246        Get base_item    to iFrom
   247        Get Item_limit   to iTo
   248        Move (iFrom+iTo-1) to iTo
   249        Get Dynamic_Update_state to iOldDyn
   250        Set Dynamic_Update_state to 0 // let's not show item changing
   251        For iCrnt from iFrom to iTo
   252            Set New_Item to iCrnt
   253            Send DoAutoFind
   254        loop
   255        Set New_item to iOld
   256        If iOldDyn ;
   257            Set Dynamic_Update_state to 2 // 2=only paint what's dirty
   258        forward get row_save to iErr
   259        Function_Return iErr
   260    end_function
   261
   262    // changes to improve autofind logic. Do autofind before changing
   263    
   264    Procedure Item_Change Integer iFromItem Integer iToItem returns integer
   265        integer iNewTo
   266        integer iOldCur iNewCur
   267        if (Changing_State(self)) procedure_Return iToItem
   268        // it is possible though unlikely that autofind will change the current item. If this
   269        // happens we might need to recalculate where we should change from and to. This will
   270        // only happen if autofind is on the main file which represents a pretty odd way to use
   271        // tables.
   272        Get Current_item to iOldCur
   273        send DoAutoFind // if autofind is on main file, the entire table can be refreshed and current_item changed!
   274        Get Current_item to iNewCur
   275        If (iOldCur<>iNewCur) Begin // current_item changed. We will adjust iToItem and iFromItem to make the best of this
   276             // if iFromItem was not the old current_item this is a very strange event - Just return
   277             If (iOldCur<>iFromItem) procedure_return iNewCur
   278             Move iNewCur to iFromItem // adjust iFromItem to be the newly changed current_item
   279             // Adjust iToItem  relative to change the in iFromItem. If the new iToItem is out of bounds, do nothing
   280             Move (iToItem - iOldCur + iNewCur) to iToItem
   281             If (iToItem<0 or iToItem>=item_count(self)) procedure_return iNewCur
   282        end
   283        forward get msg_item_change iFromItem iToItem to iNewTo
   284        procedure_return iNewTo
   285    end_Procedure
   286
   287
   288    
   289    // this was added as a bug fix in 12.1. It is possible to navigate into a skipfound item if you
   290    // are navigating from a blank grid row to an existing row. This happens because the runtime skipfound
   291    // logic checks the file buffer's status and this happens before the row navigation. Changing this in runtime
   292    // would be a deep change which could create side-effects. The safer fix here does a check after the item
   293    // change has occurred. It checks to see if the item has landed in an improper skipfound field. If it has it
   294    // tried to get out the field. This should workaround this issue with a minimum of side effects. You will only
   295    // see a change with skipfound fields. 
   296    Procedure SkipFoundAdjust 
   297        Boolean bSkipFound
   298        Integer iNewItem iFile iStat
   299        Get Current_Item to iNewItem
   300        Get item_option iNewItem SkipFound to bSkipFound
   301        If bSkipFound Begin
   302             Get data_file iNewItem to iFile
   303             If (iFile) Begin
   304                Get_Attribute DF_FILE_STATUS of iFile to iStat
   305                If (iStat<>DF_FILE_INACTIVE) Begin
   306                    // if we landed on an active skipfound field, we need to move somewhere else
   307                    Send next // move to valid forward next item 
   308                    If (Current_Item(Self)=iNewItem) Begin
   309                        // in rare case that forward navigate failed, go backwards
   310                        Send Previous
   311                    End
   312                End
   313             End
   314        End
   315    End_Procedure
   316
   317
   318End_Class // DfTable
   319
   320
   321Use DD_Deomx.pkg // mixin support for dd classes
   322use DD_CdDeo.pkg // mixin support for exported descriptions
   323
   324
   325
   326Class dbGridDD is a dbGridDS
   327  Import_Class_Protocol Extended_DEO_Mixin
   328  Import_Class_Protocol Extended_DEO_Status_Help_Mixin
   329  Import_Class_Protocol Code_Description_Mixin
   330  // we are implementing Scan_servers directly in this class to get the embedded buttonF
   331  //Import_Class_Protocol Extended_DEO_Prompt_Mixin
   332
   333  Procedure Construct_Object
   334     Forward Send Construct_Object
   335     Send Define_Code_Description_Mixin
   336  End_Procedure // Construct_Object
   337
   338  
   339  Procedure Set Current_Item Integer Itm
   340     // we must if there are no items. In which case do nothing
   341     If ( Focus(Desktop)=self AND ;
   342          (Current_Item(self)<Item_Count(self)) AND ;
   343          Item_Count(self) AND ;
   344          Extended_deo_State(self) ) ;
   345             Send Update_Focus_Field
   346     Forward Set Current_Item to Itm
   347  End_Procedure // Set Current_Item
   348
   349  
   350  Procedure Set New_Item Integer Itm
   351     // we must if there are no items. In which case do nothing
   352     If ( Focus(Desktop)=self AND ;
   353          Item_Count(self) AND ;
   354          Extended_deo_State(self) ) ;
   355             Send Update_Focus_Field
   356     Forward Set New_Item to Itm
   357  End_Procedure // Set Current_Item
   358
   359    
   360    Function Ele_Prompt_Object integer ele integer item# returns integer
   361        Integer iObj
   362        Integer iDSO
   363        Integer iFile
   364        Integer iField
   365        Forward Get Ele_Prompt_Object ele Item# to iObj
   366        If (iObj=0 AND Extended_DEO_State(self)) Begin
   367            // only check if not combo column (combos don't use prompt lists)
   368            If not (Column_Combo_state(self,Item#) ) begin
   369                Get Server to iDSO
   370                If iDSO Begin
   371                    Get Data_File of ele item Item# to iFile
   372                    If iFile Begin
   373                        Get Data_Field of ele item Item# to iField
   374                        Get File_Field_Prompt_Object of iDSO iFile iField to iObj
   375                        // if checkbox don't show prompts for simple validation tables
   376                        If (iObj=DD_Global_Validation_Prompt_Object and checkbox_item_state(self,Item#)) ;
   377                            move 0 to iObj
   378                    End
   379                End
   380            End
   381        End
   382        Function_Return iObj
   383    End_Function
   384
   385
   386
   387  //************************************************************************//
   388  // Assign_DD_Label                                                        //
   389  // This assigns the DEO's from the DD. This uses short labels and         //
   390  // sets column headers.                                                   //
   391  //************************************************************************//
   392  
   393  Procedure Assign_DD_Label Integer iDSO Integer iFile Integer iField ;
   394       Integer iDEO Integer iItem
   395    String sName
   396    Get File_Field_Label of iDSO iFile iField DD_LABEL_SHORT to sName
   397    Set Header_Label item iItem to sName
   398  End_Procedure
   399
   400  // this fixes grid bug where insert_row loses the current changed value.
   401  // this only happens when you've changed an item in the top row and you press
   402  // the up arrow.
   403  
   404  procedure Up_Row
   405    Integer iCur
   406    get current_item to iCur
   407    // if row 0 and the current item is changed..just set the value to
   408    // itself. Local value bypasses all the DD stuff (which will happen later)
   409    if (current_row(self)=0 AND item_changed_state(self,iCur)) ;
   410        set local_value item iCur to (value(self,iCur))
   411    forward send up_row
   412  End_Procedure
   413
   414  // Hook to set prompt buttons for header and embedded
   415  
   416  Procedure Scan_Servers
   417    Integer iMode
   418    Forward Send Scan_Servers
   419
   420    If Not (Extended_DEO_State(self)) ;
   421       Procedure_Return
   422
   423    Get Prompt_Button_Mode to iMode
   424    If iMode eq PB_PromptAuto ;
   425       Send Create_Prompt_Button iMode
   426    If (pbEmbeddedPrompts(self)) send CreateEmbeddedPrompts
   427  End_Procedure
   428
   429    // notification that an item option has been changed by the DD. This
   430    // change already made to prototype object and must now be made items
   431    // for that column. Note that only the options that matter trigger this
   432    // notification
   433    
   434    Procedure Item_Options_Changed integer iItem
   435        integer i iRows iCols
   436        integer iCur iOptions
   437        get Row_Count  to iRows
   438        Get Item_Limit to iCols
   439        Get Item_Options of (ProtoType_object(self)) iItem to iOptions
   440        For i from 0 to (iRows-1)
   441            Move (i * iCols + iItem) to iCur
   442            Set Item_Options iCur to iOptions
   443            Set Form_Options iCur to iOptions
   444        Loop
   445    end_procedure
   446
   447    // notification that an item mask value has been changed by the DD. This
   448    // change already made to the first form must be made to all other items
   449    // in the same column
   450    
   451    Procedure Item_Mask_Changed integer iItem
   452        integer i iRows iCols
   453        string sMask
   454        get Row_Count  to iRows
   455        Get Item_Limit to iCols
   456        Get Form_Mask iItem to sMask
   457        For i from 1 to (iRows-1)
   458            Set Form_Mask (i * iCols + iItem) to sMask
   459        Loop
   460        set dynamic_update_state to 1 // required to paint changes
   461    end_procedure
   462End_Class
   463
   464Struct tGridComboHelper
   465    boolean   bDataOnly        // internal. Data only no description
   466    integer   eCodeDisplay     // can be accessed via get/set Column_Combo_Code_Display_Mode
   467    string[]  sDataValues      // internal. array of data values
   468End_Struct
   469
   470
   471
   472
   473Class dbGrid Is A dbGridDD
   474
   475    Procedure Construct_Object
   476        forward send Construct_object
   477
   478        
   479        property integer piActiveColumn 0 // use by comboFillItem callback to figure out column
   480
   481        // Array of of helpers. One is set for each active combo column
   482        
   483        Property tGridComboHelper[] pGridComboHelpers
   484
   485    end_procedure
   486
   487    
   488    Function pGridComboHelper integer iColumn returns tGridComboHelper
   489        tGridComboHelper GridComboHelper
   490        tGridComboHelper[] GridComboHelpers
   491        Get pGridComboHelpers to GridComboHelpers
   492        // If this is accessed before the column helper is created, create it,
   493        If (SizeOfArray(GridComboHelpers)<=iColumn) Begin
   494            // if turning on a column combo, intialize the helper struct
   495            Move CB_CODE_DISPLAY_DESCRIPTION to GridComboHelper.eCodeDisplay // default value
   496            Set pGridComboHelper iColumn to GridComboHelper // initializes the struct
   497        End
   498        Else Begin
   499            Move GridComboHelpers[iColumn] to GridComboHelper
   500        End
   501        Function_Return GridComboHelper
   502    end_function
   503
   504    
   505    Procedure Set pGridComboHelper integer iColumn tGridComboHelper GridComboHelper
   506        tGridComboHelper[] GridComboHelpers
   507        Get pGridComboHelpers to GridComboHelpers
   508        Move GridComboHelper  to GridComboHelpers[iColumn]
   509        Set pGridComboHelpers to GridComboHelpers
   510    End_Procedure
   511
   512    // aumgent to clear the datavalues in the helper object
   513    
   514    Procedure Column_Combo_Delete_Data integer iColumn
   515        tGridComboHelper GridComboHelper
   516        forward send Column_Combo_delete_Data iColumn
   517        Get pGridComboHelper iColumn to GridComboHelper
   518        Move (ResizeArray(GridComboHelper.sDataValues,0)) to GridComboHelper.sDataValues
   519        Set pGridComboHelper iColumn to GridComboHelper
   520    End_Procedure
   521
   522// 12.1: helper is added as needed in pGridComboHelper, not here.
   523//    // augment to create and remove a helper object as needed.
   524//    { MethodType=Property  NoDoc=True }
   525//    Procedure Set Column_Combo_State integer iColumn boolean bState
   526//        boolean bOldState
   527//        tGridComboHelper GridComboHelper
   528//        Get Column_Combo_State iColumn to bOldState
   529//        If ( not(bOldState) and bState) begin
   530//            // if turning on a column combo, intialize the helper struct
   531//            Move CB_CODE_DISPLAY_DESCRIPTION to GridComboHelper.eCodeDisplay // default value
   532//            set pGridComboHelper iColumn to GridComboHelper // initializes the struct
   533//        end
   534//        Forward Set Column_Combo_state iColumn to bState
   535//    End_Procedure
   536
   537    
   538    Function Column_Combo_Code_Display_Mode integer iColumn Returns integer
   539        tGridComboHelper GridComboHelper
   540        Get pGridComboHelper iColumn to GridComboHelper
   541        Function_return GridComboHelper.eCodeDisplay
   542    End_Function
   543
   544    
   545    
   546    
   547    
   548    
   549    Procedure Set Column_Combo_Code_Display_Mode integer iColumn integer eVal
   550        tGridComboHelper GridComboHelper
   551        Get pGridComboHelper iColumn to GridComboHelper
   552        Move eVal to GridComboHelper.eCodeDisplay
   553        Set pGridComboHelper iColumn to GridComboHelper
   554    End_Function
   555
   556    // For a column, pass data and return the description
   557    
   558    Function Column_Combo_Data_To_Description integer iColumn String sValue returns String
   559        tGridComboHelper GridComboHelper
   560        integer i iItems
   561
   562        Get pGridComboHelper iColumn to GridComboHelper
   563
   564        If (GridComboHelper.bDataOnly or GridComboHelper.eCodeDisplay=CB_CODE_DISPLAY_CODE) begin
   565            Function_Return sValue
   566        end
   567
   568        // search for the data in our list of data values for this column
   569        Move (SizeOfArray(GridComboHelper.sDataValues)) to iItems
   570        For i From 0 to (iItems-1)
   571            If (GridComboHelper.sDataValues[i]=sValue) Begin
   572                Get Column_Combo_Value iColumn I to sValue
   573                Function_Return sValue
   574            end
   575        end
   576
   577        // else if no match we will use the data as the description
   578        Function_Return sValue
   579    End_function
   580
   581    // for a column, pass description and find the data
   582    
   583    Function Column_Combo_Description_to_Data integer iColumn String sValue returns String
   584        integer iItem
   585        tGridComboHelper GridComboHelper
   586
   587        Get pGridComboHelper iColumn to GridComboHelper
   588
   589        // if data only, just return the data
   590        If (GridComboHelper.bDataOnly or GridComboHelper.eCodeDisplay=CB_CODE_DISPLAY_CODE) begin
   591            Function_Return sValue
   592        end
   593
   594        // some kind of translation is needed. See if we have a match in our combo-list and then find the data value
   595        Get Column_Combo_Find iColumn 0 sValue to iItem
   596        If (iItem >= 0) begin // if value is found
   597            Move GridComboHelper.sDataValues[iItem] to sValue
   598        end
   599        // else if not found, Just return the data (validation can be handled by the dd).
   600
   601        Function_Return sValue
   602    End_Function
   603
   604    // Public augmentation point. Pass current datavalue and all info
   605    // in record buffer. Should return what you want to see on the screen.
   606    // Augment to support various description/data Display formats.
   607    //
   608    
   609    Function Column_Combo_Description_Value integer iColumn integer eMode String sDescVal String sDataVal returns string
   610        If (eMode=CB_CODE_DISPLAY_CODE) begin
   611            Move sDataVal to sDescVal
   612        end
   613        Else If (eMode=CB_CODE_DISPLAY_BOTH) begin
   614            Move (sDataVal * "-" * sDescVal) to sDescVal
   615        end
   616        // else if deisplay description, juyst return the description as is
   617        Function_Return sDescVal
   618    End_Function // Column_Combo_Description_Value
   619
   620
   621
   622    // Message to add Items to a list. Similar to Add_Item except
   623    // it handles an optional second parameter. If no 2nd param the first
   624    // is used in its place. This will normally be used inside of Fill_List.
   625    //
   626    // Send Column_Combo_Add_Item iColumn Descr_Value {Data_Value}
   627    //
   628    Procedure Column_Combo_Add_Item integer iColumn String sDescription String sDat
   629        String  sData
   630        Integer iItem
   631        tGridComboHelper GridComboHelper
   632
   633        // If sDat not passed, use sDescription for both display and database values
   634        Move (If(num_arguments<3, sDescription, sDat)) to sData
   635
   636        Get Column_Combo_item_count iColumn to iItem   // get this before we add the item.
   637
   638        Get pGridComboHelper iColumn to GridComboHelper
   639
   640        // If no items we have a new list. We want to know if it is data only
   641        // or data descr. By default, assume that it is data-only
   642        If (iItem=0) begin
   643            Move True to GridComboHelper.bDataOnly
   644        end
   645
   646        // this lets us convert the description. By default it returns the
   647        // value passed. This allows you to pass a data param that gets
   648        // changed for display purposes.
   649        Get Column_Combo_Description_Value iColumn GridComboHelper.eCodeDisplay sDescription sData to sDescription
   650
   651        Forward send Column_Combo_Add_Item iColumn sDescription            // used by the display list
   652
   653        Move sData to GridComboHelper.sDataValues[iItem]                 // the actual data value
   654
   655        // If description does not match the data we set this flag false
   656        If (sDescription<>sData AND GridComboHelper.bDataOnly ) begin
   657            Move false to GridComboHelper.bDataOnly
   658        end
   659        set pGridComboHelper iColumn to GridComboHelper
   660    End_Procedure
   661
   662
   663    // this is the one the callback calls. We must figure out the column and pass it on
   664    
   665    Procedure ComboFillItem integer iItem string sData string sDescription ;
   666                            integer iFile RowId riId
   667        integer iCol
   668        Get piActiveColumn to iCol
   669        Send ColumnComboFillItem iCol iItem sData sDescription iFile riId
   670    End_Procedure
   671
   672    
   673    Procedure ColumnComboFillItem integer iCol integer iItem string sData string sDescription ;
   674                            integer iFile RowId riId
   675        If (sDescription='') Move sData to sDescription
   676        Send Column_Combo_Add_Item iCol sDescription sData
   677    End_Procedure
   678
   679    
   680    Procedure Column_Combo_Add_Blank_Item integer iCol String Blank_Desc
   681        integer iItems
   682        Get Column_combo_Item_count iCol to iItems
   683        Send ColumnComboFillItem iCol iItems '' Blank_Desc 0 (NullRowId())
   684    End_Procedure
   685
   686    // Fills the list by loading the entire defined data-file
   687    //
   688    
   689    Procedure Column_Combo_Fill_List integer iCol
   690        integer iFile iField eDisplayMode
   691        handle  hoSrvr hoEle hoTable
   692
   693        Send Column_Combo_Delete_Data iCol
   694        Set piActiveColumn to iCol
   695        Get Server to hoSrvr
   696        Get Prototype_object to hoEle
   697        Get Data_File  of hoEle iCol to iFile
   698        Get Data_Field of hoEle iCol to iField
   699        If (hoSrvr AND iFile) Begin
   700            Get Item_Field_Property GET_File_Field_Table_Object iCol to hoTable
   701            // this does a callback of ComboFillItem for each item in the list
   702            Send File_Field_Fill_List of hoSrvr iFile iField Self msg_ComboFillItem
   703            If (hoTable AND not(Column_Combo_Entry_State(self,iCol)) AND Allow_Blank_State(hoTable)) begin
   704                 //Column_Combo_Allow_Undefined_state(Self,iCol)) begin
   705                    Send Column_Combo_Add_Blank_Item iCol DD_BLANK_CODE_DESCRIPTION
   706            end
   707        End
   708    End_Procedure
   709
   710    // augment to update the DD each time this changes
   711    
   712    Procedure Combo_Item_Changed integer iItem
   713        Forward Send combo_item_Changed iItem
   714        Set Item_Field_Current_Value iItem to (Value(Self,iItem)) // this updates the DD
   715    end_Procedure
   716
   717    // If a combo column, convert from description to data and forward
   718    
   719    Procedure Set Item_Field_Current_Value Integer iItem String sValue
   720        integer iCol
   721        Boolean bCombo
   722        Get Column iItem to iCol
   723        Get Column_Combo_State iCol to bCombo
   724        If bCombo begin
   725            Get Column_Combo_Description_to_data iCol sValue to sValue
   726        end
   727        Forward Set Item_Field_Current_Value iItem to sValue
   728    End_Procedure // Set Item_Field_Current_Value
   729
   730    // If a combo column, convert from data to description and forward
   731    
   732    Procedure Item_Value_Changed Integer iItem String sValue
   733        Boolean bCombo
   734        integer iCol
   735        Get Column iItem to iCol
   736        Get Column_Combo_State iCol to bCombo
   737        If bCombo begin
   738            Get Column_Combo_Data_to_Description iCol sValue to sValue
   739        end
   740        Forward Send Item_Value_Changed iItem sValue
   741    End_Procedure
   742
   743
   744    // augment to refresh all dynamic code tables
   745    
   746    Procedure Activate returns integer
   747        integer iRVal iCol iColumns
   748        Handle hoTable
   749        String  sValue
   750        boolean bCol
   751
   752        Forward Get MSG_Activate to iRVal
   753
   754        if not iRVal Begin
   755            Get Item_limit to iColumns
   756            For iCol From 0 to (iColumns-1)
   757                Get Column_Combo_State iCol to bCol
   758                If bCol begin
   759                    Get Item_Field_Property GET_File_Field_Table_Object iCol to hoTable
   760                    If (hoTable AND (Static_State(hoTable)=0 OR Table_Loaded_State(hoTable)=0)) Begin
   761                        Send Column_Combo_Fill_List iCol
   762                    End
   763                End
   764            Loop
   765        End
   766        Procedure_Return iRVal
   767    End_Procedure // Activate
   768
   769    // augment to refill a combo validation table if needed (if the val table is not static)
   770    Function Item_Entry Integer iMsg Integer iItem Returns Integer
   771        Integer iResult iCol
   772        Boolean bCol
   773        Handle hoTable
   774        Forward Get Item_Entry iMsg iItem to iResult
   775        If (iResult=0) Begin
   776            Get Column iItem to iCol
   777            Get Column_Combo_State iCol to bCol
   778            If bCol Begin
   779                Get Item_Field_Property GET_File_Field_Table_Object iCol to hoTable
   780                If (hoTable and (not(Static_State(hoTable)) or not(Table_Loaded_State(hoTable))) ) Begin
   781                    // if a combo column that has a DD validation table that is not statuc or not loaded, fill it now
   782                    Send Column_Combo_Fill_List iCol
   783                End
   784            End
   785        End
   786        Function_Return iResult
   787    End_Function
   788
   789
   790    // This Replaces DD message with one that understands
   791    // description and data difference.    //
   792    
   793    Function Data_Value integer iItem Returns String
   794        string sValue
   795        integer iCol
   796        Boolean bCombo
   797
   798        Get Column iItem to iCol
   799        Get Column_Combo_State iCol to bCombo
   800        If bCombo begin
   801            Get Value iItem to sValue
   802            Get Column_Combo_Description_to_Data iCol sValue to sValue
   803        end
   804        else begin
   805            Forward Get Data_Value iItem to sValue
   806        end
   807        Function_Return sValue
   808    End_Function
   809
   810    // Augmented to handle entry_state=F combos. These type of combos must
   811    // have a default value (since something always appears in the form). So
   812    // we must check for cases where we have a value in the combo that is
   813    // not reflected in the DD buffer. In such a case, update the buffer with
   814    // a default value. Called by entry_defaults
   815    //
   816    
   817    Procedure ColumnComboEntryDefaults integer iItem handle hoDSO integer iFile integer iField
   818        integer iCol
   819        boolean bColCombo bEntry
   820
   821        Get Column iItem to iCol
   822        Get Column_combo_state iCol to bColCombo
   823        If bColCombo begin
   824            get Column_Combo_Entry_State iCol to bEntry
   825            If not bEntry begin
   826                // set default value in DD to the data value of current combo
   827                Set File_Field_Default_Value of hoDSO iFile iField to (Data_Value(Self,iItem))
   828            end
   829        end
   830    End_Procedure
   831
   832
   833    // change so that list is initialized first. Also, set extended_deo_state before
   834    // attempting to fill list (it needs that information).
   835    
   836    Procedure Attach_Deo_To_Server
   837        Handle hoSrvr hoEle
   838        integer iCols iCol iItems
   839        boolean bColCombo bExtended
   840
   841        Get Server to hoSrvr
   842        If hoSrvr begin
   843            Get Extended_DSO_State of hoSrvr to bExtended
   844            Set Extended_DEO_State to bExtended
   845        end
   846        Get ProtoType_object to hoEle
   847        Get Item_Count of hoEle to iCols
   848        For iCol From 0 to (iCols-1)
   849            Get Column_Combo_state iCol to bColCombo
   850            If bColCombo begin
   851                If not bExtended begin
   852                    Error DFERR_PROGRAM "Column Combos in DEO Grids must be attached to a DDO"
   853                    Move iCols to iCol // force exit from for loop
   854                end
   855                Else Begin
   856                    // if the list is empty fill it. This way static lists are only loaded one time
   857                    Get Column_Combo_Item_count iCol to iItems
   858                    If (iItems=0) Send Column_Combo_fill_list iCol
   859                end
   860            end
   861        Loop
   862        Forward Send Attach_deo_to_Server
   863    End_Procedure
   864
   865    // augment to unset capslock if the display mode is not Code. If not code display the data that is displayed is
   866    // not the data being sent to the DD and it should not be capslocked by the DD. Also set a bigger form_margin.
   867    // You'd only need a big form_margin if entry_state is true which would be unlikely with a description combo (but it is supported).
   868    
   869    Procedure Copy_Item_Options Integer iDSO Integer iFile Integer iField Integer iDEO Integer iItem
   870        Integer eDisplayMode
   871        Boolean bCombo bDataOnly
   872        tGridComboHelper GridComboHelper
   873        Forward Send Copy_Item_Options iDSO iFile iField iDEO iItem
   874        If Not (Extended_deo_State(self)) Procedure_Return
   875        Get Column_combo_state iItem to bCombo
   876        If bCombo begin
   877            Get pGridComboHelper iItem to GridComboHelper
   878            Get Column_Combo_Code_Display_mode iItem to eDisplayMode
   879            If (eDisplayMode<>CB_CODE_DISPLAY_CODE and not(GridComboHelper.bDataOnly)) Begin
   880                Set Column_Capslock_State iItem to False // this might be been set true by the forward send of this message
   881                set Form_Margin   iItem to 255 // allow 255 characters which should be enough for any combo
   882                set Form_DataType iItem to ASCII_WINDOW // If display description-- make it a string
   883            end
   884        end
   885    End_Procedure
   886
   887
   888    
   889    Procedure Set Local_Value integer iItem string sVal
   890        integer iCol
   891        Boolean bCombo
   892        Get Column iItem to iCol
   893        Get Column_Combo_State iCol to bCombo
   894        If bCombo begin
   895            get Column_Combo_Data_To_Description iCol sVal to sVal
   896        end
   897        Forward Set Local_Value iItem to sVal
   898    End_Procedure // Set Local_Value
   899
   900    
   901    procedure Prompt
   902        integer iCol
   903        get Current_Col to iCol
   904        If not (Column_Combo_State(self,iCol)) Begin
   905            Forward send Prompt
   906        end
   907    end_procedure
   908
   909     
   910     // 12.1 Fix: We must augment Entry_update to handle column combos where the description is different than the
   911     // data. In such a case, we do not want the internal entry_update ot move data from the column. Instead,
   912     // we will do this manually. If the item should not be updated, we will stop it by setting its state temporarily
   913     // to NoPut/NoEnter. This change is purposefully made to be as targeted as possible. It only
   914     // changes entry_update for the one column. If you don't use combos, this has no impact at all.
   915     // It might have made more sense to completely rewrite the entry_update but it seems safer to just make as
   916     // small a change as spossible.
   917     Procedure Entry_Update Integer iFile Integer iDoAll
   918           Integer iBase iLimit iItem iDataFile iDataField iCols iNoPut iNoEnter
   919           String sVal
   920           Integer[] iDescCol iDescNoEnter iDescNoPut
   921
   922           // only do this with normal Finds and autofinds.. DD saves never calls this method. Finds always pass
   923           // iDoAll as 1 (saves are 3). This check is done to keep the change as limited as possible
   924           If (iDoAll=1) Begin
   925               Get Item_Limit to iLimit  // number of cols in row
   926               Get Base_Item to iBase    // first item in row
   927               For iItem from 0 to (iLimit-1)
   928                   // We only care about column_combo_state=T, Column_Combo_display_modes that use descriptions
   929                   // and columns that are bound to the DD and columns that are not displayonly
   930                   If (Column_Combo_State(Self,iItem) and ;
   931                       Column_Combo_Code_Display_Mode(Self,iItem)<>CB_Code_Display_Code and ;
   932                       Data_File(Self,iItem)>0) Begin
   933
   934                       Get Column_Option iItem noPut to iNoPut
   935                       Get Column_Option iItem noEnter to iNoEnter
   936                       // if it is noput and noenter it is displayonly and there is nothing to do
   937                       // Entry_update will update items unless both noput and noenter are set. You'd think
   938                       // that NoPut would stop it, but for finds it does not.
   939                       If not (iNoPut and iNoEnter) Begin
   940                           Move (iBase+iItem) to iDescCol[iCols]   // keep track of the item to change
   941                           Move iNoEnter to iDescNoEnter[iCols]    // the item's NoEnter
   942                           Move iNoPut   to iDescNoPut[iCols]      // the item's NoPut
   943                           // Changing this will stop the runtime from moving data from the item to the buffer
   944                           Set Item_Option iDescCol[iCols] noPut to True
   945                           Set Item_Option iDescCol[iCols] noEnter to True
   946                           Increment iCols
   947
   948                       End
   949                   End
   950               Loop
   951           End
   952
   953           // the normal entry_update
   954           Forward Send Entry_Update iFile iDoAll
   955
   956           // For any items we changed, we must manually do the Entry_update and restore the item state
   957           For iItem from 0 to (iCols-1)
   958               Get Data_Field iDescCol[iItem] to iDataField
   959               Get Data_File  iDescCol[iItem] to iDataFile
   960               Get Data_Value iDescCol[iItem] to sVal // converts from description to data
   961               Set_Field_Value iDataFile iDataField to sVal // upadate the buffer
   962               // restore items
   963               Set Item_Option iDescCol[iItem] noPut   to iDescNoput[iItem]
   964               Set Item_Option iDescCol[iItem] noEnter to iDescNoEnter[iItem]
   965           Loop
   966
   967     End_Procedure
   968
   969
   970End_Class
   971