Documentation for brwsutil

brwsutil is a utility that functionally replaces the progress BROWSER. It offers much greater flexibility on the size, content and presentation of the contents of the browser than the Progress equivilent does.


Table of contents


brwsutil Overview

brwsutil requires a 'supporting' include file that defines the layout and tables used for your browser. This include file is intentended allow all encapsulation of the records and fields that make up a browser, making using the browser related functions much easier.

brwsutil internally implements the concepts of a selected record and a highlighted record. There is usually, but not always a selected record. That record is the one that brwsutil beleive is the current record. The highlighted record is the record that is visually highlighted on the browser. If there his a highlighted record, it implies there is a selected record. You cannot have a highlighted record if you don't have a selected record.


brwsutil API

{brwsutil.i DEFINE [NEW] deffile}
Definess the common data and initializes brwsutil. NEW is optional, but if not needed, must be replaced with an empty parameter. 'deffile' is the name of the supporting definition include file that brwsutil will use to learn how to fetch records and fields. This is a required parameter.

Because of the nature of the Progress pre-processor, you can only include one definition of a brwsutil browser per compilation unit (i.e. per .p). A definition can be spread over multiple .p files (the children of the defining .p replacing the DEFINE NEW with DEFINE " "), but there can never be more than one browser per source file.

If you do not need/want the ability for brwsutil to be shared across multiple .p files, you can define a pre-processor symbol called LOCAL_DEF (with any value) and brwsutil will not define its variables as SHARED.

{brwsutil.i CLEAN_UP}
Allows the brsutil logic to clean up and release any allocated resources when it completes. You must be sure that anywhere there is a {brwsutil.i DEFINE NEW ...}, there must be a CLEAN_UP section as well.

{brwsutil.i FRAME_NAME}
Returns the name of the frame that brsutil is using for display of the data.

{brwsutil.i LIST_BACKGROUND_COLOR_NAME}
Returns an expression that will yield a color specification used for defining the lister frame. This should really only be used in the support include file when the DOWN frame that will hold the lister records is defined.

{brwsutil.i VIEW}
Makes the browser frame visible if it hasn't already been made so

{brwsutil.i HIDDEN}
Makes the browser frame hidden if it is visible

{brwsutil.i REFRESH}
Causes brwsutil to rebuild the list of records corrently being displayed. You would use this if you inserted or deleted records, changed fields in records that are part of the lister, changed the order of the lister, etc.

brwsutil will generally not disturb the screen if it is at all possible to avoid it. This means you can safely refresh the browser without annoying flicker if nothing has changed. However, just because there may be no screen change, there is a lot of overhead to refreshing a screen. Don't do this lightly - make sure you really need to do it.

{brwsutil.i LINE_UP}
brwsutil will attempt to move the highlighted bar one line up. If the line is already at the start of the record list, nothing happens.

{brwsutil.i LINE_DOWN}
brwsutil will attempt to move the highlighted bar one line down. If the line is already at the end of the record list, nothing happens.

{brwsutil.i PAGE_UP}
brwsutil will attempt to move one page back through the list of records. If the browser is currently showing the first page, then brwsutil will move the highlight bar to the first record. If the highlight bar is already on the first record, nothing happens.

{brwsutil.i PAGE_DOWN}
brwsutil will attempt to move one page forward through the list of records. If the browser is currently showing the last page, then brwsutil will move the highlight bar to the last record. If the highlight bar is already on the last record, nothing happens.

{brwsutil.i HOME}
brwsutil will attempt to move to the first record in the record list. If the browser is already on the first record, nothing happens.

{brwsutil.i END}
brwsutil will attempt to move to the last record in the record list. If the browser is already on the last record, nothing happens.

{brwsutil.i UNHIGHLIGHT}
this will cause brwsutil to 'unhighlight' any currently selected and highlighted record. This will not change the brwsutils internal idea of what record is the 'current' one.

{brwsutil.i HIGHLIGHT}
This will cause brwsutil to 'highlight' the current record, if there is any. This will not change the brwsutils internal idea of what record is the 'current' one.

