CHAPTER 2: HANDLING THE FILE SELECTOR -------------------------------------- [NOTE: Words enclosed in asterisks (i.e. *word*) should be read as italicized text. This text file is copyright 1993 by Clayton Walnum. All rights reserved.] As we said in the previous chapter, the AES supplies us with several ready-made forms that we can use in our programs. Now that we know how to use one of the simpler forms, the alert box, we can move on to one of GEM's most frequently used form, the file selector. In this chapter, we'll learn to call up a file selector and to use the information it returns to create a full pathname for any file the user selects. --What Is a File Selector?-- If you've used your ST at all, there's no doubt that you're already familiar with the file selector. Virtually every GEM program written for the ST uses the file selector to get file information from users. The file selector's basic function is simple: it allows the user to select a file. However, because selecting a file requires the ability to move from one directory to another, as well as scroll through a list of filenames too large to fit into the selector's window, things are a little more complicated. Figure 2.1 shows a typical file selector. At the top is the directory line, which shows the directory the user is currently logged into. In the figure, the user is logged into the DEVPAC folder of the D: drive. Notice the *.* following the path. These wild cards mean that the file selector should show every file listed in the directory. If the file portion of the path was changed by the programmer to, say, *.PRG, only programs with a .PRG extension would appear in the file selector's window, as shown in figure 2.2. [INSERT FIGURES 2.1 AND 2.2 HERE] The user cannot change the file-filter portion of the pathname, but he can change the directory. There are three ways to do this: 1) by clicking the directory line, typing the new path, and then clicking the top of the file window; 2) by clicking a directory in the file window to move to that directory; or 3) by clicking the button in the upper- left corner of the file window to move back one directory. To the right of the file window is the filename field. This field is usually blank until the user selects a file. However, the programmer can give GEM a default filename to display when he calls the file selector box. The user can select a file three ways: 1) by typing the file into the filename field and then clicking the OK button; 2) by clicking the filename in the file window and then selecting the OK button; or 3) by simply double-clicking the filename in the file window. Besides its button and file bar, the file window also contains scroll bars, which allow the user to see files that are not currently displayed in the window. This scroll bar is operated by clicking it with the mouse pointer. --The Program-- In the CHAP2 folder of your *ST Assembly Language Workshop* disk, you'll find two files. PROG2.S is the source code for this chapter's sample program, which is reprinted at the end of this chapter. PROG2.PRG is the assembled, runnable program file. If you would like to assemble the program yourself, please consult your assembler's manual or refer to this book's Appendix A. When you run the program, a file selector box appears on the screen. Use the file selector to move from one directory to another. Finally, select a file, and then click the OK or Cancel button. If you select the cancel button the program will return to the desktop. If you select the OK button, the program will display the full pathname for the file you chose. Press Return to return to the desktop. --Getting the Current Drive-- Now, let's look at the program's code, to see what's happening. The program begins just like our previous one, by setting up a stack, releasing unused memory, and initializing the GEM application with a call to *appl_init*. After that, the fun begins. When you call the file selector box, you need to give it a path for its pathname line. This could be any path you like. However, it is standard practice to have the file selector already set to the currently active path, usually the path from which the program was run. To get this information for the file selector, we must first get the current drive letter and the current path. Then we must combine these into a single string. Right after our call to *appl_init*, you'll see the code in which we get the current drive letter. It looks like this: move #DGETDRV,-(sp) trap #1 addq.l #2,sp add #'A',d0 move.b d0,pathname move.b #':',pathname+1 The first three lines are a call to GEMDOS function #25, *Dgetdrv*, which returns in D0 a number representing the currently active drive. A 0 indicates drive A, a 1 indicates drive B, a 2 indicates drive C, and so on. Once we have the drive number in D0, we need to convert it to an ASCII character for our path string. We do this in line 4 above. The *#'A'* is just another way of representing the ASCII value of "A." By adding the ASCII value of "A" to the drive number, we end up with the drive letter we want. We did this type of conversion in volume 1 of *The Assembly Language Workshop*. The last step is to move the drive letter to the first byte of our pathname string, and place a colon in the second byte of the pathname string. This is accomplished in the last two lines above. Note that the label *pathname* represents the address of a 128-byte string. You can find this string in the program's BSS. Why do we need 128 bytes? Because the file selector will use this area to return the user's selected path to us. Because paths can be long, we must be sure to provide plenty of space. Otherwise, other data may get overwritten by the pathname string returned by the file selector. --Getting the Current Path-- Now that we have the current drive letter, we need to get the current path. The five lines of code following the call to *Dgetdrv* accomplish this task. The code looks like this: move #0,-(sp) move.l #pathname+2,-(sp) move #DGETPATH,-(sp) trap #1 addq.l #8,sp The above is a call to GEMDOS function #71, *Dgetpath*. As you can see, the call requires that three parameters be placed on the stack. The first is the number of the drive for which you want to retrieve the path. For this word value, 0 represents drive A, 1 represents drive B, and so on. The second parameter is the address of the buffer where *Dgetdrv* should store the path string. In the second line above, we're giving the function the address of the third byte of our pathname string. Why? Because we've already got the drive letter and a colon on the first two bytes. By giving the function the address of the third byte, it'll not only return the path string to us, but will also automatically tack it onto our drive specifier. The third and final parameter is the function number. --Pathnames, File Selector Style-- Even after we've got the entire pathname, we're still not ready to call up the file selector. We need to add a default file specification to the end of the path. The usual default is \*.*, which will allow the file selector to show every file in the current directory. However, you could use any default. For example, if you were asking the user for a DEGAS picture file, you'd probably want to use the default of \*.PI?, which filters out all files except those with extensions starting with PI. If you look at the sample program, right after the call to *Dgetpath*, you'll see where we add the \*.* to our path string. First, we must search for the end of the string. We do this by scanning the string from the beginning until we find a null. Once we've found the position of the null character, we simply tack on the \*.* characters. --Calling the File Selector-- Now, our pathname string is ready for the file selector box. We now bring up the file selector and let the user choose the file. The code in our sample program that brings up the file selector box looks like this: move #FSEL_INPUT,control0 move #0,control1 move #2,control2 move #2,control3 move #0,control4 move.l #pathname,addr_in move.l #filename,addr_in+4 jsr aes Because the file selector is an AES call (function #90, *fsel_input*), we must initialize the control array, as well as place the necessary values in the other arrays. With this call, the only other arrays we use are addr_in and int_out. We use the addr_in array to tell the file selector where the pathname and filename strings are. The file selector will also use these two strings to return the selected filename and pathname to us. The file selector will use the first two words of the int_out array to return an error code and a button code, respectively. The error code will be 0 if an error occurred or greater than 0 if no error occurred. The button code will be 0 for the cancel button and 1 for the OK button. Note that there is a slightly modified version of the *fsel_input* call for newer versions of GEM (Rainbow TOS) This new call, named *fsel_exinput*, allows the programmer to replace the "Item Selector" string that appears at the top of the file selector box with something more meaningful, such as "Choose file to load." A typical call to the new file selector looks like this: move #FSEL_EXINPUT,control0 move #0,control1 move #2,control2 move #3,control3 move #0,control4 move.l #pathname,addr_in move.l #filename,addr_in+4 move.l #string,addr_in+8 jsr aes There are only three differences between the *fsel_exinput* and the older *fsel_input* call. First, the function number for the new call, represented above by the constant FSEL_EXINPUT, is 91. Second, you must indicate that your addr_in array is three elements long by loading a 3 into control3. Finally, you need to store into the third element of addr_in the address of the string you want at the top of the file selector. Note that, on TOS versions earlier than 1.4, the *fsel_exinput* will generate a run-time error (bad function number). If you want to use this call safely, you must, within your program, check for the version of TOS under which your program is currently running. If the version is earlier than 1.4, you must stick with the old *fsel_input* function. --Constructing the Selected Path-- After the user selects a path and filename with the file selector, he closes the file selector by clicking the OK or Cancel buttons. If the value stored in the second element of int_out indicates that the OK button was pressed, we must build the final, complete pathname, using the information returned by the file selector. The chosen pathname will be in the *pathname* string, and the chosen filename will be in the *filename* string. The first step in constructing the final pathname is to scan the path string for the null that marks the end of the string. Once we've found the null, we must scan backward until we find a backslash character. We need to do this because the path, as returned from the file selector, still includes the default file specification. For example, the path may look like C:\UTILITY\*.*. We of course must replace the *.* with the actual file selected. Once we've found the backslash character, we tack the filename on. Using the example path above, our final path should look something like C:\UTILITY\FILECOPY.PRG. This final pathname contains all the information we need to access the chosen file. In the sample program, the code that creates the final pathname is fairly straight forward and heavily commented. You should have little trouble seeing how it works. After constructing the final pathname, the sample program simply displays the path on the screen and waits for the user to press a key, after which the application terminates and the program returns control to the desktop. --Conclusion-- The file selector is typical of most GEM forms: It provides a convenient way for users to give information to a program. Likewise, it makes it easy for the programmer to request this information, by using an device with which the user is already familiar. Every GEM program you write should use the file selector wherever it's necessary for the user to choose a file. --Summary-- * The GEMDOS function #25, *Dgetdrv*, returns the currently active drive in D0. * The GEMDOS function #71, *Dgetpath*, returns the currently selected path into a string whose address is one of the function's parameters. * The AES call #90, *fsel_input*, brings the file selector box up onto the screen. * On TOS version 1.4 or newer, the AES call #91, *fsel_exinput*, lets you bring up a file selector and replace the standard "Item Selector" string with a string of your own. * When the file selector returns the chosen pathname and filename, it's up to the programmer to use them to create the complete path.