Now you need to check the SCREEN-VALUE of the field to see whether the user typed anything into it. The reason for this check is that the LEAVE trigger could fire under other circumstances, such as if the user clicked on a button while the cursor was positioned in the field. This action would fire the LEAVE event, but you want to disregard such events unless the user actually entered a value in the field.

But which field? You are defining the same trigger code for all the fields.

ABL defines another built-in handle that gives you what you need: the SELF handle. Within a trigger block, this handle resolves to the object the trigger has fired for.

To identify a field in a trigger:

  1. Check to see whether the user entered a value using this statement and start a block that is executed only if the user did:
      IF SELF:SCREEN-VALUE NE "" THEN
      DO:

    It is important to make this check because the LEAVE trigger fires in many different ways, including clicking on a button while the cursor is in the field. You need to check that the user really entered a value to filter on.

  2. Inside this block, use the list of fill-in field handles to disable them again, now that the user has had a chance to enter a value into one of them:
        DO iField = 1 TO NUM-ENTRIES(cFillIns):
          ASSIGN hField = WIDGET-HANDLE(ENTRY(iField, cFillins))
            hField:SENSITIVE = NO.
        END. 

    Remember that you defined the cFillIns variable in the Definitions section specifically because you want its value to be scoped to the whole procedure, so that its value persists between trigger or procedure calls. To remind yourself, and other readers of your code, that a variable like this is scoped to the entire procedure, you can use a naming convention such as putting a g for global on the front of the variable name. Even though such a variable is not actually global to the session, it is global to this procedure, so its scope is worth noting in some way.

  3. Get the handle to the query, along with the current PREPARE-STRING:
        ASSIGN hQuery = QUERY CustQuery:HANDLE
          cPrepare = hQuery:PREPARE-STRING.

    You are saving off the current PREPARE-STRING before closing and re-preparing the query so that you can restore it later in the block in case the user chooses not to continue with the filtering. There is a special case you have to deal with here. If the query has not yet been re-prepared dynamically, then the PREPARE-STRING attribute is unable to return the FOR EACH statement used in the original static OPEN QUERY statement. In this case, you just have to set the variable to the default query string:

        /* If the query has not been prepared dynamically just restore
          the original query definition. */
        IF cPrepare = ? THEN
          cPrepare = "FOR EACH Customer".
  4. Close the query and re-prepare it using its handle and the value the user entered into whichever one of the fill-in fields:
        hQuery:QUERY-CLOSE().
        hQuery:QUERY-PREPARE("FOR EACH Customer WHERE " + SELF:NAME +
        (IF SELF:DATA-TYPE = "CHARACTER" THEN
          " BEGINS " ELSE " = ")
          + QUOTER(SELF:SCREEN-VALUE) +
          " BY " + SELF:NAME). 

    The QUERY-CLOSE step is actually optional. The QUERY-PREPARE method closes the query automatically.

    Using the DATA-TYPE attribute of the SELF handle, the code identifies the data type of the field the user is filtering on. If the field is CHARACTER, the code builds a query retrieving all Customers where the field value BEGINS with what the user typed in, which is the SELF:SCREEN-VALUE attribute. Otherwise, it retrieves records where the field value is equal to the filter value. You could, of course, set up such a query to filter records in any way that you want. Finally, the FOR EACH statement sorts the record by the filter field, using the SELF:NAME attribute.