GtkAda User's Guide

Version 1.2.4

Document revision level 1.36

Date: 1999/10/21 10:16:11

E. Briot, J. Brobecker, A. Charlet, P. Durif


Table of Contents


Copyright (C) 1998-1999, Emmanuel Briot, Joel Brobecker, Arnaud Charlet

This document may be copied, in whole or in part, in any form or by any means, as is or with alterations, provided that (1) alterations are clearly marked as alterations and (2) this copyright notice is included unmodified in any copy.

Introduction

The home page for GtkAda, that will always contain the latest news for this binding, is

http://gtkada.eu.org

The home page for gtk is

http://www.gtk.org

This is GtkAda version 1.2.4. This package is an Ada95 graphical library for the Gimp Toolkit, which means this is a set of packages to allow you to easily create some graphical interfaces under X11 and Win32, using Ada95 as a programming language.

From now on, major version numbers will follow Gtk+ (e.g 1.2, 1.3), meaning that stable versions of GtkAda will have major number 1.2 and development versions will have number 1.3. Every widget from gtk 1.2 has been implemented, and the test program found in the gtk release has been reimplemented in Ada (have a look at the testgtk/ directory).

This binding was tested on the following systems:

using the following compilers:

the following versions of gtk:

and the following versions of glade:

Although versions up to 0.2.1 were compatible with Gtk-1.0, this one is Gtk-1.2 specific. If you are looking for a binding to Gtk-1.0, please consider downloading GtkAda 0.2.1.

If you manage to use it on other systems (which should probably be straightforward - just recompile GtkAda), please let us know so that we can add to the above list.

This documentation is largely inspired from the Gtk+ documentation written by Ian Main and Tony Gale.

GtkAda uses extensively the object oriented programming capabilities, access to subprograms, exceptions and genericity (in particular to define and handle callbacks) provided by Ada 95. As a result, this library provides a secure, easy to use and extensible toolset.

A complete example of the use of GtkAda is provided at the end of this document.

Getting started with GtkAda

How to build and install GtkAda

To build and install GtkAda, simply do the following steps:

You then have to make sure that the dynamic library libgtkada is known by your system, by typically running ldconfig, editing /etc/ld.conf or add the path that contains libgtkada (by default /usr/local/lib, or $prefix/lib if you specified the --prefix option during the configure step) to your LD_LIBRARY_PATH

How to compile an application with GtkAda

A script, gtkada-config, is provided to simplify the build of an application:

gnatmake <main-file> `gtkada-config`

Architecture

There are three major libraries: GTK, GDK and GLIB. GTK is based on GDK and GLIB, GDK is based on GLIB and the underlying windowing library (X-Window or Win32).

+---------------------------------------------+
|             Your Application                |
|               +-----------------------------+
|               |            GTK              |
|       +-------+-----------------------------+
|       |           GDK                       |
+-------+--------------+--+-------------------+
|          GLIB        |  | X-Window / Win32  |
+----------------------+  +-------------------+

GTK is the interface you will use most, but it is sometimes needed to use directly GDK and GLIB.

The Gtk components (resp. Gdk and Glib) are available in packages whose name start with Gtk_ (resp. Gdk_ and Glib).

