REALbasic Cross-Platform Application Development

Because one of the properties you are asked to identify refers to the kind of file types your application will be handling, this is a good point to introduce how REALbasic handles documents and folders and discuss the cross-platform implications of it all.

The way in which the folders and files are organized in each platform vary greatly. Each platform has standards for where applications, documents, preference files, and so on should be stored. Additionally, each platform uses a different file system with different requirements.

In this section I will review the FolderItem class and how it interacts with the underlying operating system; then I'll cover how each operating system organizes its files.

Table 4.1 documents the file systems in use on the three platforms:

Table 4.1. Windows, Macintosh, and Linux File Systems

File System

Characteristics

Operating System

FAT16

8.3 filenames

DOS

FAT32

Long filenames (255 character limit)

Windows 95

NTFS

Long filenames (255 character limit)

Windows NT

HFS

31 Character Limit

"Classic" Mac

HFS Plus

255 Character Limit

OS X

NTFS and HFS Plus are the two default file systems on Windows and Macintosh OS X, respectively. Windows application files end in .exe; Macintosh application files end in .app. You do not necessarily have to use extensions on other files, but REALbasic uses file extensions to detect file types, so my advice is to use them. The extension is also no longer limited to three characters. When compiling for Carbon, filenames are limited to 31 characters.

FolderItem Class

A FolderItem is a reference to both folders and files. Depending on which platform you first learned about computers, you may use the term directory instead of folders; I happen to use the two terms interchangeably.

Although folders and files may seem like entirely different things to you, they have a lot in commonhence, a single class to represent both.

Referencing a FolderItem: Paths and URLs

Folders and files are organized into hierarchical trees. At the root of the tree is the volumetypically a hard drive or some other storage deviceand this volume can contain folders or files of different types. All three platforms organize their folders and files in a different way. Figures 4.7, 4.8 and 4.9 show the basic structure for each platform:

Figure 4.7. Windows XP file structure.

Figure 4.8. Macintosh OS X file structure.

Figure 4.9. Linux file structure.

Momentarily I will show you how all these folders are supposed to be used, but for now, it's important to see that all three are organized into trees. You refer to a specific folder or file by its path. The path is the path you have to take from the root folder to get to the individual file. Referring to the Linux structure shown in Figure 4.9, consider the following path:

/usr/var/tmp/temp.file

The path starts at the root folder, which on UNIXlike systems is represented by a forward slash. If you were navigating this file structure in a graphical user interface, you would next find the usr folder and click it. Inside /, you'd look for var and click it, and so on, until you got to temp.file.

The / is the path separator. Each platform uses a different separator to separate the elements in the path.

Paths that show the entire path from the root of the volume are called absolute paths, but you can sometimes use a shortcut, called a relative path, to identify a file relative to another file.

Using the same Linux path from the preceding example, suppose I am in the var folder and want to refer to the temp.file relative to the var folder. I would write the path like this:

tmp/temp.file

You should notice that the path does not start with a forward slash (/). This indicates that it is a relative path and not an absolute path. It also starts with the child of the item being used as the point of reference.

This is important: on Linux, filenames and paths are case sensitive. That means the following paths refer to different files:

/usr/var/tmp/TEMP.file

Neither Windows nor Macintosh paths are case sensitive.

The same concepts apply to Windows as well, but with a different separator and a different way of referring to the root directory. Here is an absolute path to a similar file in Windows:

C:\WINDOWS\TEMP\temp.file

In Windows, the drive is represented by a letter, followed by a colon. This is customizable, but the primary volume is usually C:. The separator is a backslash (\) instead of a forward slash (/). An example of a relative path follows:

TEMP\temp.file

In this example, the path is relative to the WINDOWS folder. Also, remember that paths are not case sensitive on Windows, so I could just as easily do this:

temp\temp.file

And I would be referring to the same file.

Since OS X, Macintosh uses a UNIXlike approach to paths, but REALbasic throws a curve ball at you. It actually makes sense why they do this, but it can be confusing at first. Prior to OS X, Macintosh used the colon (:) as the file separator; it worked like the Linux path with the exception that it used a : rather than a /. Because REALbasic will compile on pre-OS X Macintosh computers, the native reference to Macintosh paths is the old-fashioned : rather than the current /.

Absolute Paths

FolderItem.AbsolutePath as String

This returns the absolute path in the native format of each platform.

FolderItem.URLPath as String FolderItem.ShellPath as String

For Windows, the FolderItem.ShellPath uses the short filename, which means that it must comply with the original DOS filename constraints. Each FolderItem must have a name of eight or fewer characters, and, at most, a three-character extension. Short filenames are automatically generated by taking the first six letters of the filename and appending a tilde (~) followed by a number. If there are no other files with the same first six letters, the number is 1. The number gets incremented if a file with the same first six letters exists. Directories (or folders) don't have extensions. If it's a file, the extension is truncated to three characters.

C:\Program Files\Microsoft Office\

can be truncated to

C:\PROGRA~1\MICROS~1

Remember that the paths are not case sensitive, but it is common to see the short name rendered in all uppercase, as was the practice when using DOS.

Although you commonly use URLs to locate remote files using the HTTP protocol, it can also be used to locate files on the file system as well. The URL path from the previous example is:

FILE:///Program%20Files/Microsoft%20Office/

The HTTP portion has been replaced with FILE to show that it's a file on the local file system. The URL has also been encoded, which means that the spaces have been removed. Finally, a URL always uses the UNIXlike practice of using forward slashes for the separator and not backslashes, even when using a Windows machine. This makes the URLPath property a truly cross-platform way of referring to the path of a FolderItem.

Getting a FolderItem

There are three ways to get a reference to a FolderItem. The first is with the New operator, and the other two use global functions GetFolderItem() and GettrueFolderItem(). The New operator works as follows:

Dim f as FolderItem f = New FolderItem("file.txt")

When you instantiate a FolderItem in this manner, you can pass it either an absolute or a relative path. In this example, I passed a relative path and when you do that, the folder in which the application resides is the folder that the path is relative to. Therefore, this FolderItem is a reference to a file called "file.txt" in my REALbasic application folder.

I can also pass an absolute path, but regardless of whether it is absolute or relative, I either have to use the application-specific absolute path or add an additional argument that signifies which form of path reference I am going to use.

The options are

FolderItem.PathTypeAbsolute FolderItem.PathTypeShell FolderItem.PathTypeURL

Note that if you use the class constant PathTypeURL as the format for your path, you won't have to worry as much about which platform your application is running on.

In addition to instantiating a FolderItem, REALbasic offers two global functions that make getting a reference to a FolderItem easy:

REALbasic.GetFolderItem(aPath as String, [aPathType as Integer]) as FolderItem REALbasic.GetTrueFolderItem(aPath as String, [aPathType as Integer]) as FolderItem

You can use these functions anywhere, anytime, and you can pass it a path and an optional argument specifying which path format to use, just like you do when using the New operator. The only difference between the two is that GetFolderItem() resolves aliases and returns a reference to the file the alias points to, whereas GettrueFolderItem() returns a reference to the alias itself, without resolving it. This is an important distinction because you can royally screw things upfor example, you want to delete the alias, while preserving the original file. To do this, you need to use GetTRueFolderItem(), like so:

Dim f as FolderItem f = GetTrueFolderItem("someAlias.txt") f.Delete

Otherwise, if you did the same thing, using the same path, with the only difference being that you used GetFolderItem() instead, you would have deleted the original file, and not the alias.

Relative Paths

There are many situations when it makes the most sense to use relative paths to refer to a folder or file; for example, it is more than likely that absolute paths are not cross-platform compatible. It also makes your application more flexible because you can move the root directory without changing any path reference other than the one that refers to the root directory itself. This is especially true if you are saving a reference to a path for use at a later time, such as saving path information in a preferences file that will be opened later.

There are also some platform-specific quirky reasons for avoiding absolute paths. Macintosh computers have the unusual capability to have more than one volume mounted with the same name. This is an issue only when using the Carbon, colon-delimited format for the absolute path.

REALbasic provides a way to store relative paths using the FolderItem.GetSaveInfo() method. It does not return a simple string representation of the relative path, however. REALbasic uses a string of binary data to represent the relative path, and at least one reason for doing this is that you can leverage some unique Macintosh qualities. If you use the data returned from the GetSaveInfo() method to open a file at a later date, Macintosh will find the file, even if it has moved, because of how Macs handle aliases.

FolderITem.GetSaveInfo(RelativeTo as FolderItem, SaveInfoMode as Integer) as String FolderItem.GetRelative(aSaveInfoPath as String) as FolderItem

These two FolderItem functions are used in tandem. GetSaveInfo() produces the binary data that references the file, and GeTRelative returns a file, based on the SaveInfo data that is passed to it. I'll use a somewhat artificial example to show you how this can be used:

Dim f,g, h as FolderItem Dim s as String f = GetFolderItem("C:\Apps") g = f.Child("myApp").Child("myPrefs.txt") s = g.GetSaveInfo(f, FolderItem.SaveInfoRelativeMode) h = f.GetRelative(s) MsgBox h.AbsolutePath // displays "C:\Apps\MyApp\myPrefs.txt"

In this example, I have a reference to an Apps folder in the variable f, along with a reference to a document called myPrefs.txt, which is in the myApp folder, which happens to be a child of the original Apps folder; this is variable g. I want to save the location of g relative to the f folder, so I call

s = g.GetSaveInfo(f, FolderItem.SaveInfoRelativeMode)

The value returned to s represents data that REALbasic will use to find g relative to f at some future time. If you were saving this in a preference file, you would first need to encode s in a format that can be saved as text while preserving the underlying data. A good way to do this is to encode it in Base64.

In the example, for the sake of convenience, I don't save it. I just use the string to get another reference to the file, which I get by calling this method:

h = f.GetRelative(s)

Although you will generally use GetSaveInfo() to get a relative path, you can also pass a class constant to GetSaveInfo() which causes it to store absolute path data. You still get a reference to the folder using GeTRelative(), but this ensures that the absolute path data is used. This means that if the folder you are using as the base reference has moved, an error will be generated because the file cannot be found in the original absolute path.

FolderItem.SaveInfoDefaultMode FolderItem.SaveInfoRelativeMode FolderItem.SaveInfoAbsoluteMode

How Folders and Files Are Organized

Each platform has a systematic way of organizing files that defines where system software, applications, and documents are stored. REALbasic simplifies the task of keeping track of the location of your files on different platforms in two ways. First, there is a group of global functions that return references to special folders, such as the preferences folders, for example. In REALbasic 2005, they have updated this functionality with the SpecialFolders function, which is always available and which returns references to a large number of special folders, each depending on which platform the user is on.

The available functions are as follows:

REALbasic.PreferencesFolder as FolderItem REALbasic.ApplicationSupportFolder as FolderItem REALbasic.DesktopFolder as FolderItem REALbasic.FontsFolder as FolderItem REALbasic.StartupItemsFolder as FolderItem REALbasic.SystemFolder as FolderItem REALbasic.TemporaryFolder as FolderItem REALbasic.TrashFolder as FolderItem

The following functions are available, but have been deprecated as of REALbasic 2005:

REALbasic.AppleMenuFolder as FolderItem REALbasic.ExtensionsFolder as FolderItem REALbasic.ControlPanelsFolder as FolderItem

