Chapter 11. Files

In any application that you write, you’ll probably manipulate data. Some of this data you may want, or need, to keep—even after your application quits or the system restarts. Perhaps it is data that your application needs to run, or perhaps it is data that your users create and may wish to return to at a later time. If you want that data to be there when your application quits and is later restarted, you’ll have to store that information in a persistent form—that is, as a file.

In this chapter, you will learn some of the concepts that you’ll need to store and retrieve data in files in Mac OS X. You’ll also learn the tasks every application that stores and retrieves data should perform. As a final step, you’ll add code to the Moon Travel Planner application to open, display, save, and close an itinerary file for planning a trip to the moon.

Managing Files on the Mac

In our discussion of opening and saving files, we will focus on storing and retrieving user data, which most applications must do at some time or another. To store and retrieve user data on a physical storage device, there are two things that you must do:

  • Communicate with the physical storage device to transfer the data and organize it on disk.

  • Allow users to choose the data to retrieve and the locations in which to store data, and to make other, similar choices about the data that you are storing for the users.

    Note

    If your application needs to store its own data that doesn’t need to be visible to users, you can use the file management tasks that you learn here; you simply don’t have to interface with the user.

The Mac OS provides APIs that help you to manage these tasks. These are:

  • The File Manager, which helps your application manage the transfer of your data to the physical storage device

  • Navigation Services, which provides the interface for users to navigate through the file system and choose the destination or source of the information that they are saving or retrieving

The File Manager

The File Manager is that part of the operating system that handles the organization and transfer of data located on physical data storage devices. All of the operations that you perform to implement basic opening and saving functionality in your application are based on the most fundamental file system object: the file.

Files

A file is a named, ordered sequence of bytes stored on a volume. A volume is a portion of a physical storage device that is formatted to contain files. A volume can be the entire disk or just part of the disk. For instance, a CD is formatted as a single volume. The hard drive of your computer can be formatted to contain multiple volumes.

All computer users have encountered files. The average user is probably most familiar with documents, which are files that a user can open and edit (for example, the text of a term paper, written in an application that handles word processing, such as AppleWorks). Nonetheless, there are many other types of files. An email browser can store information about a given user’s network connection and email account in a preferences file, and a computer game can store information about the state of the game for a particular session. The executable for your application is also a file.

You should know the following before you start working with files:

  • How files are organized

  • How to identify a file

  • How to access a file

  • The size of a file

The organization of the file system

Let’s take a brief look at how files are arranged into a coherent system of data storage. Although your application does not, for the most part, need the information in this section to perform its basic file management chores, it provides some background for the file handling about which you will learn in later sections.

A file system is a way of organizing files and the information necessary to access them. Mac OS X uses a hierarchical file system to organize files on a volume. In the hierarchical file system used by Mac OS X, files are grouped into directories. These directories are also referred to as folders. A directory is a named collection of files and other directories. Each directory can contain any combination of files and other directories. The directory in which a file is located is that file’s parent directory. This holds true for directories as well; for each subdirectory, the directory within which it is located is known as the parent directory.

Each volume has a single root directory, which serves as the base of the directory hierarchy on that volume; all other directories are subdirectories of the root directory. In the example shown in Figure 11.1 the folder titled “MyVolume” is the root directory of a volume named “MyVolume”.

The hierarchical file system

Figure 11-1. The hierarchical file system

Note

The root directory of a volume is not the same as the volume itself. Although they may appear to be the same to the user (for instance, a user can double-click a volume in the Finder to navigate through the hierarchy on that volume), the root directory of a volume and the volume itself are represented and handled differently by the file system.

A volume appears on the desktop only after it has been mounted. For nonejectable drives, this occurs automatically at system startup. For CDs (and similar ejectable media), this occurs after the CD is inserted into the computer’s CD drive. When a volume is mounted, the File Manager assigns the volume a volume reference number. You can use this volume reference number to refer to the volume only for as long as it is mounted.

There are two different ways in which the hierarchical organization of the file system is represented:

  • The way in which the file system is represented to the user

  • The way in which the file system is represented on the physical storage media

The Finder and the file system