GtkAda uses an object oriented approach of the components (called `widget'), whose hierarchy is given in the following section.

Widgets Hierarchy

The simple rule followed by GtkAda to implement each widget is the following: Given a widget Gtk_Xxx, its definition can be found in the package Gtk.Xxx in the file gtk-xxx.ads.

For example, the Gtk_Text type is defined in package Gtk.Text located in the file gtk-text.ads.

Here is the complete hierarchy of Gtk widgets:

1    Gtk_Data
2       Gtk_Tooltips
2       Gtk_Adjustment

1    Gtk_Widget
2       Gtk_Calendar

2       Gtk_Container
3          Gtk_Bin
4             Gtk_Alignment
4             Gtk_Event_Box
4             Gtk_Frame
5                Gtk_Aspect_Frame
4             Gtk_Handle_Box
4             Gtk_Invisible
4             Gtk_Item
5                Gtk_List_Item
5                Gtk_Menu_Item
6                   Gtk_Check_Menu_Item
7                      Gtk_Radio_Menu_Item
6                   Gtk_Tearoff_Menu_Item
5                Gtk_Tree_Item
4             Gtk_Viewport
4             Gtk_Window
5                Gtk_Color_Selection_Dialog
5                Gtk_Dialog
6                   Gtk_Input_Dialog
5                Gtk_File_Selection
5                Gtk_Font_Selection_Dialog
5                Gtk_Plug
3          Gtk_Box
4             Gtk_Button_Box
5                Gtk_Vbutton_Box
5                Gtk_Hbutton_Box
4             Gtk_Color_Selection
4             Gtk_Combo
4             Gtk_Gamma_Curve
4             Gtk_Status_Bar
3          Gtk_Button
4             Gtk_Option_Menu
4             Gtk_Toggle_Button
5                Gtk_Check_Button
6                   Gtk_Radio_Button
3          Gtk_Clist
4             Gtk_Ctree
3          Gtk_Fixed
3          Gtk_Layout
3          Gtk_List
3          Gtk_Menu_Shell
4             Gtk_Menu
4             Gtk_Menu_Bar
3          Gtk_Notebook
4             Gtk_Font_Selection
3          Gtk_Packer
3          Gtk_Paned
3          Gtk_Scrolled_Window
3          Gtk_Socket
3          Gtk_Toolbar
3          Gtk_Table
3          Gtk_Tree

2       Gtk_Drawing_Area
3          Gtk_Curve

2       Gtk_Editable
3          Gtk_Entry
4             Gtk_Spin_Button
3          Gtk_Text

2       Gtk_Misc
3          Gtk_Arrow
3          Gtk_Image
3          Gtk_Label
4             Gtk_Accel_Label
4             Gtk_Tips_Query
3          Gtk_Pixmap

2       Gtk_Preview
2       Gtk_Preview_Info
2       Gtk_Progress
3          Gtk_Progress_Bar

2       Gtk_Range
3          Gtk_Scale
3          Gtk_Scrollbar

2       Gtk_Ruler
2       Gtk_Separator

The number represents the type's depth in the hierarchy.

Contents

The almost full set of widget that comes with Gtk 1.2 has been bound, and you should be able to use all of these widgets from your Ada program.

Although it is not quite complete yet, the Gdk binding (the low level layer) will probably not evolve soon, unless some people (why not you?) send us patches, or at least ask for specific functions.

The specs have been evolving a lot since version 0.5, but things should really stabilize now. We hope that the changes in the next versions will not break existing code, but we certainly can not guaranty anything.

We have tried to adopt a consistent naming scheme for Ada identifiers:

WARNING: all the generic functions allocate some memory for internal structures. This memory is freed by gtk itself, by calling some Ada functions. Therefore the generic packages have to be instanciated at library level, not inside a subprogram, so that the functions are still defined when gtk needs to free the memory.

Hierarchical composition of a window

Typically, a window is created in which one can insert a box containing either:

Each component is a widget (Window Gadget), even the windows.

Signal handling

A signal is `emited' by a widget when an action is performed by the user on this widget. If the widget has one or more procedures attached to this signal, they are then executed. Such procedure will be called callback in this document.

To associate a callback to a widget, you have to connect this callback to the specific widget, specifying which signal will be handled. This is done with the Connect function.

Depending on their type, the widgets can emit zero (e.g Gtk_Label), one or several different signals.

A signal is identified by a string, e.g

For a specific widget, you can connect several callbacks on the same signal. They will be executed in the order in which they have been connected.

One single callback can be connected several times on the same widget and even on different widgets.

The choice made by GtkAda to use generics may look complicated but is actually simple to use and more important, ensures type checking.

For example, you can connect to a Gtk_Button an action to execute each time the button is clicked (signal "clicked"). Here is how to do it:

declare
   Button : Gtk.Button.Gtk_Button;
   Cb_Id  : Glib.Guint;

begin
   Gtk.Button.Gtk_New (Button => Button, Label => "Load");
   Cb_Id := Callbacks.Button_Callback.Connect (
      Obj  => Button,                     --  the emitting widget
      Name => "clicked",                  --  the generated signal
      Func => Callbacks.Load'Access);     --  the signal handler
end;

The package Button_Callback is an instanciation of the package Gtk.Signal.Void_Callback with the type Gtk.Button.Gtk_Button.

This package defines an access type to a procedure Button_Callback.Callback and the corresponding connect function Button_Callback.Connect.

The callbacks provided to this function have to respect the Button_Callback.Callback type. Below is an example of how to declare this package.

with Gtk.Signal;
with Gtk.Button;

package Callbacks is
   --  Define callbacks that apply on the widget that detected the
   --  corresponding signal.
   --  These callbacks have only one parameter: the widget itself

   package Button_Callback is new Gtk.Signal.Void_Callback
     (Widget_Type => Gtk.Button.Gtk_Button_Record);

   --  Which is equivalent to:
   --     package Button_Callback is
   --
   --        type Callback is access procedure
   --           (Widget : access Gtk.Button.Gtk_Button_Record);
   --        --  Definition of the Callback type
   --
   --        function Connect
   --          (Obj    : access Gtk.Button.Gtk_Button_Record'Class;
   --           Name   : in String;
   --           Func   : in Callback;
   --           After  : in Boolean := False)
   --           return Guint;
   --         --  Connect a callback to a button
   --
   --     end Button_Callback;

   procedure Load (Widget : access Gtk.Button.Gtk_Button_Record);
   --  The callback procedure

end Callbacks;

Callbacks.Load is a procedure that can be used to handle a signal since its profile conforms with Button_Callback.Callback.

Note that Callbacks must be declared at the library level to ensure its lifetime and the presence of its variables during the execution of the application. This is needed so that GtkAda can take care of freeing memory.

Although GtkAda tries to check whether you gave enough arguments to your callback handlers, it is of course easier if you know what gtk+ expects! The easiest way to find out is to look at the C header files. Each widget is described in its own C file, and has two C structures associated with it. One of them is the "class" structure, and contains a serie of pointers to functions. Each of these functions has the same name as the signal name, and the arguments are the one that are expected in the handlers.

For instance, consider the following extract from gtkbutton.h:

struct _GtkButtonClass
{
  GtkBinClass        parent_class;
  
  void (* pressed)  (GtkButton *button);
  void (* released) (GtkButton *button);
  void (* clicked)  (GtkButton *button);
  void (* enter)    (GtkButton *button);
  void (* leave)    (GtkButton *button);
};

This means that the Gtk_Button widget redefines five new signals, called respectively "pressed", "release", ... Each of them expects a handler looking like:

   procedure Pressed_Handler (Button : access Gtk_Button_Record;
                              Data   : ...);

The type of Data is given by one of the generic parameters to the packages in Gtk.Signal.

For more information on how signals work, we recommand having a look at the book [1].

Start an application with GtkAda

You need to perform some initializations to start a GtkAda application:

--  predefined units of the library
with Gtk.Rc;
with Gtk.Main;
with Gtk.Enums;
with Gtk.Window;
...
--  My units
with Callbacks;
...
procedure Application is
   procedure Create_Window is ...

begin
   --  Set the locale specific datas (e.g time and date format)
   Gtk.Main.Set_Locale;

   --  Initializes GtkAda
   Gtk.Main.Init;

   --  Load the resources
   Gtk.Rc.Parse ("application.rc");

   --  Create the main window
   Create_Window;

   --  Signal handling loop
   Gtk.Main.Main;
end Application;

the Create_Window procedure looks like

   procedure Create_Window is
      Main_Window : Gtk.Window.Gtk_Window;
      ...
   begin
      Gtk.Window.Gtk_New
        (Window   => Main_Window,
         The_Type => Gtk.Enums.Window_Toplevel);

      --  From Gtk.Widget:
      Gtk.Window.Set_Title (Window => Main_Window, Title  => "Editor");

      --  Construct the window and connect various callbacks

      ...
      Gtk.Window.Show_All (Main_Window);
   end Create_Window;

General GTK documentation

This section describes briefly how to use the GTK toolset. This is largely inspired from the GTK+ documentation. It is recommended that you read this documentation for all general GTK topics.

The GTK toolkit is based on two lower level layers: GDK and Glib. It is sometimes needed to call these layers directly. See section Description of the GDK hierarchy for a brief description of these packages.

Each widget is declared in a separate package. The file, package and type names can be automatically retrieved from one another. For example, the type Gtk_Text is defined in the package Gtk.Text which is defined in the file gtk-text.ads.

You may want to look at the sources themselves to find informations on a specific widget.

Package Gtk.Main - high level routines

These procedures are described in the order in which they should be called:

Package Gtk.Signal - connecting callbacks

This section describes three of the generic packages provided by Gtk.Signal. You can connect as many callback per signal and widgets as needed. For a given widget and signal, the various callbacks will be executed in the order in which they have been connected.

On the other hand, a same callback routine can be connected to several widgets and signals. The Connect function returns an integer (Glib.Guint) that identifies the connection, allowing you to destroy it later by giving this identifier and the appropriate widget. It is also possible to destroy all the connections to a widget.

The first package (Void_Callback) provides a way to create callbacks that have no specific parameter other than the emitting widget:

generic
   type Base_Type is new Gtk.Object.Gtk_Object_Record with private;

package Void_Callback is

   type Callback is access procedure (Widget : access Base_Type);

   function Connect
     (Obj    : access Base_Type'Class;
      Name   : in String;
      Func   : in Callback;
      After  : in Boolean := False)
      return Guint;
end Void_Callback;

The callback procedure that can be connected with the Connect function must follow the profile defined by the Callback type. The object parameter will be the widget that emitted the signal. For the Connect function, the parameters are:

The second package (Callback) whose callbacks accept an additional data dynamically allocated during the call to Connect and whose initial value is set by the Func_Data parameter

generic
   type Base_Type is new Gtk.Object.Gtk_Object_Record with private;

   type Data_Type (<>) is private;
   --  The type of the data for the callback
   --  This type need not be an access type (as opposed as what
   --  happens in C). A new access is created by the connect function.

package Callback is

   type Callback is access procedure
     (Widget : access Base_Type;
      Data   : in Data_Type);
   --  Callback function for Signal_Connect below

   function Connect
     (Obj       : access Base_Type'Class;
      Name      : in String;
      Func      : in Callback;
      Func_Data : in Data_Type;
      After     : in Boolean := False)
      return Guint;
end Callback;

The last package Object_Callback provides callbacks that can be connected to any kind of widget. (the Obj parameter can be any kind of object), but the callback parameter, as for previous packages will still have to be the same as the one specified during the instanciation.

At run time, the callback will get the widget parameter Slot_Object that has been given to Connect.

This is for example useful when you need to handle a button click depending on the window that contains the button instead of the button itself.

generic
   type Base_Type is new Gtk.Object.Gtk_Object_Record with private;

package Object_Callback is
   type Callback is access procedure (Object : access Base_Type);

   function Connect
     (Obj         : access Gtk.Object.Gtk_Object_Record'Class;
      Name        : in String;
      Func        : in Callback;
      Slot_Object : access Base_Type'Class;
      After       : in Boolean := False)
      return Guint;
end Object_Callback;

Here a sample code extracted from a text editor that corresponds to the text loading operation. The Callbacks.Load procedure is a callback executed when the user clicks on the "Load" button. This callback will create the file selection window (Gtk_File_Selection) and make it modal to force the user to select a file before continuing to work. When the user clicks on the "Ok" button of the file selection window, the Callbacks.Ok procedure is called with the parameter selection window.

with Gtk.Button;
package Callbacks is

   procedure Load (Widget : in out Gtk.Button.Gtk_Button);
   --  Callback for the ``Load'' button of the text editor

end Callbacks;

with Gtk.Signal, Gtk.Window, Gtk.Main;
with Gtk.Widget, Gtk.File_Selection, Glib;
with Editor;

package body Callbacks is

   package Files_Cb is new Gtk.Signal.Object_Callback
     (Gtk.File_Selection.Gtk_File_Selection);

   --  A callback of type File_Cb.Callback corresponding to the
   --  Cancel button of the file selection window.

   procedure Cancel
     (Files : access Gtk.File_Selection.Gtk_File_Selection_Record) is
   begin
      --  Hide the file selection window

      Gtk.File_Selection.Hide (Files);

      --  This window is no longer modal

      Gtk.Main.Grab_Remove (Files);
   end Cancel;

   -- A callback of type File_Cb.Callback corresponding to the ``OK''
   -- button of the file selection window.

   procedure Ok
     (Files : access Gtk.File_Selection.Gtk_File_Selection_Record) is
   begin
      Editor.Load (Gtk.File_Selection.Get_Filename (Files));
      Gtk.File_Selection.Hide (Files);
      Gtk.Main.Grab_Remove (Files);
   end Ok;

   -- Internal procedure to initialize a Gtk_File_Selection

   procedure Initialize_File_Selection
     (Files : access Gtk.File_Selection.Gtk_File_Selection_Record;
      Label : String;
      OK_CB : Files_Cb.Callback)
   is
      Cb_Id : Glib.Guint;
   begin
      Gtk.File_Selection.Gtk_New (Files, Label);

      --  Hide the create/remove buttons

      Gtk.File_Selection.Hide_Fileop_Buttons (Files);
      Cb_Id := Files_Cb.Connect (
         Obj         => Gtk.File_Selection.Get_Ok_Button (Files),
         --  The signal applies to the OK button

         Name        => "clicked", -- Event to detect
         Func        => OK_CB,     -- Callback procedure
         Slot_Object => Files);    -- Parameter given to the callback

      Cb_Id := Files_Cb.Connect (
         Obj         => Gtk.File_Selection.Get_Cancel_Button (Files),
         Name        => "clicked",
         Func        => Cancel'access,
         Slot_Object => Files);
   end Initialize_File_Selection;

   -- The file selection window

   Input_File_Sel : Gtk.File_Selection.Gtk_File_Selection;
   Input_File_Sel_Existe : Boolean := False;

   -- The ``Load'' callback body

   procedure Load (Widget : access Gtk.Button.Gtk_Button_Record) is
   begin
      if not Input_File_Sel_Existe then
         Initialize_File_Selection
           (Input_File_Sel, "File to load ?", OK'Access);
         Input_File_Sel_Existe := True;
      end if;

      Gtk.Main.Grab_Add (Input_File_Sel);

      --  Show the file selection window

      Gtk.File_Selection.Show (Input_File_Sel);
   end Load;

end Callbacks;

Two procedures to destroy a specific or any connection.

procedure Disconnect
  (Object     : access Gtk.Object.Gtk_Object_Record'Class;
   Handler_Id : in Guint);

procedure Handlers_Destroy
  (Obj : access Object.Gtk_Object_Record'Class);

Package Gtk.Tooltips

type Gtk_Tooltips_Record is new Gtk.Data.Gtk_Data_Record with private;
type Gtk_Tooltips is access all Gtk_Tooltips_Record'Class;

The message hint that pops up in a little window when the mouse stay on a widget long enough without moving.

Package Gtk.Adjustment

type Gtk_Adjustment_Record is new Data.Gtk_Data_Record with private;
type Gtk_Adjustment is access all Gtk_Adjustment_Record'Class;

These objects allow you to create a link between a scrollbar and a scrollable widget (text, scrolled_window).

Package Gtk.Widget

type Gtk_Widget_Record is new Object.Gtk_Object_Record with null record;
type Gtk_Widget is access all Gtk_Widget_Record'Class;

Root of the widget tree. Here are a few useful primitives that are inherited by all the widgets.

Package Gtk.Label

type Gtk_Label_Record is new Misc.Gtk_Misc_Record with private;
type Gtk_Label is access all Gtk_Label_Record'Class;

To display and then modify a simple string. A Gtk_Label can emit no signal (see Event_Box if you want to connect a signal)

Create a new label

procedure Gtk_New (Label : out Gtk_Label; Str : in String);

Change the label string

procedure Set_Text (Label : access Gtk_Label_Record; Str : in String);

Package Gtk.Scrollbar

type Gtk_Scrollbar_Record is new Gtk.GRange.Gtk_Range_Record with private;
type Gtk_Scrollbar is access all Gtk_Scrollbar_Record'Class;

Horizontal or vertical scrollbars that can be associated with the Widget you want to scroll using Adjustment (see, eg Gtk.Text.Get_Vadj).

procedure Gtk_New_Hscrollbar
  (Widget     : out Gtk_Scrollbar;
   Adjustment : in Gtk.Adjustment.Gtk_Adjustment);

procedure Gtk_New_Vscrollbar
  (Widget     : out Gtk_Scrollbar;
   Adjustment : in Gtk.Adjustment.Gtk_Adjustment);

Package Gtk.Drawing_Area

type Gtk_Drawing_Area_Record is new Gtk.Widget.Gtk_Widget_Record
  with private;
type Gtk_Drawing_Area is access all Gtk_Drawing_Area_Record'Class;

procedure Gtk_New (Widget : out Gtk_Drawing_Area);
procedure Size
  (Darea  : access Gtk_Drawing_Area_Record;
   Width  : in Glib.Gint;
   Height : in Glib.Gint);

You can get the Gdk_Window associated with a widget to draw in it using the GDK primitives of Gdk_Drawable which is a subtype of Gdk_Window, see the GDK section.

This window exists only when the widget is actually realized, e.g after the call to Realize or Show of the Gtk_Drawing_Area.

It is possible to draw in the initialization procedure after the procedure Show or when the Gtk.Main.Main loop is running, on the "configure_event" signal.

A Gtk_Drawing_Area is a very low level object, in particular when it is hidden by another window and then exposed, it is not redrawn automatically. It is up to the application to handle the expose event, by connecting a callback on the "expose_event" signal.

Package Gtk.Text

To manipulate text.

type Gtk_Text_Record is new
  Gtk.Editable.Gtk_Editable_Record with private;
type Gtk_Text is access all Gtk_Text_Record'Class;

End of line are marked by a Ascii.LF. When the application modifies the text (using, e.g Insert), it can first freeze it (Freeze) to prevent any change from the user and any automatic update of the display, then unfreeze it (Thaw) to allow the user to interact again.

procedure Gtk_New
  (Widget : out Gtk_Text;
   Hadj   : access Gtk.Adjustment.Gtk_Adjustment_Record'Class
     := Gtk.Adjustment.Null_Adjustment;
   Vadj   : access Gtk.Adjustment.Gtk_Adjustment_Record'Class
     := Gtk.Adjustment.Null_Adjustment);

procedure Freeze (Text : access Gtk_Text_Record);

procedure Thaw (Text : access Gtk_Text_Record);

function Get_Hadj (Widget : access Gtk_Text_Record)
  return Gtk.Adjustment.Gtk_Adjustment;

function Get_Vadj (Widget : access Gtk_Text_Record)
  return Gtk.Adjustment.Gtk_Adjustment;

function Get_Length (Text : in Gtk_Text) return Guint;

procedure Insert
  (Text   : access Gtk_Text_Record;
   Font   : in Gdk.Font.Gdk_Font'Class;
   Fore   : in Gdk.Color.Gdk_Color;
   Back   : in Gdk.Color.Gdk_Color;
   Chars  : in String;
   Length : in Gint);

By default, the text shown is not modifiable by the user. To change this, use the Set_Editable procedure:

procedure Set_Editable
  (Text : access Gtk_Text_Record; Editable : in Boolean);

To avoid cutting words at the end of a line:

procedure Set_Word_Wrap
  (Text      : access Gtk_Text_Record;
   Word_Wrap : in Boolean);

Also inherits primitives from Gtk.Editable.Gtk_Editable:

Package Gtk.Container

type Gtk_Container_Record is new
  Gtk.Widget.Gtk_Widget_Record with private;
type Gtk_Container is access all Gtk_Container_Record'Class;

General notion that permits to create a hierarchical structure of widgets, such as Gtk_Box, Gtk_Table, Gtk_EventBox, ...

To add/remove a widget in a Gtk_Container:

procedure Add
  (Container : access Gtk_Container_Record;
   Widget    : access Gtk.Widget.Gtk_Widget_Record'Class);

procedure Remove
  (Container : access Gtk_Container_Record;
   Widget    : access Gtk.Widget.Gtk_Widget_Record'Class);

The procedures Pack_Start and Pack_End of Gtk_Box and Attach of Gtk_Table let you add widgets and specify their position.

To set the width of the border around the Gtk_Container:

procedure Border_Width
  (Container    : access Gtk_Container_Record;
   Border_Width : in Gint);

Package Gtk.Table

type Gtk_Table_Record is new Gtk.Container.Gtk_Container_Record
  with private;
type Gtk_Table is access all Gtk_Table_Record'Class;

To group widgets in a table. The same widget can cover several boxes of the table. The size of the boxes depends on what they contain.

procedure Gtk_New
  (Widget      : out Gtk_Table;
   Rows        : in Glib.Gint;
   Columns     : in Glib.Gint;
   Homogeneous : in Boolean);

If Homogeneous is True, all the boxes will have the same size, determined by the largest widget and the longest widget. Otherwise, the width of a column (resp. heigh of a row) will be determined by the largest widget of the column.

To attach a widget to a table

procedure Attach
  (Table         : access Gtk_Table_Record;
   Child         : access Gtk.Widget.Gtk_Widget_Record'Class;
   Left_Attach   : in Gint;
   Right_Attach  : in Gint;
   Top_Attach    : in Gint;
   Bottom_Attach : in Gint;
   Xoptions      : in Gtk_Attach_Options;
   Yoptions      : in Gtk_Attach_Options;
   Xpadding      : in Gint;
   Ypadding      : in Gint);

procedure Attach_Defaults
  (Table         : access Gtk_Table_Record;
   Widget        : access Gtk.Widget.Gtk_Widget_Record'Class;
   Left_Attach   : in Gint;
   Right_Attach  : in Gint;
   Top_Attach    : in Gint;
   Bottom_Attach : in Gint);

Attach the widget Child to the position indicated by Left_Attach, Right_Attach, Top_Attach and Bottom_Attach. These indexes start from 0 to the number of column or rows specified at table creation. The origin (0, 0) of the frontiers is located in upper left.

    0          1          2
   0+----------+----------+
    |          |          |
   1+----------+----------+
    |          |          |
   2+----------+----------+

The values of Xoptions and Yoptions can be a logical or of several values:

Xpadding and Ypadding give the widths in pixels of the free space reserved around the widget.

For the Attach_Defaults procedure, the default values of Xoptions and Yoptions are equals to Expand or Fill and those of Xpadding and Ypadding are 0.

Package Gtk.Scrolled_Window

type Gtk_Scrolled_Window_Record is new Container.Gtk_Container_Record
  with private;
type Gtk_Scrolled_Window is access all Gtk_Scrolled_Window_Record'Class;

To create a scrollable zone in which you can put any kind of widget, like e.g a table of buttons. If the window is too small, all the widgets won't be visible, but they will remain accessible using the scrollbars.

procedure Gtk_New
  (Scrolled_Window :    out Gtk_Scrolled_Window;
   Hadjustment     : access Adjustment.Gtk_Adjustment_Record'Class
     := Adjustment.Null_Adjustment;
   Vadjustment     : access Adjustment.Gtk_Adjustment_Record'Class
     := Adjustment.Null_Adjustment);

Create a Gtk_Scrolled_Window by optionally specifying adjustment rules.

To get the adjustment rules (to attach scrollbars).

function Get_Hadjustment
  (Scrolled_Window : access Gtk_Scrolled_Window_Record)
   return Adjustment.Gtk_Adjustment;

function Get_Vadjustment
  (Scrolled_Window : access Gtk_Scrolled_Window_Record)
   return Adjustment.Gtk_Adjustment;

Package Gtk.Notebook

type Gtk_Notebook_Record is new Gtk.Container.Gtk_Container_Record
  with private;
type Gtk_Notebook is access all Gtk_Notebook_Record'Class;

The NoteBook Widget is a collection of pages that overlap each other, each page containing different information. This widget has become more common lately in GUI programming, and it is a good way to show blocks of similar information that warrant separation in their display.

Package Gtk.Box

type Gtk_Box_Record is new
  Gtk.Container.Gtk_Container_Record with private;
type Gtk_Box is access all Gtk_Box_Record'Class;
subtype Gtk_Vbox is Gtk_Box;
subtype Gtk_Hbox is Gtk_Box;

Horizontal (Gtk_New_Hbox) and vertical (Gtk_New_Vbox) boxes that can contain several widgets. you can add widgets from

procedure Gtk_New_Vbox
  (Box         : in out Gtk_Box;
   Homogeneous : in  Boolean;
   Spacing     : in  Gint);
procedure Initialize_Vbox
  (Box         : access Gtk_Box_Record'Class;
   Homogeneous : in  Boolean;
   Spacing     : in  Gint);

procedure Gtk_New_Hbox
  (Box         : in out Gtk_Box;
   Homogeneous : in  Boolean;
   Spacing     : in  Gint);
procedure Initialize_Hbox
  (Box         : access Gtk_Box_Record'Class;
   Homogeneous : in  Boolean;
   Spacing     : in  Gint);

procedure Pack_Start
  (In_Box  : access Gtk_Box_Record;
   Child   : access Gtk.Widget.Gtk_Widget_Record'Class;
   Expand  : in Boolean := True;
   Fill    : in Boolean := True;
   Padding : in Gint    := 0);

procedure Pack_End
  (In_Box  : access Gtk_Box_Record;
   Child   : access Gtk.Widget.Gtk_Widget_Record'Class;
   Expand  : in Boolean := True;
   Fill    : in Boolean := True;
   Padding : in Gint    := 0);

See section Package Gtk.Table

Package Gtk.Button

type Gtk_Button_Record is new Bin.Gtk_Bin_Record with private;
type Gtk_Button is access all Gtk_Button_Record'Class;

A simple button with a label and signals (e.g clicked).

Package Gtk.Toggle_Button

type Gtk_Toggle_Button_Record is new Gtk.Button.Gtk_Button_Record
  with private;
type Gtk_Toggle_Button is access all Gtk_Toggle_Button_Record'Class;

Toggle buttons are very similar to normal buttons, except they will always be in one of two states, alternated by a click. They may be depressed, and when you click again, they will pop back up. Click again, and they will pop back down.

Package Gtk.Check_Button

type Gtk_Check_Button_Record is new
  Toggle_Button.Gtk_Toggle_Button_Record with private;
type Gtk_Check_Button is access all Gtk_Check_Button_Record'Class;

Check buttons inherit many properties and functions from the the toggle buttons above, but look a little different. Rather than being buttons with text inside them, they are small squares with the text to the right of them. These are often seen for toggling options on and off in applications.

Package Gtk.Event_Box

type Gtk_Event_Box_Record is new Gtk.Bin.Gtk_Bin_Record with private;
type Gtk_Event_Box is access all Gtk_Event_Box_Record'Class;

To associate a callback or clip a widget that is not able to do it, like labels for example. Note that this container can have only one child.

procedure Gtk_New (Widget : out Gtk_Event_Box);

This widget is a child of Gtk_Container and inherits in particular of:

procedure Add (Container : access Gtk_Container_Record;
               Widget    : access Gtk.Widget.Gtk_Widget_Record'Class);

Package Gtk.Window

type Gtk_Window_Record is new Bin.Gtk_Bin_Record with private;
type Gtk_Window is access all Gtk_Window_Record'Class;

The base window, emits the signals "destroy", "delete_event".

Package Gtk.File_Selection

type Gtk_File_Selection_Record is new Gtk.Window.Gtk_Window_Record
  with private;
type Gtk_File_Selection is access all Gtk_File_Selection_Record'Class;

To select a file in the directories tree. Nothing special to know about it, just use it! There are three buttons Ok, Cancel and Help. You can modify these buttons (e.g change the labels, connect callbacks, ...) with the various Get_..._Button functions.

procedure Gtk_New
  (File_Selection : out Gtk_File_Selection; Title : in String);

To associate a callback with the buttons of the file selector:

function Get_Ok_Button
  (File_Selection : access Gtk_File_Selection_Record)
   return Gtk.Button.Gtk_Button;

function Get_Cancel_Button
  (File_Selection : access Gtk_File_Selection_Record)
   return Gtk.Button.Gtk_Button;

To consult or set the filename:

function Get_Filename
  (File_Selection : access Gtk_File_Selection_Record)
   return String;

procedure Set_Filename
  (File_Selection : access Gtk_File_Selection_Record;
   Filename : in String);

To hide the file manipulation buttons (destruction, ...):

procedure Hide_Fileop_Buttons
  (File_Selection : access Gtk_File_Selection_Record);

Package Gtk.Enums

This package defines all the enumerated types used by the GTK hierarchy.

Description of the GDK hierarchy

GTK is based on a lower level layer called GDK that contains some simple drawing and window primitives.

Package Gdk.Color

Color types and table of colors:

type Gdk_Color is private;
type Gdk_Colormap is new Root_Type with private;

Allows one to get black and white colors by asking the Gdk_Colormap of a widget:

function White (Colormap : in Gdk_Colormap) return Gdk_Color;
function Black (Colormap : in Gdk_Colormap) return Gdk_Color;
Wrong_Color : exception;

See for example Get_Default_Colormap.

You can of course create your own colors (provided your screen can display them), by calling the following functions :

declare
   Color : Gdk_Color;
   Name  : String := "brown";  --  the name of the color to be created
begin
   --  First, create a color from its name
   Color := Gdk.Color.Parse (Name);
   Gdk.Color.Alloc (Gtk.Widget.Get_Default_Colormap, Color);

   --  Or create a color from its values
   Gdk.Color.Set_Rgb (Color,
                      Red   => 20000, 
                      Green => 20000,
                      Blue  => 65500);
   Gdk.Color.Alloc (Gtk.Widget.Get_Default_Colormap, Color);
end; 

Package Gdk.Point

type Gdk_Point is private;

Used to describe polygons.

type Gdk_Points_Array is array (Positive range <>) of Gdk_Point;

function Get_X (Point : in Gdk_Point) return Glib.Gint16;
function Get_Y (Point : in Gdk_Point) return Glib.Gint16;
procedure Set_X (Point : in out Gdk_Point; X : Glib.Gint16);
procedure Set_Y (Point : in out Gdk_Point; Y : Glib.Gint16);

Package Gdk.Window

type Gdk_Window is new Root_Type with private;

To clear a window (e.g before drawing ):

procedure Clear (Window : in out Gdk_Window);

Package Gdk.Drawable

subtype Gdk_Drawable is Gdk.Window.Gdk_Window;
   procedure Draw_Rectangle (Drawable : in Gdk_Drawable'Class;
                             Gc       : in Gdk.GC.Gdk_GC'Class;
                             Filled   : in Boolean := False;
                             X        : in Gint;
                             Y        : in Gint;
                             Width    : in Gint;
                             Height   : in Gint);
 
   procedure Draw_Point (Drawable : in Gdk_Drawable'Class;
                         Gc       : in Gdk.GC.Gdk_GC'Class;
                         X        : in Gint;
                         Y        : in Gint);
 
   procedure Draw_Line (Drawable : in Gdk_Drawable'Class;
 
   procedure Draw_Line (Drawable : in Gdk_Drawable'Class;
                        Gc       : in Gdk.GC.Gdk_GC'Class;
                        X1       : in Gint;
                        Y1       : in Gint;
                        X2       : in Gint;
                        Y2       : in Gint);
 
   procedure Draw_Arc (Drawable : in Gdk_Drawable'Class;
                       Gc       : in Gdk.GC.Gdk_GC'Class;
                       Filled   : in Boolean := False;
                       X        : in Gint;
                       Y        : in Gint;
                       Width    : in Gint;
                       Height   : in Gint;
                       Angle1   : in Gint;
                       Angle2   : in Gint);
 
   procedure Draw_Polygon (Drawable : in Gdk_Drawable'Class;
                           Gc       : in Gdk.GC.Gdk_GC'Class;
                           Filled   : in Boolean;
                           Points   : in Gdk.Point.Gdk_Points_Array);
   procedure Draw_Text
     (Drawable    : in Gdk_Drawable'Class;
      Font        : in Gdk.Font.Gdk_Font;
      Gc          : in Gdk.GC.Gdk_GC;
      X           : in Glib.Gint;
      Y           : in Glib.Gint;
      Text        : in String);

for all the procedures (in particular Draw_Arc), the parameters are:

Package Gdk.GC

A Gdk_GC is a graphic context that must be associated, during its creation, with a Gdk_Window, and which is needed for each drawing primitive of Gdk_Drawable. A Gdk_GC let you, for example, set the colors to use for a drawing.

   type Gdk_GC is new Root_Type with private;
 
   procedure Gdk_New (GC     :    out Gdk_GC;
                      Window : in     Gdk.Window.Gdk_Window'Class);
 
   procedure Destroy (GC : in out Gdk_GC);
 
   procedure Set_Foreground (GC    : in out Gdk_GC;
                             Color : in     Gdk.Color.Gdk_Color);
 
   procedure Set_Background (GC    : in out Gdk_GC;
                             Color : in     Gdk.Color.Gdk_Color);
 
   procedure Set_Font (GC   : in out Gdk_GC;
                       Font : in     Gdk.Font.Gdk_Font'Class);

Package Gdk.Font

A font describes the graphical aspect of the characters, their size, kind, ....

Under X11, each font has a name of the form:

"-adobe-courier-medium-i-*-*-15-*-*-*-*-*-*-*"

The X command xfontsel lets you select a font and build its associated name automatically.

   type Gdk_Font is new Root_Type with private;
 
   procedure Load
     (Font      :    out Gdk_Font;
      Font_Name : in     String);
 
   procedure Fontset_Load
     (Font         :   out Gdk_Font;
      Fontset_Name : in    String);
 
   function Id (Font : in Gdk_Font) return Gint;
 
   function "=" (Fonta, Fontb : in Gdk_Font) return Boolean;
 
   function String_Width
     (Font : in Gdk_Font;
      Str  : in String) return Gint;
 
   function Text_Width
     (Font : in Gdk_Font;
      Text : in String) return Gint;
 
   function Char_Width
     (Font : in Gdk_Font;
      Char : in Character) return Gint;
 
   function String_Measure
     (Font : in Gdk_Font;
      Str  : in String) return Gint;
 
   function Text_Measure
     (Font : in Gdk_Font;
      Text : in String) return Gint;
 
   function Char_Measure
     (Font : in Gdk_Font;
      Char : in Character) return Gint;

Width gives the sum of the characters' widths, Measure gives a value similar to Width except that it takes into account the attributes of the font (e.g italic).

Draw in a widget

To select a drawing area, use a Gtk_Drawing_Area widget. The drawing procedures apply to the Gdk window (see Gdk_Window and Gdk_Drawable) of the Gtk_Drawing_Area widget via a graphic context (see Gtk_Gc).

Typically the code will look like:

with Glib;
with Gdk.Window;
with Gdk.Drawable;
with Gdk.GC;
with Gdk.Font;

procedure Draw (Drawing : in out Gtk.Drawing_Area.Gtk_Drawing_Area) is
   Gdkw : Gdk.Window.Gdk_Window;
   GC   : Gdk.GC.Gdk_GC;
   Font : Gdk.Font.Gdk_Font;
   use type Glib.Gint;

begin
   -- Get the Gdk window

   Gdkw := Gtk.Drawing_Area.Get_Window (Drawing) ;

   -- Clear the window

   GDK.Window.Clear (Gdkw) ;

   -- Create a graphic context associated with this window

   Gdk.GC.Gdk_New (GC, Gdkw);

   -- Draw a line in this window

   Gdk.Drawable.Draw_Line
     (Drawable => Gdkw,
      GC => GC,
      X1 =>   0, Y1 =>   0,
      X2 => 100, Y2 => 100);

   -- Draw an arc

   Gdk.Drawable.Draw_Arc
     (Drawable => Gdkw,
      Gc       => gc,
      Filled   => True,
      X        => 100,
      Y        => 100,
      Width    => 200,
      Height   => 100,
      Angle1   => 0 * 64,
      Angle2   => 270 * 64);

   -- Ask for a given font

   Gdk.Font.Load (Font, "-adobe-courier-medium-i-*-*-15-*-*-*-*-*-*-*");
   Gdk.Drawable.Draw_Text
     (Drawable    => Gdkw,
      Font        => Font,
      Gc          => gc,
      X           => 50,
      Y           => 50,
      Text        => "Hello World");
   Gdk.Font.Destroy (Font);
   Gdk.GC.Destroy (GC);
end Draw;

Description of the Glib hierarchy

Glib is the low-level layer and provides various types such as Gint, Guint, ....

It also provides access to lists and double-linked lists. Some functions in GtkAda return lists of Widgets or lists of integer, so it might be useful to know how to traverse the list.

Here are the functions available (note that the packages in Glib are generics that you need to instanciate first).

function First (List : in out Glist);
function Next (List : in Glist) return Glist;
function Last (List : in Glist) return Glist;

These three functions allow you to traverse the list: get the first element, then loop while the current element is different from Last. For instance, here is how you would get the selection in a Gtk_List widget:

declare
   use type Widget_List.Glist;
   Selection : Widget_List.Glist := Gtk.List.Get_Selection (Widget);
   Current   : Widget_List.Glist := Widget_List.First (Selection);
   Item      : Gtk_Widget;
begin
   while Current /= Widget_List.Last (Selection) loop
       Item := Widget_List.Get_Data (Current);
       --  use Item however you want
       Current := Widget_List.Next (Current);
   end loop;
end;

Resource files

Resource files let you parametrize the aspect of the widgets of a GtkAda application without having to recompile it.

A resource file needs to be loaded (Gtk.Rc.Parse) before setting the corresponding window.

In this file, it is possible to specify the visual characteristics of the widgets (colors, fonts, ...). Under X, the xfontsel command allows you to easily select a font. The FontSelection widget is also a simple way to select fonts.

Here is an example of a resource file:

# application.rc
#
# resource file for "Application"

# Buttons style
style "button"
{
# BackGround Colors
#                  Red  Green  Blue
  bg[PRELIGHT] = { 0.0,  0.75, 0.0 } # Green when the mouse is on
                                     # the button
  bg[ACTIVE]   = { 0.75, 0.0,  0.0 } # Red on click
# ForeGround Colors
#                  Red  Green  Blue
  fg[PRELIGHT] = { 1.0,  1.0,  1.0 } # White when the mouse is on
                                     # the button
  fg[ACTIVE]   = { 1.0,  1.0,  1.0 } # White on click
}

# All the buttons will have the style "button"
widget_class "*GtkButton*" style "button"

# Text style
style "text"
{
  font = "-adobe-courier-medium-r-normal-*-15-*-*-*-*-*-*-*"
  text[NORMAL] = { 0.0, 0.0, 0.0 } # black
  fg[NORMAL]   = { 0.0, 0.0, 0.0 } # black
  base[NORMAL] = { 1.0, 1.0, 1.0 } # white : background color
}

# All Gtk_Text will have the "text" style
widget_class "*GtkText" style "text"

Creating and Binding new widgets

New since version 0.6, GtkAda has now a basic support for creating new widgets directly in Ada (although you can't create your own signals yet, we are still working on it).

Since GtkAda has an object oriented conception (well, at least you can program by extension), it is easy, if you want to associate your own data with a widget, to create your own type. See the examples below. You should also have a look at the testgtk/ directory.

We provide a Perl script to help you create a binding to a C widget (this is the script we have used ourselves). This will not fully automate the process, although it should really speed things up. You will probably need less than 15 min to create a new binding once you will get used to the way GtkAda works. Note that your C file should have the same format as the one used by Gtk+ itself, as far as indentation and style are concerned.

Here are the steps to create a new binding:

Threads

The 1.2 series of Gtk+ are now thread safe. The usage of tasks inside programs using this toolkit should not cause any problem provided that you protect your Gtk calls with Gdk.Threads.Enter/ Leave and that your windowing system (e.g X) is thread-safe.

We recommend however that you read the section related to this topic in the GTK+ documentation.

How to report bugs

This library is still considered beta code, and it is thus likely that you will find bugs. We have tried to test it as much as possible, essentially by converting the testgtk.c file found in the gtk distribution. We have been able to rewrite nearly all the tests. Please have a look at the testgtk, which can give you a lot of examples of how to use this toolkit.

For more general questions about gtk itself, please ask your questions to the gtk mailing list. The authors of this toolkit are far from being specialists of gtk, as it was one of our first projects with gtk.

There are two kinds of problems you can encounter:

To report errors, if you are a supported user of GNAT, send a mail to mailto:report@gnat.com, otherwise send a mail to the authors (mailto:gtkada@ada.eu.org) explaining exactly what your are doing, what is the expected result and what you actually get. Please include the required sources to reproduce the problem, in a format usable by gnatchop (basically, insert all the required sources at the end of the mail). Please try to provide a subset of your sources as small as possible.

Of course, we will welcome any patch you can provide, so that this toolkit is as useful as possible.

Object-oriented features

GtkAda has been designed from the beginning to provide a full Object oriented layer over gtk+. This means that features such as type extension, dynamic dispatching, ... are made available through the standard Ada language.

This section will describe both how things work and how you can extend existing widgets or even create your own.

General description of the tagged types

Why should I use object-oriented programing ?

Every widget in GtkAda is a tagged type, which has a number of primitive subprograms, that are known by all its children.

This means that, as opposed to what you see in C code, you don't have (well most of the time you don't) to explictly cast types, and even when you have to, Ada always makes sure that the conversion is valid.

Thus your programs are much safer, and most errors are found at compile time, as is usually the case with Ada.

For instance, if you create a table, put some widgets in it, and then, later in your program, try to access these widgets, then you do not need to know beforehand what their type is, when and by whom they were created, ... You simply ask for the children of the table, and you get in return a tagged type that contains all the information you need. You can even use dynamic dispatching without ever having to cast to a known type.

This makes GtkAda a very powerful tool for designing graphical interfaces.

If you think one of the standard widgets is nice, but would be even better if it was drawing itself in a slighlty different way, of if it could contain some other data that you need in your application, there is a very simple way to do it: just create a new type that extends the current one (see the section See section Using tagged types to extend Gtk widgets below.

Maybe you want to create your own brand new widget, that knows how to draw itself, how to react to events, ... and you want to be able to reuse it anytime you need ? Once again, using the standard Ada features, you can simply create a new tagged type and teach it how to interact with the user. See the section See section Creating new widgets in Ada below.

Type conversions from C widgets to Ada widgets

There are basically three kinds of widgets that you can use with GtkAda:

GtkAda will always be able to find and/or create a valid tagged type in the first case, no matter if you explicitly created the widget or if it was created automatically by gtk+. For instance, if you created a widget in Ada, put it in a table, and later on extracted it from the table, then you will still have the same widget.

The second case above is twofold: if the widget was explictly created by you, or at least by GtkAda, then it will always be and remain associated with a correct Ada type.

However, if the widget was created implicitly (for instance every time you create a Gtk_Button, a Gtk_Label is also created for the text displayed), then GtkAda is not, by default, able to create the corresponding Ada type. Instead, it will create a Gtk_Widget, and you will thus have to do some Uncheck_Cast to convert it back to the type you want and expect.

In the third case (imported C widgets), GtkAda is not, by default, able to create the corresponding Ada type.

The solution we suggest to solve these two issues is to 'with' the Gtk.Type_Conversion unit. In that case, every standard widget, no matter who created them, will always be correctly converted to an appropriate Ada type. So, basically, if you put the following in your main unit:

with Gtk.Type_Conversion;

begin
   Gtk.Main.Init;
   Gtk.Type_Conversion.Init;
   ...
end

then you can safely get the children of any widget (table, boxes, ...) and be sure you have the right Ada type. You won't need to explictly convert your widget to something else.

However, 'with'ing this unit means that your application will depend on every package of GtkAda, which is a little bit heavier, and explains why this is not the default. We do recommend to use it if it is not extremely important for you whether your application depends on all the packages of GtkAda.

The case of imported C widgets is a little bit trickier. Since GtkAda does not know anything about them when it is build, it can't convert magically the C widgets to Ada widgets. This is your job to teach GtkAda how to do the conversion.

We thus provide a 'hook' function, that you need to modify. This function is defined in the package Gtk.Type_Conversion. It is a function that takes a string with the name of the C widget (ex/ "GtkButton"), and expects a newly allocated pointer in return. If you don't know this type either, simply return null.

Converting your applications to the new tagged type scheme

GtkAda 0.6 is an almost complete rewrite of GtkAda. Whereas widgets used to be record types, they now are implemented as access types. This change has two benefits:

Your existing code might have to be modified a little though (sorry about that, this is for the better!).

The last things that have changed in this new version of the toolkit are some of the names of the parameters in the subprograms. This is part of a major cleanup we are doing so that things can be more homogeneous within GtkAda.

Using tagged types to extend Gtk widgets

Since version 0.6 of this toolkit, it is possible to associate your own data with existing widgets, simply by creating new types. This section will show you a simple example, but you should rather read the source code in testgtk/ where we used this feature instead of using user_data as in the C version.

type My_Button_Record is new Gtk_Button_Record with record
    --  whatever data you want to associate with your button
end record;
type My_Button is access all My_Button_Record'Class;

With the above statements, your new type is defined. Every function available for Gtk_Button is also available for My_Button. Of course, as with every tagged type in Ada, you can create your own primitive functions, with the following prototype:

procedure My_Primitive_Func (Myb : access My_Button_Record);

To instanciate an object of type My_Button in your application, do the following:

declare
   Myb : My_Button;
begin
   Myb := new My_Button_Record;
   Initialize (Myb);   --  from Gtk.Button
end;

The first line creates the Ada type, whereas the Initialize call actually creates the C widget and associates it with the Ada type.

Creating new widgets in Ada

With GtkAda, you can now create widgets directly in Ada. These new widgets can be used directly, as if they were part of gtk itself.

Creating new widgets is a way to create reuseable components. You can apply to them the same functions as would for any other widget, such as Show, Hide, ...

This section will present how to create two types of widgets, composite widgets and widgets created from scratch. Two examples are provided with GtkAda, in the directories `examples/composite_widget' and `examples/base_widget'. Please also refer to the gtk+ tutorial, that describes the basic mechanisms that you need to know to create a widget (even if the Ada code is really different from the C code...)

Creating composite widgets

A composite widget is a widget that does not do much by itself. This is more a collection of subwidgets, grouped into a more general entity. For instance, among the standard widgets, Gtk_File_Selection and Gtk_Font_Selection belong to this category.

The good news is that there is nothing special to know. Just create a new tagged type, that extends one of the standard widgets (or even another of your own widgets), provide a Gtk_New function that allocates memory for this widget, and call the Initialize function that does the actual creation of the widget and the subwidgets. There is only one thing to do: Initialize should the call the parent class's Initialize function, to create the underlying C widget.

The example directory `examples/composite_widget' reimplements the Gtk_Dialog widget as was written in C by the creators of gtk+.

Creating widgets from scratch

First, an important note: please do not read this if this is your first time using GtkAda, or if you don't really understand the signal mechanism. Creating a nice and working widget really takes a lot of messing with the low level signals.

Creating a widget from scratch is what you want to do if your widget should be drawn in a special way, should create and emit new signals, ... The example we give in `examples/base_widget' is a small target, on which the user can click, and that sends one of two signals "bullseye" or "missed" depending on where the user has clicked.

Once again, the only two functions that you must create are Gtk_New and Initialize. This time, Initialize has to do two things:

   Parent_Package.Initialize (Widget);

   --  The above line calls the Initialize function from the parent.
   --  This creates the underlying C widget, which we are going to
   --  modify with the following call:

   Gtk.Object.Initialize_Class_Record
       (Widget, Signals, Class_Record);
   --  This initializes the "class record" for the widget and
   --  creates the signals.

In the above example, the new part is the second call. It takes three arguments:

Then of course Initialize should set up some signal handlers for the functions you want to redefine. Three signals are especially useful:

Support for Glade, the Gtk GUI builder

GtkAda now comes with a support for the GUI builder Glade (this is not the glade released with Gnat for distributed systems). Not all widgets are supported yet, but we eventually hope to have all of them. If you really need one, it is easy to add the two required functions ... (and to send us patches...)

We actually provide two versions: a dynamic one and a static one. In both cases, you first need to get and install glade (http://glade.pn.org). Then start a new project (or edit an old one). It is easy enough to use, simply select the widget you want to add to your interface, and click!

Starting from version 0.4.1 of Glade, you can directly create Ada files from Glade by selection Ada95 as the language for your project. By doing so, you will tell Glade to call gate internally when using the "Write Source Code" functionality.

The most important file created by Gate is called callback_<project_name>.adb. It contains stubs for all the callbacks you declared in Glade.

Note that you can easily go back to Glade any time, modify your interface, and have GATE re-generate a set of files. All your modifications will be kept in the new files. For that, GATE creates a directory .gate/ in the current directory. Please do not delete it if you want GATE to be able to keep your changes from one version to the next.

Also note that to be able to keep track of your modifications, gate relies on patch and diff being available on your system. If you don't have a working set of diff/patch, configure will simply replace them by null operations.

GATE and DGATE currently don't support all widgets and properties available under Glade. To help you identify which widgets are not supported, GATE will generate a comment in the create_<widget>.adb file that looks like:

   --  WARNING: Unsupported widget GtkImage (image1)

Which means that while generating the file, GATE detected an unsupported widget (in this case GtkImage) whose name is image1. If you get such warnings, your file may or may not compile properly, but you won't get the complete widget hierarchy at run time.

Feel free to send us (see "How to report bugs") the XML file that causes this problem. We don't guarantee a rapid fix for each particular problem, but receiving real examples of missing functionnalities will certainly help implementing them faster.

Similarly, DGATE generates a warning to the standard output when encountering an unsupported widget:

GtkAda-WARNING **: Unsupported widget GtkImage (image1)

Here is the list of widgets that are currently not supported by GATE/DGATE.

A complete example

A window is created with 3 buttons that shows the 3 different types of callbacks provided. One interesting thing is that Callbacks.Bye is connected three times: on the "Quit" button and two times on the main window.

with Gtk.Signal;
with Gtk.Button;
with Gtk.Window;

package Callbacks is

   package Button_Callback is new Gtk.Signal.Void_Callback
     (Widget_Type => Gtk.Button.Gtk_Button_Record);
   procedure Increment (Widget : access Gtk.Button.Gtk_Button_Record);

   package Button_Message is new Gtk.Signal.Callback
     (Widget_Type => Gtk.Button.Gtk_Button_Record,
      Data_Type => String);

   procedure Message
     (Widget : access Gtk.Button.Gtk_Button_Record; Data : in String);

   package Window_Callback is new Gtk.Signal.Object_Callback
     (Widget_Type => Gtk.Window.Gtk_Window_Record);

   procedure Bye (Window : access Gtk.Window.Gtk_Window_Record);

end Callbacks;

with Gtk.Main;
with Ada.Text_Io;

package body Callbacks is

   Counter : Natural := 0;

   procedure Increment (Widget : access Gtk.Button.Gtk_Button_Record) is
   begin
      Counter := Counter + 1;
      Ada.Text_Io.Put_Line (Natural'Image (Counter));
   end Increment;

   procedure Message
     (Widget : access Gtk.Button.Gtk_Button_Record; Data : in String) is
   begin
      Ada.Text_Io.Put_Line (Data);
   end Message;

   procedure Bye (Window : access Gtk.Window.Gtk_Window_Record) is
   begin
      Gtk.Window.Destroy (Window);
      Gtk.Main.Main_Quit;
   end Bye;

end Callbacks;

with Gtk.Rc;
with Gtk.Main;
with Gtk.Enums;
with Gtk.Window;
with Gtk.Box;
with Gtk.Label;
with Gtk.Button;
with Callbacks;
with Glib;

procedure Example is

   procedure Fix_Label
     (Box : access Gtk.Box.Gtk_Box_Record; Str : String)
   is
      Label : Gtk.Label.Gtk_Label;
   begin
      Gtk.Label.Gtk_New (Label => Label, Str => Str);
      Gtk.Box.Pack_Start
        (In_Box => Box, Child => Label, Expand => False, Fill => False);
   end Fix_Label;

   procedure Create_Box
     (W : access Gtk.Window.Gtk_Window_Record;
      Str : String)
   is
      Box    : Gtk.Box.Gtk_Box;
      Button : Gtk.Button.Gtk_Button;
      Cb_Id  : Glib.Guint;

   begin
      Gtk.Box.Gtk_New_Vbox
        (Widget => Box, Homogeneous => True, Spacing => 0);
      Gtk.Window.Add (Container => W, Widget => Box);

      Fix_Label (Box, Str);

      Gtk.Button.Gtk_New (Widget => Button, Label => "Increment");
      Gtk.Box.Pack_Start (In_Box => Box, Child => Button);
      Cb_Id := Callbacks.Button_Callback.Connect
        (Obj => Button,
         Name => "clicked",
         Func => Callbacks.Increment'Access);

      Gtk.Button.Gtk_New (Widget => Button, Label => "Message");
      Gtk.Box.Pack_Start (In_Box => Box, Child => Button);
      Cb_Id := Callbacks.Button_Message.Connect
        (Obj => Button,
         Name => "clicked",
         Func => Callbacks.Message'Access,
         Func_Data => "Button message clicked");

      Gtk.Button.Gtk_New (Widget => Button, Label => "Quit");
      Gtk.Box.Pack_Start (In_Box => Box, Child => Button);
      Cb_Id := Callbacks.Window_Callback.Connect
        (Obj => Button,
         Name => "clicked",
         Func => Callbacks.Bye'Access,
         Slot_Object => W);
   end Create_Box;

   procedure Window is
      Main_Window : Gtk.Window.Gtk_Window;
      Cb_Id : Glib.Guint;

   begin
      Gtk.Window.Gtk_New
        (Window => Main_Window, The_Type => Gtk.Enums.Window_Toplevel);

      --  Inherited from Gtk.Widget:

      Gtk.Window.Set_Title
        (Window => Main_Window, Title => "Some Title");
      Create_Box (Main_Window, "my box") ;

      --  Connect the Callbacks

      Cb_Id := Callbacks.Window_Callback.Connect
        (Obj => Main_Window,
         Name => "destroy",
         Func => Callbacks.Bye'Access,
         Slot_Object => Main_Window);
      Cb_Id := Callbacks.Window_Callback.Connect
        (Obj => Main_Window,
         Name => "delete_event",
         Func => Callbacks.Bye'Access,
         Slot_Object => Main_Window);
      Gtk.Window.Show_All (Main_Window);
   end Window;

begin
   Gtk.Main.Set_Locale;
   Gtk.Main.Init;
   Gtk.Rc.Parse ("example.rc"); 
   Window;
   Gtk.Main.Main;
end Example;

Bibliography

We recommand the following documents. Most of them were written with C in mind, but should be easily adapted most of the time after you read the rest of this document.


This document was generated on October, 21 1999 using texi2html 1.56k.