{brwsutil.i REPOSITION_TO_RECID theRecid doneFlag}
This will attempt to position the lister to the record specified by the passed record id. Exactly which record 'theRecid' applies to is determined by your support routine (which will be called to fufill the positioning request). 'doneFlag' is set TRUE if the browser was able to be repositioned to the passed record or FALSE if something failed.

If the positioning is successful, the screen is possible updated and the highlight bard moved to the target record. If the positioning fails, the screen and highlighted record (if any) are not disturbed.

{brwsutil.i EMPTY_BROWSER}
This will clear the browser out completly. The actual screen data is cleared off and the internal record list is cleared out. The highlight bar is turned off. The browser itself remains visible on screen, along with any graphic lines and labels.

This is really useful for those times when you need to put a browser on screen but you may not actually have any data for it to display or you just need to visually clear out a previously filled browser.

{brwsutil.i SYNCH_BUFFERS SyncOK}
This will cause brwsutil to load all the buffer defined in the support file with the contents of what brwsutil considers to be the current record. It also insures that the 'current iteration' of the DOWN frame that brwsutil work with is also the one corrosponding to the current record (and with that, the current buffers). SyncOK is set to TRUE if the buffers and current frame iteration were successfully updated, FALSE if not.

It's important to realize that the buffers defined for use by brwsutil in the include support file are NOT kept in synch with what the user and/or highlight bar is doing. In fact, they is almost always OUT OF SYNC with the what appears to be the active record from brwsutils standpoint. The same is true of what the current iteration of the brwsutil DOWN frame is. brwsutil frequently moves buffers and current iterations arond when checking and updating the record list.

The ONLY way to insure that you are looking at the right record is to either use this service or use the GET CURRENT_RECORD service. Note that the GET CURRENT_RECORD service WILL NOT synchornize the actual buffers and will not synchronize the current down frame iteration. The result of either service is only good until the next brwsutil action (either deliberatly invoked by you or implicitly invoked by a trigger).

