\newcounter{theexercise} \setcounter{theexercise}{1} \newenvironment{exercises}{% \section{Exercises} \begin{enumerate}}{% \end{enumerate}} \newcommand{\exercise}[1]{% \item[Exercise \arabic{theexercise}]\hfil% \addtocounter{theexercise}{1} \\ #1} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \chapter{Getting the Dialogue} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \section{Creating and Manipulating Objects} XPCE is an object-oriented system and defines four predicates that allows you to create, manipulate, query and destroy objects from Prolog: new/2, send/[2-12], get/[3-13] and free/1: \begin{code} ?- new(X, point(5, 6)). X = @791307/point \end{code} Created an instance of class `point' at location (5,6). In general, The first argument of new/2 provides or is unified to the {\em object reference}, which is a Prolog term of the functor @/1. The second argument is a ground Prolog term. The function describes the class from which an instance is to be created, while the arguments provide initialisation arguments. New/2 may also be used to create objects with a predefined object reference: \begin{code} ?- new(@s, size(100, 5)). Yes \end{code} Created an instance of class size with width 100 and height 5. We call @s a `named' or `global' object reference. \begin{code} ?- send(@791307, x, 7). Yes \end{code} Send {\em Manipulates} objects. In the example above, the X-coordinate of the point is changed to 7. The first argument of send is the object-reference. The second is the {\em selector} and the remaining arguments (up to 10) provide arguments to the operation. \begin{code} ?- get(@791307, distance, point(0,0), D). D = 9 \end{code} Get {\em requests} the object to return (compute) a value. In this case this is the (rounded) distance to the origin of the coordinate system. The arguments to get/[3-13] are the same as for send/[2-12], but get/[3-13] requires one additional argument for the return value. Finally, the following call destroys the created object. \begin{code} ?- free(@791307). Yes \end{code} \section{Creating Windows is More Fun} In the previous section we have seen the basic operations on objects. In this section we will give some more examples to clarify object manipulation. In this section we will use windows and graphics. \begin{code} ?- new(P, picture('Hello World')), send(P, display, text('Hello World'), point(20, 20)), send(P, open). P = @682375 \end{code} First created a picture (=graphics) window labeled `Hello World', displays a text (graphical object representing text) on the picture at location (20,20) from the top-left corner of the window and finally opens this window on the display: \postscriptfig[width=3in]{hello}{Screendump for the `Hello World' window} \section{Architecture} XPCE is a C-library written in an object-oriented fashion.% \footnote{It was developed before C++ was in focus. We are considering C++ but due to the totally different viewpoints taken by XPCE (symbolic, dynamically typed) and C++ (strong static typing) it is unclear whether any significant advantage is to be expected.} This library is completely dynamically typed: each data element is passed the same way and the actual type may be queried at runtime if necessary. Also written in C is a meta-layer that describes the functions implementing the methods and the C-structures realising the storage. The meta-layer is written in the same formalism as the rest of the system and describes itself. C-functions allow for invoking methods (functions) in this library through the meta-layer. This sub-system is known as the message passing system or PCE virtual machine. This machine implements the 4 basic operations of XPCE: new(), send(), get() and free(). The `host' language (Prolog in this document) drives the PCE virtual machine instructions through the Prolog to C interface. PCE predefines class `host' and the instance @prolog. Messages send to this object will call predicates in the Prolog environment: \begin{code} ?- send(@prolog, format, 'Hello World~n', []). Hello World \end{code} makes Prolog call PCE send() through its foreign interface. In turn, the message to the @prolog is mapped on the predicate format. Note however that both systems have their own data representation. Try \begin{code} ?- send(@prolog, member, A, [hello, world]). \end{code} PCE has nothing that resembles a logical variable and thus will complain that `A' is an illegal argument. Neither the following will work as Prolog lists have no counterpart in XPCE:% \footnote{The empty list [] is an atom, and thus maybe passed!} \begin{code} ?- send(@prolog, member, hello, [hello, world]). \end{code} And finally, Calls to PCE cannot be resatisfied: \begin{code} ?- send(@prolog, repeat), fail. No \end{code} Figure~\ref{fig:control} summarises the data and control flow in the XPCE/Prolog system. \postscriptfig[width=\textwidth]{control}{Data and Control flow in PCE/Prolog} \section{The Manual Tools} \label{sec:manualtools} The manual tools are started using the Prolog predicate manpce/[0,1]% \footnote{Quintus: user_help/0. To use manpce/1, load the library(pce_manual).} Just typing manpce creates a small window on your screen with a menu bar. The most useful options from this menu are: \begin{description} \item[File/Demo Programs] starts a demo-starter which also provides access to the sources of the demo's. \item[Browsers/Class Hierarchy] examines the inheritance hierarchy of PCE classes. Most classes are right below class object. Useful sub-trees are program_object (PCE's class en executable world), recogniser (event handling) and visual (anything that can appear on the screen. \item[Browsers/Class Browser] is the most commonly used tool. It displays attributes of a class. Various searching options allow for finding candidate methods or classes. The {\bf Scope} option needs explanation. By default it is set to `own', making the tool search only for things (re)defined on this class. Using scope `Super' will make the browser show inherited functionality too. Using scope `sub' is for searching the entire class hierarchy below the current class.% \footnote{The current implementation does not show {\em delegated} behaviour.} \item[Browsers/Global Objects] visualises all predefined (named) objects: @pce, @prolog, @display, @arg1, @event, etc. \item[Browsers/Search] Provides a {\sc wais} search facility for the entire hypercard system. \item[Browsers/Group OverView] provides an overview of functionally related methods. Typing in the window searches. \item[Tools/Visual Hierarchy] provides an overview of the consists of hierarchy of GUI components. \item[Tools/Inspector] to analyse the structure of objects. \item[History] allows you to go back to previously visited cards. \end{description} Manpce/1 accepts a classname, in which case it switches the ClassBrowser to this class. It also accepts a term of the form `class <-selector' or `class ->selector', in which case it will pop up the documentation card of the indicated card. Try \begin{code} ?- manpce((display ->inform)). \end{code} \begin{exercises} \exercise{% Start the PCE manual tools and find answers to the following questions: \begin{enumerate} \item What is the super-class of class `device'? \item Find the description of class `tree'. Which different layouts are provided by class tree? \item Which attributes describe the appearance of a box? \item Which classes (re)define the ->report method? \end{enumerate} } \exercise{% Examine the structure of the inheritance path visualisation as shown in the top-right window of the ClassBrowser using the VisualHierachy tool. } \exercise{% Class device defines compound (`nested') graphical objects. Define a predicate make_text_box(?Ref, +Width, +Height, +String), which creates a box with a centered text object. Test your predicate using: {\tt\obeyspaces ?- send(new(P, picture), open), \\ make_text_box(B, 100, 50, 'Hi There'), \\ send(P, display, B, point(50, 20)). } } \exercise{% Class tree and class node define primitives for creating a hierarchy of graphical objects. The method `directory <-directories' allow you to query the Unix directory structure. Write a predicate show_directory_hierarchy(+{\em DirName}) with displays the Unix directory hierarchy from the given root. Some methods and classes you might want to use are: class picture, class tree, class node, class directory and the methods `node->son', `directory <-directories' and `directory <-directory'. } \end{exercises} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \chapter{Programming Techniques} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \section{Control Structure of Graphical Applications} Interactive terminal operated applications often are implemented as simple `Question-Answer' dialogues: the program provides a form, the user answers the question(s) and the program continues its execution. Graphical interfaces often allow the user to do much more than just answering the latest question presented by the program. This flexibility complicates the design and implementation of graphical interfaces. Most interface libraries use an `event-driven' control-structure. \subsection{Event Driven Applications} Using the event-driven control structure, the program creates the UI objects and instructs the graphical library to start some operation when the user operates the UI object. The following example illustrates this: \begin{code} ?- send(button(hello, message(@prolog, format, 'Hi There~n')), open). Yes \end{code} This query creates a button object and opens the button on your screen. The button is instructed to call the Prolog predicate format/1 with the given argument when it is pressed. The query itself just returns (to the Prolog toplevel). Using this formalism, the overall structure of your application will be: \begin{code} initialise :- initialise_database, create_GUI_components. call_back_when_GUI_xyz_is_operated(Context ...) :- change_application_database(Context ...), update_and_create_GUI_components. \end{code} This type of control structure is suitable for applications that define operations on a database represented using the Prolog database or some external persistent database (see also chapter~\ref{chap:data}. Unfortunately `good' style Prolog programming often manipulates data represented on the Prolog runtime stacks because destructive operations on the Prolog database are dangerous and loose two important features of Prolog: logical variables and backtracking. The event-driven approach is commonly used by applications that present and/or edit some external (persistent) database. As long as name conflicts in the Prolog program and the global PCE name spaces (classes and named object references (e.g.\ @prolog, @main_window)) are avoided, multiple applications may run simultaneously in the same XPCE/Prolog process. For example the PCE manual runs together with the various PCE demo applications. \subsection{Asking Questions: Modal windows} Prolog applications that want to manipulate data represented on the Prolog runtime stacks cannot use the event-driven approach presented above. Unlike with event-driven applications, a single-threaded Prolog system can only run one task. The overall structure of such an application is: \begin{code} application :- initialise(Heap), create_GUI_components(Heap), process_events_until_computation_may_proceed, proceed(Heap), ... \end{code} The `process_events_until_computation_may_proceed' is generally realised using the methods `frame <-confirm' combined with `frame ->return'. Suppose we have a diagnose system that presents a graphics representation of the system to be diagnosed. The application wants to the the user's hypothesis for the faulty component. The window has implemented a selection mechanism, an ok button and a label to ask the question. The relevant program fragment could read:% \footnote{The variables are supposed to the instantiated to the proper UI components. The construct ``Diagram?selection'' denotes a PCE `obtainer'. When the button is pressed, PCE will send a message ->return to the frame. The argument to this message the return value of `get(Diagram, selection, Selection)'.} \begin{code} .... send(Label, format, 'Select hypothesis and press "OK"'), send(OkButton, message, message(Frame, return, Diagram?selection)), get(Frame, confirm, Hypothesis), .... \end{code} \section{Executable Objects} In the previous sections we have seen some simple examples of the general notion of `PCE Executable Objects'. Executable objects in PCE are used for three purposes: \begin{itemize} \tick{Define action associated with GUI component} This is the oldest and most common usage. A simple example is below: \begin{code} ?- new(D, dialog('Hello')), send(D, append, button(hello, message(@prolog, format, 'Hello~n', []))), send(D, append, button(quit, message(D, destroy))), send(D, open). \end{code} \tick{Code fragment argument as method parameter} The behaviour of various methods may be refined using a code object that is passed as an (optional) parameter. For example, the method `chain->sort' is defined to sort (by default) a chain of names alphabetically. When the chain contains other objects than names or sorting has to be done on another criterium, a code object may be used to compare pairs of objects. Suppose `Chain' is a chain of class objects which have to be sorted by name: \begin{code} ... send(Chain, sort, ?(@arg1?name, compare, @arg1?name)), ... \end{code} `?' is the class-name of a PCE `obtainer'. When executed, an obtainer evaluates to the result of a PCE get-operation: @arg1?name% \footnote{Equivalent to ?(@arg1, name): `?' is a Prolog infix operator.} evaluates to the return value of `get(@arg1, name, Name)'. \tick{Implement a method} A method represents the mapping of a `selector' to an `action'. The action is normally represented using a PCE executable object. Define methods is discussed further in section~\ref{sec:classes}. \end{itemize} \subsection{Procedures and Functions} Like send- and get-operations, PCE defines both procedures and functions. Procedures return success/failure after completion and functions return a value (a PCE object or integer) or failure. PCE defines the following procedures: \begin{quote} \begin{tabular}{ll} \bf message & Invoke a send-operation \\ \bf assign & Variable assignment (see also `var') \\ \bf if & Conditional branch \\ \bf and & Sequence and logical connective \\ \bf or & logical connective \\ \bf not & logical connective \\ \bf while & loop \\ \verb$==$ & Equivalence test \\ \verb$\==$ & Non-Equivalence test \\ \verb$>, =, =<, >, >=$ & Arithmetic tests \\ \end{tabular} \end{quote} and defines the following functions \begin{quote} \begin{tabular}{ll} \bf ? & Evaluates the result of get-operation \\ \bf progn & Sequence returning value \\ \bf when & Conditional function \\ \bf var & Variable (returning <-_value) \\ \bf *, +, -, / & Arithmetic operations \\ \end{tabular} \end{quote} A function is evaluated iff \begin{enumerate} \item It is the receiver of a get- or send-operation {\bf and} the method is not defined on class function or a relevant subclass. These classes define very few methods for general object manipulation, which normally start with an '_' (underscore). \item It is passed as an argument to a method and the argument's type-test does not allow for functions. This is true for most arguments except for behaviour that requires a function. \item It appears as part of another code object. \end{enumerate} \section{Creating PCE Classes} \label{sec:classes} A PCE class defines common% \footnote{Note that object can define individual methods and variables.} storage details and behaviour of its instances (objects). A PCE class is an instance of class `class'. Behaviour, variables and other important properties of classes are all represented using normal PCE objects. These properties allow the Prolog programmer to query and manipulate PCE's class world using the same primitives as for normal object manipulation: send(), get(), new() and free(). \subsection{The Prolog Front End} The XPCE/Prolog interface defines a dedicated syntax for defining XPCE classes that have their method implemented in Prolog.% \footnote{The XPCE/CommonLisp interface defines a dedicated syntax for defining XPCE classes and methods where the implementation is realised by PCE executable objects. See \cite{PCE:lisp}} The definition of an XPCE class from Prolog is bracketed by: \begin{code} :- pce_begin_class(ClassName, SuperClassName, [Comment]). :- pce_end_class. \end{code} These two predicates manipulate Prolog's macro processing and Prolog's operator table, which changes the basic syntax. An (additional) instance variable for the class is defined using \begin{code} variable(Name, Type, Access, [Comment]). \end{code} Where `Name' is the name of the instance variable, `Type' defines the type and `Access' the implicit side-effect-free methods: {\tt\{none,get,send,both\}}. The definition of a method is very similar to the definition of an ordinary Prolog clause: \begin{code} name(Receiver, Arg1:Type1, ...) :-> "Comment":: Normal Prolog Code. \end{code} Defines a send method for the current class. `Receiver', `Arg1', ... are Prolog variables that are at runtime bound to the corresponding XPCE objects. The body of the method is executed as any normal Prolog clause body. The definition of a get method is very similar: \begin{code} name(Receiver, Arg1:Type1, ..., ReturnValue:ReturnType) :-> "Comment":: Normal Prolog Code. \end{code} The body is supposed to bind `ReturnValue' to the return value of the method or fail. \subsection{Called methods} Some methods are called by the infra-structure of XPCE. These methods may be redefined to modify certain aspects of the class. The most commonly redefined methods are: \begin{table} \begin{tabular}{lp{5in}} initialise & Called when an instance of the class is created \\ unlink & Called when an instance is destroyed \\ event & Called when an event arrives on the (graphical) object \\ geometry & Called when the size/position of a (graphical) object is changed \\ \end{tabular} \end{table} \subsection{Examples} \subsubsection{Defining a two-dimensional matrix} \label{sec:2dvector} The first example defines a class two-dimensional matrix of fixed size. We want to be able to say: \begin{code} ?- new(@matrix, matrix(3,3)). ?- send(@matrix, element, 2, 2, x). ?- send(@matrix, element, 1, 2, o). \end{code} We will implement the two-dimensional matrix as a subclass of the one dimensional vector: \begin{code} :- pce_begin_class(matrix(width, height), vector, "Two-dimensional array"). variable(width, int, get, "Width of the matrix"). variable(height, int, get, "Height of the matrix"). initialise(M, Width:int, Height:int) :-> "Create matrix fom width and and height":: send(M, send_super, initialise), send(M, slot, width, Width), send(M, slot, height, Height), Size is Width * Height, send(M, fill, @nil, 1, Size). element(M, X:int, Y:int, Value:any) :-> "Set element at X-Y to Value":: get(M, width, W), get(M, height, H), between(1, W, X), between(1, H, Y), Location is X + (Y * (W-1)), send(M, send_super, element, Location, Value). :- pce_end_class. \end{code} \subsubsection{Defining a graphical matrix (table)} In the second example, we will define a graphical matrix of textual values and similar access functions to set/get the (string) values for the cells. We will use class device as a starting point. Class device defines a compound graphical object. \begin{code} :- pce_begin_class(text_table(width, height), device, "Two-dimensional table"). initialise(T, W:int, H:int, CellWidth:[int], CellHeight:[int]) :-> "Create from Width, Height and cell-size":: send(T, send_super, initialise), default(CellWidth, 80, CW), default(CellHeight, 20, CH), Pen = 1, between(1, W, X), between(1, H, Y), GX is (X-1) * (CW-Pen), GY is (Y-1) * (CH-Pen), send(T, display, new(B, box(CW, CH)), point(GX, GY)), send(B, pen, Pen), new(Txt, text('', center)), xy_name(X, Y, XYName), send(Txt, name, XYName), send(Txt, center, point(GX + CW/2, GY + CH/2)), send(T, display, Txt), fail ; true. element(T, X:int, Y:int, Value:char_array) :-> "Set text of cell at X-Y":: xy_name(X, Y, XYName), get(T, member, XYName, Text), send(Text, string, Value). xy_name(X, Y, Name) :- get(string('%d,%d', X, Y), value, Name). :- pce_end_class. \end{code} \begin{exercises} \exercise{% Extend class matrix with a get-method <-element. See what happens if you define the return-type incorrect (for example as `int'). } \exercise{% We would like to {\em visualise} a matrix using a text_table object, such that changes to the matrix are reflected on the text_table and modifications of the text_table are reflected in the matrix. XPCE does not provide a built-in model/controller architecture such as SmallTalk. There are various ways to relate objects to each other: object-level attributes (see `object <->attribute'), instance-variables and finally `hyper-links'. See class hyper. Try to realise the mapping using hyper objects. See class hyper and `object ->send_hyper'. What are the (dis)advantages of all these techniques? } \exercise{% Advanced usage! Suppose the matrix is used in heavy computations and modified often. This would imply passing through the mechanism described above many times. The display is not updated for each intermediate state (unless you explicitly request this using `graphical ->flush'). To avoid part of the computation as well, you can use the mechanism described by `graphical ->request_compute' and `graphical ->compute'. Try to implement this interface. } \end{exercises} \section{Types} XPCE is an untyped language like Prolog. This allows the system to define container classes such as class chain, vector, hash_table etc.\ in a comfortable fashion. Unlike Prolog however, XPCE {\bf allows} you to declare types. Types are used for four purposes: \begin{itemize} \tick{Documentation} Given the name, argument-names and argument-types for each method generally suffices to get a strong clue on what it does. \tick{Run-time trapping of errors} The earlier errors are trapped, the easier it is to locate the errornous statement in your source! \tick{Run-time conversion} Each type defines a validation method and a conversion method. If the first fails, the second is tried to convert the input value to the requested type. For example if the expected argument is of type `int' and you provide the string ``554'' it will gracefully convert ``554'' to the integer 554 and pass this value to the implementation of the method. \tick{Ensuring some level of consistency in the data} The predicate checkpce/0 collects all instances and verifies that each instance-variable is consistent with the type-declaration. \end{itemize} \subsection{Types are not (only) classes} Types are first-class objects that defined {\em validation} and {\em conversion}. Type objects are seldomly defined using \mbox{new(X,~type(...))}, but normally by conversion of a type-description to a type object. Here is a brief summary of the syntax used by `type <-convert': \begin{tabular}{lp{4in}} \tt Name=Type &Argument named `Name' of type `Type' \\ \tt Type ... &Zero or more of these arguments. Used in variable-arguments methods. \\ \verb$Type1|Type2$ &Disjunction (either of the types) \\ \tt Type* &The type or the constant @nil \\ \tt [Type] &The type or @default (optional argument) \\ \tt \{Name1,Name2\} &One of these names \\ \tt int &An integer (the only non-object datum) \\ \tt 3..5 &An integer 3 $\leq$ value $\leq$ 5 \\ \tt 3.2..5.6 &A real (float) in this range \\ \tt 3.. &An integer $\geq$ 3 \\ \tt ..3 &An integer $\leq$ 3 \\ \tt ClassName &Any instance of this class (or sub-class) \\ \tt alien:char * &Alien (C) data. In this case a char * \end{tabular} \subsection{Programming conversion} The reserved get-method <-convert has the task to convert an incomming object to an instance of the specified class. \begin{exercises} \exercise{% If an argument is specified as type `bool', what values are acceptable as input. } \exercise{% Advanced usage: A very common usage for conversion is a class with uniquely named reusable objects. Try to define such a class. You will have to define the methods ->initialise, <-lookup and <-convert. } \end{exercises} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \chapter{Tracing and Debugging} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% This chapter provides a brief overview of the tracer. Besides the tracer, the VisualHierachy and Inspector tools are very useful tools for locating problems with your application. See section~\ref{sec:manualtools}. \section{Tracing PCE Methods and Executable Objects} XPCE offers tracing capabilities that are similar to those found in many Prolog and Lisp environments. The XPCE tracer is defined at the level of class program_object. Executable objects (messages, etc.), and methods are the most important subclasses of class program_object. Execution of program_objects can be monitored at three `ports': \begin{itemize} \tick{The call port} Entry point of executing the object. \tick{The exit port} The object executed successfully. \tick{The fail port} Execution of the object failed. \end{itemize} On each port, the user can specify a {\bf trace-} point or a {\bf break-} point. A trace-point will cause a message printed when the port is `passed'. A break-point will do the same and start the interactive tracer (similar to a {\it spy-} point in Prolog. The Prolog predicates ({\tt no})tracepce/1 and ({\tt no})breakpce/1% \footnote{The XPCE debugging predicates are defined in the library pce_debug. For Prolog systems lacking autoload you need to do {\tt ?- [library(pce_debug)].}} provide a friendly interface for setting trace- and break-points on methods. Example: \begin{code} ?- tracepce((point->x)). Trace variable: point <->x ?- new(P, point), send(P, x, 20). PCE: 2 enter: V @892203/point <->x: 20 PCE: 2 exit: V @892203/point <->x: 20 P = @892203/point \end{code} Both tracepce/1 and breakpce/1 accept a specification of the form $<$Class$>$ {\tt ->, <- or -}$<$Selector$>$. \begin{code} ?- tracepce((box->device)). ?- send(new(@p, picture), open). ?- send(@p, display, new(@b, box(20,20))). \end{code} \subsection{Trapping situations: conditional breaks} Suppose a graphical is at some point erased from the screen while you do not expect this to happen. How do you find what is causing the graphical to disappear and from where this action is called? If your program is small, or at least you can easily narrow down the possible area in your code where the undesired behaviour happens, just using the Prolog tracer will generally suffice to find the offending call. What if your program is big, you don't know the program to well and you have no clue? You will have to work with conditional trace- and breakpoints to get a clue. If you know the reference of the offending object (lets assume @284737), you can spy all send-operations invoked using: \begin{code} ?- send(@vmi_send, trace, @on, full, @receiver == @284737). \end{code} If---for example---you finally discovered that the object is sent the message ->device: @nil, you can trap the tracer using: \begin{code} ?- send(@vmi_send, break, @on, call, and(@receiver == @284737, @selector == device)). \end{code} \begin{exercises} \exercise{% Consider PceDraw. Use the XPCE tracer to find out what happens to an object if the {\em Cut} operation is activated. } \end{exercises} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \chapter{Dialogue Windows in Depth} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \section{Modal Dialogue Windows (Prompters)} A common use is to query or inform the user during an ongoing task. The task is blocked until the user presses some button. For example, the user selected `Quit' and you want to give the user the opportunity to save before doing so or cancel the quit all the way. {\em Model} dialog windows are the answer to the problem. In XPCE, Windows by themselves are not {\em modal}, but any window may be operated in a {\em modal} fashion using the method `frame <-confirm'. This method behaves as `frame->open' and next starts an event-dispatching subloop until `frame ->return' is activated. So, our quit operation will look like this: \begin{code} quit :- new(D, dialog('Quit my lovely application?')), ( application_is_modified -> send(D, append, button(save_and_quit, message(D, return, save_and_quit))) ; true ), send(D, append, button(quit, message(D, return, quit))), send(D, append, button(cancel, message(D, return, cancel))), get(D, confirm, Action), send(D, destroy), ( Action == save_and_quit -> save_application ; Action == quit -> true ), send(@pce, die). \end{code} \section{Entering Values} Operations often require multiple values and dialog windows are a common way to specify the values of an operation. Actually executing the operation is normally associated with a button. Below is an example of a dialog for this type of operations. \begin{code} create_window :- new(D, dialog('Create a new window')), send(D, append, new(Label, text_item(label, 'Untitled'))), send(D, append, new(Class, menu(class, choice))), send_list(Class, append, [ class(window), class(picture), class(dialog), class(browser), class(view) ]), send(D, append, new(Width, slider(width, 100, 1000, 400))), send(D, append, new(Height, slider(height, 100, 1000, 200))), send(D, append, button(create, message(@prolog, create_window, Class?selection, Label?selection, Width?selection, Height?selection))), send(D, append, button(cancel, message(D, destroy))), send(D, open). create_window(Class, Label, Width, Height) :- get(Class, instance, Label, Window), send(Window?frame, set, @default, @default, Width, Height), send(Window, open). \end{code} Note that a menu accepts the method ->append: menu_item. Class menu_item defines a type-conversion converting any value to a new menu_item using the value as `menu_item <->value'. The visible labels of the menu_items are generated automatically (see `menu_item <-default_label'). The action associated with the button is a message invoking the Prolog predicate create_window/4. Four obtainers collect the values to be passed. \section{Editing Attributes of Existing Entities} All dialog items that may be used to edit attributes (i.e.\ represent some value) define a <->default value and the methods ->restore and ->apply. <->default specifies an XPCE {\em function} object or plain value. On ->restore, the <-selection is restored to the <-default or the result of evaluating the <-default function. ->apply will execute <-message with the current <-selection if the <-selection has been changed by the user. Class dialog defines the methods ->restore and ->apply, which will invoke these methods on all items in the dialog that understand them. This schema is intended to define {\em attribute} editors comfortably. Below we define a dialog-window to edit some appearance values of a box object. \begin{code} edit_box(Box) :- new(D, dialog(string('Edit box %N', Box))), send(D, append, text_item(name, Box?name, message(Box, name, @arg1))), send(D, append, new(S, slider(pen, 0, 10, Box?pen, message(Box, pen, @arg1)))), send(S, width, 50), send(D, append, button(apply)), send(D, append, button(restore)), send(D, append, button(cancel, message(D, destroy))), send(D, default_button, apply), send(D, open). \end{code} \section{Status, Progress and Error Reporting} XPCE defines a standard protocol for reporting progress, errors, status, etc. This protocol is implemented by two methods: the send-method ->report and the get-method <-report_to. The aim is to provide the application programmer with a mechanism that allows for reporting things to the user without having to know the context of the objects involved. \subsection{Generating reports} For example, you define a predicate my_move that moves a graphical to some location, but not all locations are valid. You can simply define this using: \begin{code} my_move(Gr, Point) :- ( valid_position(Gr, Point) -> send(Gr, move, Point) ; send(Gr, report, warning, 'Cannot move %N to %N', Gr, Point) ). \end{code} The first argument of ->report is the type of report. The reporting mechanism uses this information to decide what to do with the report. The types are: {\em status}, {\em warning}, {\em error}, {\em inform}, {\em progress} and {\em done}. \subsection{Handling reports} A default report handling mechanism is defined that will try to report in a {\em label} object called {\em reporter} in a dialog window in the frame from where the command was issued. If no such label can be found important (error, inform) reports are reported using a confirmer on the display. Less serious (warning) are reported using the ->alert mechanism (->bell, ->flash) and others are ignored. Most applications therefore can just handle all reports by ensuring the frame has a dialog window with a label called {\em reporter} in it. In specific cases, redefining ->report or <-report_to can improve error reporting. \begin{exercises} \exercise{% Define a dialog based application that allows you to inspect the properties of Prolog predicates. Use predicate_property/2 and source_file/2. The dialog should have appropriate fields for the file the predicate is defined in, whether it is dynamic or not, multiple, built-in, etc. Use a browser to display all available predicates. Double-clicking on an predicate in the browser should show the predicate in the dialog. } \end{exercises} \section{Custom Dialog-Items} As from XPCE version 4.7 (ProWindow 1.1), graphics and dialog_items are completely integrated. This implies that custom dialog-items can be constructed using general graphics as well as other dialog-items. Dialog-items should respond in a well-defined manner to a number of messages and have standard initialisation arguments. These methods are: \begin{description} \sendmethod{item}{initialise}{Name:name, Default:[any], Message:[code]*} Items are normally created from their {\em Name}, which is processed by `char_array <-label_name' to produce a capitalised visible label, the {\em Default} value and a message to execute on ->apply or operation of the item. \sendmethod{item}{selection}{Value:any} Sets the selection. After this, <-modified is supposed to return @off. \getmethod{item}{selection}{Value:any} Return the current selection. \sendmethod{item}{default}{Value:any} Sets the {\em default} value and the ->selection. If ->restore is called after the user modified the item and before it is ->apply'd, the <-selection should be restored to the <-default. Normally called by ->initialise. Normally implemented by setting the -default slot and calling ->restore. \sendmethod{item}{restore}{} Normally simply invokes ->selection with <-default. \sendmethod{item}{apply}{Force:[bool]} If <-modified returns @on or Force equals @on, forward the <-selection over @arg1 of the <-message. \end{description} The XPCE library (`@pce <-home'/prolog/library) contain various examples of custom dialog items. See for example pce_font_item.pl (consists of three cycle-menus for specifying a PCE font), pce_style_item.pl (enter a style (font, colour, underline, etc.) for an editor-fragment). The dialog editor contains various examples as well. \begin{exercises} \exercise{% Study the pce_font_item.pl file. Use the font_item to control the font of a simple text object. Analyse the structure of the application using the Visual Hierarchy tool. } \end{exercises} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \chapter{Graphics} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% In this chapter we will build a little direct-manipulation graphical editor. The application consists of a graphical editor that allows the use to define entities and their interrelations involved. \section{Graphical Devices} A graphical device (class device) defines a compound graphical object with it's own local coordinate system. Graphical devices may be nested. Graphical devices are usually subclassed to define application-specific visual representations. As a first step towards our graphical application, we will define a box with centered text and a possibly grey background. \begin{code} :- pce_begin_class(text_box, device, "Box with centered text"). resource(size, size, 'size(100, 50)', "Default size"). resource(label_font, font, '@helvetica_bold_12', "Font for text"). initialise(TB, Label:[name]) :-> "Create a box with centered editable text":: send(TB, send_super, initialise), get(TB, resource_value, size, size(W, H)), get(TB, resource_value, label_font, Font), send(TB, display, box(W, H)), send(TB, display, new(T, text(Label, center, Font))), send(T, transparent, @off), send(TB, recenter). recenter(TB) :-> "Center text in box":: get(TB, member, box, Box), get(TB, member, text, Text), send(Text, center, Box?center). geometry(TB, X:[int], Y:[int], W:[int], H:[int]) :-> "Handle geometry-changes":: get(TB, member, box, Box), send(Box, set, 0, 0, W, H), send(TB, recenter), send(TB, send_super, geometry, X, Y). fill_pattern(TB, Pattern:image*) :-> "Specify fill-pattern of the box":: get(TB, member, box, Box), send(Box, fill_pattern, Pattern). fill_pattern(TB, Pattern:image*) :<- "Read fill-pattern of the box":: get(TB, member, box, Box), get(Box, fill_pattern, Pattern). string(TB, String:char_array) :-> "Specify string of the text":: get(TB, member, text, Text), send(Text, string, String). string(TB, String:char_array) :<- "Read string of the text":: get(TB, member, text, Text), get(Text, string, String). :- pce_end_class. \end{code} \section{Making Graphicals Sensitive} \label{sec:sensitive} \subsection{Adding popup menus to graphicals} In this section we will use the text_box defined in the previous section to define a graphical editor that allows you to create a graph of with text-boxes as nodes. In the first step we will define window that allows us to add new text-boxes to it using a background popup. We will prompt for the text to be put in the box. \begin{code} :- use_module(library(pce_prompter)). make_graph_editor(E) :- new(E, picture('Graph Editor')), send(E, popup, new(P, popup(options))), send_list(P, append, [ menu_item(add_new_box, message(@prolog, add_new_box, E, E?focus_event?position)), menu_item(clear, and(message(@display, confirm, 'Clear entire drawing?'), message(E, clear))) ]), send(E, open). add_new_box(E, Pos) :- prompter('Name of box', [ name:name = Name ]), send(E, display, text_box(Name), Pos). \end{code} \subsection{Using gestures} A gesture is an object that is designed to parse mouse-button event sequences into meaningful actions. XPCE predefines gestures for clicking, moving, resizing, linking and drag-drop of graphical objects. All gesture classes are designed to be subclassed for more application specific handling of button-events. There are two ways to connect a gesture to a graphical object. The first is `graphical ->recogniser' which makes the gesture work for a single graphical object. The alternative is to define an ->event method for the graphical. Below we will use this to extend our text-box class with the possibility to move and resize the boxes.% \footnote{In this example we define the class text_box in small parts that can be loaded on top of each other. We use {\tt :- pce_extend_class(+Class).} to continue the class-definition.} \begin{code} :- pce_extend_class(text_box). :- pce_global(@text_box_recogniser, make_text_box_recogniser). make_text_box_recogniser(R) :- new(R, handler_group(new(resize_gesture), new(move_gesture), popup_gesture(new(P, popup(options))))), TB = @arg1, send_list(P, append, [ menu_item(rename, message(TB, rename), end_group := @on), menu_item(delete, message(TB, free)) ]). event(TB, Ev:event) :-> ( send(TB, send_super, event, Ev) -> true ; send(@text_box_recogniser, event, Ev) ). rename(TB) :-> "Prompt for new name":: prompter('Rename text box', [ name:name = Name/TB?string ]), send(TB, string, Name). :- pce_end_class. \end{code} \section{Graphs: Using Connections} \label{sec:connect} A connection is a line connecting two graphical objects. A connection has typed end-points that can attach to a {\em handle} of the same type. If multiple such handles exist, the system will attach to the `best' one using some simple heuristics. Below is the code fragment that allows you to link up graphicals by dragging from the one to the other with the left-button held down. \begin{code} :- pce_extend_class(text_box). handle(0, h/2, link, west). handle(w, h/2, link, east). handle(w/2, 0, link, north). handle(w/2, h, link, south). :- pce_global(@link_recogniser, new(connect_gesture)). event(TB, Ev:event) :-> ( send(TB, send_super, event, Ev) ; send(@text_box_recogniser, event, Ev) ; send(@link_recogniser, event, Ev) ). :- pce_end_class. \end{code} \section{Reading the Diagram} XPCE has been designed to be able to create drawings that can be interpreted. This design is exemplified by the use of connections. If the drawing had been built from just lines, boxes and text it would have been difficult to convert the drawing into a Prolog representation of a graph. Connections explicitly relate {\em graphical object} instead of positions. Below is a Prolog fragment that generates a Prolog fact-list from the graph. \begin{code} assert_graph(E, Predicate) :- functor(Term, Predicate, 2), retractall(Term), send(E?graphicals, for_all, if(message(@arg1, instance_of, connection), message(@prolog, assert_link, Predicate, @arg1?from?string?value, @arg1?to?string?value))). assert_link(Predicate, From, To) :- Term =.. [Predicate, From, To], assert(Term). \end{code} \begin{exercises} \exercise{% Extend the graph-editor's frame with a dialog window that allows for saving the graph to the Prolog database. } \exercise{% The example in section~\ref{sec:connect} uses a generic connect gesture and a generic connection. Make a refinement of class connect_gesture that creates instances of a class arc_connection. Define class arc_connection as a subclass of class connection that has a popup attached which allows the user to delete the connection. } \exercise{% Write a predicate to create a graph from a list of Prolog facts defining the graph. Add a possibility to load a graph to the dialog window. } \exercise{% Experiment with the `graphical ->layout' method to create some layout for the graph-elements created in the above exercise } \end{exercises} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%% Process interface %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \input process.tex %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%% Using XPCE as a database %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \chapter{Representation and Storing Application Data} There are various alternatives for representing application data in XPCE/Prolog. The most obvious choice is to use Prolog. There are some cases where XPCE is an alternative worth considering:% \footnote{ Using XPCE for storage of application-data has been used in three Esprit-funded projects for the development of knowledge acquisition tools: `Shelley' (storage only), The `Common Kads WorkBench' (using XPCE for OO control structure) and `KEW' (Common Lips, Using XPCE for storage only). In the `Games' project XPCE user-defined classes are only used for specialising the UI library. CLOS is used for storing application data. Representing `knowledge' in XPCE to be used for reasoning in Prolog or Lisp is difficult due to the lack of `natural' access to the knowledge base. Representing text and drawings in XPCE works well.} \begin{itemize} \tick{Store data that is difficult to represent in Prolog} The most typical example are hyper-text and graphics. XPCE has natural data-types for representing this as well as a save and load facilities to communicate with files. \tick{Manipulations that are hard in Prolog} XPCE has a completely different architecture than Prolog: its basic control-structure is message passing and it's data-elements are global and use destructive assignment. These properties can make it a good alternative for storing data in the recorded or clause database of Prolog. \tick{You want to write a pure OO system} Not only can you model your interface, but also the {\em application} as a set of XPCE classes. This provides you with a purely object oriented architecture where XPCE is responsible for storage and message passing and Prolog is responsible for implementing the methods. \end{itemize} \section{Data Representation Building Blocks} \label{chap:data} In this section we will discuss the building-blocks for data-representation using XPCE. We start with the data {\em organising} classes: \begin{itemize} \tick{chain} A chain is a single-linked list. Class chain defines various set-oriented operations and iteration primitives. \tick{vector} A vector is a dynamically expanding one-dimensional array. Multi-dimensional arrays may be represented as vectors holding vectors or using a large one-dimensional array and redefine the access methods. See also section~\ref{sec:2dvector}. \tick{hash_table} A hash-table maps an arbitrary key on an arbitrary value. Class hash_table defines various methods for finding and iteration over the associations stored in the table. Class chain_table may be considered to associate a single key with multiple values. \end{itemize} For the representation of frames and relations, XPCE offers the following building-blocks: \begin{itemize} \tick{sheet} A sheet is a dynamic attribute-value set. Cf. a property list in Lisp. \tick{Refining class object} A common way to define storage primitives is by modeling them as subclasses of class object. This way of modeling data allows you to exploit the typing primitives of XPCE. Modeling as classes rather than using dynamic sheets also minimises storage overhead. \tick{hyper} A hyper is a relation between two objects. The classes hyper and objects provide methods to define the semantics of hypers and to manipulate and exploit them in various ways. A hyper is a simple and safe way to represent a relation between two objects than cannot rely on their mutual existence. \end{itemize} \section{A Simple Database} In this section we will define a simple family database.% \footnote{Unfortunately we cannot use class data for representing dates as the current implementation only ranges from the Unix epoch (Jan 1 1970 to somewhere in the 22-nth century).} This example used hyper-objects to express marriage and child relations. The advantage of hyper-objects is that they will automatically be destroyed if one of the related objects is destroyed and they may be created and queried from both sides. \begin{code} :- pce_begin_class(person, object, "Person super-class"). variable(name, name, both, "Name of the person"). variable(date_of_birth, name, both, "Textual description of date"). initialise(P, Name:name, BornAt:name) :-> send(P, send_super, initialise), send(P, name, Name), send(P, date_of_birth, BornAt). father(P, M:male) :<- "Get my father":: get(P, hypered, father, M). mother(P, M:female) :<- "Get my mother":: get(P, hypered, mother, M). sons(P, Sons:chain) :<- "Get my sons":: get(P, all_hypers, Hypers), new(Sons, chain), send(Hypers, for_all, if(@arg1?forward_name == son, message(Sons, append, @arg1?to))). daughters(P, Daughters:chain) :<- "Get my daughters":: get(P, all_hypers, Hypers), new(Daughters, chain), send(Hypers, for_all, if(@arg1?forward_name == daughter, message(Daughters, append, @arg1?to))). :- pce_end_class. :- pce_begin_class(female, person, "Female person"). mary(F, Man:male) :-> "Marry with me":: ( get(F, husband, Man) -> send(F, report, error, '%N is already married to %N', F, Man), fail ; new(_, hyper(F, Man, man, woman)) ). husband(F, Man:male) :<- "To whom am I married?":: get(F, hypered, man, Man). deliver(F, M:male, Name:name, Date:name, Sex:{male,female}, Child:person) :<- "Deliver a child":: ( Sex == male -> new(Child, male(Name, Date)), new(_, hyper(F, Child, son, mother)), new(_, hyper(M, Child, son, father)) ; new(Child, female(Name, Date)), new(_, hyper(F, Child, daughter, mother)), new(_, hyper(M, Child, daughter, father)) ). :- pce_end_class. :- pce_begin_class(male, person, "Male person"). mary(M, F:female) :-> "Marry with me":: send(F, mary, M). wife(M, Female:female) :<- "To whom am I married?":: get(M, hypered, woman, Female). :- pce_end_class. \end{code} \begin{exercises} \exercise{% Load the file family.pl containing the example above and enter a simple database by hand. Inspect the data-representation using the inspector and online manual tools. } \exercise{% Design and implement a simple graphical editor for entering a database. What visualisation will you choose? What UI technique will you use for marriage and born children? } \exercise{% The current implementation does not define an object that reflects the entire database. Define and maintain a hash-table that maps names onto person entries. You can redefine destruction of an object by redefining the ->unlink method. } \end{exercises}