Release 10.1A: OpenEdge Development:
Progress Dynamics Advanced Development

> ## Disabling actions based on a data value

> For example, suppose that you want users of the `oemaintwin` window you built to be able to edit or delete only `Customers` and `Orders` for `Customers` in the USA. To do this, you must disable the buttons that allow those operations, depending on the value of the currently selected `Customer`, and enable or disable the fields in the viewers on the different folder pages accordingly.

> ##### Using the TableIOType property

> The first thing you must do is to change the basic display mode of the toolbar to leave viewers disabled until someone specifically wants to do an update. That way, the viewers are initially disabled, and the user must have access to the Modify button to make changes.

> In earlier versions of the ADM and its SmartPanels, this was done with the `PanelType` property, which you could set to one of two states:

> - `Save` — Puts the buttons and their `TableIO-Target`s into a state where everything is enabled and the user just makes changes and selects the Save button to record them.

> - `Update` — Disables `TableIO-Target`s and places the buttons into a state where the user must select the Update button to enable the viewers and begin to make changes, and then select Save when done.

> The toolbar equivalent of this property is `TableIOType`, and it has the same two possible values: `Save` and `Update`. The default setting is `Save`, so things start out enabled. You must change the setting to `Update` for this example to work. Change the value in the Container Builder, by bringing up the toolbar’s dynamic property sheet, as shown in Figure 3–9, and changing the property value there.

> Figure 3–9: Dynamic Properties window

> There is another way to make run-time changes to the object in a custom super procedure that executes custom code when the window is initialized. You can define a custom super procedure for the toolbar itself, but because this must be done as part of the overall window initialization, this example shows the code in a custom super procedure for the container window. Where you put your code is a matter of overall organization and preference; there is always more than one solution to a programming problem.

> ##### Defining a custom super procedure for the window

> Define a New Structured Procedure in the AppBuilder and create an `initializeObject` internal procedure for it. Because this code is executed on behalf of the window, it first must get the handle of the `Toolbar` object. To do this, add a `ContainerToolbar` link from the toolbar to the window (`THIS-OBJECT`), which might be useful for other purposes as well, as is described in a later section of this chapter. Alternatively, the code could simply check among the window’s `Container-Targets` for the handle of an object with the `ObjectName` of `FolderPageTop`. Again, there is almost always more than one way to solve a problem.

> Given the toolbar handle, you then must set the `TableIOType` property to `Update`. This code sample uses the `{get}` and `{set}` include files to do the job. You could also use the `DYNAMIC-FUNCTION` syntax and the equivalent `get` and `set` functions for the same effect. This is also mostly a matter of personal preference and coding style. This all must happen before the `RUN SUPER` statement so that the property is set before all the objects are initialized, as shown in the following code:

>     Procedure initializeObject:
>     /*-------------------------------------------------------------------------
>       Purpose:     Override of initializeObject to reset the TableIOType
>                    property value to 'update' so that Viewers are initially
>                    disabled.
>       Parameters:  <none>
>     -------------------------------------------------------------------------*/
>     DEFINE VARIABLE hToolbar AS HANDLE     NO-UNDO.
>       {get ContainerToolbarSource hToolbar}.
>       {set TableIoType 'update' hToolbar}.
>       RUN SUPER.
>     END PROCEDURE.

> ##### Registering the custom super procedure

> As always, remember to register your super procedure in the Repository by selecting that option in the AppBuilder’s File menu. Save it as a Procedure in the appropriate product module. We have called the procedure oemaintwinsuper.p.

> Once you have done this, attach it as the custom super procedure for the container window. You can do this in the Container Builder, as shown in Figure 3–10.

> Figure 3–10: Container Builder for oemaintwin

> Note that you can set the custom super procedure for objects that are edited in the AppBuilder, such as browsers and viewers, in the AppBuilder’s property sheets for those objects.

> As mentioned elsewhere, you do not specify the relative pathname where the procedure is actually stored because you are just providing its `LogicalObjectName` as stored in the Repository. This name includes the .p filename extension but not the pathname. The pathname is attached automatically at run time by looking up the product module record in the Repository.

> ##### Defining a custom super procedure for a viewer

> Now that you have set the `TableIOType` property, you must create code to selectively enable and disable both the toolbar buttons and the viewer fields depending on the value of the Customer Country field. Put this code into a custom super procedure for the `custcommentsv` viewer that we have named customersuper.p. See the "Defining a custom super procedure for the window" section for more information on how to create this procedure.

> ##### Using the modifyDisabledActions procedure In the toolbar

> To control the toolbar buttons and associated menu items, change the `DisabledActions` or `HiddenActions` attributes. In this example, you should disable the buttons and menu items rather than hide them so that they do not appear and disappear as the user changes records, which would not be an appropriate user interface. In other situations you can remove the button or menu item from the toolbar altogether by adding it to the `HiddenActions` property.