{brwsutil.i GET REPOSITION_ROW theRow}
Returns the row that the REPOSITION statement will attempt to seek to when the browser list needs to be redrawn to complete a REPOSITION. Possible values are ? (center the highlight bar, 0 (try to keep the highlight bar where it was - if nothing highlighted, then center) or a number between 1 and the max # of rows.

{brwsutil.i GET BROWSER_ROWS rowCount}
Returns the maximum number of rows available on the browser. There may well be less records actually displayed on the browser, but there will never be more. If the record set being used for driving the browser has less records than the browser can hold, the actual number of available rows will be less than the figure returned by this call.

{brwsutil.i GET AVAILABLE_ROWS rowCount}
Returns the number of rows in the browser that have records loaded. In most cases, this is the same as the maximum number of rows on the browser. In cases where there are less records available than the browser can display, they can differ. Any type of looping or multi-row processing should be bound by this number, not the number of rows the browser could have.

{brwsutil.i GET CURRENT_ROW theRowNum}
Returns the 'current' row number. This is the row the browser thinks is the currently focused row and, if the highlight bar is enabled, is also the one the highlight bar is on. If there is no current row, ? is returned.

{brwsutil.i GET HIGHLIGHT_ROW theRowNum}
Returns the row the highlight bar is currently on. This will always be the same as the value returned by the 'CURRENT_ROW' service, except when the highlight bar is disabled. In that case, it'll return ? (like CURRENT_ROW, you can also get ? back if there is no current row).

{brwsutil.i GET CURRENT_RECORD resultRecid}
This will return the RECID of the record for the currently selected row. This row may not be highlighted at the moment (if you had made a previous UNHIGHLIGHT call), but you would still get the RECID of it. If no record is selected (which implies none is highlighted either), you will receive a ? back.

The RECID you receive back is a function of the support routine you connect to brwsutil. This is generally the same RECID you would explect to pass to brwsutil to position to this record via REPOSITION_TO_RECID.

{brwsutil.i GET RECID_OF_ROW theRowNum resultRecid}
For the passed row 'theRowNum', returns the RECID of the master record for that row. If you specify an out of bounds row, then ? is returned. You can select any row between 1 and AVAILBLE_ROWS.

{brwsutil.i GET HIGHLIGHT_COLOR_NAME theColorSpec}
Returns the current highlight color specification that brwsutil is using to color the highlighted line.

{brwsutil.i GET PROMPT_COLOR_NAME theColorSpec}
Returns the current "PROMPT-FOR" color specification that brwsutil is using to color the highlighted line. Note that unless the line has fields that will be ENABLEd for input, this specification really means zippo.

{brwsutil.i GET BACKGROUND_COLOR_NAME theColorSpec}
Returns the current background color specification that brwsutil is using to 'unhighlight' lines when it moves off of them

{brwsutil.i GET VALUE_CHANGED_EVENT_REASON theReason}
Returns the list of valid reason codes that brwsutil uses to determine if the VALUE_CHANGED event should be fired. For a complete description of the possible reason codes, see the SET VALUE_CHANGED_EVENT_REASON description.

{brwsutil.i GET LAST_VALUE_CHANGED_EVENT_REASON theReason}
Designed to be called from within the VALUE_CHANGED trigger for a browser, this service returns the list of reason codes for the VALUE_CHANGED trigger was fired. This will never have more reasons than were set with the SET VALUE_CHANGED_TRIGGER_REASON, but it may have less than the full list (only the actual reasons for invoking the trigger are returned).

You can use this information to tailor the response your trigger takes as a result of the selected change. See the documentation on SET VALUE_CHANGED_EVENT_REASON for a listing of the possible reasons.

{brwsutil.i SET REPOSITION_ROW theRow}
Sets the row that the REPOSITION statement will attempt to seek to when the browser list needs to be redrawn to complete a REPOSITION. Possible values are ? (center the highlight bar, 0 (try to keep the highlight bar where it was - if nothing highlighted, then center) or a number between 1 and the max # of rows. If the REPOSITION command doesn't require changing the current page (i.e. the destination record is part of the currently displayed page), the highlight bar will be moved to it without 'scrolling' the browser so that new record is at your specified line. Also, with certain positions, the position request may be ignored (especially if you are positioning near the end or start of the record set).

{brwsutil.i SET CURRENT_ROW theRowNum}
Sets the 'current' row number. This is the row the browser thinks is the currently focused row and, if the highlight bar is enabled, is also the one the highlight bar is placed on. If you pass an invalid row, the request is ignored.

{brwsutil.i SET HIGHLIGHT_COLOR_NAME theColorSpec}
Sets the highlight color specification that brwsutil is to use to color the highlighted line.

{brwsutil.i SET PROMPT_COLOR_NAME theColorSpec}
Sets the "PROMPT-FOR" color specification that brwsutil is to use to color the highlighted line. This is, by default, the system input color. This really doesn't matter unless you are using brwsutil with fields that you intend on ENABLEing input for.

{brwsutil.i SET BACKGROUND_COLOR_NAME theColorSpec}
Sets the current background color specification that brwsutil is to use to 'unhighlight' lines when it moves off of them

{brwsutil.i SET NO_HIGHLIGHT}
Turns the highlight bar off and makes sure it stays off. Nothing short of a call to SET HIGHLIGHT will cause the highlight bar to turn off.

There are times when you want to display a list of items via brwsutil but you don't really want to pick anyone of them out with a highlight bar. If the list was going to be static (no changes and no repositions), you could simply use the UNHIGHLIGHT option of brwsutil. Problems occur if you later try to REPOSITION_TO_RECID or REFRESH because either of these implcitly reactivate the highlight bar.

The SET NO_HIGHLIGHT method will cause the highlight bar to stay off, regardless of any other actions. Nothing else (besides SET HIGHLIGHT) will cause it to relight.

{brwsutil.i SET HIGHLIGHT}
Allows the highligh bar to be redisplayed after a previous call to SET NO_HIGHLIGHT. This will not, by itself, turn the highlight bar on - it just allows it to be turned on by any of the normal brwsutil movement methods, REFRESH or by a HIGHLIGHT command.

{brwsutil.i SET VALUE_CHANGED_EVENT_REASON theReason}
brwsutil can fire the VALUE_CHANGED trigger whenever the current record that is displayed is changed (the physical RECID changes) or when the contents of the currently selected record change or both. By default, it only fires when the physical RECID changes.

You can pass any combination of the following tokens. If you are passing more than one, seperate them with a comma.

Implementation notes

Never directly reference what is defined withint brwsutil.i or the support include file
The stuff inside these files is PRIVATE and should remain so. If you start referencing or worse, modifying things from them, you are asking for a lot of pain. I will regularly change things in brwsutil just to break code that tries to rely on such sloppy techniques.

If you need something, use the available methods. If it's not available via the methods, rethink what you need. It's always possible that it's a legitimate need that isn't yet in brwsutil. In that case, let development know and perhaps it can be added. DOn't just bypass everything because you think you can - life is too short for that kind of pain.

Never directly reference brwsutil defined buffers
The buffers that are given over to use for brwsutil via the include file should not be directly referenced, in ANY way outside of brwsutil. brwsutil manipulates these buffers in strange and unusual ways and they almost never contain records the corrospond to the 'current' record on the brwsutil lister. The only exception to this is when using brwsutil to enable input in which case you don't have a choice. However, in these cases, make sure you use SYNCH_BUFFERS and read the notes on enabling for input.

If you need to know what the record brwsutil has currently selected is, use the GET CURRENT_RECORD or SYNCH_BUFFERS methods to find out. Note that what is returned by thes methods is only valid until the next brwsutil action occurs, either explicitly (as a result of you invoking some other method) or implicitly (as a result of brwsutil responding to an event). SYNCH_BUFFERS in particular is VERY sensitive to changes, implicit or explicit, via brwsutil. Since it just loads the buffers brwsutil usually uses up to corrospond to the 'current' record, nearly any implicit or explicit call to brwsutil will change their value. GET CURRENT_RECORD is more stable because the returned value won't change. What brwsutil considers to be the current record may change, but the record represented by the RECID in the value returned will not. However, if you do something silly like use the returned value to load a brwsutil defined buffer directly, then you are going to be in a world of hurt. Use only buffers you've defined outside of brwsutil.

Using multiple instances of brwsutil
Because of the nature of Progress, much of brwsutil is bound at compile time. This is why we need a support include file instead of just passign everything in via methods.

The downside is that without doing antyhing special, you can only have one copy of brwsutil in any compilation unit (that is, any one .p). For most needs, this is fine. However, there are times you need to have more than one browser active at the same time.

By adding an extra parameter to the end of each and every single invocation of brwsutil, you can actually have multiple browsers. This is really, really not too efficiant since the entirity of brwsutil is include as many times as you have different 'instance names'. However, if you need it, you need it.

Here's a very brief example using two browsers, one called 'item' and one called 'event'. The code would look something like:

{brwsutil.i DEFINE NEW myitem.i item}
{brwsutil.i DEFINE NEW myevent.i event}

{brwsutil.i REFRESH item}
{brwsutil.i VIEW item}

IF DisplayEvents THEN DO:
  {brwsutil.i REFRESH event}
  {brwsutil.i REPOSITION_TO_RECID TheEvent ReposOK event}
  {brwsutil.i VIEW event}
END.
The tags 'event' and 'item' are constant instance names. Don't quote them and don't even think for a minute you can use an expression. It works just ducky, but you need to put that last, extra parameter/instance name on every single call to brwsutil. Don't worry though, if you forget, the Progress compiler will likely blow itself to bits (a sure sign that something has happened).

Using &NO_TRIGGERS to disable default triggers
By default, brwsutil supplies a set of triggers (discussed in a later section) to simulate the abilities of the BROWSE widget - namely, the ability of the brwsutil.i associated frame to actually respond to events applied to it.

Usually this is beneficial and useful. However, there may be times that you do not want to have those triggers included. In such a case, defining the preprocessor symbol NO_TRIGGERS before the {brwsutil.i DEFINE} invocation will suppress them. For example:

&SCOPED NO_TRIGGERS
{brwsutil.i DEFINE NEW m-qqbws.i}
&UNDEFINE NO_TRIGGERS
Note that this is not usually necessary as you can override the triggers anyway simply by defining your own triggers. There may be some rare times when you need to suppress trigger inclusion. A possible example might be that an include file that has triggers that are supposed to override the brwsutil supllied triggers. However, due to inclusion dependencies, the include file must come physically *before* the {brwsutil.i DEFINE} and if &NO_TRIGGERS wasn't defined, the brwsutil supplied triggers would in fact override the triggers defined in the previous include file.

All in all, need for this is likely to be very rare.

Using brwsutil for ENABLEd browsers
brwsutil supports the ability to have editable browser. That is, to use brwsutil to allow the user to scroll through records and actually be able to edit the contents of the fields for each records line.

You really don't need to do anything special brwsutil to have this ability - brwsutil doesn't really care either way. However, there are some things to be aware of when doing it. Mostly, the 'problems' you run into are due to Progress issues and can be maddening, but they are all easy to solve (especially since I'm giving you the ansers right here).


Triggers and events

brwsutil implements an event model similar to that of an actual Progress browser. It both responds to events and generates events.

Since brwsutil is based around a DOWN frame, in order for it to process events, those events must be applied to the brwsutil frame. This can be done via the use of the APPLY keyword or by making sure that the frame has focus and is receiving user keyboard events.

Events that brwsutil will respond to:

If you need to programmatically manipulate the browser, it's up to you to decide if you want to use the brwsutil services or apply an event. It will really depend on what better fits into your environment.

Events that brwsutil generates:

To capture these events, you would use normal triggers, just like you would for a browser. For example, to receive notification when a record changes, you would have code similar to:

  ON "VALUE-CHANGED" OF FRAME {brwsutil.i FRAME_NAME} DO:
    DEFINE VARIABLE TheRecID     AS RECID           NO-UNDO.

    /* Get the record */
    {brwsutil.i GET_CURRENT_RECORD theRecID}
       
    /* Handle a record being 'unselected' */
    IF theRecID = ? THEN DO:
      UserKeyText:SCREEN-VALUE = "".
      RETURN.
    END.

    /* Fetch the record */
    FIND myRecord WHERE RECID(myRecord) = theRecID NO-LOCK.
    UserKeyText:SCREEN-VALUE = myRecord.keyField.
  END.


Structure of the brwsutil support file

The brwsutil support include file (specified on the brwsutil DEFINE call) allows the generic brwsutil utility to work with your specific browser tables and fields. It is a structured include file with a predefined set of needed 'hooks' or call tags.

There is a sample template browser that is reasonably easy to work with. It is called brwstmpl.i and in the include directory.

Required tags for the support include file:

{brwstmpl.i DEFINE [NEW]}
You use this section to define any variables, buffers, etc that you will need to satisfy the remaining sections. You must also define the frame that brwsutil will use.

Note that 'NEW' is optionally passed as the 2nd parameter. If you are not defining any buffers/variables as SHARED, you can ignore this.

Note the use of the '{brwsutil.i LIST_BACKGROUND_COLOR_NAME}' for color the list. If you don't use this, you will not be able to manipulate the list color via the SET BACKGROUND_COLOR service. By default, this variable will be the equivilent of the user-color.c-list color.

Sample Section:


&SCOPED FrameName myListFrame

&IF "{1}" = "DEFINE" &THEN
  /* Define Buffers */
  DEFINE {2} SHARED BUFFER StuYear FOR stu-year.
  DEFINE {2} SHARED BUFFER StuBase FOR stu-base.
  DEFINE {2} SHARED BUFFER GradeCatalog FOR grade-catalog.
  DEFINE {2} SHARED BUFFER RoomCatalog FOR room-catalog.
  DEFINE {2} SHARED BUFFER StatusCode FOR status-code.

  /* Define the frame */
  DEFINE {2} SHARED FRAME {&FrameName}
    {{0} FORMATTED_FIELD_LIST}
  WITH {{0} LINES_IN_BROWSE} DOWN ROW 4 CENTERED OVERLAY
    COLOR VALUE({brwsutil.i LIST_BACKGROUND_COLOR_NAME})
    TITLE COLOR VALUE(the-color.c-title) " ".

  /* Scope the frame */
  FORM WITH FRAME {&FrameName}.

{brwstmpl.i FRAME_NAME}
The name fo the frame that will be used by brwsutil. This call is utilized as a function, so it should literally contain the name of the frame.

Sample Section:

&ELSEIF "{1}" = "FRAME_NAME" &THEN
  TheListFrame

{brwstmpl.i LINES_IN_BROWSE}
This is the number of lines down in the browser to display. You can have up to 99 lines in a browser. This call is utilitized as a function and should return the number of lines to use. This can be an expression.

Sample Section:

&ELSEIF "{1}" = "LINES_IN_BROWSE" &THEN
  CURRENT-WINDOW:SCREEN-LINES - 14

{brwstmpl.i FORMATTED_FIELD_LIST}
This is a list of fields, qualified with table names, that will appear in the lister. This list is generally directly included in the frame definition. It can have COLUMN-LABEL attributes, FORMAT attributes, positioning attributes (i.e. AT, TO, etc). Anything you could put into a DEFINE FRAME statment is valid here.

Sample Section:

&ELSEIF "{1}" = "FORMATTED_FIELD_LIST" &THEN
  StuYear.stu-name        COLUMN-LABEL "Student Name"
  StuYear.stu-id          COLUMN-LABEL "ID"
  GradeCatalog.grade-name COLUMN-LABEL "Grade"
  RoomCatalog.room-name   COLUMN-LABEL "Home Room"
  StuBase.gender          COLUMN-LABEL "Gender"
  StatusCode.status-code  COLUMN-LABEL "Status"

{brwstmpl.i DISPLAY_FIELD_LIST}
This is a list of fields, qualified with tables, that will be used by a DISPLAY statement to display one line of the lister. It can contain things like WHEN and @ qualifiers, but should not contain and FORMAT, LABELS/COLUMN-LABELS or any other formatting attributes.

Sample Section:

&ELSEIF "{1}" = "DISPLAY_FIELD_LIST" &THEN
  StuYear.stu-name
  StuYear.stu-id
  GradeCatalog.grade-name
  RoomCatalog.room-name
  StuBase.gender
  StatusCode.status-code

{brwstmpl.i SIMPLE_FIELD_LIST}
The simple field list is just that - a very simple list of fields from the DISPLAY_FIELD_LIST and FORMATTED_FIELD_LIST sections, but with absolutely no qualifiers/attributes of any kind (no FORMAT, LABEL, WHEN, etc).

Sample Section:

&ELSEIF "{1}" = "SIMPLE_FIELD_LIST" &THEN
  StuYear.stu-name
  StuYear.stu-id
  GradeCatalog.grade-name
  RoomCatalog.room-name
  StuBase.gender
  StatusCode.status-code

Often times, this list will appear identically either the FORMATTED_FIELD_LIST or the DISPLAY_FIELD_LIST (like our example). In this case, it's completely acceptable to 'abreviate' the specification to something simple like:

&ELSEIF "{1}" = "SIMPLE_FIELD_LIST" &THEN
  {{0} DISPLAY_FIELD_LIST}

{brwstmpl.i COMPARE_FIELD_LIST resultFlag}
This section is used to compare the the contents of each field between what is currently being displayed and what it actually in the record buffers. The result is used to determine if a line of the display is 'dirty' and in need if being redrawn.

The result of this entire call is a logical expression returning TRUE if the screen values match the buffer values or FALSE if they don't.

Sample Section:

&ELSEIF "{1}" = "COMPARE_FIELD_LIST" &THEN
  &SCOPED ScreenValue INPUT FRAME {&FrameName}
  {2} = (
      (StuYear.stu-name        = {&ScreenValue} StuYear.stu-name)
  AND (StuYear.stu-id          = {&ScreenValue} StuYear.stu-id)
  AND (GradeCatalog.grade-name = {&ScreenValue} GradeCatalog.grade-name)
  AND (RoomCatalog.room-name   = {&ScreenValue} RoomCatalog.room-name)
  AND (StuBase.gender          = {&ScreenValue} StuBase.gender)
  AND (StatusCode.status-code  = {&ScreenValue} StatusCode.status-code)
      ).
Note the use of the pre-processor directive to make this easier to read. Also, please note that it's implied that elsewhere in the include file the above section was excerpted from, the pre-processor symbol 'FrameName' was defined (meaning it's not automatically defined, but you can manually do so or just change the 'ScreenValue' pre-processor definition).

Also, be aware that if you truncate a field on the browser (i.e. use the FORMAT modifier to change/shorten the length of the displayed field) that you will need to modify the buffer comparisons to take that into account.

For example, if in the above example, the 'StuYear.stu-name' field was "shortened" using a FORMAT "x(20)", you would need to modify the comparison above to read

   SUBSTR(StuYear.stu-name, 1, 20) = {&ScreenValue} StuYear.stu-name
If you didn't do this, the section would always return FALSE and brwsutil will constantly and uselessly refresh the screen.

Finally, remember it is the format mask, not the actual displayed length, that you need to keep in mind when doing comparison. If you've not changed the format mask but have used the VIEW-AS FILL-IN SIZE xxx statement to "shrink" the field, you would NOT use a SUBSTR to reduce the size of the comparison string. (the screen value of it would still be it's full length - VIEW-AS only modifies how much you see (FORMAT actually truncates it)).

{brwstmpl.i FIND_BY_DIR dir findRecID}
This section is used to move through the record list. You are responsible for writting code that will take a pre-processor token with the values FIRST, LAST, PREV and NEXT and use them to move from the 'current' record to the one specified. If you were able to find the record, you should set the 'findRecID' flag to the RECID that you found or to ? if there was an error in the lookup. You should do all record lookups using NO-LOCK and NO-ERROR.

You only really need to bother with the main record for the browser. Whenever brwsutil needs to display a record, it will call the LOAD_DEPENDENTS section after this section to fetch all records. You could leave that section empty and just put everything into this section - that is your choice.

From an efficiancy standpoint however, it's better to handle the absolute minimum number of records to fufill this request. There are many times that brwsutil calls this service that it will not be needing to display a record. In those many cases, the extra records, if fetched in this area, would be fetched needlessly. There is also the consideration that you would also have to duplicate the ancillary record loading in the FIND_BY_RECID section.

Also consider that while the example shows use of the FIND statement, there is no reason why this couldn't be a GET statement that was associated with a QUERY.

Sample Section:

&ELSEIF "{1}" = "FIND_BY_DIR" &THEN
  IF LookupKeyNum = 1 THEN
    FIND {2} StuYear WHERE {{0} FIND_FILTER} NO-LOCK
                     USE-INDEX stu-name NO-ERROR.
  ELSE
    FIND {2} StuYear WHERE {{0} FIND_FILTER} NO-LOCK
                     USE-INDEX stu-id NO-ERROR.

  {3} = RECID(StuYear).

{brwstmpl.i FIND_BY_RECID recid findOk}
This section is used to move to a specific record. You are responsible for taking the passed 'recid' and trying to load the specified record. If you are able to find the record, you should set 'findOK' to TRUE. If you can't find the record, set it to FALSE. You should do all record lookups using NO-LOCK and NO-ERROR.

You only really need to bother with the main record for the browser. Whenever brwsutil needs to display a record, it will call the LOAD_DEPENDENTS section after this section to fetch all records. You could leave that section empty and just put everything into this section - that is your choice.

From an efficiancy standpoint however, it's better to handle the absolute minimum number of records to fufill this request. There are many times that brwsutil calls this service that it will not be needing to display a record. In those many cases, the extra records, if fetched in this area, would be fetched needlessly. There is also the consideration that you would also have to duplicate the ancillary record loading in the FIND_BY_DIR section.

Also consider that while the example shows use of the FIND statement, there is no reason why this couldn't be a REPOSITION TO RECID statement that was associated with a QUERY.

Sample Section:

&ELSEIF "{1}" = "FIND_BY_RECID" &THEN
  FIND StuYear WHERE (RECID(StuYear) = {2})
                 AND {{0} FIND_FILTER}
               NO-LOCK NO-ERROR.
  {3} = AVAILABLE StuYear.

{brwstmpl.i FIND_FILTER}
This section is a 'filter' for your FIND statements. It is never directly invoked by brwsutil, but serves as convient 'macro' for keeping the common selectives in one place.

You certainly could just leave this section blank (or omit it entirely) if you wanted and directly code the selection criteria into the FIND_BY_DIR and FIND_BY_RECID calls, but if you need to change it, you'll need to revisit every FIND in those calls.

If you are using a QUERY instead of discrete calls, you can either use this section in your OPEN QUERY statement or omit it.

Sample Section:

&ELSEIF "{1}" = "FIND_FILTER" &THEN
       ((StuYear.school-year-id = SchoolYearID)
    AND (StuYear.bldg-id        = BldgID))

{brwstmpl.i LOAD_DEPENDENTS loadOK}
brwsutil will invoke this section whenever it needs to load any 'depenedent' buffers needed to complete a record display. It would only be called after a successful call to the FIND_BY_DIR or FIND_BY_RECID services, so you can assume any buffers normally loaded by those services will be available for reference here (if either of those services fail, brwsutil will NOT call this service).

You should load all ancillary buffers NO-LOCK. If you have a problem loading a buffer (or buffers) that should be considered an error (vs an OUTER-JOIN condition), you should set 'loadOK' to FALSE. Otherwise, if you are successful, be sure to set 'loadOK' to TRUE.

If you have no dependent buffers, just place a single line in the section that sets 'loadOK' to TRUE.

Sample Section:

&ELSEIF "{1}" = "LOAD_DEPENDENTS" &THEN
  FIND StuBase OF StuYear NO-LOCK NO-ERROR.
  FIND GradeCatalog OF StuYear NO-LOCK NO-ERROR.
  FIND RoomCatalog OF StuYear NO-LOCK NO-ERROR.
  FIND StatusCode OF StuYear NO-LOCK NO-ERROR.

  /* Return status */
  {2} =     AVAILABLE StuBase
        AND AVAILABLE GradeCatalog
        AND AVAILABLE RoomCatalog
        AND AVAILABLE StatusCode.

{brwstmpl.i CLEAR_BUFFERS}
This service 'clears' the buffers that were previously loaded by brwsutil via the FIND_BY_DIR, FIND_BY_RECID and LOAD_DEPENDENTS services.

After brwsutil finishes loading a page of records, it has no need to maintain any buffers with context. To keep any scoping problems to a minimum, brwsutil will use this service to clear all those buffers.

This is a GOOD thing - I'd strongly recommend not trying to subvert this by leaving this section blank and then later trying to reference the contents of the buffers. brwsutil does strange things and there is no guarantee that what you see in a buffer matches what is on the screen (or that all the buffers are even related to each other).

If you need to get the 'current' record, use the brwsutil GET CURRENT_RECORD service. Don't access the buffers.

Sample Section:

&ELSEIF "{1}" = "CLEAR_BUFFERS" &THEN
  FIND StuYear      WHERE RECID(StuYear) = ? NO-ERROR.
  FIND StuBase      WHERE RECID(StuBase) = ? NO-ERROR.
  FIND GradeCatalog WHERE RECID(GradeCatalog) = ? NO-ERROR.
  FIND RoomCatalog  WHERE RECID(RoomCatalog) = ? NO-ERROR.
  FIND StatusCode   WHERE RECID(StatusCode) = ? NO-ERROR.

Last Updated October 3, 1996