The Finder is responsible for representing the file system to the user. The Finder reflects the basic characteristics of the hierarchical file system. Volumes are represented by icons of disks, directories are represented by folder icons, and user-created files are represented by document icons. A given directory can contain any combination of directories and files; files cannot contain other files or directories. Volumes, directories, and files are identified explicitly by their names (which are displayed next to their icons) and identified implicitly by their location within the directory hierarchy. Thus, to a user and to the file system, the file named main.c in the folder MoonTravelPlanner is different from the file named main.c in the folder MyMTPProject. No two files or subdirectories within a given directory may have the same name. Figure 11.2 shows a typical Finder window in Mac OS X, representing one part of the file system hierarchy on the volume “Flower Power”.

Note

On HFS Plus and HFS volumes, the File Manager is not case-sensitive when comparing names, so, within a given directory, main.c and Main.c would both refer to the same file. On UFS volumes (available on Mac OS X), file names are case-sensitive, so main.c and Main.c would be different files. You will learn more about these volume types in the next section.

The file system, as portrayed by the Finder

Figure 11-2. The file system, as portrayed by the Finder

The volume format

The volume format is the physical representation of the file system organization on the data storage device. It defines the way that information is laid out on the volume. The default volume format used by the Mac OS is HFS Plus (HFS stands for Hierarchical File System). Mac OS X supports other volume formats, too; namely:

  • Universal File System (UFS); similar to the standard volume format of most Unix operating systems

  • HFS, which was the default volume format for Macintosh systems before Mac OS 8.1

  • UDF (Universal Disk Format), for DVD volumes

  • ISO 9660, for CD-ROM volumes

The two volume formats that you will encounter most frequently on Mac OS X are HFS Plus and UFS. The volume format not only specifies how all of the user files and folders are arranged on the volume, it also includes all of the structures needed to retrieve the data.

For the most part, unless you are writing a disk-checking utility or some other application that requires low-level access to the volume, you do not need to know the details of how the volume format represents this information; the File Manager takes care of interpreting this information and locating the appropriate structures on the volume. There are some cases, however, in which you may want to know about the particular volume format used by the system on which your application is running; for instance, that filenames on HFS Plus volumes are case-insensitive, but filenames on UFS volumes are not. If you try to create a file named MyFile in the same directory that contains the file myfile on an HFS Plus volume, the File Manager returns a duplicate filename error. The same attempt will succeed on a UFS volume, however.

If you’re still curious and would like to learn more about the volume formats available on Mac OS X, see the File Manager documentation in Carbon Help (available in the Project Builder Help menu).

Identifying a file

If you want to manipulate files, you need to know how to refer to them. When your application needs to open a particular file, you have to tell the File Manager which file you are referring to, in a way that the File Manager understands. The File Manager uses the FSRef data type to represent a file; therefore, you use FSRefs to specify files to the File Manager.

An FSRef structure uniquely identifies a file. The FSRef data type is an opaque structure that contains the identity of the volume on which the file resides, as well as the information that the File Manager needs to locate the file on that volume. Because this structure is opaque, its information is private to the File Manager; you cannot access the structure directly. To create a new FSRef or to access the information contained in an FSRef, you must use File Manager functions provided for that purpose.

You can use the File Manager function FSMakeFSRefUnicode to create an FSRef structure for a file. If you are creating a new file, you can also get an FSRef directly from the function that you use to create the file, FSCreateFileUnicode. To create an FSRef structure (or to create a new file), you must know:

  • The file’s name. Filenames consist of any sequence of 1 to 255 characters and are represented by the HFSUniStr255 data type. By default, the File Manager uses a Unicode encoding for filenames. Files located within the same directory must have unique names. If you try to create a file in a directory that uses the same name as a file that already exists within that directory, the File Manager will return an error.

    Warning

    You shouldn’t use non-printing characters in a filename. Non-printing characters in a filename can be very confusing when the filename is displayed to the user. The null character is illegal in filenames on Mac OS X. You’ve already seen that HFS Plus and HFS treat the case of names differently from UFS volumes. There may be constraints on filenames other than those imposed by the File Manager, depending upon the volume format.

  • The file’s location. A file’s location has two parts: the volume on which the file resides, and the file’s location on that volume. You specify the location of a file on a volume by specifying the directory in which it resides. The directory in which a file is located is called the file’s parent directory. In most cases, you specify the location of a file by the FSRef of the file’s parent directory. Because the FSRef already contains information about the volume on which the directory (and therefore the file) resides, you do not have to specify the volume separately.

    Note

    In some cases, you may not have an FSRef for the file’s parent directory, but you may have other information that you can use to specify its location. For instance, there are a number of programming interfaces (such as AppleScript, the Keychain Manager, and QuickTime, to name a few) that still refer to files with an FSSpec data type.

    The FSSpec data type is the structure that the File Manager previously used to represent files and directories, prior to Mac OS 9.0. If your application interfaces with a part of the operating system that uses FSSpec structures, you can create an FSRef from the FSSpec by using the function FSpMakeFSRef. If you need to convert your FSRef into an FSSpec to pass to another part of the operating system, you can call the File Manager function, FSGetCatalogInfo.

    Note, however, that an FSSpec may refer to a file that has not yet been created, but an FSRef cannot. If you know the volume on which a file is located and the directory ID (a number that uniquely identifies the directory) of the file’s parent directory, you can use the File Manager function PBMakeFSRefSync to create an FSRef for the file or its parent directory. For more information on these two functions, see the File Manager documentation in Carbon Help (available in the Project Builder Help menu).

