10.7 Informing the user

10.7.1 Aim of the report mechanism

Objects perform actions on behalf of the user. Sometimes it is desirable to inform the application user of progress, status or problems. In older applications, the action code often contained knowledge about the environment in which the object was embedded. This harms reusability and therefore XPCE provides a generic mechanism to deal with such messages.

10.7.2 The report interface

Whenever an object needs to inform the user, the programmer can use the `object->report' method. The definition of this method is below.

object ->report: type, format, argument ...
Inform the user. The message is constructed from format and the argument list. See `string->format' for a description of the C printf-like formatting syntax of XPCE. type describes the nature of the message and is used by the reporting mechanism to decide on the presentation-style. See section 10.7.2.1 for the available types and their meaning.

The object posting the report message will normally not be able to display the message. The main task of the implementations of this method in class object, visual, frame and dialog is to divert the report to an object that can present it. By default, this is either a label or the display object (@display).

The implementation of `object->report' will simply check whether there is a current event (see @event), which implicates the action was initiated by the user and then forward the ->report message to the `event<-receiver', which is normally the controller activated by the user. If there is no current event, the action must be initiated by a user query to the host-language and therefore `object->report' will print its feedback to the host-language console using `@pce->format'.

The implementation of `visual->report' will simply forward the message to its containing visual object. If the message arrives at an instance of class frame, the implementation of `frame->report' will broadcast the message to each of its windows, but avoid sending it back to the window it came from. If this fails and the frame is a transient frame for another frame, the report is forward to the main frame. Class dialog will look for an object named reporter. If it can find such an object (typically a label), the report is sent to this object.

If all fails, the report will arrive at the @display object, which takes care of default handling. See section 10.7.2.1 on how the different report types are handled.

10.7.2.1 Information types

The report type indicates the semantic category of the report and defines how it is handled. The following report types are defined:

10.7.3 Redefining report handling

There are two aspects in which the reporting mechanism can be redefined. The first concerns the location and the other the presentation of the report. The location is changed by defining the method <-report_to. All the generic implementations of this method will first invoke <-report_to on itself. If this method yields an answer, the report is forwarded to this answer. For example, class text_buffer defines a <-report_to that forwards all reports to its associated editor object.

The presentation is changed by changing the implementation of `label->report' or `display->report. As this determines the look and feel of an application, applications should be reluctant using this.

10.7.4 Example

The typical way to exploit the report mechanism is by attaching a label named reporter to a dialog window of the applications frame. For example, an application consisting of a menu-bar, browser and a graphical window window could choose to make place for a small dialog for feedback at the bottom. The following fragment would build the window layout of such an application:

reportdemo :-
        new(Frame, frame('Reporter demo')),
        new(B, browser),
        send(new(picture), right, B),
        send(new(MD, dialog), above, B),
        send(new(RD, dialog), below, B),
        send(Frame, append, B),
        
        send(MD, append, new(MB, menu_bar)),
        send(MD, pen, 0),
        send(MD, gap, size(0,0)),
        send(MB, append, new(File, popup(file))),
        send_list(File, append,
                  [ menu_item(load,
                              message(@prolog, load, Frame),
                              end_group := @on),
                    menu_item(quit, message(Frame, destroy))
                  ]),

        send(RD, append, label(reporter)),
        send(RD, gap, size(5, 0)),
        send(Frame, open).

Now suppose the implementation of the `load' action takes considerable time. The implementation below reads a file assuming each line in the file contains a word.

:- pce_autoload(finder, library(find_file)).
:- pce_global(@finder, new(finder)).

load(Frame) :-
        get(@finder, file, exists := @on, FileName),
        send(Frame, report, progress,
             'Loading %s ...', FileName),
        get(Frame, member, browser, Browser),
        new(File, file(FileName)),
        send(File, open, read),
        (   repeat,
                (   get(File, read_line, Line)
                ->  send(Line, strip),
                    send(Browser, append, Line),
                    fail
                ;   !,
                    send(File, close)
                )
        ),
        send(Frame, report, done).

The result is shown in figure 18.

Figure 18 : The `reporter' demo