These functions work as expected. To get a reference to the preferences folder, do this:

Dim f as FolderItem f = PreferencesFolder

The SpecialFolder() function is a more flexible approach. Pass the name of a special folder and it will return a reference to it if the folder exists, Nil if it doesn't.

REALbasic.SpecialFolder(aFolderName as String) as FolderItem

Because each platform uses a different set of special folders, I will document each platform separately.

Table 4.2. Macintosh File Special Folders

.AppleMenu

Nil

.ApplicationData

Mac HD:Users:{username}:Library:Application Support:

.Applications

Mac HD:Applications:

.Bin

Mac HD:bin:

.ControlPanels

Nil

.Cookies

Nil

.Desktop

Mac HD:Users:{username}:Desktop:

.Documents

Mac HD:Users:{username}:Documents:

.Etc

Mac HD:private:etc:

.Extensions

Nil

.Favorites

Mac HD:Users:{username}:Library:Favorites:

.Fonts

Mac HD:System:Library:Fonts:

.History

Mac HD:Users:{username}:Sites:

.Home

Mac HD:Users:

.InternetCache

Mac HD:Library:Caches:

.Library

Mac HD:Library:

.Mount

Mac HD:Volumes:

.Music

Mac HD:Users:{username}:Music:

.NetworkPlaces

Nil

.Pictures

Mac HD:Users:{username}:Pictures:

.Preferences

Mac HD:Users:{username}:Library:Preferences:

.Printers

Mac HD:System:Library:Printers:

RecentItems

Mac HD:Users:{username}:Library:Recent Documents:

SBin

Mac HD:sbin:

SendTo

Nil

SharedApplicationData

Mac HD:Library:Application Support:

SharedApplications

Nil

SharedDesktop

Nil

SharedDocuments

Mac HD:Users:Shared:

SharedFavorites

Nil

SharedPreferences

MacHD:Library:Preferences:

SharedStartupItems

Nil

SharedTemplates

Nil

ShutdownItems

Nil

StartupItems

Nil

System

Mac HD:System:

Templates

Nil

Temporary

Mac HD:private:var:tmp:folders.501:TemporaryItems:

Trash

Mac HD:Users:{username}:.Trash:

UserBin

Mac HD:usr:bin:

UserHome

Mac HD:Users:{username}:

UserLibrary

Nil

UserSBin

Mac HD:usr:sbin:

Var

Mac HD:private:var:

VarLog

Mac HD:private:var:log:

Windows

Nil

Table 4.3. Windows XP Special Folders

AppleMenu

C:\Documents and Settings\{username}\Start Menu\Programs\

ApplicationData

C:\Documents and Settings\{username}\Application Data\

Applications

C:\Program Files\

Bin

Nil

ControlPanels

Nil

Cookies

C:\Documents and Settings\{username}\Cookies\

Desktop

C:\Documents and Settings\{username}\Desktop\

Documents

C:\Documents and Settings\{username}\My Documents\

Etc

Nil

Extensions

C:\WINDOWS\system32\

Favorites

C:\Documents and Settings\{username}\Favorites\

Fonts

C:\WINDOWS\Fonts\

History

C:\Documents and Settings\{username}\Local Settings\History\

Home

Nil

InternetCache

C:\Documents and Settings\{username}\Local Settings\Temporary Internet Files\

Library

Nil

Mount

Nil

Music

C:\Documents and Settings\{username}\My Documents\My Music\

NetworkPlaces

C:\Documents and Settings\{username}\NetHood\

Pictures

C:\Documents and Settings\{username}\My Documents\My Pictures\

Preferences

C:\Documents and Settings\{username}\Application Data\

Printers

C:\Documents and Settings\{username}\PrintHood\

RecentItems

C:\Documents and Settings\{username}\Recent\

SBin

Nil

SendTo

C:\Documents and Settings\{username}\SendTo\

SharedApplicationData

C:\Documents and Settings\All Users\Application Data\

SharedApplications

C:\Program Files\Common Files\

SharedDesktop

C:\Documents and Settings\All Users\Desktop\

SharedDocuments

C:\Documents and Settings\All Users\Documents\

SharedFavorites

C:\Documents and Settings\All Users\Favorites\

SharedPreferences

C:\Documents and Settings\All Users\Application Data\

SharedStartupItems

C:\Documents and Settings\All Users\Start Menu\Programs\Startup\

SharedTemplates

C:\Documents and Settings\All Users\Templates\

ShutdownItems

Nil

StartupItems

C:\Documents and Settings\{username}\Start Menu\Programs\Startup\

System

C:\WINDOWS\system32\

Templates

C:\Documents and Settings\{username}\Templates\

Temporary

C:\Documents and Settings\{username}\Local Settings\Temp\

Trash

C:\Documents and Settings\{username}\Desktop\Recycle Bin\

UserBin

Nil

UserHome

Nil

UserLibrary

Nil

UserSBin

Nil

Var

Nil

VarLog

Nil

Windows

C:\WINDOWS\

Table 4.4. Linux Special Folders

AppleMenu

Nil

ApplicationData

Nil

Applications

Nil

Bin

/bin/

ControlPanels

Nil

Cookies

Nil

Desktop

Nil

Documents

Nil

Etc

/etc/

Extensions

Nil

Favorites

Nil

Fonts

Nil

History

Nil

Home

/home/

InternetCache

Nil

Library

/lib/

Mount

/mnt/

Music

Nil

NetworkPlaces

Nil

Pictures

Nil

Preferences

Nil

Printers

Nil

RecentItems

Nil

SBin

/sbin/

SendTo

Nil

SharedApplicationData

Nil

SharedApplications

Nil

SharedDesktop

Nil

SharedDocuments

Nil