The combination of a file’s name and its location uniquely identifies a file. Of course, you’re probably still wondering how you go about getting the name and location of the file. Most applications will do file handling while working with user files and will use Navigation Services to interface with the user for operations performed on these files. Navigation Services returns an FSRef for the file or directory chosen by the user. For an open operation, Navigation Services returns an FSRef to the file to open. For a save operation, Navigation Services returns the FSRef of the file’s parent directory. You will learn more about Navigation Services in Section 11.1.2.

Warning

You should note that an FSRef is used to refer to a file or directory only while the volume on which the file resides is mounted. To create a persistent reference to a file, which remains valid when the volume is unmounted, use the Alias Manager. For more information, see the Alias Manager documentation in Carbon Help.

File access

To transfer information between your application and file, you need to access the file through an access path. When you open a file, the File Manager creates an access path to the file. This path specifies the route to be followed when accessing the file on the volume. This access path is described by a file reference number. The file reference number is a number greater than 0 that uniquely identifies the path that you have opened. You use this reference number to specify the path when you read data from, and write data to, the file. Multiple paths to the same file can be opened simultaneously; each path has a different file reference number.

For each open access path to a file, the File Manager also maintains certain information that you need to know about. Specifically, it maintains the file mark and the permissions. The file mark identifies the current position within the file. The file mark is the number of the next byte in the file that will be written or read; each time a byte is written or read, the file mark is moved. Unless you have just opened the access path (which automatically sets the file mark to the beginning of the file), you should set the position of the file mark before you begin to access data in a file, to ensure that the read or write operation begins where you actually want it to begin. For instance, before you read in a user’s moon travel itinerary, you probably want to set the file mark to the beginning of the file. If you don’t, the File Manager will begin reading data from the current position of the file mark, which may not be the file’s beginning.

As you create more complicated applications and you begin to save different types of data in a file (for example, you may choose to allow your users to store pictures of their destination spots with their moon travel itineraries), knowing where in the file your current operation will begin is even more important. If you start from an unknown spot, you run the risk of missing the boundaries between different types of data and improperly interpreting the contents of the file.

You should also be aware of the permissions associated with an access path to a file. The permissions specify which operations you can perform on the file via that access path. There are three basic types of permission you can have: permission to read the file, permission to write to the file, and permission to read and write the file. When you open a file, you request the type of permissions that you will need to get your job done. For instance, if you are opening a file that is editable by the user, you should ask for read and write permission; you should be able to read data from the file to display it to the user and write data to the file when the user wants to save the changes that he has made. On the other hand, if you are opening a document that the user should be able to read, but not change, you can request read-only permission, to display the information to the user.

While more than one access path can read a file at one time, only one path at a time may have write access. If you try to open a file and another access path is already open to the file that has conflicting permissions, your request for access to the file will be denied. Thus, if you ask for read/write permission to a file that is already being written to via another access path, the File Manager will return an error and an invalid reference number.

File size

Knowing how to identify and access a file is crucial to any type of file handling that you do; knowing how to express the size of a file is less critical, but still important. Although the simple file handling that you will do later in this chapter doesn’t really require that you know the file’s size, as your application becomes more complex and the amount of data that you wish to store grows, you will want to know how to measure a file’s size.

The maximum file size has several constraints. One constraint is the size of the volume on which it is located. Another possible constraint is the size of the value used by the File Manager to specify file sizes. On Mac OS X, this is a signed 64-bit value; if it were up to the File Manager alone, you could create a file that was 263 bytes, or 8 million terabytes.

Note

There may be additional constraints on the size of a file, depending upon the type of the volume on which the file resides. For more information, see the File Manager documentation in Carbon Help.

