Переглянути джерело

+ Added first and fourth article

michael 24 роки тому
батько
коміт
03afb21830
2 змінених файлів з 2090 додано та 0 видалено
  1. 591 0
      docs/gtk1.tex
  2. 1499 0
      docs/gtk4.tex

+ 591 - 0
docs/gtk1.tex

@@ -0,0 +1,591 @@
+\documentclass[10pt]{article}
+\usepackage{a4}
+\usepackage{epsfig}
+\usepackage{listings}
+\lstset{language=Delphi}%
+\lstset{basicstyle=\sffamily\small}%
+\lstset{commentstyle=\itshape}%
+\lstset{keywordstyle=\bfseries}%
+\lstset{blankstring=true}%
+\newif\ifpdf
+\ifx\pdfoutput\undefined
+  \pdffalse
+\else
+  \pdfoutput=1
+  \pdftrue
+\fi
+\begin{document}
+\title{Programming GTK in Free Pascal}
+\author{Florian Kl\"ampfl\\and\\Micha\"el Van Canneyt}
+\date{July 2000}
+\maketitle
+\section{Introduction}
+The GTK library is a popular widget library for the X-Windows system.
+It is used as the basis for the GIMP graphical manipulation program 
+and for the GNOME application framework. With its ports to Microsoft 
+Windows and BeOS, it allows to program a graphical interface for any
+application in a platform independent way.
+
+GTK is implemented in C, but it is possible to access its functionality from
+Free Pascal. For this, its headers have been translated to Pascal,
+so a program written in Free Pascal can make use of the functionality 
+offered by GTK and its companion libraries GDK and GLIB. In fact, there is
+an open source project (Lazarus) which makes use of GTK in order to build
+an open-source alternative to the visual classes offered by Inprise's
+Delphi.
+
+This article intends to present an introduction to programming GTK in
+Free Pascal. It by no means covers all of the functionality that GTK 
+offers, as this would probably require a complete manual.
+
+The first section gives some general considerations on the GTK toolkit.
+
+\section{GTK is a C library}
+Since GTK is an external library, some import units describing the calls in
+the libraries are needed. Three libraries make up the GTK programming kit:
+\begin{description}
+\item[glib] this library contains some general programming tools, and
+defines platform independent types, which are used throughout the other
+libraries. To use this library, it is sufficient to include the 
+\lstinline|glib| unit in your \lstinline|uses| clause.
+\item[gdk] encapsulates the Windowing system (X or Windows) underlying GTK. 
+It contains routines to draw on the screen, and react to various mouse or 
+keyboard events. To use these
+routines, the \lstinline|gdk| unit must be included in the \lstinline|uses|
+ clause of a unit or program.
+\item[gtk] contains the widget library. This is a series of controls such
+as edit boxes, drop-down lists and many more, which are organised in an OOP
+structure. Since the library is written in C, there is no programming 
+language support for this structure.
+
+All definitions of the gtk library are contained in the gtk unit, which
+must be included in the \lstinline|uses| clause of any program or unit that needs their
+functionality.
+\end{description}
+
+The GTK toolkit was programmed in C. This has some consequences for the
+Pascal interface, since some C constructs do not port easily to Pascal.
+When using the Pascal translation of the C headers, the following must be
+kept in mind:
+
+\begin{enumerate}
+\item Reserved words: Pascal reserved words in types, record element names
+etc. have been prepended with the word 'the'. For example \lstinline|label|
+becomes \lstinline|thelabel|.
+\item Functions and procedures have been kept with the same names. 
+\item Types have been prepended with T, that is, the C type
+\lstinline|GtkWidget| has become \lstinline|TGtkWidget|.
+\item Pointers to types have been defined as the type name, prepended with a
+P.  \lstinline|GtkWidget *| becomes \lstinline|PGtkWidget|. 
+\item Records with bit-size elements: C allows to store parts of a record in 
+individual bits; whereas in Pascal, the minimum size of an element in a
+record is a byte. To accommodate this, functions were defined to retrieve
+or set single bits from a record. The functions to retrieve a bit
+have the name of the record field. The procedure to set a bit has 
+the name of the field prepended with 'set\_'.
+For example
+\begin{lstlisting}[language=c]{cpackedstruct}
+struct SomeStruct
+{
+  gchar *title;
+  guint visible        : 1;
+  guint resizeable     : 1;
+};
+\end{lstlisting}
+translates to 
+\begin{lstlisting}{ppackedstruct}
+TSomeStruct = record
+ title : Pgchar;
+ flag0 : word;
+end;
+function  visible(var a: TGtkCListColumn): guint;
+procedure set_visible(var a: TGtkCListColumn; __visible: guint);
+function  resizeable(var a: TGtkCListColumn): guint;
+procedure set_resizeable(var a: TGtkCListColumn;__resizeable: guint);
+\end{lstlisting}
+\item Macros. Many C macros have not been translated. The typecasting 
+macros have been dropped, since they're useless under Pascal. 
+Macros to access  record members have been translated, but they are to be
+considered as {read-only}. So they can be used to retrieve a value, but 
+not to store one. e.g. 
+\begin{lstlisting}{macro}
+function GTK_WIDGET_FLAGS(wid : pgtkwidget) : longint;
+\end{lstlisting}
+can be used to retrieve the widget flags, but not to set them. so things like 
+\begin{lstlisting}{invaliduseofmacro}
+GTK_WIDGET_FLAGS(wid):=GTK_WIDGET_FLAGS(wid) and someflag;
+\end{lstlisting}
+will not work, since this is a function, and NOT a macro as in C. 
+\item Calling conventions: A C compiler uses another calling convention 
+than the Free Pascal compiler. Since many GTK functions need a callback,
+these callback must use the C calling convention. This means that every
+function that is called by GTK code, should have the \lstinline|cdecl| 
+modifier as a part of its declaration.
+\end{enumerate}
+
+Compiling a GTK application is no different than compiling any other Free
+Pascal application. The only thing that needs to be done is to tell the free
+Pascal compiler where the gtk, gdk and glib libraries are located on your
+system. This can be done with the \verb|-Fl| command-line switch. For
+example, supposing the gtk library is located in \verb|/usr/X11/lib|, the
+following command-line could be used to compile your application:
+\begin{verbatim}
+ppc386 -Fl/usr/X11/lib mygtkapp.pp
+\end{verbatim}
+This example supposes that the gtk unit is be in your unit search path. If
+it is not, you can add it with the \verb|-Fu| switch.
+
+\section{The bricks of a GTK application}
+The building-blocks of a a GTK application are the {\em widgets}. 
+Widgets are the equivalent of Delphi's controls. And although GTK 
+is not an object oriented library, the library defines a record 
+\lstinline|TGtkWidget| which contains all settings common to all
+widgets; all widgets start with this record, and add their own 
+specific data to it. This creates a tree-like structure with all 
+the widgets present in the GTK library, to which your own widgets 
+can be added.
+
+All functions that create a particular widget return a pointer
+to a \lstinline|TGtkWidget| record. It is not recommended to 
+manipulate the contents of the widget record directly; GTK offers 
+many functions to manipulate the members of the record, e.g.  
+\lstinline|gtk_widget_set_parent| or \lstinline|gtk_widget_get_name|.
+To this set of functions, each new widget adds a few functions that are 
+specific to this particular widget. 
+
+Each widget has a specific function and a specific look; there are many
+widgets to choose from. A complete list of widgets is outside the scope of
+this article; the GTK reference manual offers an overview of available
+widgets. In general it can be said that most widgets one would expect
+are present in the GTK library: Edit fields, buttons, check-boxes, various 
+lists, menus, combo-boxes, tree views, and some pre-defined dialogs. 
+
+Any of these widgets is created with a \lstinline|gtk_WIDGET NAME_new| call. 
+This call can accept arguments; The number and type of arguments depend 
+on the widget. 
+For example, to create a button that displays a text, the call is defined 
+as follows:
+\begin{lstlisting}{}
+gtk_button_new_with_label(ALAbel : PChar)
+\end{lstlisting}
+All widgets can be destroyed with the \lstinline|gtk_widget_destroy| call,
+irrespective of their type.
+
+\section{Showing things on the screen}
+To show things on the screen, it is necessary to create a window. A window
+is created with the the \lstinline|gtk_window_new| call. This call accepts 
+as an argument the type of window to be created. 
+
+Creating a window creates it's structure in memory, but doesn't show it on 
+screen. To show this window on the screen,a call to the 
+\lstinline|gtk_widget_show| function is needed, as can been seen in
+example 1.
+\lstinputlisting{gtkex/ex1.pp}
+If the window contains widgets, the \lstinline|gtk_widget_show| function
+must be called for each widget. 
+
+Looking at example 1, one notices 2 special calls: \lstinline|gtk_init| and
+\lstinline|gtk_main|. These calls should be present in any program that uses
+the GTK library.  
+
+The first call initialises the GTK library. Among other things, it reads
+the command-line to see e.g. which display should be used.
+
+The second call is the heart of the GTK widget library: It starts the
+message loop of GTK. This call will not return, unless somewhere else
+in the program \lstinline|gtk_main_quit| is called. As long as the call
+doesn't return, GTK will wait for events such as mouse clicks, key-presses
+and so on. It will handle these events, but it will not notify you of any 
+of these events except if you specifically ask for it. 
+
+A window by itself is of course not very interesting. To make it more
+interesting, some elements should be added. 
+
+Adding a widget to a parent is done with the \lstinline|gtk_container_add|
+call. This call places a widget in a container. A container is a widget 
+which can contain other widgets; not all widgets are containers, however.
+
+Example 2 shows how to add a widget (a button) to a container (the window
+in this case). It also shows that the container has some specific
+properties, which can be manipulated as well (in this case, the border
+width). Since not each widget is a container, the window pointer must be
+typecasted to \lstinline|GTK_CONTAINER| in order to be accepted by the
+container handling calls.
+
+\lstinputlisting{gtkex/ex2.pp}
+
+Adding more than 1 widget to a container is not trivial in GTK. The reason
+for this is that GTK has not been designed to set widgets at a specific
+location in their parent widget. Instead, GTK asks that you 'pack' your
+objects in their parent widget. This means that if the parent widget is
+resized, it's child widgets are resized as well, depending on the packing
+options that were set.
+
+One of the reasons that the GTK library was set up this way, is that the
+size of a widget is not well-defined. For instance, the size of a button
+depends on whether it is the default widget of the window or not. Given 
+that this is so, the placement of such a button is not well-defined either.
+
+The most common ways of packing widgets in a parent widget are the
+following:
+\begin{enumerate}
+\item using a vertical box.
+\item using a horizontal box.
+\item using a table.
+\end{enumerate}
+We'll discuss these ways in the subsequent. There are other ways, but these
+are probably the most important ones.
+
+\subsection{Using boxes}
+
+A horizontal or vertical box can be used to contain a row or column of
+widgets. Various options can be set to modify the spacing between the 
+widgets, the alignment of the widgets in the box, or the behaviour of 
+the box when the user resizes the parent widget. Boxes work only in 
+one direction. The widgets inside a horizontal box always have the height of
+the box, and widgets in a vertical box always have the width of the vertical
+box.
+
+You can create a horizontal box with the \lstinline|gtk_hbox_new| call. 
+It accepts 2 arguments: The first one is a boolean. It tells GTK whether the
+children should have the same space in the box. The second one is an
+integer, which tells GTK how much space to leave between the widgets in the
+box. Likewise, a vertical box can be created with the
+\lstinline|gtk_vbox_new| call. This call accepts the same arguments as the
+first box.
+
+Adding widgets to a box happens with the \lstinline|gtk_box_pack_start| or
+\lstinline|gtk_box_pack_end| calls. The former adds a widget at the start
+of the box, the latter adds a widget at the end of the box. Both functions
+accept the same arguments:
+\begin{lstlisting}{boxarguments}
+(Box : PGtkBox; Widget: PGtkWidget;
+ expand gboolean; fill : gboolean;padding : guint);
+\end{lstlisting}
+The \lstinline|expand| argument tells the box whether it should take the
+size of it's parent widget, or whether it should resize itself so that it is
+just large enough to fit the widgets. The latter allows to justify the
+widgets in the box (but only if the box is {\em not} homogeneous.  
+If the box should keep the size of it's parent, then the \lstinline|fill| 
+argument decides what is done with the extra space available. 
+
+If \lstinline|fill| is \lstinline|True| then the extra space is
+divided over the widgets. If \lstinline|fill| is \lstinline|False| then the
+extra space is put in between the widgets. 
+
+The \lstinline|padding| adding tells the box to add extra space for this 
+particular widget.
+
+The following program shows the use of a box:
+\lstinputlisting{gtkex/ex3.pp}
+What the program does is the following: It creates a window, which it splits
+up in two halves by means of the \lstinline|totalbox| widget. This is a
+vertical box, which will contain two other boxes: a vertical box and a
+horizontal box. Each of these two boxes is filled with buttons.
+The behaviour of the boxes can be seen when the window is resized.
+
+The effect of the various arguments to the pack calls can be seen by
+changing the arguments and recompiling the example.
+
+\subsection{Using tables}
+A table is used to set widgets in a grid inside the parent widget. It acts
+like a grid with cells, in which you can 'hang' your widgets. If the user 
+resizes the parent widget, then the size of the grid cells changes, and 
+the widgets will change their location and size, based on the size of the 
+new grid cells. 
+
+To create a table to manage a window's layout, the \lstinline|gtk_table_new|
+call is used. It accepts 3 arguments: the number of rows, the number of
+columns and a boolean which tells GTK whether the cells should always have
+the same size or not. 
+
+To add a widget to a table, the \lstinline|gtk_table_attach| call is used.
+It is declared as
+\begin{lstlisting}{}
+gtk_table_attach(Table: PGtkTable;Widget: PGtkWidget;
+                 Left, right, top, bottom : guint;
+                 Xoptions,Yoptions : TGtkAttachOptions;
+                 Xpadding,Ypadding : Guint);
+\end{lstlisting}
+The first two options of this call are self-explanatory. The next four
+options, however, need some explanation. Contrary to what the name 'table'
+suggests, these do {\em not} specify the coordinates of cells; instead, they
+specify the grid lines that delimit the cells. 
+
+\begin{figure}
+\caption{Placing widgets in a table.\label{fig:table}}
+\begin{center}
+\ifpdf
+\epsfig{file=table.pdf}
+\else
+\epsfig{file=table.eps}
+\fi
+\end{center}
+\end{figure}
+
+Figure \ref{fig:table} represents a table with 5 rows and 5 columns, with
+cells of the same size. The call to create this table could be:
+\begin{lstlisting}{}
+maintable:=gtk_table_new(5,5,TRUE);
+\end{lstlisting}
+To hang a widget in this table, so it starts in cell (2,1) and ends in cell
+(3,2), where the cells are counted starting with 0, it is necessary to tell 
+GTK that the widget starts at horizontal grid line 2, and ends at horizontal
+grid line 4. Vertically, it starts at grid line 1, and ends at grid line 3. 
+This means that the following call would place the widget at its correct
+location:
+\begin{lstlisting}{}
+gtk_table_attach(maintable,mybutton,
+                 2,4,1,3,
+                 GTK_EXPAND OR GTK_FILL,GTK_EXPAND OR GTK_FILL,
+                 0,0);
+\end{lstlisting}
+GTK delivers a shorter form of this call:
+\begin{lstlisting}{}
+gtk_table_attach_defaults(maintable,mybutton,2,4,1,3);
+\end{lstlisting}
+The parameter \lstinline|GTK_EXPAND or GTK_FILL| tells GTK that the widget 
+should always take up the full space assigned to it.
+
+The following example program illustrates the use of a table in a gtk
+application:
+\lstinputlisting{gtkex/ex4.pp}
+The example creates a table with 6 rows and 6 columns. It places 3 buttons, 
+each at a different location in the table, with different sizes. The first
+button has a width and height of 1 cell and is located at cell (1,1). The 
+second has a width and height of two cells, and is located at cell (3,1).
+The last button is 4 cells wide and has a height of 1 cell, and is located
+at cell (1,4). When the window is resized, the cells are resized as well,
+and the buttons follow the size of the cells.
+
+{\em Remark:} because the table has homogeneous cells, the minimum width 
+and height of the cells is determined by the first button (in this case). 
+Since all cells must have the same size, this means that the minimum size 
+of the window is 6 times the size of the first button (plus a border).
+
+\section{Reacting to user input}
+So far, the example programs did not react to button clicks or any other user
+action other than closing the window. To make a window respond to user
+actions, it is necessary to install signal callbacks or event handlers.
+
+The difference between signals and events is that signals come from the GTK
+toolkit. Events come from the underlying window system (X or Windows).
+For example, 'button\_pressed' is an event that is generated by the window
+system when the user clicks with his mouse. It is possible to react to 
+this event. 
+
+On the other hand, a button widget defines a signal 'clicked'. 
+The 'clicked' event will occur when the user clicks on the button. 
+So, many signals that are defined by GTK widgets are just a translation 
+of events to something specific for that widget.
+
+Since calls to connect to a signal or to an event are the same, in what
+follows the discussion will be restricted to signals, but all that is 
+said is also true for events.
+
+GTK has essentially 2 ways to install signal callbacks. The only difference
+between these calls is the arguments that the callback function accepts.
+
+The first way to install a callback is using the
+\lstinline|gtk_signal_connect| function. This function is declared as
+follows:
+\begin{lstlisting}{}
+TGtkSignalFunc = procedure ;cdecl;
+Function gtk_signal_connect(TheObject:PGtkObject;Name:Pgchar;
+                            Func:TGtkSignalFunc;Data:gpointer):guint;cdecl;
+\end{lstlisting}
+
+The first argument of this call (\lstinline|TheObject|) is the object 
+(usually a widget) to which you want to assign an event handler. The second
+parameter, \lstinline|Name|, is the event you wish to catch with this
+callback (an example could be 'clicked' for a button). The third argument
+(\lstinline|Func|) is the function that should be called when the event occurs. 
+The \lstinline|Data| argument is a pointer to arbitrary data. This pointer
+will be passed on to the callback function \lstinline|func| when the event
+occurs.
+
+The \lstinline|gtk_signal_connect| function returns an integer. This integer 
+is the number of the callback for this event. It is possible to attach more 
+than one callback to an event in GTK. When the event occurs, the callbacks 
+will be executed in the order that they have been attached to the widget. 
+
+The declaration of the \lstinline|TGtkSignalFunc| type requires that every
+callback function that is passed to GTK must be typecast. Since GTK defines
+only one function to set a signal handler, this is necessary, since
+callbacks can have different forms. This mechanism is error-prone, since
+in this manner it is possible to pass a function to GTK which has the wrong
+number of arguments.
+
+However, most callbacks must be of the form:
+\begin{lstlisting}{}
+Function (Widget : PGtkWidget; Data : GPointer) : guint;cdecl;
+\end{lstlisting}
+Such a callback function accepts 2 arguments: the first argument
+(\lstinline|Widget|) is the widget which caused the event 
+(for example, the button which was clicked). The second argument is a
+pointer to arbitrary data. This is the pointer that was passed as
+\lstinline|Data| when the callback was installed.
+
+Signals are identified by their name. The GTK reference guide contains a
+complete list of supported signals.
+
+The first example shows how a handler for the 'destroy' signal of the
+window is installed. When the window-manager kills the window, this 
+signal is sent. The \lstinline|gtk_main_quit| instructs GTK that it 
+should stop processing X events and exit the \lstinline|gtk_main| call.
+
+A second method to connect a callback to a signal is using the
+\lstinline|gtk_signal_connect_object| call. This call is defined as
+follows:
+\begin{lstlisting}{}
+Function gtk_signal_connect_object(theobject:PGtkObject;
+                                   name:Pgchar;
+                                   func:TGtkSignalFunc;
+                                   slot_object:PGtkObject):guint;cdecl
+\end{lstlisting}
+It is similar in function to the \lstinline|gtk_signal_connect| function,
+only it doesn't allow to pass arbitrary data to the signal handler. Instead,
+the handler must {\em always} be of the following form:
+
+\begin{lstlisting}{}
+Function (AnObject : PGtkObject);
+\end{lstlisting}
+The \lstinline|slot_object| pointer that was provided in the call to
+\lstinline|gtk_signal_connect_object| will be passed as the
+\lstinline|AnObject| argument to this function. Many GTK functions have the
+above form; this makes it possible to attach a GTK internal function to a
+signal.
+
+To illustrate this, the second example is modified so that clicking the button
+will also close the window:
+\lstinputlisting{gtkex/ex5.pp}
+In the example, the call to \lstinline|gtk_signal_connect_object| will
+connect the 'clicked' signal of the button to the
+\lstinline|gtk_widget_destroy| function of GTK, and passes the pointer to
+the window widget to it. When the user clicks the button, the
+\lstinline|gtk_widget_destroy| function will be called with as argument the
+pointer of the window object. As a result, the window widget will be
+destroyed, it's 'destroy' signal will be activated, and the
+\lstinline|gtk_main_quit| function will be called through the program's
+'destroy' handler for the window.
+
+Since the signal handler connect call returns an integer by which it can 
+be identified, it is possible to manipulate or even remove the handler 
+once it has been installed.
+
+For instance it is possible to temporarily disable a signal handler with the 
+\lstinline|gtk_signal_handler_block| call, and to enable it again with the
+\lstinline|gtk_signal_handler_unblock| call. An example of how to do this
+can be found in the following example:
+\lstinputlisting{gtkex/ex6.pp}
+There are other things that one can do with signal handlers, but a complete
+discussion of all possibilities is outside the scope of this article.
+
+Some widgets do not have their own window; i.e. they do not receive events
+from the underlying windowing system. An example of such a widget is a
+label. A label just draws it's text on it's parent widget, and nothing else.
+
+To be able to respond to certain events, an event-box must be used, and the 
+window-less widget must be placed in it. An event-box can be created with
+the \lstinline|gtk_event_box_new| call. This call accepts no arguments.
+
+To this event-box, a window-less widget can be added. The event-bow will
+then capture events for the window-less widget. The following example shows
+how to use an event-box to detect when the mouse pointer is moved over a
+label:
+\lstinputlisting{gtkex/ex7.pp}
+If the mouse pointer is moved over the first label, the text of the second
+label is adapted accordingly. The example also shows the use of
+\lstinline|gtk_widget_show_all|, which shows a widget and all widgets
+contained in it.
+
+\section{A touch of style}
+The look of a GTK application is controlled through the use of styles. A
+style controls the colors in which a widget is drawn, in various states.
+As an example: a widget may be drawn differently depending on whether it 
+has focus or not. How to draw the widget in each of this states is described
+in the style of the widget.
+
+GTK recognizes the following states of a widget:
+\begin{description}
+\item[NORMAL] The normal state of a widget. No mouse over it.
+\item[PRELIGHT] Is the state of a widget when the mouse is over it.
+\item[ACTIVE] Is the state of a widget when it is pressed or
+clicked.
+\item[INSENSITIVE] if the widgets is disabled ('grayed').
+\item[SELECTED] When the object is selected.
+The GTK unit has a constant for each of these states; it is the above name
+with \lstinline|GTK_STATE_| prefixed, so e.g. \lstinline|GTK_STATE_NORMAL|
+for the normal state.
+
+\end{description}
+
+
+Each widget class has a default style in which it is drawn. If you wish to
+change the way all these widgets look, you should change the default style
+of this class. If you want to change the way one particular widget looks,
+you should make a new style, and apply it to that particular widget. It is
+possible to make a copy of an existing style and modify the copy before
+applying it.
+
+It is also possible to change the default style of widgets. Changing the
+default style of widgets will have effect on all widgets created after the
+new style was set. Widgets created before that will be unaffected.
+
+The following example shows how to set the color of a label. It takes a
+copy of the standard label style, and modifies it so the foreground color
+becomes red. It then applies the modified style to the first label.
+The second label is unaffected by this change
+\lstinputlisting{gtkex/ex8.pp}
+
+The last example shows how to change the color of a button when the mouse
+moves over it.
+\lstinputlisting{gtkex/ex9.pp}
+
+\section{Carrying on}
+In the previous sections, some basic concepts of GTK have been introduced.
+However, GTK is a big toolkit and much more can be said about it. It is
+outside of the scope of the current article to describe all Widgets in the
+GTK library. The range of offered widgets is broad, and there probably is a
+widget for each conceivable task. If there is a missing widget, it is always
+possible to write a new widget. 
+
+In principle, therefore, GTK is suitable to write large applications, also
+when writing in Free Pascal. However, the fact that it is written in C and 
+it's interface is C oriented, justifies the writing of a small Pascal Object 
+Oriented wrapper around it. 
+The following arguments show the need for such a wrapper:
+\begin{enumerate}
+\item C has no object-oriented language constructs. This makes it necessary
+to do a lot of typecasts in GTK calls. Using Classes or Objects, this is no
+longer necessary, and will improve code readability substantially.
+\item C uses null-terminated strings. It can be useful to wrap calls that
+need a null-terminated string into one that accepts a normal string as an
+argument. Using ansistrings will make the conversion to null-terminated
+strings easier.
+\item The signal mechanism of GTK destroys the strong type checking of
+Pascal. When compiling the statement 
+\begin{lstlisting}{}
+  Button.OnClick:[email protected]
+\end{lstlisting}
+The compiler checks that the \lstinline|OnButtonClick| method can be
+assigned to the \lstinline|OnClick| event. Under GTK, it is possible
+to pass any function as a handler for a signal, as in
+\begin{lstlisting}{}
+gtk_signal_connect (PGTKOBJECT (window), 'destroy',
+                    GTK_SIGNAL_FUNC (@destroy), NULL);
+\end{lstlisting}
+This can lead to errors if the \lstinline|destroy| procedure accepts a
+different number of arguments, or other arguments than the ones that 
+GTK provides. Therefore it would be a good idea to implement methods that
+would force type checking, e.g:
+\begin{lstlisting}{}
+Button.AddOnClick(@MyForm.OnButtonClick);
+\end{lstlisting}
+Such a statement would only compile if the \lstinline|OnButtonClick| would
+be of the right type.
+\end{enumerate}
+Additional benefits of making such a wrapper would be simpler code, and
+hence better readability. Both improve the maintainability of the code as well,
+which are all important factors when writing a large application.
+\end{document}

+ 1499 - 0
docs/gtk4.tex

@@ -0,0 +1,1499 @@
+\documentclass[10pt]{article}
+\usepackage{a4}
+\usepackage{epsfig}
+\usepackage{listings}
+\usepackage{tabularx}
+\lstset{language=Delphi}%
+\lstset{basicstyle=\sffamily\small}%
+\lstset{commentstyle=\itshape}%
+\lstset{keywordstyle=\bfseries}%
+\lstset{blankstring=true}%
+\newcommand{\file}[1]{\textsf{#1}}
+\usepackage[pdftex]{hyperref}
+\newif\ifpdf
+\ifx\pdfoutput\undefined
+  \pdffalse
+\else
+  \pdfoutput=1
+  \pdftrue
+\fi
+\begin{document}
+\title{Programming GTK in Free Pascal: Making a real-world application.}
+\author{Florian Kl\"ampfl\\and\\Micha\"el Van Canneyt}
+\date{January 2001}
+\maketitle
+\section{Introduction}
+In the third article on programming the GTK toolkit, the use of several
+GTK widgets is demonstrated by building a real-world application. 
+
+The main widgets to be shown are the Toolbar, CList and Tree widgets. 
+Along the way, some other widgets such as a dialog will be shown as well.
+
+The program to show all this will be a small file explorer. It will not
+perform all functions that one would expect from a file explorer, but it
+is not meant to be, either. It just demonstrates how one could go about when
+making a file explorer. 
+
+The File explorer will have 2 main components. One is a directory tree
+which can be used to select a directory. the other is a Clist, a component
+that presents a list of items in a table with headings. The Clist will be
+used to display the files in the directory selected in the directory tree.
+
+The functionality included will be limited to viewing the properties of
+a file, and deleting a file. The view can be customized, and sorting of
+columns by clicking the column header is possible.
+
+Each window developed in the article will be described in a record, i.e.
+all window elements will have a field in a record that points to the 
+GTK widget used. Several forms will be developed, and each form will be
+put in a separate unit. Signal callbacks will in general receive a
+'userdata' pointer that points to the window record. This approach mimics
+the object oriented approach of GTK, and is similar to the approach in
+Delphi, where instead of a object, a window class is used.
+
+\section{The main window}
+The main window will consist of a menu, a tool bar, a directory tree and
+the file list. The bottom of the screen will contain a statusbar. Between
+the directory tree and the file list is a splitter that can be used to
+resize the directory tree.
+
+Right-clicking on the file list will show a popup menu, from which file
+actions can be selected.
+
+All the widgets in the main window will be stored in a big record
+\lstinline|TMainWindow|:
+\begin{lstlisting}{}
+  TMainWindow = Record
+    FDir,
+    FMask : String;
+    Window : PGtkWindow;
+    Menu : PGtkMenuBar;
+    Toolbar : PGtkToolBar;
+    DirTree : PGtkTree;
+    FileList : PGtkClist;
+    Pane : PGtkPaned;
+    StatusBar : PGtkStatusBar;
+    FilesHeader,DirHeader : PGtkLabel;
+    // helper objects - Menu
+    Accel : PGtkAccelGroup;
+    MFile,
+    MView,
+    MColumns,
+    MHelp,
+    // Main menu items
+    PMFiles : PGtkMenu;
+    MIFile,
+    MIFileProperties,
+    MIFileDelete,
+    MIExit,
+    MiColumns,
+    MIAbout,
+    MIHelp : PGtkMenuItem;
+    MIShowTitles,
+    MIShowExt,
+    MIShowSize,
+    MiShowDate,
+    MIShowAttrs : PGtkCheckMenuItem;
+    // Files PopupMenu Items:
+    PMIFileProperties,
+    PMIFileDelete : PGtkMenuItem;
+    // Packing boxes
+    VBox,
+    LeftBox,
+    RightBox : PGtkBox;
+    // Scroll boxes
+    TreeScrollWindow,
+    ListScrollWindow : PGtkScrolledWindow;
+    // Tree root node.
+    RootNode : PGtkTreeItem;
+  end;
+  PMainWindow = ^TMainWindow;   
+\end{lstlisting}
+The record resembles a form class definition as used in \lstinline|Delphi|, it
+contains all possible widgets shown on the window.
+
+The most important ones are of course the \lstinline|DirTree| and \lstinline|FileList|
+fields, the \lstinline|Menu| which will refer to the main menu and the
+\lstinline|PMfiles| which will hold the popup menu. The Status bar is of course
+in the \lstinline|StatusBar| field, and the \lstinline|ToolBar| field will hold the main
+toolbar of the application. 
+
+The \lstinline|FDir| field will be used to hold the currently shown 
+directory and the \lstinline|FMask| field can be used to store a file mask that
+determines what files will be shown in the list.
+
+All these fields are filled in using the function \lstinline|NewMainForm| :
+\begin{lstlisting}{}
+Function NewMainForm : PMainWindow;
+\end{lstlisting}
+
+The function starts as follows :
+\begin{lstlisting}{}
+begin
+  Result:=New(PMainWindow);
+  With Result^ do
+    begin
+    FMask:='*.*';
+    Window:=PgtkWindow(gtk_window_new(GTK_WINDOW_TOPLEVEL));
+    gtk_window_set_title(Window,SFileExplorer);
+    gtk_widget_set_usize(PgtkWidget(Window),640,480);
+    gtk_signal_connect (PGTKOBJECT (window), 'destroy',
+                    GTK_SIGNAL_FUNC (@destroy), Result);
+    gtk_widget_realize(PgtkWidget(window)); 
+\end{lstlisting}
+This is a more or less standard GTK setup for a window. Note that the
+pointer to the window record is passed to the 'destroy' signal handler
+for the window, and that the window widget is realized (so a actual 
+window is created). The necessity for the 'realize' call is explained below.
+
+After the window is created, the main widgets on the form are created:
+\begin{lstlisting}{}
+Menu:=NewMainMenu(Result);
+ToolBar:=NewToolbar(Result);
+StatusBar:=PgtkStatusBar(gtk_statusbar_new);
+FileList:=NewFileList(Result);
+DirTree:=NewDirtree(Result);
+PMFiles:=NewFilePopupMenu(Result);
+\end{lstlisting}
+
+The functions used to create these widgets will be discussed further on.
+\begin{description}
+\item[Menu] The menu is created in the function \lstinline|NewMainMenu|
+\item[ToolBar] The toolbar is created in the \lstinline|NewToolbar| function.
+\item[FileList] The CList component which will show the file data. Created
+using \lstinline|NewFileList|.
+\item[DirTree] The directory tree showing the directory structure of the
+disk is created using \lstinline|NewDirtree|.
+\item[PMFiles] is the popup menu for the file list and is created in the
+\lstinline|NewFilePopupMenu| function.
+\end{description}
+Each function will set the fields which contain the helper widgets.
+
+After the main widgets have been created, it is time to put them on the
+form, and the rest of the \lstinline|NewMainForm| function is concerned 
+mainly with  placing the widgets in appropriate containers. 
+
+A splitter widget in GTK is called a \lstinline|paned window|. It can be created
+using one of the following functions:
+\begin{lstlisting}{}
+function gtk_hpaned_new : PGtkWidget;
+function gtk_vpaned_new : PGtkWidget;
+\end{lstlisting}
+Since the directory tree and file explorer window will be located left to
+each other, a \lstinline|gtk_hpaned_new| call is needed for the file explorer.
+
+The \lstinline|paned window| has 2 halves, in each of which a widget can be
+placed. This is done using the following calls:
+\begin{lstlisting}{}
+procedure gtk_paned_add1(paned:PGtkPaned; child:PGtkWidget);cdecl;
+procedure gtk_paned_add2(paned:PGtkPaned; child:PGtkWidget);cdecl;
+\end{lstlisting}
+The first function adds a widget to the left pane, the second to the right
+pane (or the top and bottom panes if the splitter is vertical).
+
+With this knowledge, the Directory Tree and File List can be put on the
+form. In the case of the file explorer, 2 widgets will be packed in vertical
+boxes which are on their turn put the left and right panes of the splitter: 
+\begin{lstlisting}{}
+Pane:=PgtkPaned(gtk_hpaned_new);
+DirHeader:=PgtkLabel(gtk_label_new(pchar(SDirTree)));
+LeftBox:=PGtkBox(gtk_vbox_new(false,0));
+gtk_box_pack_start(Leftbox,PGtkWidget(DirHeader),False,False,0);
+gtk_box_pack_start(Leftbox,PgtkWidget(TreeScrollWindow),true,True,0);
+gtk_paned_add1(pane,PGtkWidget(Leftbox));
+\end{lstlisting}
+The left-hand side vertical box (\lstinline|LeftBox|) contains a label 
+(\lstinline|DirHeader|) which serves as a  heading for the directory tree (\lstinline|DirTree|). 
+It displays a static text (in the constant \lstinline|SDirTree|).
+
+The right pane can be filled in a similar way with the file list:
+\begin{lstlisting}{}
+FilesHeader:=PgtkLabel(gtk_label_new(pchar(SFilesInDir)));
+RightBox:=PGtkBox(gtk_vbox_new(false,0));
+gtk_box_pack_start(Rightbox,PGtkWidget(FilesHeader),False,False,0);
+gtk_box_pack_start(Rightbox,PGtkWidget(ListScrollWindow),true,True,0);
+gtk_paned_add2(pane,PGtkWidget(Rightbox));
+\end{lstlisting}
+The right-hand side vertical box contains a label \lstinline|FileHeader| 
+which serves as a heading for the file list (\lstinline|FileList|). 
+It will be used to display the current directory name 
+(\lstinline|SFilesInDir| constant).
+
+After the directory tree and file view have been put in a paned window,
+all that is left to do is to stack the statusbar, paned window, toolbar 
+and menu in a vertical box \lstinline|VBox| which covers the whole window:
+\begin{lstlisting}{}
+  VBox:=PGtkBox(gtk_vbox_new(false,0));
+  gtk_container_add(PGtkContainer(Window),PgtkWidget(VBox));
+  gtk_box_pack_start(vbox,PGtkWidget(Menu),False,False,0);
+  gtk_box_pack_start(vbox,PGtkWidget(ToolBar),False,False,0);
+  gtk_box_pack_start(vbox,PGtkWidget(Pane),true,true,0);
+  gtk_box_pack_start(vbox,PGtkWidget(StatusBar),false,false,0);
+  gtk_widget_show_all(PGtkWidget(vbox));
+  end;
+end;
+\end{lstlisting}
+The destroy signal of the window does nothing except destroying the
+main window record and telling GTK to exit the event loop:
+\begin{lstlisting}{}
+procedure destroy(widget : pGtkWidget ; Window : PMainWindow); cdecl;
+begin
+  gtk_clist_clear(Window^.FileList);
+  dispose(Window);
+  gtk_main_quit();
+end;
+\end{lstlisting}
+The call to \lstinline|gtk_clist_clear| serves to clear the file list window.
+The necessity for this call will be explained below.
+
+\section{The file list}
+The file list is constructed using the GTK CList widget. This is a powerful
+widget that contains a lot of functionality, comparable to the
+\lstinline|TListView| component found in Delphi.
+
+A the file list  widget is created using the following function:
+\begin{lstlisting}{}
+Function NewFileList(MainWindow : PMainWindow) : PGtkClist;
+
+Const 
+  Titles : Array[1..6] of pchar = 
+         ('Name','ext','Size','Date','Attributes','');
+
+begin
+  MainWindow^.ListScrollWindow:=
+    PGtkScrolledWindow(gtk_scrolled_window_new(Nil,Nil));
+  gtk_scrolled_window_set_policy(MainWindow^.ListScrollWindow,
+                                 GTK_POLICY_AUTOMATIC,
+                                 GTK_POLICY_AUTOMATIC);
+  Result:=PGtkClist(Gtk_Clist_New_with_titles(6,@Titles));
+  gtk_Container_add(PGTKContainer(MainWindow^.ListScrollWindow),
+                    PGtkWidget(Result));
+\end{lstlisting}
+A Clist object is not capable of drawing scroll bars if it contains too many
+items for its size, so first a \lstinline|Scrolled Window| is created in which
+the Clist object is embedded. A scrolled window is a container widget which 
+does nothing except providing scrollbars for the widget it contains.
+
+A scrolled window is created using the \lstinline|gtk_scrolled_window_new|
+function:
+\begin{lstlisting}{}
+function gtk_scrolled_window_new(hadjustment:PGtkAdjustment;
+                                 vadjustment:PGtkAdjustment):PGtkWidget
+\end{lstlisting}
+The \lstinline|Adjustment| parameters can be used to pass scrollbar widgets
+that the scrolled window should use to do it's work.
+If none are passed, the scrolled window will create the needed scrollbars
+by itself.
+
+The visibility of the scrollbars can be controlled with the policy property
+of the scrolled window:
+\begin{lstlisting}{}
+gtk_scrolled_window_set_policy(scrolled_window:PGtkScrolledWindow;
+                               hscrollbar_policy:TGtkPolicyType; 
+                               vscrollbar_policy:TGtkPolicyType)
+\end{lstlisting}
+The horizontal and vertical policies can be set to the following values:
+\begin{description}
+\item[GTK\_POLICY\_AUTOMATIC] Scrollbars are only visible if they are needed.
+\item[GTK\_POLICY\_ALWAYS] Scrollbars are always visible.
+\end{description}
+
+After the creation of the scrolled window, the file list is created and
+added to the scrolled window. A CList widget can be created using 2 calls;
+\begin{lstlisting}{}
+function  gtk_clist_new (columns:gint):PGtkWidget;
+function  gtk_clist_new_with_titles (columns:gint;
+                                     titles:PPgchar):PGtkWidget;
+\end{lstlisting}
+In both cases, the number of columns in the list must be passed. If
+the column header titles are fixed and known, they can be passed in the
+\lstinline|gtk_clist_new_with_titles| call, but they can still be set and
+retrieved later on with the following calls:
+\begin{lstlisting}{}
+Procedure gtk_clist_set_column_title(clist:PGtkCList;
+                                     column:gint;
+                                     title:Pgchar);cdecl;
+function  gtk_clist_get_column_title(clist:PGtkCList;
+                                     column:gint):Pgchar;cdecl;
+\end{lstlisting}
+Note that the column indices are 0 based.
+
+After the CList widget has been created, some properties can be set:
+\begin{lstlisting}{}
+gtk_clist_set_shadow_type(Result,GTK_SHADOW_ETCHED_OUT);
+\end{lstlisting}
+This call sets the border around the clist. The possible values for
+the last parameter (the \lstinline|TGtkShadowType|) of 
+\lstinline|gtk_clist_set_shadow_type| are:
+\begin{description}
+\item[GTK\_SHADOW\_NONE] No border.
+\item[GTK\_SHADOW\_IN] the clist appears lowered.
+\item[GTK\_SHADOW\_OUT] the clist appears raised.
+\item[GTK\_SHADOW\_ETCHED\_IN] the clist appears with a lowered frame.
+\item[GTK\_SHADOW\_ETCHED\_OUT] the clist appears with a raised frame.
+\end{description}
+
+The justification of a column in the list can be set:
+\begin{lstlisting}{}
+gtk_clist_set_column_justification(result,2,GTK_JUSTIFY_RIGHT);
+\end{lstlisting}
+column 2 will contain the file sizes, so it is set right-justified.
+Other possible values are for justification are 
+\lstinline|GTK_JUSTIFY_LEFT|, \lstinline|GTK_JUSTIFY_CENTER|, and 
+\lstinline|GTK_JUSTIFY_FILL|,  which have their obvious meanings.
+
+To be able to select multiple items (or rows) at once, the selection mode of
+the CList must be set: 
+\begin{lstlisting}{}
+gtk_clist_set_selection_mode(Result,GTK_SELECTION_MULTIPLE);
+\end{lstlisting}
+Possible modes of selection are:
+\begin{description}
+\item[GTK\_SELECTION\_SINGLE] Only one row can be selected at any given
+time.
+\item[GTK\_SELECTION\_BROWSE] Multiple items can be selected, however the
+selection will always return 1 item.
+\item[GTK\_SELECTION\_MULTIPLE] Multiple items can be selected, and the
+selection will contain all selected items.
+\item[GTK\_SELECTION\_EXTENDED] The selection is always \lstinline|Nil|.
+\end{description}
+The selection is a field (\lstinline|selection|) of type \lstinline|PGList| in the 
+\lstinline|TGtkCList| record. A \lstinline|PGlist| is a pointer to a doubly linked 
+list with data pointers. More details about this will follow.
+
+The elements in the list list can be sorted. 
+\begin{lstlisting}{}
+gtk_clist_set_auto_sort(Result,True);
+If DefCompare=Nil then
+  DefCompare:=Result^.compare; 
+gtk_clist_set_compare_func(Result,
+                           TGtkCListCompareFunc(@FileCompareFunc));
+\end{lstlisting}
+By default, a CList sorts by comparing the texts in the current sort column 
+of the items in the list. This sorting happens using the \lstinline|compare| 
+function of the CList. The standard \lstinline|compare| function of the list 
+is saved here in a variable \lstinline|DefCompare|, so it can still be used. 
+Using the  \lstinline|gtk_clist_set_compare_func| the compare function to be 
+used when sorting can be set, and it is set to the function 
+\lstinline|FileCompareFunc|,  which will be discussed later on.
+
+The \lstinline|gtk_clist_set_auto_sort| can be used to set the auto-sort
+feature of the Clist. If auto-sort is on, adding new items to the CList will
+insert them in the correct order. If auto-sort is off, new items are
+appended to the beginning or end of the list.
+
+After the sort function is set, handlers are attached to 2 signals:
+\begin{lstlisting}{}
+gtk_signal_connect(PgtkObject(Result),'button_press_event',
+                   TGtkSignalFunc(@ShowPopup),MainWindow);
+gtk_signal_connect(PgtkObject(Result),'click_column',
+                   TGtkSignalFunc(@FileColumnClick),MainWindow);
+\end{lstlisting}
+The first handler connects to a mouse button press event. This will be used
+to detect a right mouse click, and to show a popup menu:
+\begin{lstlisting}{}
+Procedure ShowPopup(Widget : PGtkWidget; 
+                    Event : PGdkEventButton; 
+                    Window : PMainWindow);cdecl;
+
+begin
+  if (event^.thetype=GDK_BUTTON_PRESS) and 
+     (event^.button=3) then
+    gtk_menu_popup(Window^.PMFiles,Nil,Nil,Nil,NIl,3,event^.time);
+end;
+\end{lstlisting}
+The \lstinline|gtk_menu_popup| function does nothing but showing the menu;
+when a menu item is clicked, the menu will close by itself.
+
+The second handler connects to the 'click\_column' event. This event is
+emitted if the user clicks on the column header. It will be used to switch
+the sort order of the file list:
+\begin{lstlisting}{}
+Procedure FileColumnClick(List : PGtkCList;Column:gint; Window : PMainWindow);cdecl;
+
+Var 
+  I  : longint;
+  NS : TGtkSortType;
+   
+begin
+  If Column<>List^.sort_column Then
+    begin
+    gtk_clist_set_sort_type(List,GTK_SORT_ASCENDING);
+    gtk_clist_set_sort_column(list,Column);
+    end
+  else
+    begin
+    If (List^.Sort_type=GTK_SORT_ASCENDING) Then 
+      NS:=GTK_SORT_DESCENDING
+    else
+      NS:=GTK_SORT_ASCENDING;
+    gtk_clist_set_sort_type(List,NS);
+    end;
+  gtk_clist_sort(list);
+end;
+\end{lstlisting}
+The function starts by retrieving the current sort column. If it is
+different from the column the used clicked on, then 2 things are done:
+\begin{enumerate}
+\item The sort type is set to ascending.
+\item The sort column is set to the column the user clicked.
+\end{enumerate}
+If, on the other hand, the user clicks on a column that is the sort column, 
+the sort type is simply reversed. After the sort column and sort type are 
+set, the list is epxlicitly sorted. (neither of the calls that set the sort 
+order or sort column forces a sort).
+
+The sort happens using the \lstinline|compare| function (\lstinline|FileCompareFunc|)
+that was set when the CList was created:
+\begin{lstlisting}{}
+Function FileCompareFunc(List:PGtkCList; Row1,Row2 : PGtkCListRow) : Longint; Cdecl;
+
+Var 
+  SC : Longint;
+
+begin
+  SC:=List^.sort_column;
+  If SC in [2,3] then
+    begin
+    SC:=SC-2;
+    Result:=PLongint(Row1^.Data)[SC]-PLongint(Row2^.Data)[SC];
+    end
+  Else
+    Result:=DefCompare(List,Row1,Row2);  
+end;
+\end{lstlisting}
+This function receives 3 arguments:
+\begin{itemize}
+\item The list that needs to be sorted.
+\item 2 pointers to the row objects that must be compared.
+\end{itemize}
+The result must be an integer that is negative if the first row should come
+before the second or larger than zero if the second row should come before
+the first. If the result is zero then the columns are considered the same.
+
+The function checks what the sort column is. If it is not the size (2) or 
+date (3) column, then the default row compare function (which was saved in 
+the \lstinline|DefCompare| variable when the list was created) is used to
+compare the rows. If the size or date columns must be compared, the user 
+data associated with the rows is examined. As will be shown below, the user
+data will point to an array of 2 Longint values that describe the size and
+datestamp of the file. The approriate values are compared and the result is 
+passed back.
+
+To fill the file list with data, the \lstinline|FillList| function is 
+implemented:
+\begin{lstlisting}{}
+Function FillList(List : PGtkCList; 
+                  Const Dir,Mask : String) : Integer;
+
+Var
+  Info : TSearchRec;
+  Size : Int64;
+  I,J : longint;
+  
+begin
+  Result:=0;
+  Size:=0;
+  gtk_clist_freeze(List);
+  Try
+    gtk_clist_clear(List);
+    If FindFirst (AddTrailingSeparator(Dir)+Mask,
+                  faAnyFile,Info)=0 then
+      Repeat
+        Inc(Size,Info.Size);
+        AddFileToList(List,Info);
+        Inc(Result);
+      Until FindNext(Info)<>0;
+    FindClose(info);
+  finally
+    For I:=0 to 4 do
+      begin
+      J:=gtk_clist_optimal_column_width(List,i);
+      gtk_clist_set_column_width(List,i,J);
+      end;
+    gtk_clist_thaw(List)
+  end;
+end;
+\end{lstlisting}
+This function is very straightforward. To start, it 'freezes' the list with
+\lstinline|gtk_clist_freeze|; this prevents the list from updating the
+screen each time a row is added or deleted. Omitting this call would cause
+serious performance degradation and screen flicker.
+
+After freezing the list, it is cleared; Then a simple loop is implemented
+that scans the given directory with the given file mask using the
+\lstinline|FindFirst|/\lstinline|FindNext| calls. For each file found
+it calls the \lstinline|AddFileToList| function, that will actually add the
+file to the list view, using the information found in the search record.
+
+The \lstinline|AddTrailingSeparator| adds a directory separator to a 
+string containing the name of a directory if this does not end on a 
+separator yet. It can be found in the \file{futils} unit.
+
+After the loop has finished, the optimal width for each column is 
+retrieved using the \lstinline|gtk_clist_optimal_column_width| function
+and the result is used to set the column width. As a result, the columns will
+have the correct size for displaying all items.
+
+When this has been done, the list is 'thawed' with \lstinline|gtk_clist_thaw|,
+which means that it will repaint itself if needed. This happens in a 
+\lstinline|finally| block since the \lstinline|gtk_clist_freeze| and 
+\lstinline|gtk_clist_thaw| work with a reference counter. For each 'freeze' 
+call the counter is increased. It is decreased with a 'thaw' call. When the
+counter reaches zero, the list is updated.
+
+The function that actually adds a row to the list view is quite simple:
+\begin{lstlisting}{}
+Procedure AddFileToList(List : PGtkCList; Info : TSearchRec);
+
+Var
+  Texts : Array[1..6] of AnsiString;
+  FSD   : PLongint;
+  I     : longint;
+      
+begin
+  Texts[1]:=ExtractFileName(Info.Name);
+  Texts[2]:=ExtractFileExt(Info.Name);
+  Texts[3]:=FileSizeToString(Info.Size);
+  Texts[4]:=DateTimeToStr(FileDateToDateTime(Info.Time));
+  Texts[5]:=FileAttrsToString(Info.Attr);
+  Texts[6]:='';
+  i:=gtk_clist_append(List,@Texts[1]);
+  FSD:=GetMem(2*SizeOf(Longint));
+  FSD[0]:=Info.Size;
+  FSD[1]:=Info.Time;
+  gtk_clist_set_row_data_full (List,I,FSD,@DestroySortData);
+end;
+\end{lstlisting}
+The \lstinline|gtk_clist_append| call accepts 2 paramers: a CList, and a
+pointer to an array of zero-terminated strings. The array must contain as
+much items as the CList has columns (in the above, the last column is 
+always empty, as this gives a better visual effect). The call adds a column
+at the end of a list; An item can be inserted at the beginning of the list
+with \lstinline|gtk_clist_append|, which accepts the same parameters. An
+item can be inserted at certain position:
+\begin{lstlisting}{}
+gtk_clist_insert(clist:PGtkCList; row:gint; thetext:PPgchar);cdecl;
+\end{lstlisting}
+Note that all these calls do the same thing if the 'auto sort' was set for
+the CList.
+
+The \lstinline|FileAttrsToString| function converts file attributes to a
+string of characters that indicate whether a given attribute is present.
+It can be found in the \file{futils} unit and will not be shown here.
+
+After the file data was appended to the CList, an array of 2 longints is
+allocated on the heap. The first longint is filled with the size of the
+file, the second with the date of the file. The pointer to this array is
+then associated with the row that was just inserted with the
+\lstinline|gtk_clist_set_row_data_full| call. There are 2 calls to
+associate data with a row:
+\begin{lstlisting}{}
+gtk_clist_set_row_data(clist:PGtkCList; 
+                       row:gint; 
+                       data:gpointer);cdecl;
+gtk_clist_set_row_data_full(clist:PGtkCList; 
+                            row:gint; data:gpointer; 
+                            destroy: :TGtkDestroyNotify);
+\end{lstlisting}
+the first call is used to add data to a clist that will not need to be
+destroyed if the row is deleted. The second call can be used to pass a
+callback that will be called when the row is destroyed. 
+
+In the case of the file list, the \lstinline|DestroySortData| call is
+used to dispose the array with sort data:
+\begin{lstlisting}{}
+Procedure DestroySortData(FSD : Pointer);cdecl;
+ 
+begin
+  FreeMem(FSD);
+end;                                                                            
+\end{lstlisting}
+The reason that the file list is cleared when the main window is destroyed
+now becomes apparent: when the list is cleared, all data associated with
+the file list is freed. If the call to \lstinline|gtk_clist_clear| is
+omitted before destroying the main window, the list is not cleared and all
+data stays in memory even after the window closes.
+
+The display of the column titles of the file list can be switched on or off.
+To do this a check menu item ('Hide titles') is added to the 'View' menu. 
+If the menu is clicked, the following callback is executed:
+\begin{lstlisting}{}
+Procedure ToggleFileListTitles(Sender : PGtkCheckMenuItem;
+                               Window : PMainWindow);cdecl;
+
+begin
+  If active(Sender^)=0 then
+    gtk_clist_column_titles_show(Window^.FileList)
+  else  
+    gtk_clist_column_titles_hide(Window^.FileList)
+end;
+\end{lstlisting}
+The \lstinline|active| function checks whether a check menu item is currently 
+checked ot not and shows or hides the titles. 
+
+Not only can the column titles be switched on or off, it is also possible to
+control whether or not a given column must be shown;
+
+Under the 'View' menu, there is a 'Hide columns' submenu that contains 4
+check menus that can be used to toggle the visibility of the columns in the
+file list. All the check menu items are connected to the following callback:
+\begin{lstlisting}{}
+Procedure ToggleFileListColumns(Sender : PGtkCheckMenuItem;
+                                Window : PMainWindow);cdecl;
+
+Var Col : Longint;
+
+begin
+  With Window^ do
+    If Sender=MIShowExt Then
+      Col:=1
+    else if Sender=MiShowSize Then
+      Col:=2
+    else if Sender=MIShowDate then
+      Col:=3
+    else 
+      Col:=4;
+   gtk_clist_set_column_visibility(Window^.FileList,
+                                   Col,
+                                   (Active(Sender^)=0));   
+end;
+\end{lstlisting}
+The call gets as 'user data' a pointer to the main window record. Using this
+it checks which menu emitted the call, and updates the corresponding column
+with the \lstinline|gtk_clist_set_column_visibility| function.
+
+More attributes of a CList can be set, but they will not be discussed here; 
+the GTK documentation and tutorial offer an overview of the possibilities. 
+
+The selection mode of the CList has been set to allow selection of multiple
+rows. The Clist maintains a linked list (A Glist) with the rows that are
+part of the selection. The linked list contains the indexes of the selected
+rows in it's associated data.
+
+The linked list \lstinline|Glist| is often used in GTK applications. 
+It consists of the  following records:
+\begin{lstlisting}{}
+TGList = record
+  data gpointer;
+  next,prev : PGlist;
+end;
+PGlist=^TGlist;
+\end{lstlisting}
+The selection of a CList is of type \lstinline|PGlist|. The \lstinline|data|
+pointer can be typecasted to an integer to return the index of a selected
+row.
+
+The following function walks the selection linked list and stores the
+associated filenames in a \lstinline|TStrings| class:
+\begin{lstlisting}{}
+Procedure GetFileSelection (List : PGtkClist; Selection : TStrings);
+
+Var
+  SList : PGList;
+  Index : Longint;
+  P : PChar;
+  
+begin
+  Selection.Clear;
+  Slist:=List^.Selection;
+  While SList<>nil do
+    begin
+    Index:=Longint(SList^.Data);
+    gtk_clist_get_text(List,Index,0,@p);
+    Selection.Add(StrPas(p));
+    SList:=g_list_next(SList);
+    end;
+end;
+\end{lstlisting}
+The \lstinline|gtk_clist_get_text| retrieves the text of a given cell in the
+CList (a similar function exists to set the text) , and the
+\lstinline|g_list_next| jumps to the next element in the linked list.
+
+The \lstinline|TStrings| class is the standard string container as defined
+in the \lstinline|Classes| unit of Free Pascal (or Delphi).
+
+The above function will be used to retrieve the list of selected files so
+operations can be done on the selection.
+
+To retrieve the first (and possibly only) item of a selection, and the
+number of items in a selection, the following functions can be used:
+\begin{lstlisting}{}
+Function GetFileFirstSelection (List : PGtkClist) : String;
+
+Var
+  SList : PGList;
+  Index : Longint;
+  P : PChar;
+  
+begin
+  Result:='';
+  Slist:=List^.Selection;
+  If SList<>nil then
+    begin
+    Index:=Longint(SList^.Data);
+    gtk_clist_get_text(List,Index,0,@p);
+    Result:=StrPas(p);
+    end;
+end;
+
+Function GetFileSelectionCount (List : PGtkClist) : Longint;
+
+Var
+  SList : PGList;
+  
+begin
+  Slist:=List^.Selection;
+  Result:=0;
+  While SList<>nil do
+    begin
+    Inc(Result);
+    SList:=g_list_next(SList);
+    end;
+end;
+\end{lstlisting}
+These functions will be used further on.
+
+The filelist is now ready to be used. To be able to select a directory from
+which the files should be displayed, a Tree widget is used. How to create
+this tree and connect it to the file list is explained in the next section.
+
+\section{The directory tree}
+The directory tree will allow the user to browse through the directories on
+his system. When a directory is selected, the file view should be updated 
+to show the files in the selected directory.
+
+To make the directory tree more efficient and less memory consuming, the
+tree is not filled with the whole directory tree at once. Instead, only 2
+levels of directories will be put in the tree. The tree is progessively
+filled as the user expands the directory nodes. 
+
+The directory tree is created in the following function:
+\begin{lstlisting}{}
+Function NewDirtree (MainWindow : PMainWindow) : PGtkTree;
+ 
+begin
+  Result:=PGtkTree(gtk_tree_new());
+  With MainWindow^ do
+    begin
+    TreeScrollWindow:=PGtkScrolledWindow(gtk_scrolled_window_new(Nil,Nil));
+    gtk_widget_show(PGtkWidget(TreeScrollWindow));
+    gtk_scrolled_window_set_policy(TreeScrollWindow,
+                                 GTK_POLICY_AUTOMATIC,
+                                 GTK_POLICY_AUTOMATIC);
+    gtk_scrolled_window_add_with_viewport(TreeScrollWindow,PGtkWidget(Result));
+    RootNode:=PGtkTreeItem(gtk_tree_Item_new_with_label(Pchar(PathSeparator)));
+    gtk_tree_append(Result,PgtkWidget(RootNode));
+    scandirs(PathSeparator,Result, RootNode,True,MainWindow);
+    gtk_tree_item_expand(rootnode);
+    end;
+end;                                                                            
+\end{lstlisting}
+The function starts off by creating the tree widget which is the return
+value of the function.
+
+Similar to the Clist, the tree widget does not possess functionality 
+for displaying scroll bars, so a 'scrolled window' is created, 
+in which the tree widget is placed. 
+
+A tree can have one or more tree items connected to it. Each of these tree
+items can in turn have a tree associated with it, which in turn can again
+have tree items associated. This way the tree is recursively constructed.
+
+The directory tree is filled with 1 tree item, which will represent the root
+directory of the disk which is browsed with the file explorer; The
+\lstinline|gtk_tree_item_new_with_label| call returns a new tree item,
+which is then appended to the tree using the \lstinline|gtk_tree_append|
+call.
+
+After this is done, the directories below the root directory are scanned and
+appended to the root node in the \lstinline|scandirs| function, explained
+below. If the root node was filled, then it is expanded with
+\lstinline|gtk_tree_item_expand| (it can be collapsed with
+\lstinline|gtk_tree_item_collapse|)
+
+The \lstinline|scandirs| function scans a given directory for subdirectories
+and appends each directory to a subtree of a given node. The subtree is
+created if needed:
+\begin{lstlisting}{}
+Procedure Scandirs(Path: String; Tree : PgtkTree;
+                   Node: PGtkTreeItem ; SubSub : Boolean;
+                   Window : PMainWindow);
+
+Var
+  NewTree : PGtkTree;
+  NewNode : PGtkTreeItem;
+  Info : TSearchRec;
+  S,FP : AnsiString;
+
+begin
+  NewTree:=Nil;
+  FP:=AddTrailingSeparator(Path);
+  If FindFirst(FP+'*.*',faAnyfile,Info)=0 then
+    Try 
+      repeat
+        If ((Info.Attr and faDirectory)=faDirectory) then
+          begin
+          S:=Info.Name;
+          If (S<>'.') and (S<>'..') then
+            begin
+            If (Node<>Nil) then
+              begin
+              If (NewTree=Nil) and (node<>Nil) then
+                begin
+                NewTree:=PGtkTree(gtk_tree_new);
+                gtk_tree_item_set_subtree(Node,PGtkWidget(NewTree));
+                end
+              end
+            else
+              NewTree:=Tree;                
+            NewNode:=PGtkTreeItem(gtk_tree_item_new_with_label(Pchar(S)));
+            gtk_tree_append(NewTree,PgtkWidget(NewNode));
+            gtk_signal_connect(PGtkObject(NewNode),'select',
+                               TGtkSignalFunc(@DirSelect),Window);
+            gtk_signal_connect(PGtkObject(NewNode),'expand',
+                               TGtkSignalFunc(@DirExpand),Window);
+            If SubSub then 
+              ScanDirs(FP+S,Tree,NewNode,False,Window);
+            gtk_widget_show(PGtkWidget(NewNode));
+            end;
+          end;  
+      until FindNext(Info)<>0;
+    Finally 
+      FindClose(Info);
+    end;
+  gtk_widget_show(PGtkWidget(Node));
+end;    
+\end{lstlisting}
+The routine is a simple loop. If a subdirectory is found then a new 
+tree widget is created (\lstinline|newTree|) and appended to the 
+given node with the \lstinline|gtk_tree_item_set_subtree| call.
+
+For each found subdirectory a new treeitem is created and appended to 
+the subtree. 2 signals handlers are connected to the created tree item,
+one for 'select' signal which is emitted when the user selects a tree item, 
+and one for the 'expand' signal which is emitted when the user expands a 
+node. Each of these handlers gets as data a pointer to the main window
+record.
+ 
+The \lstinline|SubSub| parameter is used to control the recursive behaviour.
+If it is set to \lstinline|True|, the \lstinline|Scandirs| function will
+call itself recursively, but only once. As a result only 2 levels of
+subdirectories are scanned. 
+
+Finally, the created nodes are shown.
+
+When the user expands a node, the \lstinline|DirExpand| function is
+called:
+\begin{lstlisting}{}
+Procedure DirExpand(Item : PGtkTreeItem; Window : PMainWindow);cdecl;
+ 
+Var 
+  Dir : String;
+  SubTree : PGtkTree;
+  SubNodes : PGList;
+  Node : PGtkTreeItem;
+   
+begin
+  SubTree:=PgtkTree(Item^.SubTree);
+  SubNodes:=gtk_container_children(PGtkContainer(SubTree));
+  While SubNodes<>Nil do
+    begin
+    Node:=PgtkTreeItem(SubNodes^.Data);
+    If (Node^.SubTree<>Nil) then 
+      gtk_tree_item_remove_subtree(Node);
+    Scandirs(GetPathName(Node),Nil,Node,False,Window);
+    SubNodes:=g_list_remove_link(SubNodes,SubNodes);
+    end;
+end;
+\end{lstlisting}
+The function starts by retrieving the subtree of the tree item that
+triggered the callback. It then retrieves the list of subnodes (treeitems)
+of the subtree which represent the subdirectories of the directory node 
+that is about to be expanded. The Tree object descends from the GTK
+container object, and keeps its treeitems in the container's children
+list. This list is a Glist. The \lstinline|gtk_container_children| returns
+a copy of the list containing the children.
+
+Then a simple loop is executed: for each of
+the found nodes, the subtree is destroyed if it exists:
+\lstinline|gtk_tree_item_remove_subtree| removes a subtree from a treeItem
+and destroys it. 
+
+After the subtree is destroyed, at the subirectory is scanned for possible
+subdirecties (remark that the \lstinline|SubSub| parameter is set to 
+\lstinline|false|) and the subtree is recreated if needed.
+
+The directory corresponding to a given node is calculated in the 
+\lstinline|GetPathName| function, explained below.
+
+The next cycle of the loop is started by removing and destroying the first
+element of the GList with the \lstinline|g_list_remove_link| call:
+the call returns the new start of the list with the element removed. By
+passing the first element of the list as the element to be removed the 
+whole list is traversed.
+
+When the user selects a tree item, the list view must be updated with
+the files in that directory. This is done in the \lstinline|DirSelect|
+handler for the 'select' signal:
+\begin{lstlisting}{}
+Procedure DirSelect(Item : PGtkTreeItem; Window : PMainWindow);cdecl;
+  
+begin
+  ShowDir(Window,GetPathName(Item));
+end;
+
+Procedure ShowDir (Window : PMainWindow; Dir : String);
+
+begin
+  With Window^ do
+    begin
+    FDir:=Dir;
+    FillList(FileList,Dir,FMask);
+    gtk_label_set_text(FilesHeader,pchar(Format(SFilesInDir,[Dir])));
+    end;
+end;
+
+\end{lstlisting}
+The \lstinline|Showdir| function will be called from other places as 
+well hence it is put separately; The \lstinline|DirSelect| function
+does nothing but to call the ShowDir function after it has calculated the
+path of the treeitem that triggered the 'select' signal:
+\begin{lstlisting}{}
+Function GetPathName(Item : PGtkTreeItem) : String;
+
+Var P : PChar;
+    PTree : PGtkTree;
+begin
+  gtk_label_get(PgtkLabel(PGtkBin(Item)^.Child),@P);
+  Result:=StrPas(P);
+  If (PGtkWidget(item)^.Parent<>Nil) then
+    begin
+    PTree:=PGtkTree(PgtkWidget(Item)^.Parent);
+    If (Ptree^.Level<>0) Then
+      Result:=AddTrailingSeparator(GetPathName(PgtkTreeItem(PTree^.Tree_Owner)))+Result
+    end;  
+end;
+\end{lstlisting}
+It is a simple recursive mechanism. The only issue with this
+routine is that one should know that the parent of a tree item is a tree,
+and that the owner of the tree (in it's \lstinline|Tree_Owner| field) is 
+in turn again a treeitem. The \lstinline|Level| field of a tree determines
+at what level the tree is located (i.e. the number of nodes present above 
+the tree) and can be used to check when the algorithm should stop.
+
+An alternate approach would have been to associate with each node some 
+user data, such as a string that is the full path name of the node.
+
+With this, the tree is created and is linked to the file list, so the 
+user has the capability to select any directory and display it's contents;
+The user can also customize the view of the file list. 
+
+However, no actions can be performed on the files. This is treated in the
+next sections, where a toolbar and popup menu are used to allow the user to 
+do things with the shown files.
+
+\section{Adding a popup menu}
+To allow the user to do something with the displayed files, a popup menu is
+addd to the file list. Adding a popup menu is not different from adding a 
+main menu to a form, just it will not be attached to a menu bar. The popup
+menu will be hidden till the user right-clicks in the file list.
+
+The popup menu is created in the following function:
+\begin{lstlisting}{}
+Function NewFilePopupMenu (MainWindow : PMainWindow) : PGtkMenu;
+
+begin
+  result:=PGtkMenu(gtk_menu_new);
+  gtk_signal_connect(PGtkObject(result),'show',
+                     TGtkSignalFunc(@PMFilesActivate),MainWindow);
+  With MainWindow^ do
+    begin
+    PMIFileProperties:=AddItemToMenu(Result,Accel,'_Properties','',
+                                     TgtkSignalFunc(@DoProperties),
+                                      MainWindow);
+    PMIFileDelete:=AddItemToMenu(Result,Accel,'_Delete','<ctrl>d',
+                                 TgtkSignalFunc(@DeleteFile),
+                                 MainWindow);
+    end; 
+end;
+\end{lstlisting}
+The \lstinline|AddItemToMenu| functions were developed in an earlier
+articles, and have been collected in the 'menus' unit. 
+
+The 'show' handler attached to the menu is used to set the state
+of some of the menu items when the menu pops up:
+\begin{lstlisting}{}
+Procedure PMFilesActivate(Widget : PGtkWidget; Window : PMainWindow); cdecl;
+
+Var State : TGtkStateType;
+
+begin
+  if GetFileSelectionCount(Window^.FileList)>1 then
+    State:=GTK_STATE_INSENSITIVE
+  else
+    State:=GTK_STATE_Normal;
+  gtk_widget_set_state(PgtkWidget(Window^.PMIFileProperties),State);
+end;
+\end{lstlisting}
+When more than 1 file is selected in the file view, the properties menu item
+is disabled. 
+
+The popup menu will appear if the user clicks the right button in the file
+list; The necessary event handler for that (\lstinline|ShowPopup|) was 
+attached to the CList and discussed earlier on.
+
+The delete menu item has the following 'click' handler:
+\begin{lstlisting}{}
+Procedure DeleteFile(Widget : PGtkWidget; Window : PMainWindow); cdecl;
+
+Var i : longint;
+    S : TStringList;
+    
+begin
+  S:=TStringList.Create;
+  Try
+    GetFileSelection(Window^.FileList,S);
+    For I:=0 to S.Count-1 do
+      begin
+      For I:=0 to S.Count-1 do
+        SysUtils.DeleteFile(Window^.FDir+S[i]);
+      end;
+  Finally
+    If S.Count>0 then
+      RefreshFileView(Window);
+    S.Free;
+  end;        
+end;
+\end{lstlisting}
+The routine simply retrieves the selection list and deletes all files
+present in it; After that the file view is refreshed.
+
+The properties popup menu action will be treated later on.
+
+\section{Adding a toolbar}
+The toolbar in the file explorer application will contain 2 buttons with
+a pixmap on them; the pixmap will be loaded from data compiled into the
+binary. The actions performed by the toolbar buttons will be the same as
+the actions in the popup menu: show a file's properties and delete the file.
+
+The creation of the toolbar for the file explorer program is done in the
+following function:
+\begin{lstlisting}{}
+Function NewToolbar (MainWindow : PMainWindow) : PGtkToolbar;
+
+begin
+  Result:=pGtkToolBar(gtk_toolbar_new(GTK_ORIENTATION_HORIZONTAL,
+                                      GTK_TOOLBAR_ICONS));
+  gtk_toolbar_append_item(result,
+                          Nil,
+                          'File Properties',
+                          nil,
+                          CreateWidgetFromXPm(PgtkWidget(MainWindow^.Window),
+                                              @PropertiesXPM),
+                          TgtkSignalFunc(@DoProperties),
+                          MainWindow);
+  gtk_toolbar_append_item(result,
+                          Nil,
+                          'Delete File',
+                          Nil,
+                          CreateWidgetFromXPm(PgtkWidget(MainWindow^.Window),
+                                              @DeleteXPM),
+                          TgtkSignalFunc(@DeleteFile),
+                          MainWindow);
+end;
+\end{lstlisting}
+The \lstinline|gtk_toolbar_new| function creates a new toolbar. The first
+argument to this call specifies the orientation for the toolbar. Possible
+values for the orientation are:
+\begin{description}
+\item[GTK\_ORIENTATION\_HORIZONTAL] The toolbar is filled horizontally;
+\item[GTK\_ORIENTATION\_VERTICAL] The toolbar is filled vertically;
+\end{description}
+The second argument determines the style of the toolbar; it can have the
+following values:
+\begin{description}
+\item[GTK\_TOOLBAR\_TEXT] Toolbuttons just show a text.
+\item[GTK\_TOOLBAR\_ICONS] Toolbuttons just show a pixmap.
+\item[GTK\_TOOLBAR\_BOTH] toolbuttons show both a pixmap and text.
+\end{description}
+The style determines what widgets will be placed on new toolbuttons that
+are added with the \lstinline|gtk_toolbar_append_item| or
+\lstinline|gtk_toolbar_prepend_item| calls. If buttons are added to the 
+toolbar manually, the style has no effect.
+
+The \lstinline|gtk_toolbar_append_item| call adds a new toolbar button 
+to the end of a toolbar. The \lstinline|gtk_toolbar_prepend_item| item 
+inserts a new button at the beginning of the toolbar. Both accept the 
+following arguments:
+\begin{enumerate}
+\item a pointer to the toolbar to which the item should be added.
+\item a zero-terminated string with the text to be shown on the button.
+\item a zero-terminated string with the tooltip text (the hint) for the button.
+\item a zero terminated private tooltip text for the button.
+\item an icon wiget, usually a GtkPixmap.
+\item A callback function of type \lstinline|TGtkSignalFunc| that will be 
+executed when the user clicks the button.
+\item Callback data pointer which will be passed to the callback.
+\end{enumerate}
+A toolbutton can also be inserted at a certain position with the
+\lstinline|gtk_toolbar_insert_item| call. It accepts an additional (last) 
+argument, the position at which to insert the toolbutton. 
+
+For the toolbar of the file explorer program, the buttons contain no text
+(since the \lstinline|GTK_TOOLBAR_ICONS| style was chosen for the toolbar)
+they do contain an icon, a pixmap widget.
+
+The pixmap widget is created with the following function:
+\begin{lstlisting}{}
+function CreateWidgetFromXPM (Window : PGtkWidget; 
+                              Data : PPChar) : PGtkWidget;
+
+Var
+  mask   : PGdkBitmap;
+  pixmap : PGdkPixMap;
+
+begin
+  pixmap:=gdk_pixmap_create_from_xpm_d(window^.window,@mask,nil,ppgchar(Data));
+  Result:=gtk_pixmap_new(Pixmap,Mask);
+  gtk_widget_show(Result);
+end;
+\end{lstlisting}
+This function accepts 2 arguments: A GTK window, and a pointer to an array
+or zero-terminated strings which describe the pixmap. With these it creates 
+a gdk pixmap object with the \lstinline|gdk_pixmap_create_from_xpm_d| call.
+this function expects the following arguments:
+\begin{enumerate}
+\item A pointer to a GDK window object. In the above, the GDK window of the
+main window widget is used. This explains why the \lstinline|gtk_widget_realize|
+call was made when creating the main window: When the widget is realized, a
+window is allocated to it. If the main window widget was not realized, then
+it's gdk window would be nil.
+\item The address of a \lstinline|PGdkBitmap| which will be used to store
+the mask of the created pixmap. The mask determines the transparent items
+in the bitmap, and can be used when creating a pixmap widget. This may be
+nil.
+\item A pointer to a color that should be considered the transparent
+color. This may be nil, in which case a default color is used.
+\item A pointer to a XPM pixmap structure. 
+\end{enumerate}
+After the GDK pixmap and the mask were created, a pixmap widget is created
+from the GDK bitmap, and the widget is shown.
+
+The pixmap data is in XPM format. The XPM format is an array of
+zero-terminated strings which are organized as follows:
+\begin{enumerate}
+\item A string describing the pixmap dimensions and the number of colors.
+The string is of the form 
+\begin{verbatim}
+'width height #colors chars/color',
+\end{verbatim}
+So the string
+\begin{verbatim}
+'16 16 4 1'
+\end{verbatim}
+means a 16x16 bitmap, using 4 colors, described by 1 character per color.
+\item A series of strings that describe the color. the number of strings
+should equal the count specified in the first string. The color descriptions
+should have the following form:
+\begin{verbatim}
+'X c #YYYYYY'
+\end{verbatim}
+here 'X' must be replaced by N characters, where N is the number of
+characters per color that was specified in the first string. The YYYYYY
+is a RGB color value, in hex format. Each red,green or blue value must
+contain 2 or 4 characters. The string '\#FF0000' would describe red, just as
+'\#FFFF00000000' would describe red.
+
+Instead of a rgb value, 'None' can be specified to indicate a transparent
+color.
+Some examples of valid colors would be:
+\begin{verbatim}
+'. c #000000',      { Black }
+'# c #000080',      { Dark Blue }
+'a c None',         { Transparent }
+'b c #f8fcf8',      { greyish }
+\end{verbatim}
+\item A series of strings of characters, each string describes one line of
+the pixmap and is composed of the color characters described in the color
+section. Each line has the same length, namely the width of the image
+multiplied with the number of characters per color. Obviously, there 
+should be as many strings as the height of the pixmap.
+\end{enumerate}
+The \file{fxbitmaps} unit contains 2 such bitmaps; comments have been added.
+
+After the toolbar has been added, the main form is finished. The 
+form in action is shown in figure \ref{fig:mainwin}.
+\begin{figure}[ht]
+\caption{The main window in action.}\label{fig:mainwin}
+\epsfig{file=gtk4ex/mainwin.png,width=\textwidth}
+\end{figure}
+
+The toolbar contains a button to show the properties dialog. This dialog
+will show the various properties of a file, and is discussed in the next
+section.
+
+\section{Adding some dialogs}
+Adding some dialogs to the file explorer program is not so difficult.
+Three are created, an about dialog, a file properties dialog, and a dialog
+that allows to enter a file mask which will then be applied to the file
+view. All three dialogs will be based on the standard GTK dialog.
+
+Adding a dialog that shows the properties of a file is quite easy. 
+The standard GTK dialog widget contains 3 widgets; a vertical box 
+(\lstinline|vbox|) which can be used to drop widgets in, a separator 
+and a horizontal box (\lstinline|action_area|), which can be used to 
+put buttons (such as an 'OK' button) in.
+
+The file properties dialog consists mainly of a table packed with labels and
+some checkboxes. It is created in the following function:
+\begin{lstlisting}{}
+Type
+  TFilePropertiesDialog = Record
+    Window : PgtkDialog;
+    Table  : PGtkTable;
+    OkButton : PGtkButton;
+    Labels : Array[0..1,0..NrTableLines] of PGtkLabel;
+    CheckBoxes : Array[CheckBoxLineStart..NrTableLines] of PgtkCheckButton;
+  end;
+  PFilePropertiesDialog = ^TFilePropertiesDialog;
+  
+Function NewFilePropertiesDialog(FileName : String) : PFilePropertiesDialog;
+Const 
+  CheckAttrs : Array [CheckBoxLineStart..NrTableLines] of Integer 
+             = (faReadOnly,faArchive,faHidden,faSysFile);
+
+Var 
+  Info : TSearchRec;
+  I : Longint;
+  
+begin
+  Result:=New(PFilePropertiesDialog);
+  With Result^ do
+    begin
+    Window:=PgtkDialog(gtk_dialog_new);
+    gtk_window_set_title(PgtkWindow(Window),SPropsTitle);
+    gtk_window_set_modal(PgtkWindow(Window),True);
+    gtk_window_set_policy(PgtkWindow(Window),0,0,0);
+    gtk_window_set_position(PGtkWindow(Window),GTK_WIN_POS_CENTER);
+    OkButton:=PGtkButton(gtk_button_new_with_label(SOK));
+    gtk_box_pack_start(PgtkBox(Window^.action_area),PGtkWidget(Okbutton),False,False,5);
+    gtk_window_set_focus(PGtkWindow(Window),PGtkWidget(OkButton));
+    gtk_widget_show(PGtkWidget(OkButton));
+\end{lstlisting}
+The above are standard things: The dialog window title is set, the dialog is
+made modal, the resizing of the window is prohibited with the
+\lstinline|gtk_window_set_policy| call. Then the window is told that it
+should position itself in the center of the screen with the
+\lstinline|gtk_window_set_position| call. The position specifier can be one
+of the following:
+\begin{description}
+\item[GTK\_WIN\_POS\_NONE] The window manager will decide where the window
+goes.
+\item[GTK\_WIN\_POS\_CENTER] The window is placed at the center of the
+screen.
+\item[GTK\_WIN\_POS\_MOUSE] The window is placed where the mouse cursor is.
+\end{description}
+After the window properties have been set, an OK button is placed in the
+action area, and it gets the focus.
+
+Next, a table is created with \lstinline|NrTableLines+1| rows and 2 columns,
+and put in the vbox area:
+\begin{lstlisting}{}
+Table:=PgtkTable(gtk_table_new(NrTableLines+1,2,TRUE));
+gtk_box_pack_start(PGtkBox(Window^.vbox),PGtkWidget(Table),True,True,10);
+\end{lstlisting}
+Then the table is filled with labels that describe the various properties;
+the left column contains labels that simplu
+\begin{lstlisting}{}
+For I:=0 to NrTableLines do
+  begin 
+  Labels[0,i]:=PGtkLabel(gtk_label_new(LabelTexts[i]));
+  gtk_label_set_justify(Labels[0,I],GTK_JUSTIFY_RIGHT);
+  gtk_table_attach_defaults(Table,PgtkWidget(Labels[0,I]),0,1,I,I+1); 
+  end;
+For I:=0 to CheckboxLineStart-1 do
+  begin 
+  Labels[1,i]:=PGtkLabel(gtk_label_new(''));
+  gtk_label_set_justify(Labels[1,I],GTK_JUSTIFY_LEFT);
+  gtk_table_attach_defaults(Table,PgtkWidget(Labels[1,I]),1,2,I,I+1); 
+  end;
+\end{lstlisting}
+The file attributes will be represented with checkboxes:
+\begin{lstlisting}{}
+For I:=CheckboxLineStart to NrTableLines do
+  begin
+  checkBoxes[i]:=PgtkCheckButton(gtk_check_button_new_with_label(CheckBoxTexts[I]));
+  gtk_widget_set_state(PGtKWidget(CheckBoxes[i]),GTK_STATE_INSENSITIVE);
+  gtk_table_attach_defaults(Table,PgtkWidget(CheckBoxes[i]),1,2,I,I+1); 
+  end;  
+\end{lstlisting}
+The checkboxes are made inactive, so the user cannot change them.
+
+After all labels and checkboxes are put in place, the file information
+is put into various places:
+\begin{lstlisting}{}
+gtk_label_set_text(Labels[1,0],PChar(ExtractFileName(FileName)));
+gtk_label_set_text(Labels[1,1],PChar(ExtractFilePath(FileName)));
+gtk_label_set_text(Labels[1,2],PChar(ExtractFileExt(FileName)+SFile));
+If FindFirst(FileName,faAnyFile,Info)=0 Then
+  begin
+  gtk_label_set_text(Labels[1,3],PChar(FileSizeToString(Info.Size)));
+  gtk_label_set_text(Labels[1,4],PChar(DateTimeToStr(FileDateToDateTime(Info.Time))));
+  For I:=CheckboxLineStart to NrTableLines do
+    If (CheckAttrs[i] and Info.Attr)=CheckAttrs[i] then
+      gtk_toggle_button_set_active(PgtkToggleButton(CheckBoxes[I]),True);
+  FindClose(Info);
+  end;
+\end{lstlisting}
+Finally, the 'destroy' callback for the window is set, and the OK button's
+'click' signal is attached to the destroy method of the window widget:
+\begin{lstlisting}{}
+  gtk_signal_connect(PGtkObject(Window),'destroy',
+                     TGTKSignalFunc(@DestroyPropDialog),Result);
+  gtk_signal_connect_object(PgtkObject(OKButton),'clicked',
+                            GTK_SIGNAL_FUNC(@gtk_widget_destroy),
+                      PGTKOBJECT(Window));
+  end;    
+end;
+\end{lstlisting}
+Showing the properties dialog is simple:
+\begin{lstlisting}{}
+Procedure ShowFilePropertiesDialog(Dialog : PFilePropertiesDialog);
+
+begin
+  gtk_widget_show_all(PgtkWidget(Dialog^.Window));
+end;
+\end{lstlisting}
+
+The result of all this is shown in figure \ref{fig:fileprops}.
+\begin{figure}[ht]
+\begin{center}
+\caption{The file properties dialog.}\label{fig:fileprops}
+\epsfig{file=gtk4ex/fileprops.png,width=8cm}
+\end{center}
+\end{figure}
+
+The handling of the mask form is a little bit more complicated than the
+properties dialog, since the mask form should return some information
+to the main form. 
+
+The creation of the mask form is again a standard matter, and the reader 
+is referred to the code on the CD-ROM to see how it is handled. The
+only thing worth noting is the handling of the click on the 'OK' button that
+appears on the form.
+\begin{lstlisting}{}
+gtk_signal_connect(PgtkObject(OKButton),'clicked',
+                   TGtkSignalFunc(@ApplyMask),Result);
+gtk_signal_connect_object(PgtkObject(OKButton),'clicked',
+                   GTK_SIGNAL_FUNC(@gtk_widget_destroy),
+                   PGTKOBJECT(Window));                                      
+\end{lstlisting}
+Two handlers are added to the 'clicked' signal of the 'OK' button.
+The first one is pointed to a function that will apply the mask, and the
+second one is redirected to the destroy method of the dialog window wigdet.
+\begin{lstlisting}{}
+Procedure ApplyMask(Widget : PGtkWidget; Window : PMaskForm);cdecl;
+ 
+begin
+With Window^ do
+  begin
+  Mask:=StrPas(gtk_entry_get_text(EMask));
+  If (CallBack<>Nil) then
+    CallBack(Mask,CallBackData);
+  end;
+end;
+\end{lstlisting}
+The \lstinline|TMaskForm| record that contains fields for all widgets on
+the mask entry form also contains 2 fields that allow the OK button to notify 
+the calling program of the new mask:
+\begin{lstlisting}{}
+  TMaskCallBack = Procedure (Mask : String; Data : Pointer);
+  TMaskForm = Record
+    { ... widget fields ... }
+    Mask : ShortString;
+    CallBack : TMaskCallBack;
+    CallBackData : Pointer;
+  end;
+  PMaskForm = ^TMaskForm;                                                       
+\end{lstlisting}
+If the callback field is set, then the \lstinline|ApplyMask| function will call
+it and pass it the new mask and some arbitrary pointer.
+
+The main form contains a 'file mask' menu item, which has the following
+'click' handler:
+\begin{lstlisting}{}
+procedure DoMask(Widget : PGtkWidget ; MainForm : PMainWindow ); cdecl;
+ 
+Var
+  S : AnsiString;
+ 
+begin
+  With NewMaskForm^ do
+    begin
+    S:=MainForm^.FMask;
+    gtk_entry_set_text(EMask,PChar(S));
+    CallBack:=@ApplyMask;
+    CallBackData:=MainForm;
+    gtk_widget_show_all(PgtkWidget(Window));
+    end;
+end;   
+\end{lstlisting}
+When the user clicks the 'file mask' menu item, A mask entry form is created.
+The current file mask is filled in the entry widget (\lstinline|EMask|).
+The callback is set, and the callbackdata is set to the pointer to the main
+window record. The callback that is executed when the user clicks the OK
+button on the mask form is the following:
+\begin{lstlisting}{}
+Procedure ApplyMask(Mask : String; Data : Pointer);
+ 
+begin
+  PMainWindow(data)^.FMask:=Mask;
+  RefreshFileView(PMainWindow(Data));
+end;
+\end{lstlisting} 
+
+The reason that this system of callbacks is needed is that the
+\lstinline|gtk_widget_show_all| immediatly returns when the mask entry form is 
+shown. Even though the mask entry form dialog is a modal dialog (i.e. it alone will
+respond to mouse clicks and key presses) the call returns immediatly,
+there is no counterpart for the Delphi \lstinline|ShowModal| function.
+
+When the \lstinline|gtk_widget_show_all| returns, the mask entry form is still on
+the screen, so the changes made in the mask form must be communicated 
+back to the main form by means of a callback which is executed when 
+the mask entry form is closed.
+
+The mask form in action is shown in figure \ref{fig:filemask}.
+\begin{figure}[ht]
+\begin{center}
+\caption{The file properties dialog.}\label{fig:filemask}
+\epsfig{file=gtk4ex/filemask.png,width=8cm}
+\end{center}
+\end{figure}
+
+\section{Finishing the application}
+In several places in this article, a reference was made to the main menu. 
+The main menu is created in the \lstinline|NewMainMenu| function; since
+menus were discussed extensively in the previous article on programming GTK,
+the code will not be presented here. The various calls developed in the
+previous article have been collected in the \file{menus} unit. One
+additional call was added which adds a check menuitem to a menu; the call is
+similar to the regular menu item calls, and will not be discussed here.
+
+The application is built in such a way that it can easily be extended. 
+Only 2 file actions have been implemented, but many more can be made.
+Missing functionality includes:
+\begin{itemize}
+\item Renaming of files. The CList allows to put an arbitrary widget into
+a cell; this functionality could be used to allow the user to change the
+filename by simply editing it.
+\item Moving and copying of files, using drag and drop.
+\item Duplicating the main window, or spawning a new window.
+\item Opening a file in another application.
+\item Improve the look of the file properties form.
+\item On Windows, support for showing different drives should be added.
+\end{itemize}
+And without doubt, many more can be found.
+
+\end{document}