SharedFavorites

Nil

SharedPreferences

Nil

SharedStartupItems

Nil

SharedTemplates

Nil

ShutdownItems

Nil

StartupItems

Nil

System

Nil

Templates

Nil

Temporary

Nil

Trash

Nil

UserBin

/usr/bin/

UserHome

/home/{username}/

UserLibrary

/usr/lib/

UserSBin

/usr/sbin/

Var

/var/

VarLog

/var/log/

Windows

Nil

Where Files Go

All three platforms organize the directory structure so that it is easy to distinguish between files that are for the use of an individual user and files that are on the file system for everyone to use.

User Directories

For all three platforms, every user has his or her own home directory, which can be found in the following locations:

Macintosh:

/Users/{username}

Windows:

C:\Documents and Settings\{username}

Linux:

/home/{username}

Applications

Applications are placed into specialized folders as follows:

Macintosh:

There are two Applications folders. The first is for applications usable by anyone logged in to the computer. The second is for the use of the user within whose home directory the Applications folder resides:

/Applications /Users/{username }/Applications

On computers that have both OS X and System 9 installed, there is a special folder for System 9 applications:

/Applications (Mac OS 9)

Windows:

C:\Program Files

Linux:

Application files usually are installed in bin directories, but there are several locations where you will find a bin directory in Linux, and each one has a particular purpose. The SpecialFolders class returns values for Bin, UserBin, Sbin, and UserSBin.

The only applications that should go in /sbin and /usr/sbin are applications that are for the use of system administrators. Basic commands that are used by all users go in /bin and /usr/bin. Neither of these locations are appropriate for you to install your REALbasic application. The programs that reside here are generally command-line programs that users execute in the shell.

If you have a command-line application to install that you want to be available to everyone on the system, the alternative is /usr/local/bin. This is where programs reside that are installed locally. You should install them here so that they will not be overwritten when a new version of the system software is installed. In most cases, your REALbasic applications should be stored in /home/{username}/bin.

/home/{username}/bin

Documents

Documents are stored in the following locations:

Macintosh:

/Users/{username}/Documents

