The code for open-file is shown below. Add this code to frame.dylan.
define method open-file
(gadget :: <gadget>) => ()
let frame = sheet-frame(gadget);
let task-list = frame-task-list(frame);
let filename
= choose-file(frame: frame,
default: task-list.task-list-filename,
direction: #"input");
if (filename)
let task-list = load-task-list(filename);
if (task-list)
frame.frame-task-list := task-list;
refresh-task-frame(frame)
else
notify-user(format-to-string("Failed to open file %s", filename),
owner: frame)
end
end
end method open-file;
The method takes a gadget as an argument and returns no values. The argument is the gadget that is used to invoke it, which in the case of the task list manager means either open-menu-button (in the File menu of the application) or open-button (on the tool bar). The open-file method then sets three local variables:
frameThis contains the frame of which the gadget argument is a part. This is a simple way of identifying the main application frame.
task-list This contains the value of the frame-task-list slot for frame. This identifies the instance of <task-list> that is being used to hold the task list information currently loaded into the task list manager.
filenameThis is the name of the file that is to be loaded into the task list manager, and the user is always prompted to supply it.
The method choose-file (a method provided by DUIM) is used to prompt for a file to load. The portion of code that performs this task is repeated here:
choose-file(frame: frame,
default: task-list.task-list-filename,
direction: #"input");
This method displays a standard file dialog box so that the user can select a file on any disk connected to the host computer. For open-file, you need to supply three arguments to choose-file: the frame that owns the dialog, a default value to supply to the user, and the direction of the interaction.
You need to supply a frame so that the system knows how to treat the frame correctly, with respect to the dialog box. Thus, while the dialog is displayed, the frame that owns it cannot be minimized, resized, or interacted with in any way; this is standard behavior for modal dialog boxes.
In this case, supplying a default value is useful in that it lets us supply the filename for the currently loaded task list as a default value. It determines this by examining the task-list-filename slot of task-list (which, remember, is defined as a local variable and represents the instance of <task-list> in use). If this slot has a value, then it is offered as a default. (Note that if the currently loaded task list has never been saved to disk, then this slot is #f, and so no default is offered.)
The direction of interaction should also be specified when calling choose-file, since the same generic function can be used to prompt for a filename using a standard Open File dialog or a standard Save File dialog. In this case, the direction is #"input", which indicates that data is being read in (that is, Open File is used).
The rest of the open-file method deals with loading in the task list information safely. It consists of two nested if statements as shown below.
if (filename)
let task-list = load-task-list(filename);
if (task-list)
frame.frame-task-list := task-list;
refresh-task-frame(frame)
else
notify-user(format-to-string
("Failed to open file %s", filename),
owner: frame)
end
end
The clause
if (filename) ... end
is necessary to handle the case where the user cancels the Open file dialog: on cancelling the dialog, the open-file method should return silently with no side effects.
If a filename is supplied, then it is read from disk and converted into a format that is readable by the application, in the line that reads
let task-list = load-task-list(filename);
The function load-task-list is described in Section 5.3.1.4 on page 56.
The clause
if (task-list) ... else ... end
is necessary to handle the case where the filename specified does not contain data that can be interpreted by load-task-list. If task-list cannot be assigned, then the else code is run. This calls the function notify-user, which is a simple way to display a short message to the user in a message box.
If task-list can be assigned (that is, the contents of the specified file have been successfully read by load-task-list), then two lines of code are run. The line
frame.frame-task-list := task-list;
assigns the frame-task-list slot of frame to the value of task-list.
The line
refresh-task-frame(frame)
calls a method that refreshes the list of tasks displayed in the task list manager, so that the contents of the newly loaded file are correctly displayed on the screen. The method refresh-task-frame is described in Section 5.3.3 on page 64.