Not only do you need to know how large your file can be, you also need to know how to express the size of the file. The basic measure of a file’s size is the number of bytes that it contains. A file actually has two different sizes. These are the logical size and the physical size. This comes about because the File Manager allocates space to a file in chunks called allocation blocks, which contain multiple bytes. (It wouldn’t be very efficient to allocate space one byte at a time.) The size of an allocation block is variable from one volume to another and is determined at the time of the volume’s initialization.

The physical size (or physical end-of-file) is the number of bytes currently allocated to the file. The logical size (or logical end-of-file) is the number of allocated bytes in the file that currently contain data. The difference between physical and logical size is illustrated in Figure 11.3. The file that is shown fills up only 509 bytes of its last allocation block, but all 1024 bytes are allocated to the file. For the most part, you will be concerned with the file’s logical size; it is the file’s logical size that the File Manager returns when you call the function FSGetForkSize to determine the size of a file.

Physical and logical end-of-file

Figure 11-3. Physical and logical end-of-file

The typical tasks of file management

Although there are many, many ways in which you can manipulate files (you can make copies of them, move them, open them remotely.... there are all sorts of things you can do), most applications really need to perform only a handful of tasks. These are:

  • Creating new files

  • Opening files

  • Reading files

  • Writing files

Creating a new file

To create a new file, you use the File Manager function FSCreateFileUnicode. As you discovered in Section 11.1.1.1.4 you need the name and location of the file that you wish to create in order to identify it to the File Manager. (Remember, since the file does not yet exist, you don’t yet have an FSRef for the file). You pass the new file’s name and an FSRef structure identifying the parent directory of the file (that is, identifying the folder where the file will be created) to FSCreateFileUnicode. The File Manager then creates this file on the volume, in the directory which you specified, and returns an FSRef for the new file.

For the most part, you will need to create a new file when:

  • The user chooses the Save As command from the File menu to save the current document as a new file.

  • The user chooses the Save command from the File menu, but the document that she wishes to save does not yet have a file associated with it on the volume.

In both cases, you should use Navigation Services to ask the user for the name that she wishes to give to the file and for the location where she wishes to save the file. Navigation Services then returns an FSRef for the directory in which to place the new file (that is, an FSRef for the file’s parent directory) and the file’s name, which you can use to create the file. You will learn more about Navigation Services later in this chapter.

Opening a file

Before you can access or change the data that a file contains, the file must be open. To open a file, use the File Manager function FSOpenFork. When you call FSOpenFork, you must tell the File Manager which file you wish to open, the name of the file fork that you wish to open, and the type of access that you want (that is, the permissions). The File Manager returns the file reference number that identifies the access path that it has created to the file.

Note

A file on HFS Plus volumes, and on volumes of certain other formats, can have multiple parts, called forks, that store various types of information. Traditionally, Macintosh files have had two file forks: the data fork and the resource fork.

Although the File Manager (beginning with Mac OS 9.0) allows files to have any number of named forks on volumes that support them, Apple recommends that you store your data in the data fork of your file, for compatibility with file systems that may not recognize multiple file forks. In this book, all of the file handling that you perform is done on the data fork.

You do not have to explicitly create the data fork of the file; it is automatically created for you when you create a file (as is the resource fork). To get the name of the data fork, call the File Manager function FSGetDataForkName. The function returns a constant for the data fork name.

To specify the file to open, pass an FSRef structure identifying the file. If you recently created the file (for example, if the user chose the Save As command to create a copy of the file with a different name, you would generally open the newly created copy into the existing document window, so that it becomes the active file), you can use the FSRef that you obtained from FSCreateFileUnicode. If the user chooses the Open command from the File menu, you should use Navigation Services to ask the user to identify the file to open; Navigation Services returns to your application an FSRef that identifies the file chosen by the user.

The permissions that you request depend upon the file I/O operations that you wish to perform. If you need to write to the file, you should pass the constant fsRdWrPerm to the FSOpenFork function. This requests read and write privileges for the file. If another access path is already open to the file, your request for read and write permission will be denied. If reading the file is better than nothing, you can retry the request, calling FSOpenFork again with less restrictive permissions, using the constant fsRdPerm. This constant requests read access only.

Reading and writing data

