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.
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.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.
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_TRIGGERSNote 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).
This usually implies that you do not let the normal brwsutil events fire, but you handle intercepting the UP/DOWN events and apply them. You need to do this because you need to have to option to validate/commit the current line back to the temp table and cause the browser to re-start the WAIT-FOR.
Here is some typical code:
ON CURSOR-UP OF {brwsutil.i FRAME_NAME} ANYWHERE DO: DO WITH FRAME {brwsutil.i FRAME_NAME}: /* Do any needed data validation checks for this line */ IF MyField <> "Needed Value" THEN DO: RUN err-disp.p (the-error-num). RETURN NO-APPLY. END. /* Commit the line to the buffer */ ASSIGN MyField AnotherField MoreFields. /* Disable the fields */ DISABLE MyField AnotherField MoreFields. /* Move up */ {brwsutil.i LINE_UP} /* Cause the WAIT-FOR to re-start */ APPLY "U1" TO FRAME {brwsutil.i FRAME_NAME} /* And exit (don't execute the default bwsutil trigger) */ RETURN NO-APPLY. END. END. ... /* somewhere in the mainline code */ DO WHILE TRUE WITH FRAME {brwsutil.i FRAME_NAME} ON ERROR UNDO, LEAVE ON ENDKEY UNDO, LEAVE: /* Synch the buffers (needed to make the current iteration correct) */ {brwsutil.i SYNCH_BUFFERS SyncOK} /* Activate fields */ ENABLE MyField AnotherField MoreFields. /* Wait for end (GO) or refresh (U1) */ WAIT-FOR GO, U1 OF FRAME {brwsutil.i FRAME_NAME}. /* See if we are really done (the GO trigger will */ /* set 'EditComplete' TRUE. We have to do this */ /* because there is no way to determine what event*/ /* terminated the WAIT-FOR (LAST-EVENT does not */ /* work because it only knows about user events, */ /* not APPLYd events) */ IF EditComplete THEN LEAVE. END.
ON OFF-END OF FRAME {brwsutil.i FRAME_NAME} DO: /* Define Variables */ DEFINE VARIABLE RecordOK AS LOGICAL NO-UNDO. DEFINE VARIABLE LastSeq AS INTEGER NO-UNDO. /* If in display mode, they can't add more items */ IF DisplayMode THEN RETURN NO-APPLY. /* Load the current record */ {brwsutil.i SYNCH_BUFFERS RecordOK} LastSeq = BillLine.seq-num. /* If the user is already on a blank line, we've gone as far as we will */ IF BillLine.acct-num = "" THEN RETURN NO-APPLY. /* Create a new line and position to it */ RUN CreateLineRecord(LastSeq + 1). {brwsutil.i REPOSITION_TO_RECID RECID(BillLine) RecordOK} RETURN NO-APPLY. END.
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.
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-nameIf 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.