According to Macintosh design guidelines, the documents directory should be used only for files created by the user; your program should not create anything automatically in this directory. (Your program can create directories there, but only at the user's discretion.) All other support files, which may include templates, graphics, data files, and so on that your application needs, but which are not created by the user, should go in the Application Support directory.

Windows:

C:\Documents and Settings\{username}\My Documents

Linux:

/home/{username}/

Preferences

There is no single, cross-platform way to handle preferences. What this means in practice is that you will probably have to come up with a cross-platform way to handle preferences if you want to avoid writing separate code for each platform.

Preference files are files that contain information that is saved between running your application and is usually tied to the user of the application, so that when two people use the same application, the preferences themselves are different.

REALbasic provides a method for accessing what it calls the preferences folder.

Dim Prefs as FolderItem Prefs = PreferencesFolder

Here are the paths to the folders that REALbasic returns on each platform:

Macintosh:

/Users/{username}/Library/Preferences

One advantage of sticking with the Plist format is that you can use the command-line application Defaults to manipulate the preferences for your application without actually having to do so within your application. This can be helpful during debugging if you want to clear preferences or set them to some default value between iterations.

Windows:

C:\Documents and Settings\{username}\Application Data

On Windows, there is no specific standard for handling user preferences. Although REALbasic returns the Application Data directory from PreferencesFolder(), many Windows applications use the Registry to store preferences. The Registry is a Windows-only item and REALbasic provides access to it through the RegistryItem class. For more information about the Registry, see the "Windows Registry" section later on in this chapter.

With respect to the current topic at hand, there may be some advantages to using the Registry for user preferences, but it creates a problem when programming in a cross-platform environment. Ultimately, this is the reason that the PreferencesFolder() function returns the folder it does. I recommend using this folder to store user preferences as well.

Unlike Apple's Plist format, there is no specific file format for Windows preferences. If you are using Plists for your Macintosh application, it may make sense to use the same file for Windows (and Linux for that matter). Java often makes use of properties files that use a very simple format of key/value pairs. Because I use REALbasic to write clients for Java-based content-management systems, I have a class that can read and write Java properties files. I often use this format when appropriate for preferences. The downside is that they are not suitable for any kind of hierarchical data, so when that is needed, a custom XML format is used. I also tend to stick with the Macintosh naming convention for files because it ensures that I won't overwrite any other files inadvertently, and it makes them easy to identify.

Linux:

/home/{username}

Linux and related systems usually store preferences files in the user's home directory, inside a directory whose name starts with a period. On all platforms, a file whose name starts with a period is invisible in the user interface. This is helpful with preferences files because you do not want users inadvertently overwriting them or manually adjusting them in most cases.

Again, within that directory you can store just about anything you want.

Configuration Files

It's also important to note that a preference file is different from a configuration file. Configuration files typically apply to the application itself and are not user specific. An example is a web server such as Apache. It has a configuration file called httpd.conf that stores systemwide configuration information. This kind of data is often stored in the etc directory on UNIX.

Macintosh:

/Library/Application Data

Windows:

On Windows, the Registry is likely the best place for this because it can accommodate and differentiate between user-specific and systemwide data. However, the following directory can work as well:

C:\Documents and Settings\All Users\Application Data

Linux:

Configuration files usually end in .conf and are available in the /etc Directory.

The files in the etc directories are static and do not change. No executable files can be installed here. The primary use of etc directories is for configuration files. User-specific configuration files should go in /home/{username}/etc. Otherwise, they should be installed in the appropriate etc directory. For example, if your application resides in /usr/local/bin, your configuration files should be in /usr/local/etc.

Log Files

Log files are stored in the following locations:

Macintosh:

Log files are stored in

/var/log

In Applications, Utilities is an application called Console that will enable you to view log files (a similar program called Event Viewer is available in Windows). It can be used to view any file that ends with the .log extension, but by default it reads the system and application logs and can be very helpful when debugging REALbasic applications.

Windows:

Many log files are scattered in the C:\WINDOWS folder. Some log files end in .log or .Evt;.Evt files are called Event logs and can be accessed in Control Panel, Administrative Tools, Event Viewer.

Linux:

/var/log

The var directories are used to store data that changesthe "var" is short for "variable." Because the data that is stored here is variable, the user under whose name the application is running must have write access to it. The kinds of variable data that go here are logs and caches.

File Types

Macintosh uses a system of four-character codes to designate application types and file types. It is my understanding that they are migrating away from this in favor of other means of linking a file extension to a particular application, but it is still in use and you can make use of it in your application.

You can register an app type with Apple at the following address:

http://developer.apple.com/datatype/

Case matters. I registered an application type and was surprised that I didn't run into any problems finding one still available. Apparently there are enough variants to satisfy everyone.

Mac also checks file extensions, and this can lead to some confusing behavior if you are not used to it. For example, you can have two different files with the same extension, both of which store text, but when you double-click the file, a different application launches to open the file. This is because the system determines which application to use based on the application code.

The file type, too, can be designated in a four-character code. You are most likely to run into this when working with the FileTypes class and the FolderItem class.

File Type Sets

The RSS Reader application will be working with RSS files, so the appropriate file types need to be established for it. There are several competing implementations for RSS, so I will create two file types.

In the Project Tab, click Add FileType Set, and a FileTypes icon will appear in the Project Editor. Select the item and name it RSSTypes. If you double-click the FileType icon, the RSSTypes tab will appear. In the upper-left side of this tab is an Add File Type button, which you should click to make the ListBox below it editable.

When you refer to file types in your code, you will refer to each individual type as if it were a member of the FileType set. (The FileType set is not a class, but you treat it as such.) Because this set is named RSSTypes, you would use that name, followed by the name of the type itself that is designated in the RSSTypes tab, refer to Figure 4.10:

Figure 4.10. FileType sets.

In this example, I created two types, one called RSS1 and the other called RDFRSS, to reflect the different RSS formats. I give each one a Display Name and an Object Name. In most cases, you should use the same name. The Object Name is the name you use when referring to it in code:

RSSTypes.RSS1 RSSTypes.RDFRSS

When you set the value for which file types Macintosh applications will accept on drops, you will be presented with a choice based on the file types you have established, as shown in Figure 4.11.

Figure 4.11. Macintosh application Accept File Type configuration.

All you need to do is select the check box beside the types that your application will accept and click the OK button.

Volume and VolumeCount Functions

Volume(anIndex as Integer) as FolderItem VolumeCount as Integer

Volumes are mounted drives on your computer. This includes local hard drives as well as mounted network drives, DVDs, and so on. The hard drive on which the operating system software resides is always Volume(0). Because your computer can have more than one drive mounted at a time, REALbasic provides the VolumeCount function to let you know how many you have. You get a reference to a volume by calling the Volume() function with the index position of the desired volume passed as an argument.

Because each platform uses a different way to describe a path to a folder, it's usually best to use REALbasic's cross-platform way to do it.

Volume(0).Child("Program Files").Child("Microsoft Office")

is equivalent to

C:\Program Files\Microsoft Office

Navigating the Directory Tree

Rather than using paths, REALbasic provides a group of methods that allow you to navigate the directory tree using Child and Parent properties.

FolderItem.Child(aName as String) as Folderitem FolderItem.Item(anIndex as Integer) as FolderItem FolderItem.TrueChild(aName as String) as FolderItem FolderItem.TrueItem(anIndex as Integer) as FolderItem FolderItem.Directory as Boolean FolderItem.Parent as FolderItem FolderItem.Count as Integer

Here is an example of how you can navigate the directory tree programmatically using recursion. This subroutine starts with a base folder and compiles a list of paths for all the descendent folders:

Function getList(aParent as FolderItem, aList() as String) Dim x,y as Integer y = aParent.Count For x = 1 to y aList.Append(aParent.Item(x).AbsolutePath) If aParent.Item(x).Directory = True Then getList(aParent.Item(x), aList) End If Next End Function

There are two important observations to make. The first is that the FolderItem.Item() method is an example of a 1-based index. Unlike Arrays, which start at 0, the first child FolderItem encountered is referenced using the number 1. Second, you may have noticed that I do not return a value. Arrays are passed by reference, so it doesn't need to return a value. aList() is a reference to the original Array, and each item added is added to the original Array.

File Properties

Parent refers to the directory that contains the FolderItem.

FolderItem.Parent as FolderItem

If you need to know when the file was created, or the last time it was modified by someone, you can check the following two properties:

FolderItem.CreationDate as Date FolderItem.ModificationDate as Date

The Length property indicates how large the file is in bytes:

FolderItem.Length as Integer

The following properties refer to different aspects of a file's name:

FolderItem.Name as String FolderItem.DisplayName as String FolderItem.ExtensionVisible as Boolean

The Name property refers to the actual name of the file, which includes the file extension. DisplayName is the name that the user sees in the user interface and may or may not include the extension, depending on how the computer is configured. The ExtensionVisible will let you know what the configuration is with respect to extension visibility. You should always use DisplayName for the name shown to the user, but when referring to the file in your code or in the path, you must use Name.

The property will return the file type, according to the platform the application is running on:

FolderItem.Type as String

Windows and Linux rely on the extension to determine the file type, but things are a little less clear on a Macintosh. There are some Mac-specific properties that impact this. Originally, Macintosh computers didn't use file extensions at alla lot of Mac aficionados looked down on file extensions as being a little archaic and not user friendly. To identify a file's type, they used a four-character code to represent it. For example, TEXT is the four-character code for a text file. In addition to a MacType, there is also a Creator code which is four characters long. The Creator associated with a file is what determines the application that opens when that file is double-clicked.

Now, Apple is moving away from creator codes and type codes and even uses file extensions. For those of you who are interested, they will be moving to a new system for identifying file types called Uniform Type Identifiers (UTI), and you can read about it here:

http://developer.apple.com/macosx/uniformtypeidentifiers.html

The FolderItem class also has properties that let you read the type and creator codes for Macintosh files. You encountered a reference to these in the FileTypes section:

FolderItem.MacCreator as String FolderItem.MacType as String

Here is a small sampling of some common types. I added single quotes so that you could see space characters (see the example for PDF), but they are not part of the four-character type code:

Table 4.5. Common File Types

Gif

'GIFf'

PDF

'PDF'

Text

'TEXT'

Unicode Text

'utxt'

Movie

'MooV'

Whether you call it a trash can or a recycling bin, the cute little receptacle that our platforms all have is really just a folder like any other folder, and you can get a reference to it as a property of any FolderItem. You can also get references to the temporary folder and the desktop folder.

FolderItem.TrashFolder as FolderItem FolderItem.SharedTrashFolder as FolderItem FolderItem.TemporaryFolder as FolderItem FolderItem.DesktopFolder as FolderItem

These are not the only ways to find out about these kinds of folders. REALbasic also has a SpecialFolders class that will give you access to a large range of special folders on each platform; it will be discussed in subsequent sections.

Permissions and File Access

One overriding difference between the two UNIXlike operating systems and Windows is that UNIX was designed from the ground up to be a multiuser operating system. Users on UNIXlike systems are organized into groups, and these groups are used to determine access permissions. As a consequence, all files and folders have an owner, and all files and folders belong to a group.

Permissions are categorized according to the user trying to do something to it. The user can be one of three things: the owner, a member of the same group as the file, or anybody else. This "anybody else" is usually referred to as the "world." In other words, if anybody has permission to read a file, you can say that it is "world-writeable." In REALbasic-speak, "anybody else" is referred to as "Other," which is fine, except that saying things like "other-writeable" doesn't quite sound right.

There are a few ways to find out what kind of permissions are available on any given file. The FolderItem class has a group of properties that you can check, and there is also a Permissions class.

Properties

The following properties are part of the FolderItem class.

FolderItem.Exists as Boolean

Although strictly not a permission, checking to see whether a file exists does seem to be a related idea. The FolderItem.Exists property returns a Boolean, and it lets you check to see if a FolderItem exists.

Dim f as FolderItem Dim tis as TextInputStream f = New FolderItem("myFile.txt") If f.Exists Then // Do Something Else tis = f.CreateAsTextFile End If

In this example, a FolderItem is instantiated and called "myFile.txt". Instantiating a FolderItem is not the same thing as creating a FolderItem. This is a good thing, because if you did create a FolderItem simply by instantiating a FolderItem class, you would run the risk of overwriting existing files. When you pass a filename or relative path to the FolderItem Constructor, it uses the application directory as the point of reference. In this example, I test to see whether the file exists after I instantiate it. If it does not exist, I create it as a text file.

I am also able to test to see whether the file is an alias:

FolderItem.Alias as Boolean

Finally, I am able to check to see what kind of access I have to the files:

FolderItem.isReadable as Boolean FolderItem.isWriteable as Boolean FolderItem.Locked as Boolean FolderItem.Visible as Boolean FolderItem.Permissions as Integer FolderItem.Owner as String FolderItem.Group as String

To understand the meanings of these FolderItem members, it's best to turn to the Permissions class to see how REALbasic handles permissions.

Permissions Class

The concept of permissions comes from the UNIX world and applies only to Linux and Macintosh. It's important because permissions "problems" can be the cause of any number of hassles, so it's good to get it right from the beginning.

First things first, there are a few concepts to know. Every file or directory has an owner, which is a user on the computer. Sometimes the owner is a real person, and other times it's a user account used for a particular purpose. The best example of this is root, which is the root user. The root user is all powerful and can read, write, or execute anything it wants to. You do not normally stay logged in as root for extended periods of time because it's a little risky.

Users also belong to Groups. A user's ability to read, write, or execute a file is set according to whether the user is the owner, and the group the user belongs to. Permissions can also be set so that anybody can access it; the following functions refer to this as "Other", so when you see the word "Other", read it as "Everybody". A file that is readable by everybody is also sometimes called world readable, in contrast to owner readable or group readable.

Permissions.OwnerRead as Boolean Permissions.OwnerWrite as Boolean Permissions.OwnerExecute as Boolean Permissions.GroupRead as Boolean Permissions.GroupWrite as Boolean Permissions.GroupExecute as Boolean Permissions.OtherExecute as Boolean Permissions.OtherRead as Boolean Permissions.OtherWrite as Boolean Permissions.StickyBit as Boolean Permissions.UIDBit as Boolean Permissions.GIDBit as Boolean

The FolderItem.Permissions class returns an integer, and this integer signifies the permissions available for the owner, the group, and everyone else. It does this by using a little bit of octal trickery. The basic permissions are these:

  • 4 Read Permissions

  • 2 Write Permissions

  • 1 Execute Permissions

If you want the owner to both read and write to a file, you add 4+2, and you get the number 6. If you want the owner to be able to read, write, and execute the file, the number used is 4+2+1, or 7.

You calculate this number for the owner, the group, and everybody else and list them in that order, so that if you had a file that everyone could read, write, or execute under all circumstances, you'd set the permissions to 777. If you want only the owner to be able to read it and write to it, but not execute it (let's say it's a text file), but no one else should be able to do anything, you'd set permissions to 700.

Your REALbasic application runs as the user who launched the program. This means that you could run your application on a computer without any problems, and someone else can run the same application on the same computer and find it unusable.

  • 3 Write and Execute (1 + 2)

  • 5 Read and Execute (4 + 1)

  • 6 Read and Write (4 + 2)

  • 7 Read, Write and Execute

Moving, Copying, and Deleting Files

The CopyFileTo(), MoveFileTo(), and Delete() methods let you perform some basic file operations such as copying, moving, and deleting files. The destination folder passed as an argument must be a folder and not a file, because the source folder will be copied or moved to inside the destination folder.

FolderItem.CopyFileTo(destinationFolder as FolderItem) FolderItem.MoveFileTo(destinationFolder as FolderItem) FolderItem.Delete

All of these methods share a common limitation: if the source FolderItem is a folder rather than a file, it must be empty before you copy, move, or delete it. Later on in this chapter, I provide an example that shows how to work around this limitation.

Whenever you use one of these methods, you should check the LastErrorCode property to see if any errors were generated:

FolderItem.LastErrorCode as Integer

This is a highly un-object-oriented way of handling errors, but it is a perfectly workable solution. The following table describes the potential errors.

Table 4.6. LastErrorCode Property Generated Errors

Constant

Value

Description

FolderItem.NoError

 

The last action taken was successful and no errors were generated.

FolderItemDestDoesNotExistError

100

When copying and moving a file, you must specify the destination directory into which the files will be copied or moved. This error will appear when that directory is nonexistent.

FolderItem.FileNotFound

101

Because a FolderItem reference can exist for a file or directory that does not actually exist, you might sometime find yourself trying to access a property of a FolderItem that is only valid for an actual filelike the CreationDate, for example. Doing so results in a FileNotFound error.

FolderItem.AccessDenied

102

In almost all cases, this is a permissions problem and the username the program is running under does not have adequate permissions to read the referenced file.

FolderItem.NotEnoughMemory

103

Simple enough: the file is too big for the amount of memory your computer has available.

FolderItem.FileInUse

104

The file has been opened by another application.

FolderItem.InvalidName

105

The name of this file is invalid. Bear in mind that valid or invalid filenames can differ according to the platform the application is running in.

In addition to moving, copying, and deleting files with the FolderItem, there's one more thing you can do with them that is of interestyou can launch applications:

FolderItem.Launch(Args as String, [Activate as Boolean])

All platforms have applications that are the default applications that are executed when a file with a given extension is clicked. For instance, if I double-click a file that ends with .html on my Windows computer, Internet Explorer is launched. If I do the same thing on my Macintosh, Safari opens up. The FolderItem.Launch() method provides a way to effectively do the same thing as double-clicking a file. To read an HTML file in Internet Explorer, I would do the following:

Dim f as FolderItem f = GetFolderItem("index.html") f.Launch()

If all I wanted to do was to do was launch an application, I would do this:

Dim f as FolderItem f = GetFolderItem("myApp.exe") f.Launch()

If I had arguments to pass, I could send them in the Launch() method. I also can optionally tell it to activate the application in question, which means I get to choose whether it launches in the foreground, in front of the current application, or behind the current application. The default value is TRue.

Extending FolderItem

As I said earlier, there are some severe limitations to how you can use the CopyTo() and MoveTo() functions. In a nutshell, you can't copy or move a folder if it has files and folders inside of it, which means that you have to manually move each file from one location to the next. This is a good opportunity to extend the FolderItem to give it the capability to move entire directory trees from one location to the next.

Although FolderItems can be subclassed, it's rarely much use to do so. The reason is that you often access them through the GettrueFolderItem() function, or something similar that returns a reference to a FolderItem, rather than situations where you instantiate the FolderItem itself. What this means to us is that subclassing FolderItem and extending it in that way is not a very viable option.

The ideal solution, as it turns out, is creating a new method or two in a module and using the Extends keyword to associate it with a FolderItem. The following example is for a method called DeepCopyTo(), which will copy a folder and all its contents into a new directory. It's based on an example that appeared in an earlier edition of the Language Reference that comes with REALbasic, but that wasn't available in the current release of REALbasic as I wrote this.

Sub DeepCopyTo(Extends source as FolderItem, destination as FolderItem) Dim newFolder as FolderItem If destination.Exists And destination.IsWriteable Then If source.Directory Then //it's a folder newFolder=destination.Child(source.name) newFolder.createAsFolder For i=1 To source.count //go through each item If source.Item(i).directory then //it's a folder source.Item(i).DeepCopyTo newFolder //recursively call this //routine passing it the folder Else source.Item(i).CopyFileTo newFolder //it's a file so copy it End If Next Else //it's not a folder source.CopyFileTo destination End If Else MsgBox "DeepCopyTo Failed for " + source.URLPath End If End Sub

In any application that includes the module that implements this method, you will now be able to call the DeepCopyTo() method to copy entire trees. The method starts off by checking to see that the destination directory actually exists, and that it is writeable, too. Because you cannot copy a directory that has any child elements, you have to create a new directory with the same name in the new location.

After the new directory is created, the method then loops through each child FolderItem of the source FolderItem. With each child, it tests to see if it is a directory or a file. If it's a file, it calls the CopyFileTo() method and then moves on to the next child.

If it's a directory, however, it does something interesting. It calls the DeepCopyTo() method on the child item. It has to do this because it cannot use the CopyFileTo() method. This is a technique called recursion, and there are a lot of situations where it can be particularly effective. In this case, you have no idea how deep the directory tree is, so coding the method in this way lets you effectively move a directory tree of any depth. (Up to a pointthere is a limit to how many recursions a programming language can take, and if you inadvertently put yourself into an infinite recursive loop, your program will eventually crash.)

A similar method could be developed for moving directories. You would replace the CopyFileTo() references with MoveFileTo() and add a means of deleting the original source directories and files.

Opening, Creating, and Saving Files

The first feature I'll add to the RSSReader is a cross-platform way to handle user preferences. On Windows, I will use the Windows Registry to store preferences, and on Macintosh, I'll subclass the Properties class from the previous chapter and use it as a simple mechanism for saving values. This example will illustrate how to write platform-specific code in REALbasic, as well as how to create and write files.

The new subclass must be able to read and write files from the file system. REALbasic provides methods for opening and saving a lot of different file types, which helps to address cross-platform differences. Most of them relate to saving graphic files and these are covered in Chapter 10, "Graphics." There are a two other methods worth reviewing at this stage: FolderItem.OpenAsBinary and FolderItem.SaveAsBinary. As you may have surmised, this reads and writes files in binary format.

FolderItem.CreateAsFolder FolderItem.CreateTextFile as TextOutputStream FolderItem.AppendToTextFile() as TextOutputStream FolderItem.OpenAsTextFile as TextInputStream FolderItem.CreateBinaryFile(aFileType as String) FolderItem.OpenAsBinaryFile as BinaryStream FolderItem.CreateVirtualVolume as VirtualVolume FolderItem.OpenAsVirtualVolume as VirtualVolume

The preferences file for the Preferences class will be a text file. If you want to get a reference to the appropriate preferences folder, you can do the following:

Dim f as FolderItem f = PreferencesFolder

Or you can use the SpecialFolder object:

Dim f as FolderItem f = SpecialFolder("Preferences")

The Properties example from the previous chapter showed how to read a TextInputStream object. Here's an example of a more detailed approach that takes into account some additional features of the FolderItem class:

Dim f as FolderItem Dim tis as TextInputStream f = GetFolderItem("somepath.txt") If f.Exists And Not (f.directory) Then tis = f.OpenAsTextFile While Not tis.EOF MsgBox tis.ReadLine Wend tis.Close Else MsgBox "An error occurred" End If

You can write a file using a very similar approach, except that you get a reference to a TextOutputStream. If you want to overwrite an existing file, do this:

Dim f as FolderItem Dim tos as TextOutputStream f = GetFolderItem("somepath.txt") tos = f.OpenAsTextFile tos.WriteLine("This is a line") tos.Close

BinaryFiles use BinaryStreams to read and write data. BinaryStreams will look familiar to you because they work very much like a MemoryBlock, the only difference is that you read it in sequence. There is a Position property that tracks where you are in a file and automatically adjusts itself whenever you read some data. You can also set the value of the Position property and thereby read from anywhere in the file. Unlike reading text files, which have to be read sequentially, binary files do not. If you don't want to read a text file sequentially, just open it as a binary file and read it that way. The difference is that you won't automatically be able to read lines. Rather, you'll have to select a number of bytes to read, or read characters one by one until you encounter a new line, if line numbers make a difference to you. A lot of the parsing that I did in Chapter 2 could also be done with a binary stream.

Another file type is called a VirtualVolume. A VirtualVolume is a single file that the developer can interact with as if it were a volume. This means that you can create folders in a VirtualVolume and create both text and binary files as if they were real, separate files. On the computer, all that is created is one file. You would use a VirtualVolume if you wanted to create a file format for your application that contained both text and binary data. To be example-efficient, I'll show you how to create a VirtualVolume and write both a text file and a binary file to it.

In this artificial example, I create a VirtualVolume that saves a text file with a string of text, plus a binary file that stores a double and an integer. To create a virtual volume, do this:

Dim vv As VirtualVolume Dim aFile As FolderItem Dim newVirtualFile As FolderItem Dim tos As TextOutputStream Dim bs as BinaryStream Dim d as New Date // in my neverending quest for example-efficiency

aFile = GetFolderItem("somefile.txt") //not a directory, but a file, turning aFile into a VirtualVolume If aFile.Exists Then vv = aFile.OpenAsVirtualVolume Else vv = aFile.CreateVirtualVolume End If If vv <>Nil Then newVirtualFile = vv.Root.Child("VirtualFile.txt") tos = newVirtualFile.CreateTextFile tos.Write "This is some important text." tos.Close bs = newVirtualFile.CreateBinaryFile("") bs.WriteDouble(d.TotalSeconds) bs.WriteLong(Ticks) bs.Close End If

You can read the file like so:

Dim vv As VirtualVolume Dim aFile As FolderItem Dim vTextFile As FolderItem Dim vBinFile as FolderItem Dim tis As TextInputStream Dim bs as BinaryStream Dim d as New Date Dim aTick as Integer aFile = GetFolderItem("somefile.txt") //not a directory, but a file, turning aFile into a VirtualVolume If aFile.Exists Then vv = aFile.OpenAsVirtualVolume Else Return End If If vv <> Nil Then vTextFile = vv.Root.Child("VirtualFile.txt") tis = vTextFile.OpenAsTextFile While Not tis.EOF MsgBox tis.ReadLine Wend tis.Close vBinFile = vv.Root.Child("BinaryData.bin") bs = vBinFile.OpenAsBinaryFile While Not bs.EOF d.TotalSeconds = bs.ReadDouble aTick = bs.ReadLong Wend bs.Close End If

One thing to note is that you don't have to pay attention to where you are in the binary file as you read the data because the position is automatically adjusted. Also, there are some limits to VirtualVolumes. You can only save text or binary files and you cannot use paths. You must use the Child() methods to refer to such files because paths do not work.

Категории