Once you have an open path to the file, you can get down to the real nitty-gritty: reading the data stored in the file on disk, and writing data to the file on disk. To read data from the file, use the File Manager function FSReadFork. To write data to the file, use the function FSWriteFork. Both functions work in a similar manner. Given a file reference number to an open file, the functions read or write the specified number of bytes into or out of a buffer that you are responsible for providing. If you are writing to a file, the buffer that you provide should contain the data that you wish to write to the file. If you are reading from the file, the File Manager places the specified number of bytes into the buffer that you provide; you are then responsible for interpreting that information. In both cases, the File Manager returns the actual number of bytes read or written. This number will match the number of bytes requested, unless an error condition was encountered. The most common error result that you will encounter while reading a file is the end-of-file error, which tells your application that there are no more bytes to be read from the file. If you are reading from the file in chunks (rather than all at once), you can use the end-of-file error to determine that you have read all of the information from the file.

You can read and write bytes to and from the file one by one, all at once, or in any amount in between. The size of the buffer that you use is up to you; however, an I/O size of 4 KB or more is recommended for local volumes. Disk accesses are time consuming; having a larger I/O size reduces the number of disk accesses that your application has to make and improves the efficiency of your file handling. When you read and write itinerary files in Section 11.2.4.5 and Section 11.2.5.4 you will notice that we use a smaller I/O size. Our itineraries are small files, and the inefficiency of our file handling isn’t even noticeable. As you develop more complex applications and manipulate larger files, however, the efficiency of your file handling code will become increasingly important. Your users don’t want to sit around waiting for your application to open a 6 MB file that you read in increments of 256 bytes.

Note

For improved file handling efficiency, it is also recommended that you “align” your read and write requests. This means that, whatever the size of your buffer, it should be a multiple of 4 KB and that the starting position of the read or write operation (that is, the byte number at which the operation begins) is also a multiple of 4 KB. For more information on improving the efficiency of your file accesses, see the File Manager documentation in Carbon Help.

For both FSReadFork and FSWriteFork, you can also specify the position in the file from which the operation should begin. You specify a position by providing a base location and an offset into the file from that base location. You can tell the File Manager to start counting the offset from the beginning of the file, from the logical end-of-file, or from the current position of the file mark. If you specify the constant fsAtMark in the positionMode parameter of either FSReadFork or FSWriteFork or if you do not specify a starting position, the operation begins at the current file mark.

When writing a file, the File Manager transfers the data from your application’s buffer and writes it to the disk cache in memory. Similarly, when reading a file, the File Manager transfers data from the disk to the disk cache in memory. The File Manager uses the disk cache as an intermediate buffer when reading from and writing to a file. This is useful, for instance, when certain bits of information are going to be accessed frequently. In this case, it is more efficient for the File Manager to read this information from the disk cache than to read it off of the disk itself. The same holds true for writing data to the disk. If your application is performing numerous writes, it is more efficient for the File Manager to store the data in the disk cache until it has amassed a certain number of bytes. If you know that certain information is going to be accessed often, you can tell the File Manager to cache that data by setting the pleaseCacheBit bit in the permissions parameter to either of these functions. Similarly, if you know that certain information won’t be accessed very frequently, you can request that the File Manager refrain from caching the data by setting the noCacheBit bit.

Navigation Services

If you want to provide users with the ability to save and open documents, you’ll need to do more than just save and retrieve data from a physical storage device. You’ll need to provide a way for the user to specify which file to open and where to save a document. Navigation Services is the part of the operating system that provides the user interface for opening and saving documents.

When a user wants to open a file or save a new file, your application will use Navigation Services to display a dialog that requests information about the location and identity of the file to open or save. When the user responds to the dialog, Navigation Services returns to your application the information that it needs to identify the file to File Manager functions. Navigation Services uses a structure called the reply record to return this information.

The programming model

