123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218 |
- \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{September 2000}
- \maketitle
- \section{Introduction}
- In this second article on programming the GTK toolkit, a more advanced use
- of the GTK library is presented. Techniques to create a new GTK widget
- are discussed by creating two custom widgets.
- The first widget is realized by combining existing GTK widgets to create
- a new widget, a GTKFileEdit component, modeled after the TFileEdit component
- found in the RXLib library for Delphi.
- The second widget is..
- \section{Preliminaries}
- Whatever the method used when creating new GTK widgets, it is necessary to
- split the functionality of the widget in 2 parts.
- The first part is the functionality that is common to all instances of the
- new widget. This part is by far the most important
- one, and is implemented in the 'class record'.
- The second part concerns the particular instance of the widget that is
- created. It is the actual object created by the user. This part of the
- widget is implemented in the 'Object record'.
- Creating a new widget consists of defining 2 records that contain the
- data for the class as a whole, and one for the individual instances of
- the objects.
- Then some standard methods must be implemented in order to integrate
- the new widget in the GTK library. Implementing some methods for the
- user to manipulate the properties of the new widget finishes then
- the creation of a new widget.
- Since GTK is implemented in C, the programmer must obey some rules in order
- to preserve the object-oriented aspect of the GTK library. More precisely,
- when defining the class and object structures, care must be taken to specify
- the parent object as the first element in the newly created structure. This
- will allow typecasting of the widget to its parent objects.
- Taking a look at the \lstinline|TGtkContainer| widget, we see that the declaration
- of the object record starts with the delaration of its parent widget
- \lstinline|TGtkWidget|:
- \begin{lstlisting}{}
- TGtkContainer = record
- widget : TGtkWidget;
- focus_child : PGtkWidget;
- flag0 : longint;
- resize_widgets : PGSList;
- end;
- \end{lstlisting}
- The same is true for the \lstinline|TGtkContainerClass| record:
- \begin{lstlisting}{}
- TGtkContainerClass = record
- parent_class : TGtkWidgetClass;
- n_child_args : guint;
- // ...
- end;
- \end{lstlisting}
- For both the components that will be made, such records will be made.
- \section{A filename edit component}
- The \lstinline|TGTKFileEdit| component presented here is composed out of three
- other components; first of all a single line edit control, in which the
- user can type a filename if he wishes. The second is a button. The button
- is always placed on the right edge of the edit control, and has the same
- height. The third component is an image component, which is used to display
- an image on the button\footnote{In GTK a button does not necessarily contains a
- caption, it is an empty placeholder, which can be filled with whatever
- you want, in this case an image. To have the button display a caption,
- a label is placed in it.}
- Since the edit and button component must be kept together, we use a
- \lstinline|TGtkHBox| as the 'Parent' component, and this component will be
- used to keep the edit and button control. There is no need to consider the
- image component, since it will be placed inside the button.
- Having decided that, the structure of the record for the instance of the
- component is more or less determined:
- \begin{lstlisting}{}
- Type
- PGtkFileEdit = ^TGtkFileEdit;
- TGtkFileEdit = Record
- Box : TGtkHBox;
- Edit : PGtkEntry;
- Button : PGtkButton;
- Image : PGtkPixmap;
- Dialog : PGtkFileSelection;
- end;
- \end{lstlisting}
- The first field of the record contains the parent record, as required
- by the OOP structure of GTK. The other fields are used to contain references
- to the other controls used. The \lstinline|Dialog| field will be filled with the
- reference to the file selection dialog which is created when the user clicks
- the button, at all other times it will contain a \lstinline|nil| pointer.
- Remark that the first field is a record, and all other fields are pointers.
- Since the fields of the record are 'Public' the user can access the button
- and edit components, and set or read their properties, and set additional
- signals. (e.g. a 'change' signal for the edit component)
- The class record for the {TGTKFileEdit} component should contain as a first
- field the parent class record, in this case \lstinline|TgtkHBoxClass|. Furthermore
- in the class record the default bitmap that will be displayed on the button
- will be stored. For this two fields are needed; one to keep the bitmap
- (\lstinline|DefaultPixmap|, and
- another one to keep a bitmask that is used to determine the transparant
- pixels in the bitmap (\lstinline|DefaultBitMap|):
- \begin{lstlisting}{}
- PGtkFileEditClass = ^TGtkFileEditClass;
- TGtkFileEditClass = Record
- Parent_Class : TgtkHBoxClass;
- DefaultPixmap : PGdkPixmap;
- DefaultBitMap : PGdkBitmap;
- end;
- \end{lstlisting}
- As usual, a pointer type is defined which points to the record. The fields
- of the class record will be filled in by the initialization code for our
- component, as will be shown below.
- To register the component with the GTK library, a function must be
- implemented which returns the type of the widget:
- The \lstinline|GtkFileEdit\_get\_type| function.
- When this function is called for the first time, it will register
- the new class with GTK, which will in turn supply a unique ID for the
- new component. This ID is returned and also stored, and will be returned
- the next times when the \lstinline|\_get\_type| function is called.
- Registering a new type with GTK is done by filling a \lstinline|TGtkTypeInfo|
- record with information on the new type, and passing it on to GTK with
- \lstinline|gtk\_type\_unique|:
- \begin{lstlisting}{}
- Function GtkFileEdit_get_type : Guint;cdecl;
- Const
- GtkFileEditInfo : TGtkTypeInfo =
- (type_name : 'GtkFileEdit';
- object_size : SizeOf(TGtkFileEdit);
- class_size : SizeOf(TGtkFileEditClass);
- class_init_func : TGtkClassInitFunc(@GtkFileEditClassInit);
- object_init_func : TGtkObjectInitFunc(@GtkFileEditInit);
- reserved_1 : Nil;
- reserved_2 : Nil;
- base_class_init_func : Nil
- );
- begin
- if (GtkFileEditType=0) then
- GtkFileEditType:=gtk_type_unique(gtk_hbox_get_type,@GtkFileEditInfo);
- Result:=GtkFileEditType;
- end;
- \end{lstlisting}
- The fields of the \lstinline|TGtkTypeInfo| record are filled with the following
- information:
- \begin{description}
- \item[type\_name] Contains the name of the type that must be registered.
- \item[object\_size] The size of the object record. GTK itself will allocate
- the memory when an new instance of the object is created, so it must know the
- size of the object.
- \item[class\_size] The size of the class object. Only one instance of this
- record will be created (by GTK)
- \item[class\_init\_func] The address of a function that will initialize the
- class record. This function accepts as a single arument a pointer to the
- class record to be initialized. This function will normally be called only
- once.
- \item[object\_init\_func] The address of a function that will initialize
- an instance of the object. The function must accept as a single argument
- a pointer to an instance of the object. This instance will be created by
- GTK. This function is called for each instance of the object.
- \end{description}
- The other three fields of the record are unfortunately not documented, so
- they are left blank.
- The component is registered by passing along a suitably filled
- \lstinline|TGtkTypeInfo| record together with the type of the parent class
- (acquired with it's \lstinline|gtk\_hbox\_get\_type| function) to the
- \lstinline|gtk\_type\_unique| function. this function will return a numeric ID for
- the \lstinline|GtkFileEdit| class.
- If a \lstinline|class\_init\_func| was specified when registering the new type,
- then GTK will call this method; it should initialize any class-specific
- data in the class record. In the case of the \lstinline|GTKFileEdit|, the bitmap
- which is used to fill the button is loaded:
- \begin{lstlisting}{}
- Procedure GtkFileEditClassInit (CObj : PGtkFileEditClass);cdecl;
-
- begin
- With Cobj^ do
- DefaultPixMap:=gdk_pixmap_create_from_xpm(Nil,@DefaultBitmap,
- Nil,'fileopen.xpm');
- end;
- \end{lstlisting}
- The \lstinline|gdk_pixmap_create_from_xpm| does 2 things: It loads a bitmap
- from the \textsf{fileopen.xpm} file and returns a PGdkPixmap pointer.
- At the same time it returns a pointer to a bitmask which designates the
- transparant regions of the bitmap.
- The result of this function is stored in the class record, so the bitmap
- is available when a new instance of the class is created.
- \end{document}
|