> There is a special support procedure to help you add and remove entries from the `DisabledActions` property, whose value is a comma-separated list of actions. Note that at present there is no equivalent procedure for `HiddenActions`; you will have to parse the list yourself or create your own `modifyHiddenActions` procedure based on `modifyDisabledActions`.

> First you must identify the names of the actions that you are going to disable. To identify these names, open the toolbar in the Toolbar and Menu Designer. Expand the SmartToolbars node and locate the toolbar you are using, which is `FolderPageTop` in this case. Expand that node to see the bands it uses, and then expand the `TableIoMod` band, which holds all the update-related actions. Here you can see the description of each action.

> Following the description is the actual action name in parentheses, which also appears as the Item Reference in the maintenance frame to the right, as shown in Figure 3–11. This is the name to use in setting `DisabledActions`.

> Figure 3–11: Toolbar and Menu designer

> To disable the Delete button in the `FolderPageTop` toolbar, and also the two buttons that allow you to alternate the state of the displayed record from `View` mode to `Update` mode, you must add Delete, FolderView, and FolderUpdat e to the list.

> Since you need to do this each time a `Customer` is selected, the appropriate place for the code is in a local version of the `dataAvailable` event procedure. This is published by the SDO each time the record position changes and the viewer subscribes to the event so that it can display the new values. Unlike most event procedures, which have no parameters, this one has a single `INPUT` parameter that you must define and pass on to the super procedure. See Chapter 5, "Using ADM2 Properties and Methods in Progress Dynamics," for more information on this and many other useful properties and entry points in the code that you might want to use.

> ##### Important note about parameters on overrides and publish

> Remember to double-check for parameters in any procedure that you are going to override and in any event that you publish. If you get the parameter list wrong for a local version of a procedure that you `RUN`, you will get an error at run time to remind you of your mistake. But if you get the calling sequence wrong for a `PUBLISH` statement, the event simply will not occur because there will not be any matching subscriber with the same parameter list.

> Unfortunately, when you create a custom super procedure and add code to it in the AppBuilder, the Section Editor is not aware of what object it will be associated with, so it cannot supply you with a list of all the valid procedures and functions you can override, along with their parameters, as it does when you add procedures to a static SmartObject. Therefore, make sure you get this right. Also, do not forget to include the `RUN` `SUPER` statement in the right place in your custom code. If you leave it out, the standard code for the event will not execute at all, and the results will likely be very strange.

> ##### Getting values from different linked objects

> Your custom code must first get the handle of the toolbar. Because there is no link from the toolbar to the `Comments` viewer (the viewer is not updateable, so it has no `TableIO` link), you must get the handle of the `Customer` SDO, which is the viewer’s `Data-Source`, and then get the SDO’s `Navigation-Source`, which is the toolbar.

> You also need to retrieve the value of the Country field for the selected record. You can use the `columnValue` function in the SDO to do this.

> And finally, the code must get a list of all the `TableIO-Target`s of the toolbar (which are the three updateable viewers on Pages 2, 3, and 4). This is the `TableIOTarget` property. Though the name is singular (target not targets), the data type is `CHARACTER`, because there might be a list of handles of multiple targets, stored as a comma-separated list of handles in string form, as shown in the following:

>     Procedure dataAvailable:
>     /*-------------------------------------------------------------------------
>       Purpose:    Override of dataAvailable to reset the DisabledActions of
>                   the toolbar and enabled or disable fields based on the Country.
>       Parameters:  pcRelative AS CHARACTER
>       Notes:
>     -------------------------------------------------------------------------*/
>     DEFINE INPUT  PARAMETER pcRelative AS CHARACTER  NO-UNDO.
>     DEFINE VARIABLE cCountry    AS CHARACTER  NO-UNDO.
>     DEFINE VARIABLE hDataSource AS HANDLE     NO-UNDO.
>     DEFINE VARIABLE hToolbar    AS HANDLE     NO-UNDO.
>     DEFINE VARIABLE cTargets    AS CHARACTER  NO-UNDO.
>     DEFINE VARIABLE hTarget     AS HANDLE     NO-UNDO.
>     DEFINE VARIABLE iTarget     AS INTEGER    NO-UNDO.
>     ASSIGN hDataSource = DYNAMIC-FUNCTION ('getDataSource' IN TARGET-PROCEDURE)
>            hToolbar    = DYNAMIC-FUNCTION ('getNavigationSource' IN hDataSource)
>            cCountry    = DYNAMIC-FUNCTION ('columnValue' IN hDataSource,'Country')
>            cTargets    = DYNAMIC-FUNCTION ('getTableIOTarget' IN hToolbar).

