Building an outline
In the previous chapter we created versions of FileLister that gathered the names of files in a folder into a string; we then wrote that string first into a wptext window, then into a file on disk. Now let's modify FileLister yet again, so that it lists the names of the files into an outline.
An outline, like a wptext, is edited by first ascertaining that it is "the target" and then using editing commands which do not themselves specify what window is to be operated on.
Begin by creating a new outline to write into; call it workspace.FileOutline. Having done that, modify workspace.FileLister to look like this:
on fileLister ()
local (whatFolder)
if not file.getFolderDialog("Pick a folder to list:", @whatFolder)
return
local (theFile, s = "")
fileloop (theFile in whatFolder, 1)
s = s + theFile + cr
edit (@workspace.FileOutline)
op.wipe()
op.setLineText (s)
fileLister ()
(The verb op.wipe erases the whole contents of the "target" outline, just in case it has anything in it already.)
Run this. Hmm, it didn't work very well, did it? Our pathnames have come out as a single line, and if we have a lot of pathnames the information has been truncated to just the first 255 characters.
Clearly we need to pull "s" apart into individual pathnames and write each of them as an individual line of the outline. One way to do this is to use Frontier's string verbs involving fields. A field is that part of a string delimited by any character we care to declare as a delimiter; in this case, the return-character delimits the fields of "s" that we want.
In a loop, we can run through "s", pulling out the contents of each field and writing it to a new line in the outline, like this:
on fileLister ()
local (whatFolder)
if not file.getFolderDialog ("Pick a folder to list:", @whatFolder)
return
local (theFile, s = "")
fileloop (theFile in whatFolder, 1)
s = s + theFile + cr
edit (@workspace.FileOutline)
op.wipe()
local (x)
for x = 1 to string.countFields(s, cr)
op.insert (string.NthField (s, cr, x), down)
fileLister ()
Run this; it works much better.
We learn the number of fields of "s" delimited by return-characters, using string.countFields, and prepare a "for" loop using this information; in the loop, we obtain each field, which will be a pathname, using string.NthField, and write it into a new line of the outline using op.insert.
There is, however, a certain inefficiency in this method, because it contains two loops; first, in the "fileloop," we gather all the names of the files into one string; then, in the "for" loop, we pull that string apart again.
Let's combine the two loops so that there is no "s" at all: as we gather a pathname, we write it to the outline, like this:
on fileLister ()
local (whatFolder)
if not file.getFolderDialog ("Pick a folder to list:", @whatFolder)
return
edit (@workspace.FileOutline)
op.wipe()
local (theFile)
fileloop (theFile in whatFolder, 1)
op.insert (theFile, down)
fileLister ()
If we run this, it works fine. We are, however, still being somewhat slowed down by the fact that we are creating the outline visibly, before the eyes of the user. It is often desirable to do this; for one thing, it gives the user something to watch while a long action is taking place, and for another, it's sort of fun.
But we can achieve a speed increase by working upon workspace.FileOutline invisibly.
Targets and invisible outlines
The way to do this is to declare workspace.FileOutline to be "the target" without opening it for editing.
Subsequent window-editing commands will work upon it, but the window will never be physically opened, and the overhead of having to display our actions on the screen will be absent.
Setting what window should be "the target" is done with the target verbs; it is considered good practice to save the current value of "the target," just in case there already is one, and restore that value at the end, so as not to disrupt whatever situation was in force beforehand, but we won't bother with that here, because we know that our FileLister routine lives on its own, so that there is no significant "target" when we start.
Here is our new version:
on fileLister ()
local (whatFolder)
if not file.getFolderDialog ("Pick a folder to list:", @whatFolder)
return
close (@workspace.FileOutline)
target.set (@workspace.FileOutline)
op.wipe()
local (theFile)
fileloop (theFile in whatFolder, 1)
op.insert (theFile, down)
edit (@workspace.FileOutline)
fileLister ()
Here, we actually close the FileOutline window beforehand, in case it is still open, because if it is open and we declare it to be "the target" we will get exactly the same sort of live updating of the window we were getting before, and this is just what we are trying to avoid.
Having declared it the target and written our pathnames to it, we then open the FileOutline window so that we can see our outline, fully formed, at the end of the process.
Creating a new outline
Now let's really start getting powerful. Go into the workspace table, select the FileOutline entry, and delete it.
With both the outline FileOutline in this chapter and the wptext TestWindow in the previous chapter, we created the database entries manually beforehand, and then used them in scripts to display information.
This seems primitive and clumsy; let's have FileLister itself create the outline FileOutline before writing to it. Creation of any table entry can be performed with the "new" verb; this will overwrite an existing entry of the same name, so it is common to use it in combination with the "defined" verb which checks to see whether the entry already exists, like this:
if not defined (workspace.FileOutline)
new (outlineType, @workspace.FileOutline)
In this case, however, we actually want to overwrite any previously existing workspace.FileOutline, so we won't bother with this extra test.
(Before leaving "defined", though, we should stress that, as the example illustrates, it does not take as its parameter an address but rather an actual reference to a variable or database entry.)
Observe that the first parameter of "new" requires us to declare the datatype of the entry we are creating; the various datatypes are listed in the Kind popup menu at the bottom of any database table window, and a list of constants representing their names can be found at system.compiler.language.constants.
So now FileLister looks like this:
on fileLister ()
local (whatFolder)
if not file.getFolderDialog ("Pick a folder to list:", @whatFolder)
return
new (outlineType, @workspace.FileOutline)
target.set (@workspace.FileOutline)
local (theFile)
fileloop (theFile in whatFolder, 1)
op.insert (theFile, down)
edit (@workspace.FileOutline)
fileLister ()
We could, at this point, add various refinements. For instance, we might set the font and size of the outline window after we create it (this is done with various editMenu commands), and we might supply a turning beachball cursor while we are building our list, to give the user some feedback that something is going on:
on fileLister ()
local (whatFolder)
if not file.getFolderDialog("Pick a folder to list:", @whatFolder)
return
new (outlineType, @workspace.FileOutline)
target.set (@workspace.FileOutline)
editMenu.setFont ("Geneva")
editMenu.setFontSize (9)
local (theFile)
fileloop (theFile in whatFolder, 1)
op.insert (theFile, down)
rollbeachball ()
edit (@workspace.FileOutline)
fileLister ()
A much more interesting exercise would be to add depth to our file search, so that we end up with an outline that lists both files and folders, and the files inside those folders, and mirrors in its hierarchical structure the structure of the files and folders on your hard disk.
|