When you use Navigation Services to create and display dialogs, there are six major steps that you need to take. You will use the same basic steps for any of the dialogs that you can create with Navigation Services. Using the Save dialog as an example, you will perform the following steps, which are illustrated in Figure 11.4:

  1. Create the type of dialog that you need. In the example shown in Figure 11.4 you call the NavGetDefaultDialogCreationOptions function to set up the default dialog features and then call the NavCreatePutFileDialog function to create a Save dialog. (Navigation Services provides a different dialog creation function for each type of dialog)

  2. Display the dialog by calling the NavDialogRun function.

  3. Respond to the Navigation Services event. You handle this event with a callback function, called a navigation event handler, that you register with Navigation Services when you create the dialog. When an event occurs, Navigation Services calls your handler and passes it a constant that identities the type of event. There are several types of event that your event handler may receive, but the event that signifies that the user has responded to the dialog is the user action event (identified by the kNavCBUserAction constant, passed to your navigation event handler). In the example in Figure 11.4 the event is a user action event. We will take a closer look at the navigation event handler in the next section.

  4. After your navigation event handler receives the user action event (signifying that the user has dismissed the dialog, by clicking one of its buttons or pressing Return to select the default button), you process the dialog session information from your callback function. You get the session information from the reply record that you obtain by calling the NavDialogGetReply function, and you take the action appropriate to the user’s response. For example, if you obtain the kNavUserActionSave constant from the reply record, your application should save the appropriate file.

  5. After you are finished processing the dialog session, dispose of the reply record by calling the NavDisposeReply function.

  6. When Navigation Services calls your navigation event handler with the kNavCBTerminate event, indicating that it is finished with the dialog, dispose of the dialog reference by calling the function NavDisposeDialog.

Creating and displaying a Save dialog with Navigation Services

Figure 11-4. Creating and displaying a Save dialog with Navigation Services

The navigation event handler

You’ve seen the steps involved in creating and displaying a dialog using Navigation Services. A lot of the work is done by the navigation event handler that your application defines. Let’s take a closer look at the navigation event handler.

When you create a dialog with Navigation Services, you call the NavDialogRun function to display the dialog. NavDialogRun returns after its displays the dialog. Even though the function has returned, that does not mean that the dialog has been dismissed by the user. So, how do you know when the user has, in fact, responded to the dialog? Navigation Services notifies you when the user takes an action (or when another event that you may want to know about occurs) by calling your navigation event handler. You must tell Navigation Services which event handler to use, by passing a pointer to your event handler function when you create the dialog.

The navigation event handler that you define must have the following form:

void NavEventProcPtr (NavEventCallbackMessage callBackSelector, NavCBRecPtr 
callBackParms, void *callBackUD);

When Navigation Services calls your navigation event handler, it will pass your handler the following information:

  • callbackSelector. The event that triggered the call to your navigation event handler.

  • callbackParms. A structure of type NavCBRec that contains more detailed information about the particular dialog and the event. For instance, it contains the dialog reference of the dialog being displayed, information about the event in a NavEventData structure, information about the user action (if any), and other information that you may need to respond to the event.

  • callbackUD. Application-specific data that you specify when you create the dialog. If there is any information not already provided by Navigation Services that your navigation event handler needs to respond to events, you can pass a pointer to this information in the function that you use when you create the dialog. Navigation Services passes this information back to you when it calls your navigation event handler.

There are various events that your navigation event handler may receive, although it is not obligated to handle all of them. For example, the kNavCBCustomize event notifies your application that it can prepare any custom control information that it wishes to display with the dialog. This particular event and others like it are beyond the scope of this book; instead, we will focus on the events that you have to handle to get the information necessary for opening and saving documents. For more information on the other events that your navigation event handler may handle, see the Navigation Services documentation in Carbon Help (available in the Project Builder Help menu).

There are two events that you will always want to handle. First, your navigation event handler should handle the kNavCBUserAction event. This event signifies to your application that the user has responded in some way to the dialog. When your event handler receives the kNavCBUserAction event, you can get the kind of action that the user took by calling the NavGetUserAction function. The value that you get back tells your application exactly what action to take. For instance, if your application had called Navigation Services to put up a Save dialog and the user response generated a kNavUserActionSaveAs user action, your application could go ahead and retrieve the data from the session (by calling NavGetReply) and then save the document.

If the user chose to cancel the operation, the user action returned would be kNavUserActionCancel and your application should take no further action. If you put up the dialog in response to a user request to close the window or quit the application, you should respond to kNavUserActionCancel by halting the close or quit operation and returning to your application’s normal state. There are user actions for each of the different responses that a user may give; the particular responses that you may get from any given dialog depend upon the type of dialog.

The other event that your application should handle is the kNavCBTerminate event. Navigation Services calls your event handler with this event after the dialog has been dismissed by the user and is no longer needed. This gives your application a chance to do any cleaning up that it may need to do (such as freeing any memory that it has allocated) before disposing of the dialog itself.

Persistence