> Next, the code checks the value of the `Country` field and runs modify`DisabledActions` to either add or remove the actions. Remember that the goal is to allow users to edit and delete only records related to customers in the USA. The `modifyDisabledActions` procedure takes two arguments:

> - `Add` or `Remove` — To indicate whether to add actions to or remove actions from the list of disabled actions.

> - `Action List` — A comma-separated list of action names.

> If the country is USA, the user is allowed to do updates and deletes, so the actions are removed from the disabled list, as shown:

>     IF cCountry EQ "USA" THEN
>     DO:
>         DYNAMIC-FUNCTION ('modifyDisabledActions' IN hToolbar,
>                          'Remove', 'FolderUpdate,FolderView,Delete').

> ##### Using enableFields and disableFields

> Then the code goes through the list of `TableIO-Target`s, converts each back to a handle, and runs the enableFields procedure in it. In order to avoid doing this unnecessarily, it first checks the value of the viewer’s `FieldsEnabled` property to see whether the fields are already in the right state, as shown:

>      DO iTarget = 1 TO NUM-ENTRIES(cTargets):
>             hTarget = WIDGET-HANDLE(ENTRY(iTarget, cTargets)).
>             IF NOT DYNAMIC-FUNCTION('getFieldsEnabled' IN hTarget) THEN
>                 RUN enableFields IN hTarget.
>         END.
>     END.

> Correspondingly, if the country is not equal to USA, then the actions are added to the disabled list and the code must run the disableFields procedure in the viewer. This is another case where you have to be careful to get the calling sequence right. Unlike `enableFields`, the `disableFields` procedure takes an `INPUT` parameter, which can be `All` or `Create` to indicate whether all fields are to be disabled, or just those associated with creating a new record, as shown:

>     ELSE DO:
>         DYNAMIC-FUNCTION ('modifyDisabledActions' IN hToolbar,
>                          'Add',    'FolderUpdate,FolderView,Delete').
>         DO iTarget = 1 TO NUM-ENTRIES(cTargets):
>             hTarget = WIDGET-HANDLE(ENTRY(iTarget, cTargets)).
>             IF DYNAMIC-FUNCTION('getFieldsEnabled' IN hTarget) THEN
>                 RUN disableFields IN hTarget ('All').
>         END.
>     END.

> This is the end of the code for the viewer’s localization of `dataAvailable`. Be sure to register the super procedure in the Repository and associate it with the viewer. You can set the custom super procedure for the viewer in the Repository Maintenance tool, or more conveniently in the AppBuilder, by opening the viewer and going into its property sheet, as shown in Figure 3–12.

> Figure 3–12: Property sheet for frMain

> ##### Testing the window with DisabledActions

> Now, when you run the application window and select a customer not in the USA, all the viewers are disabled. The `Delete`, `View`, and `Modify` actions in the toolbar are also disabled, as shown in Figure 3–13.

> Figure 3–13: Test of window wIth DisabledActions

> ##### Using the toolbar’s reset procedures

> The toolbar uses a set of event procedures to reset the action state for various types of actions. These procedures are executed when it seems correct to check for a state change. As a result, you do not normally need to run them yourself. For instance, they are run on a page change, which is why the action state is corrected when you switch pages.

> The event procedures that the toolbar supports are `resetTableIo`, `resetCommit`, and `resetNavigation`. These are published by objects like the viewer, and generally you do not need to invoke them explicitly. If you find that the toolbar state is not being reset properly at some particular place in your application, such as this viewer, you can simply publish the event from the viewer and the toolbar will receive it.

> Remember to publish it `FROM TARGET-PROCEDURE` so that the event is associated with the viewer itself and not its super procedure. This is what the first block of code looks like with an example of publishing `resetTableIo` to make sure that the update buttons are reset properly:

>     IF cCountry EQ "USA" THEN
>     DO:
>         DYNAMIC-FUNCTION ('modifyDisabledActions' IN hToolbar,
>                          'Remove', 'FolderUpdate,FolderView,Delete').
>         PUBLISH 'resetTableIO' FROM TARGET-PROCEDURE.
>         DO iTarget = 1 TO NUM-ENTRIES(cTargets):
>             hTarget = WIDGET-HANDLE(ENTRY(iTarget, cTargets)).
>             IF NOT DYNAMIC-FUNCTION('getFieldsEnabled' IN hTarget) THEN
>                 RUN enableFields IN hTarget.
>         END.
>     END.

> You can also publish the general-purpose reset procedure `resetTargetActions` to reset actions related to other link types, including custom links of your own. The procedure takes the name of the link as an `INPUT` parameter.

Copyright © 2005 Progress Software Corporation www.progress.com Voice: (781) 280-4000 Fax: (781) 280-4095