mirror of
				https://gitlab.com/freepascal.org/fpc/source.git
				synced 2025-10-31 17:51:38 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			592 lines
		
	
	
		
			27 KiB
		
	
	
	
		
			TeX
		
	
	
	
	
	
			
		
		
	
	
			592 lines
		
	
	
		
			27 KiB
		
	
	
	
		
			TeX
		
	
	
	
	
	
| \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:=@MyForm.OnButtonClick
 | |
| \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}
 | 