Before we look at the individual types of dialogs that you can display with Navigation Services, let’s take a look at an important feature of the Navigation Services API: persistence. Persistence is the ability of Navigation Services to store information, such as the last directory location visited by the user and the size and position of dialogs. This information is maintained on a per-application basis. Navigation Services separates preferences for Open and Save dialogs so that each dialog’s preferences are unique for each application. For example, the first time that your application brings up a Save dialog, the user may click the disclosure button to show the column view browser, allowing the user to navigate through the file system. (We’ll see more of this browser in the section on dialogs.) This browser is not shown by default. The next time that the application brings up the Save dialog, however, Navigation Services remembers that the user has revealed the column view browser. Navigation Services displays the column view browser without the user having to click the disclosure button again.

Navigation Services can also save different sets of preferences for the same type of dialog within an application. For instance, if your application uses an Open dialog for more than one purpose—say, to open a browser to go to a given URL and to open a local HTML file—you can request that Navigation Services use different preferences for each dialog by specifying preferences keys in the dialog creation options that you pass to the dialog creation function.

Navigation Services also remembers the last document opened for a given type of dialog and makes this the default selection the next time the dialog is used. If no location has been stored for a dialog or if the directory itself is not available (if its volume is unmounted, for example), the Documents folder becomes the default location.

If a dialog’s position has not been previously set, either by the user or your application, or if the dialog’s position otherwise can’t be shown, the dialog is displayed in the center of the main screen.

The Navigation Services dialogs

Now that we’ve discussed how creating and displaying dialogs with Navigation Services works, we’ll take a look at the dialogs that you use to solicit information from the user for opening and saving documents.

Standard user interface elements in Navigation Services dialogs

All Open, Save, and Choose dialogs share some basic user interface elements. By default, they appear in almost any dialog that you display using Navigation Services. These elements include:

  • A column view browser

  • A Where pop-up menu button

  • A default button

  • A Cancel button

  • A size box

The size box allows the user to resize the dialog. The Cancel button allows the user to cancel the operation, and the default button specifies the action that will be taken if the user presses Return. This is generally the action that is expected of the user, given the type of dialog that is being displayed. The Save button is the default for a Save dialog, for instance. The other two interface elements in the list above are more complex and require a bit more explanation.

The column view browser

The column view browser provides the primary user access to the file system from a Navigation Services dialog. An example of the browser list is shown in Figure 11.5.

The column view browser in a Navigation Services dialog

Figure 11-5. The column view browser in a Navigation Services dialog

When your application first displays a Navigation Services dialog, your application can specify a default location for opening, saving, or locating files. If you do not specify a default location, the column view browser shows the Documents folder. For each type of dialog that is opened afterwards, the browser defaults to the directory location in use when the user last closed that particular dialog.

Users can select multiple files from within an Open dialog by Shift-clicking within the column view browser or using the Select All command. For discontiguous selection, the user can Command-click within the browser.

The Where pop-up menu

The Where pop-up menu displays the current location and allows the user to navigate the file system hierarchy. The Where pop-up menu includes Favorite Places and Recent Places. This menu is shown in Figure 11.6.

The Where pop-up menu of a Navigation Services dialog

Figure 11-6. The Where pop-up menu of a Navigation Services dialog

Favorite Places includes all folders in the user’s Favorites folder; Recent Places contains the last five folders into which the user saved files. Navigation Services does not display files in the Where pop-up menu.

The Add to Favorites button allows the user to add the item or items currently selected in the browser list to the Favorite Places menu. Items may be folders, volumes, or servers.

Open dialog

Your application should display an Open dialog when the user selects the Open command from the File menu or when the user types the command-key equivalent, Command-O. The Open dialog allows the user to navigate to the file or files that he wishes to open. An example of the Open dialog is shown in Figure 11.7.

An Open dialog

Figure 11-7. An Open dialog

In addition to the browser list, the Open dialog also includes a Show pop-up menu. The Show pop-up menu allows the user to choose the file types displayed by the column view browser. The list of available file types is built from information provided by your application when it calls the NavCreateGetFileDialog function. The Show pop-up menu is optional; you can specify that Navigation Services leave it out when you create the dialog.

The first section of the Show pop-up menu contains an item called “All Readable Documents.” If the user selects this item, the browser displays all files that match the types that your application can open. You will learn how to specify the types of documents that your application can open in Chapter 13.

The second section of the Show pop-up menu contains an item called “All <app name> Documents” followed by your application’s “native” file types. Native file types are those types you provide in the typeList parameter of the NavCreateGetFileDialog function.

If you choose not to specify file types in the typeList parameter, you can show all files of a particular type in the browser list by using an application-defined filter function. Using a filter function would, for example, allow you to show all text files, regardless of which application created them.

