(* Copyright (C) 1992, Digital Equipment Corporation *) (* All rights reserved. *) (* See the file COPYRIGHT for a full description. *) (* *) (* Last modified on Mon Jan 4 14:13:13 PST 1993 by mhb *) (* modified on Sat Jan 2 22:26:30 PST 1993 by meehan *) (* modified on Tue Jun 16 13:08:54 PDT 1992 by muller *) <* PRAGMA LL *> (* A "FileBrowserVBT" displays the files in a directory, and allows the user to traverse the file system and to select one or more files. There are two additional widgets that can be associated with a "FileBrowserVBT". A {\em helper} \index{FBHelper} is a type-in field that displays the pathname of the directory and allows the user to type new pathnames. A {\em directory-menu}\index{FBDirMenu}\label{FBDirMenu} is a menu containing the names of each level in the directory tree, with the root at the bottom; you can go to any level in the tree by selecting the appropriate item in the menu. There are two user actions, selecting and activating. \begin{itemize} \item The user may {\it select} items, either by single-clicking on an item to select just that one, or by single-clicking and dragging to select a range. Shift-clicking adds to the selection. A change in selection is reported to the client by invoking the "selectItems" method. The client can read the current selection with "GetFile" or "GetFiles". \item The user may {\it activate} an item, either by double-clicking on it, or by typing its name in the helper followed by a carriage return. Activation of a {\it file} is reported to the client by invoking the "activateFile" method, whose default is a no-op. Activation of a {\it directory} is reported by invoking the "activateDir" method, whose default behavior is to call "Set" to display the activated directory. The client can distinguish between a double-click and a carriage return by looking at the "AnyEvent.T" passed to the activation method. A double-click will be reported as an "AnyEvent.Mouse", and the carriage return will be an "AnyEvent.Key". \end{itemize} Directories are indicated in the display by showing a slash (``/'') after the name. A background thread calls "Refresh(v)" for every open filebrowser "v", once per second, to see whether it needs to be updated (although the distributed filesystem may cause a substantial delay before the change is noticed). The filebrowser's "redisplay" method also calls "Refresh". *) INTERFACE FileBrowserVBT; IMPORT AnchorSplit, AnyEvent, Font, ListVBT, PaintOp, Shadow, TextList, TextPort, VBT; TYPE T <: Public; Public = ListVBT.T OBJECT METHODS <* LL.sup <= VBT.mu *> init (font := Font.BuiltIn; colors: PaintOp.ColorQuad := NIL ): T; <* LL = VBT.mu *> selectItems (event: AnyEvent.T); activateFile (filename: TEXT; event: AnyEvent.T); activateDir (dirname: TEXT; event: AnyEvent.T); error (err: E); END; (* The call "v.init(...)" initializes "v" as a "FileBrowserVBT". If "v.painter" is a subtype of "ListVBT.TextPainter", "init" calls "v.paint.setFont(font)". The "selector" field must be either "NIL" (in which case a new selector is created) or a subtype of "FileBrowserVBT.Selector". The initial state of the filebrowser is the current working directory, as returned by "UnixUtils.GetWD". If that directory is not readable, '/' is used. The implementation calls "v.selectItems(...)" when the user changes the selection using the mouse. The implementation calls "v.activateFile(filename, event)" either when the user double-clicks on a file in the browser (in which case "filename" will be the first selected item), or when the user types Return in the helper (in which case "filename" will be set to the tail of the contents of the helper). To find the full pathname, use | GetDir(self) & "/" & filename Don't forget that if "activateFile" is being called because of a double-click, multiple files might be selected in the browser, even though you are given only one in the "filename" parameter. The implementation calls "v.activateDir(...)" when a new directory is activated. The normal action is simply to set "v" to view that directory, relative to "GetDir(self)". If an error occurs during the activation, the "error" method is invoked. The implementation calls "v.error(...)" when an error occurs during user action in "v", and the "Error" exception cannot be raised (e.g., because it happened in a separate thread). Some examples of errors are as follows: the user has typed a nonexistent directory in the path; the current directory has become inaccessible; the user has no permission to read the directory. By default the response to such errors is that nothing happens. By overriding this method, the client can provide better information to the user. *) TYPE Selector <: ListVBT.MultiSelector; (* If you create a subtype of "FileBrowserVBT" (which is a subtype of "ListVBT.T"), and you specify a selector for it, it must be a subtype of "Selector". *) EXCEPTION Error (E); TYPE E = OBJECT v : T; text, path: TEXT := "" END; (* The argument to the "Error" exception includes the "FileBrowserVBT" itself, along with a descriptive message and the pathname in question when the error occurred. *) (* \subsubsection{The Helper} The FileBrowser's helper (see page \pageref{FBHelper}) is a single-line "TextPort". Once the user types in the helper, any selected items in the browser are unselected. If the user types Return in the broswer, that will activate the name in the Helper. If an error occurs during the activation, the "error" method of the filebrowser to which the helper is attached will be invoked. Here's the definition of a helper: *) TYPE Points = REAL; Helper <: PublicHelper; PublicHelper = TextPort.T OBJECT METHODS init (hMargin: Points := 1.5; vMargin: Points := 1.5; font := Font.BuiltIn; shadow : Shadow.T := NIL ): Helper; END; (* The arguments to "init" are passed to "TextPort.T.init". The type "Helper" overrides the "filter" and "modified" methods of the type "TextPort.T". *) PROCEDURE SetHelper (v: T; helper: Helper) RAISES {Error}; (* Sets the helper for "v" to be "helper", and fills it with "GetDir(v)". *) (* \subsubsection{The Directory-Menu} The directory-menu is described on page \pageref{FBDirMenu}. *) TYPE DirMenu <: PublicDirMenu; PublicDirMenu = AnchorSplit.T OBJECT METHODS init (font := Font.BuiltIn; shadow: Shadow.T := NIL; n : CARDINAL := 0 ): DirMenu END; (* The "font" and "shadow" control the appearance of the text within the menu. As usual, if "shadow" is "NIL", then "Shadow.None" is used instead. The parameter "n" is used by "AnchorSplit" to determine the "ZSplit" in which to install the menu. *) PROCEDURE SetDirMenu (v: T; dm: DirMenu); (* Sets the directory-menu of "v" to be "dm". *) (* \subsubsection{FileBrowser options} *) PROCEDURE SetReadOnly (v: T; readOnly: BOOLEAN); (* If "readOnly" is "TRUE", then in subsequent calls to "v.activateFile(filename)", "filename" is guaranteed to exist. "activateFile" methods is guaranteed to exist. Otherwise, the user can type the name of a non-existing file into the helper. A newly initialized\/ "FileBrowserVBT" is not read-only. *) PROCEDURE SetSuffixes (v: T; suffixes: TEXT); (* "suffixes" provides a filter on the files to be displayed. By default all files in the directory are displayed, but if "suffixes" is not the empty string, only files with the specified suffixes (and all directories) will be displayed. The format of "suffixes" is a sequence of suffixes (not including the period) separated by non-alphanumeric characters (e.g., spaces). The special suffix "$" indicates ``files with no suffix''. Calling this procedure does not force "v" to be redisplayed. *) (* \subsubsection{Setting the displayed directory} *) PROCEDURE Set (v: T; pathname: TEXT; time: VBT.TimeStamp := 0) RAISES {Error}; (* Set the display state of v. *) (* The "pathname" may be absolute or relative; if it's relative, it is relative to the current displayed directory. "pathname" is absolute if | Text.GetChar(Filename.ExpandTilde(pathname), 0) = '/' If "pathname" refers to a non-existent or inacessible directory, "Error" will be raised. The exception will also be raised if "pathname" refers to a non-existent file and "v" is read-only. If "time" is not zero and there is a helper, then the helper will take the keyboard focus and will display its new contents in replace-mode, ready for the user to type something in its place. *) PROCEDURE Unselect (v: T); (* Put "v" into the no-selection state, without changing the current directory. Equivalent to "v.selectNone()". *) PROCEDURE Refresh (v: T) RAISES {Error}; (* Update the display without changing the directory. If "v"'s domain is not empty, and its directory has been "Set", and the directory has changed since the last time it was displayed, then "v" will be marked for redisplay. "Error" is raised only if the directory has become inaccessible for some reason; in this case, the browser goes to the empty state, so that if the client catches "Error" and takes no other action, the browser will be empty but not broken. *) (* \subsubsection{Retrieving from the browser} *) PROCEDURE GetFile (v: T; shortName := FALSE; normalize := TRUE): TEXT RAISES {Error}; (* Return the first item selected, or the empty string if no files are selected. *) PROCEDURE GetFiles (v: T; shortName := FALSE; normalize := TRUE): TextList.T RAISES {Error}; (* Return the currently selected files of "v", or "NIL" if none are selected. The list includes full pathnames unless "shortName" is "TRUE", in which case it includes only the name relative to the current displayed directory. Selected objects that are directories will have a ``/'' on the end. *) (* Normalizing is done during a call to "Set" and when the user types a carriage return in the helper. It consists of expanding a tilde if present, and checking to see that all directories in the pathname really exist and are accessible. It should be done during "GetFiles" if and only if that "GetFiles" is a synchronous result of some user action, e.g., pressing a button or invoking a menu command. Normalizing can lead to the discovery of errors, raising "Error"; however, no exceptions are raised if "normalize" is "FALSE". *) PROCEDURE GetDir (v: T): TEXT; (* Return the current displayed directory of "v". Always reports a full pathname, excluding a ``/'' on the end. Returns an empty string if "v" is in the ``empty'' state. *) PROCEDURE IsDir (filename: TEXT): BOOLEAN; (* A convenience procedure to test for a ``/'' on the end of "filename", indicating that it is a directory. It does not actually query the filesystem; it only looks at the form of the name. *) END FileBrowserVBT.