The Best of Both Worlds: Browse Boxes on the Web
By Vince Russo and Steve Parker (c) 1999
On the Internet, there are two things you can count on: graphics and lists (waiting doesnt count because sometimes, though not often, the Web fools you and responds with alacrity).
Clarion Internet Connect, of course, supports lists. Not simply "lists," but list boxes as we are used to them. In fact, it supports them in multiple ways.
Falling off a log easy
By simply adding the Internet Connect extensions to your application, any existing browse procedures automatically work on the Web. Multiple columns are supported, as are navigation keys, scroll bars, toolbar navigation, double clicking, all forms of locators and, probably, a few more features weve forgotten about.
It doesnt get much easier than this.
Of course, there is a price for this ease. Users must download the Java classes and scrolling can be slow (on the order of one or two seconds per line). Further, you are at the mercy of a multitude of bugs in the various Java Virtual Machines used by different browsers.
For an example, see: http://www.par2.com/cws/c5launch.dll/faqs/coolfaqs.exe.0.
A little harder, but Java-free
(This subject was fully discussed in "Decaffeinating Forms and Browses," Clarion Online, 2, 1, August 1998 and "But it Doesnt Look Like IC," Clarion Online, 2, 2, September 1998.)
IC allows you to override the default list box class (WebJavaListClass) with an HTML class (WebHtmlListClass), producing a non-Java list:

Figure 1
While much faster and not requiring the Java classes, there is a price for this too. HTML lists are generated as HTML <select> structures. An HTML <select> only supports a single string per line. That is, you cannot get multi-column support.
Similarly, you lose all the features of Clarion list boxes you are used to: page-to-page navigation is not automatic, scroll bars dont work, double clicking is gone, etc. But, HTML lists are fast and, combined with Tony Goldsteins template modifications, the Java classes will never be downloaded.
Tear off your shirt, pound your chest, roll your own
(This subject was fully discussed in "Dual-Dual-Mode Apps," Clarion Online, 2, 4, November 1998.)
No Java, multiple columns and a total Web look and feel is also possible.
It should be no surprise that there is a price, a heavy one, for the speed and aesthetics. Not only do you lose everything mentioned above, you also lose the ability to edit or delete records (at least, without substantial additional code, but thats another story).
Because you create your own HTML, your code loses its connection to the queue from which the browse is built. This means that you have no way of knowing which record is highlighted and, therefore, which record to pass to the form.
Wouldnt it be nice if
we could have the best of both worlds?
Well, assuming you understand what the best of both is, you can.
The best of Clarion: A Clarion browse box is the result of a series of complex structures and interactions. The disk file is read into a View. The View is read to create a queue. This queue is what is actually displayed in the browse box (check the properties of a list box, then trace the label you find in the "From" prompt in the generated code). That is, the templates provide and maintain a queue for displaying records.
The queue is managed by ACCEPT, our event handler.
The browse-related events handled by ACCEPT include scrolling up and down, either one record or a page at a time, double clicking or pressing a button (like Insert, Change or Delete).
When the user presses a navigation key, ACCEPT determines whether or not the queue needs to be re-filled, doing so when required. If the window needs to be refreshed, it does so. For example, both re-filling the queue (which contains only as many entries as can fit in the list box) and refreshing the window are required by a page up or down.
This queue and its handling is the part that would be really nice to have while retaining
The best of HTML: small, fast, easily delivered and displayed files (theyre just ASCII, after all) that look web-ish.
So, what would be nice is to retain the templates queue building and ACCEPTs management of it but customize the HTML displayed instead of accepting the default HTML <select> or full Java list control.
HTML Style Pages with ACCEPT()
While it is not strictly necessary, you will get maximum benefit by applying Tony Goldsteins template modifications (http://www.cwsuperpage.com/kb).
If a list control template is populated, the queue building LOOP is present, by default. So, if we start with a standard browse or list control template on a window, the queue is taken care of.
The question, then, is: "How do we access the built in routines that fill the browse queue?" If we can do this, ACCEPT will automatically manage the queue for us and we can create an HTML shadow of the queue.
In the Clarion templates, we need access to the FillQueue Routine. The ABC templates give us direct access to the method in which items are added to the browse queue: BRWx.SetQueueRecord (called from BRWx.FETCH, which initially fills the queue, and several of the RESET methods).
Of course, the answer is that there is an embed where we can create a list box element (which automatically means that it is in the queue filling LOOP). In the Clarion templates, within the FillQueue routine is an embed, "Format an element of the browse queue." In ABC, the elements are added to the browse queue in Local Objects BRWx SetQueueRecord which takes us directly into BRWx.SetQueueRecord.
If you would rather have a single, long list of records and forego the need for navigation buttons, format your HTML in the ResetFromView method. Make sure your priority is set very low, 500 or under.
So, now we know where records are read into the queue and, by extension, where our alternate (HTML) formatting will go.
Other considerations:
To get an idea of what the HTML portion of your list will look like, you can use a visual HTML editor and "requisition" the code. If you wish to include column titles, dont forget to format them in your editor.
You will also want a string to hold your HTML "shadow list" while looping through the queue. Note that the HTML characters required for formatting can add up to several hundred characters for each record. So, be sure you make your variable long enough. 5-10,000 characters is a good starting point.
By using HTML for the display list, the connection to the underlying queue (the real list) is broken. You will want to set your standard buttons (Change, Delete, etc.) to "Hide if launched from browser."

Figure 2
These buttons, if displayed, will not work. So, there is no point in displaying them.
For the same reason, standard page navigation keys will not work. Therefore, we will need to supply our own page up and page down buttons. Optionally, you may add buttons for ScrollTop and ScrollBottom. While the standard keys will not work, we can post the relevant events and ACCEPT will recognize and act on them.
Next, we are going to substitute our own HTML for the standard list box. Therefore, we do not want to display the standard box. The temptation is to check "Hide if launched from browser" but this has undesirable consequences. If a control is hidden, no HTML will be generated, which is good. But, code in embeds related to the control will not be generated either, which is not good.
Instead, understanding that we want to substitute our HTML for the default HTML, use the option to omit default HTML generation. We do want HTML, just not that supplied by the templates.

Figure 3
There is one important effect that the standard list box has even though we will not use it. The height of the list box determines how many lines will be displayed. In turn, this determines how many records will be in the queue. Therefore, the design height of the list box indirectly determines how many records will be read when formatting our substitute HTML <table>. (No such luck with width however.)
Implementation
Unfortunately, it is not quite as simple as posting the appropriate events in the buttons embeds:
?NextPage, Accepted:
Clear(HTMLString)
Post(Event:PageDown,?Browse:1)
or
?PreviousPage, Accepted:
Clear(HTMLString)
Post(Event:PageUp,?Browse:1)
While these events will post, they will not have the expected effect. To get the desired result, we must ensure that a queue entry is in memory (i.e., emulate "selecting" an item). This provides the starting point for the next queue fill.
| Clarion Templates | ABC Templates |
Control Event Handling, before generated code. ?NextPage.Accepted Clear(HTMLString) Control Event Handling, before generated code. ?PreviousPage.Accepted Clear(HTMLString) |
Control
Event Handling, before generated code. ?NextPage.Accepted Clear(HTMLString) Get(Queue:Browse:1,Records(Queue:Browse:1) Free(Queue:Browse:1) Post(Event:PageDown,?Browse:1) Control Event Handling, before generated code. ?PreviousPage.Accepted Clear(HTMLString) Get(Queue:Browse:1,1) Post(Event:PageUp,?Browse:1) |
You may also want to consider defaulting these buttons to HIDE. Then, if you are running in a browser, unhiding them:
After Opening Window/end of ThisWindow.Init:
If WebServer.Active Unhide(?NextPage) Unhide(?PreviousPage) Enable(?NextPage) Enable(?PreviousPage) End
ABC Caveats
Management of the required queue data is much more straightforward in the Clarion templates. Using the ABC templates, the Previous Page button tends to go back to the beginning of the file rather than a single page.
There are methods that appear to be ideally suited to the task, BRW1.ScrollPage() and BRW1.Fetch for example, but they are protected. This means that they can only be called while the browse object is in scope (in simple English, this means from an embed within the browse object).
You can declare user defined events (401h and higher) and post those events to the browse in the Next and Previous buttons. Then, in BRW1.TakeEvent (within the browse object), you can call the ScrollPage or Fetch methods. But this is beyond the scope of this article.
Writing the HTML
There are two issues with the HTML. One is how to format it. The other is where to display it.
An important requirement of HTML is that you must have an opening tag and a closing tag. In the case of an HTML list, you can think of these as similar to header and footer bands in a report. While the HTML for each record will be created within the standard template loop, the header and footer need to be generated once (and only once) each.
Consider this table which displays three columns, with titles (line numbers added for reference):
1 <table border="0">
2 <tr>
3 <td>Last Name</td>
4 <td>First Name</td>
5 <td>Phone Number</td>
6 </tr>
7 <tr>
8 <td>pre:LastName</td>
9 <td>pre:FirstName</td>
10 <td>pre:PhoneNumber</td>
11 </tr>
12 </table>
Lines 1-6 are the tables header and line 12 is its footer. Lines 7-11 need to be repeated for each record.
In the "Format an element of the browse queue" or BRWx.SetQueueRecord embed:
HTMLString = Clip(HTMLString) & ‘<<tr><<td>’ & pre:LastName & ‘<</td> &|
‘<<td>’ & pre:FirstName & ‘<</td>’ &|
‘<<td> ‘ & pre:PhoneNumber & ‘<</td><</tr>’
handles formatting as records are read into the queue.
The final code will comprise three sections: writing the header HTML, writing HTMLString and writing the footer, in that order:
!Write the header
Target.WriteLN(‘<<table border="0"><<tr>’ &|
‘<<td>Last Name</td>’ &|
‘<<td>First Name</td>’ &|
‘<<td>Phone Number</td></tr>’)
!Write the list
Target.WriteLN(HTMLString)
!Write footer
Target.WriteLN(<</table>)
But, where?
We are using the standard templates, with all their standard functionalities, and substituting our HTML for the standard HTML. Indeed, we told the template to retain the control but generate no HTML for it because we intend to provide our own. So, we continue to use the standard templates: Internet, After generating HTML for Control(Browse:1), ensuring our code will not be written out until after the LOOP has completed (any Internet embed after the queue filling LOOP will do).
Problem
When you press the Previous Page button, your list may be sorted backward (this is more likely to occur in the Clarion templates).
This is because the queue is being filled from the current position back toward the beginning of the queue. The appropriate routine or method is called with "Direction = Backward." That means that we may be formatting HTMLString in reverse order.
Pretty obvious after checking out the generated code (plant palm firmly on forehead).
Solution
Any of a number of ways of handling this are possible (typical Topspeeder cleverness is shown to its best advantage by this sort of glitch).
For example, instead of reading Queue:Browse:1 directly to HTMLString in the appropriate embed, you could read it to an intermediate queue. Afterwards you would sort the work queue and format the HTML from the work queue. Minimal work and no overhead to speak of (queue to queue is all but instantaneous).
In this case, you would probably use a different embed for processing the work queue because youd need to FREE the queue and that cant be done in the embeds weve been considering.
However, it is also possible to access the templates queue directly, read it forward and format the HTML.
In the Internet, after generating HTML for control ... ?Browse:1, the queue has been completely filled. All we need to do is step through it and format our HTML there (i.e., immediately before writing the final HTML):
CLEAR(HTMLString)
Entries# = RECORDS(Queue:Browse:1) !Determine number of entries
LOOP I# = 1 TO Entries# !Loop through QUEUE
GET(Queue:Browse:1,I#) !Get record from QUEUE
IF ERRORCODE() THEN STOP(ERROR()).
HTMLString = Clip(HTMLString) & ‘<<tr><<td>’ & pre:LastName &|
‘<</td><<td>’ & pre:FirstName & ‘<</td>’ &|
‘<<td> ‘ & pre:PhoneNumber & ‘<</td><</tr>’
END !Queue entries loop
! -------- Write HTML for display ---------
!Write the header
Target.WriteLN(‘<<table border="0"><<tr>’ &|
‘<<td>Last Name</td>’ &|
‘<<td>First Name</td>’ &|
‘<<td>Phone Number</td></tr>’)
!Write the list
Target.WriteLN(HTMLString)
!Write footer
Target.WriteLN(<</table>)
Summary
Until now, we could have multi-column lists with Java or single column lists without the Java. To implement multi-column lists without Java meant forgoing Clarions built in queue management and managing the queue by hand (not a lot of fun).
By creative use of existing embeds, we can take advantage of the standard listbox queue and ACCEPTs event handling and still have multi-column, web-ish and Java-free lists.