The last section of the Show pop-up menu is reserved for the “All Documents” menu item. This option allows the display of all files, regardless of your application’s ability to open them directly.

In addition to the Show pop-up menu, the Open dialog includes a Go to: text field, into which the user can type file system paths (pathnames must begin with “/” or “~”) to navigate in the dialog.

The Open dialog also supports previews. When a user navigates through the file system using the column view browser and selects a file, a preview of the document is shown, using the actual document contents, so that she can see if she has selected the correct file. An Open dialog with preview is shown in Figure 11.8.

An Open dialog with preview

Figure 11-8. An Open dialog with preview

When the user selects a file to open and clicks the Open button (or presses Return), Navigation Services notifies your navigation event handler of the user action. Your event handler retrieves the user’s selection in the reply record, just as in the example shown in Figure 11.4. Your application can use the FSRef structure returned in this reply record to call the File Manager and open the file.

If the user action shows that the user clicked the Cancel button, your application should return to its previous state.

For further guidelines on setting up an Open dialog, see Inside Mac OS X: Aqua Human Interface Guidelines (listed in Appendix A.)

Save Changes

Your application should display a Save Changes dialog when a document has unsaved changes and the user closes the document window, selects the Quit command from the application menu, or types the command key equivalent of either command. The Save Changes dialog allows users to save changes or discard changes to a particular document or application. A Save Changes dialog is shown in Figure 11.9.

A Save Changes dialog

Figure 11-9. A Save Changes dialog

The default button for the Save Changes dialog is the Save button. When the user clicks this button, Navigation Services notifies your navigation event handler, and your event handler retrieves the user action. If the document has been previously saved, your application should call the File Manager to save the changes to the existing file. If the document has not been saved, your application should bring up a Save Location dialog (described in the next section), to allow the user to specify where to save the document and what name to use for the file.

The Cancel button cancels the operation; if the user action that your navigation event handler retrieves indicates that the user has clicked the Cancel button, your application should return to its previous state. If the user clicked the Don’t Save button, on the other hand, your application should close the document without saving changes.

For further guidelines on setting up a Save Changes dialog, see the Inside Mac OS X: Aqua Human Interface Guidelines.

Save Location

When the user chooses the Save As command from the File menu, chooses the Save command for a document that has not yet been saved, or types the command key equivalent of either command, your application should bring up a Save Location dialog. The Save Location dialog allows the user to specify a name and location for a document. The Save Location dialog should also appear if the user clicks Save in a Save Changes dialog for a previously unsaved document.

The Save Location dialog has two states, collapsed and expanded. The collapsed Save Location dialog is shown in Figure 11.10.

A collapsed Save Location dialog

Figure 11-10. A collapsed Save Location dialog

When the dialog is collapsed, the user can enter a name and choose a frequently accessed location. The parts of the collapsed Save dialog are:

  • The “Save as” text field for the document name. The user specifies the filename for the new file in this field. This field can also act as a “Go to” field: if a user enters a pathname by typing “/” or “~” as the first character, the current location in which to save the file will become the location specified by the path when the user presses Return. The user cannot specify the name of the new file in the path; he must first clear the field and then type in the filename.

  • The Where pop-up menu, which we discussed in Section 11.1.2.4.3.

  • The Cancel and Save buttons. The Save button is the default.

Clicking the disclosure button in the collapsed dialog expands the dialog and displays:

  • The column view browser, which we discussed in Section 11.1.2.4.2.

  • The Add to Favorites button.

  • The New Folder button. If the user clicks this button, Navigation Services displays a dialog that queries the user for the new folder’s name.

The expanded version of the Save Location dialog is shown in Figure 11.11.

An expanded Save Location dialog

Figure 11-11. An expanded Save Location dialog

When the user clicks a button in the dialog, Navigation Services notifies your navigation event handler, which then retrieves the user action. If the user has selected a folder in which to save the file, using either the Where pop-up menu or the column view browser, and has clicked the Save button, your application should retrieve the reply record. From the reply record, your application can get the FSRef structure that specifies the folder in which to save the new file. You can then use this FSRef to create the save file, as explained in Section 11.1.1.2.1 and save the document.

If the user action shows that the user clicked the Cancel button, your application should return to its previous state.

For further guidelines on setting up a Save Location dialog, see Inside Mac OS X: Aqua Human Interface Guidelines.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.140.197.10