Next Previous Contents

9. Miscellaneous Widgets

9.1 Labels

Labels are used a lot in GTK, and are relatively simple. Labels emit no signals as they do not have an associated X window. If you need to catch signals, or do clipping, use the EventBox widget.

Creation procedure of the class GTK_LABEL is:

make( str : STRING );

Where the sole argument is the string you wish the label to display.

To change the label's text after creation, use the pocedure:

set_text( str : STRING );

Where the argument is the new string.

You can produce multi-line labels by putting line breaks in the label string.

To retrieve the current string, use function:

get : STRING;

The label text can be justified using:

set_justify ( jtype : GTK_JUSTIFICATION )

Values for jtype are:

The label widget is also capable of line wrapping the text automatically. This can be activated using:

set_line_wrap ( wrap : BOOLEAN )

If you want your label underlined, then you can set a pattern on the label:

set_pattern ( pattern : STRING )

The pattern argument indicates how the underlining should look. It consists of a string of underscore and space characters. An underscore indicates that the corresponding character in the label should be underlined. For example, the string

"__     __"
would underline the first two characters and eigth and ninth characters.

Below is a short example to illustrate using GTK_LABEL. This example makes use of the GTK_FRAME widget to better demonstrate the label styles. You can ignore this for now as the Frame widget is explained later on.

-- example-start labels labels.e
class LABELS
inherit
   -- All constants we need are defined in the class below.
   -- eg gtk_window_toplevel etc.
   GTK_CONSTANTS
   -- This class contains common features like vegtk_init and so
   VEGTK_MAIN
   -- If we need to handle callbacks we must inherit from the below
   -- class
   VEGTK_CALLBACK_HANDLER
creation
   make
feature -- Callback handlers
   close_application is
   -- This callback quits the program
   do
       gtk_main_quit;
   end
feature -- Creation

   make is
   local
      window : GTK_WINDOW;
      hbox : GTK_HBOX
      vbox : GTK_VBOX
      frame : GTK_FRAME
      label : GTK_LABEL
   do
      -- Our init, don't forget this!
      vegtk_init
      !!window.make(Gtk_window_toplevel)
      signal_connect(window,"delete_event",$close_application)

      window.set_title("Label")
      !!vbox.make(false, 5)
      !!hbox.make(false, 5)
      window.add(hbox)
      hbox.pack_start(vbox, false, false, 0)
      window.set_border_width(5)

      !!frame.make("Normal Label")
      !!label.make("This is a Normal label")
      frame.add(label)
      vbox.pack_start(frame, false, false, 0)

      !!frame.make("Multi-line Label")
      !!label.make("This is a Multi-line label.%NSecond line%NThird line")
      frame.add(label)
      vbox.pack_start(frame, false, false, 0)

      !!frame.make("Left Justified Label")
      !!label.make("This is a Left-Justified%NMulti-line label.%NThird      line")
      label.set_justify(Gtk_justify_left)
      frame.add(label)
      vbox.pack_start(frame, false, false, 0)

      !!frame.make("Right Justified Label")
      !!label.make("This is a Right-Justified%NMulti-line label.%NFourth line, (j/k)")
      label.set_justify(Gtk_justify_right)
      frame.add(label)
      vbox.pack_start(frame, false, false, 0)

      !!vbox.make(false, 5)
      hbox.pack_start(vbox, false, false, 0)
      !!frame.make("Line wrapped label")
      !!label.make("This is an example of a line-wrapped label. It should not be taking %
            %up the entire %
         %width allocated to it, but automatically wraps the words to fit. %
         %The time has come, for all good men, to come to the aid of their party.%
         %The sixth sheik's six sheep's sick.%N%
         %     It supports multiple paragraphs correctly, and  correctly   adds %
         %many          extra  spaces.")
      label.set_line_wrap(true)
      frame.add(label)
      vbox.pack_start(frame, false, false, 0)

      !!frame.make("Filled, wrapped label")
      !!label.make("This is an example of a line-wrapped, filled label.  It should be taking %
         %up the entire              width allocated to it.  Here is a seneance to prove %
         %my point.  Here is another sentence. %
         %Here comes the sun, do de do de do.%N%
         %    This is a new paragraph.%N%
         %    This is another newer, longer, better paragraph.  It is coming to an end, %
         %unfortunately.")
      label.set_justify(Gtk_justify_fill)
      label.set_line_wrap(true)
      frame.add(label)
      vbox.pack_start(frame, false, false, 0)

      !!frame.make("Underlined label")
      !!label.make("This label is underlined!%N%
         %This one is underlined in quite a funky fashion")
      label.set_justify(Gtk_justify_left)
      label.set_pattern("_________________________ _ _________ _ _____ _ __ __  ___ ____ _____")
      frame.add(label)
      vbox.pack_start(frame, false, false, 0)

      window.show_all
      gtk_main;
   end -- make
end -- class LABELS
-- example-end

9.2 Arrows

The GTK_ARROW widget draws an arrowhead, facing in a number of possible directions and having a number of possible styles. It can be very useful when placed on a button in many applications.

Here is the creation procedure

make ( arrow_type : GTK_ARROW_TYPE; shadow_type : GTK_SHADOW_TYPE )

The following procedre allows alter arrow type and shadow type;

set ( arrow_type : GTK_ARROW_TYPE; shadow_type : GTK_SHADOW_TYPE )

The arrow_type argument may take one of the following values:

These values obviously indicate the direction in which the arrow will point. The shadow_type argument may take one of these values:

Here's a brief example to illustrate their use.

-- example-start arrows arrows.e
class ARROWS
inherit
   -- All constants we need are defined in the class below.
   -- eg gtk_window_toplevel etc.
   GTK_CONSTANTS
   -- This class contains common features like vegtk_init and so
   VEGTK_MAIN
   -- If we need to handle callbacks we must inherit from the below
   -- class
   VEGTK_CALLBACK_HANDLER
creation
   make
feature -- Callback handlers
   close_application is
   -- This callback quits the program
   do
       gtk_main_quit;
   end
feature -- Creation

   make is
   local
      window : GTK_WINDOW;
      box : GTK_HBOX;
      button : GTK_BUTTON;
   do
      -- Our init, don't forget this!
      vegtk_init
      !!window.make(Gtk_window_toplevel)
      window.set_title("Arrow Buttons")

      -- It's a good idea to do this for all windows.
      signal_connect(window,"destroy",$close_application)
      -- Sets the border width of the window.
      window.set_border_width (10);
      -- Create a box to hold the arrows/buttons
      !!box.make(false, 0)
      box.set_border_width (2);
      window.add(box)

      -- Pack and show all our widgets
      box.show;

      button := create_arrow_button(Gtk_arrow_up, Gtk_shadow_in);
      box.pack_start ( button, False, False, 3);

      button := create_arrow_button(Gtk_arrow_down, Gtk_shadow_out);
      box.pack_start ( button, False, False, 3);

      button := create_arrow_button(Gtk_arrow_left, Gtk_shadow_etched_in);
      box.pack_start ( button, False, False, 3);

      button := create_arrow_button(Gtk_arrow_right, Gtk_shadow_etched_out);
      box.pack_start ( button, False, False, 3);

      window.show;

      -- Rest in gtk_main and wait for the fun to begin!
      gtk_main;
   end -- make

feature -- Support

   create_arrow_button( arrow_type : GTK_ARROW_TYPE;
                        shadow_type :GTK_SHADOW_TYPE ) : GTK_BUTTON is
   -- Create an Arrow widget with the specified parameters
   -- and pack it into a button
   local
     arrow : GTK_ARROW;
   do
      !!Result.make;
      !!arrow.make (arrow_type, shadow_type);
      Result.add (arrow);
      arrow.show;
      Result.show;
   end
end -- class ARROWS
-- example-end

9.3 The Tooltips Widget

These are the little text strings that pop up when you leave your pointer over a button or other widget for a few seconds. They are easy to use, so I will just explain them without giving an example. If you want to see some code, take a look at the testgt program distributed with VEGTK.

Widgets that do not receieve events (widgets that do not have their own window) will not work with tooltips.

The first call you will use creates a new tooltip. You only need to do this once for a set of tooltips as the GTK_TOOLTIPS object can be used to create multiple tooltips.

The creation procedure doesn't take any parameter.

make

Once you have created a new tooltip, and the widget you wish to use it on, simply use this call to set it:

set_tip ( widget : GTK_WIDGET; tip_text : STRING; tip_private : STRING )

The first argument is the widget you wish to have this tooltip pop up for, and the text you wish it to say. The last argument is a text string that can be used as an identifier when using GTK_TIPS_QUERY to implement context sensitive help. For now, you can set it to Void.

Here's a short example:

.
.
.
local
   tooltips : GTK_TOOLTIPS;
   button : GTK_BUTTON;
do
.
.
.
   !!tooltips.make;
   !!button.make_with_label ("button 1");
.
.
.
   tooltips.set_tip (button, "This is button 1", Void);

There are other calls that can be used with tooltips. I will just list them with a brief description of what they do.

enable

Enable a disabled set of tooltips.

disable

Disable an enabled set of tooltips.

set_delay ( delay : INTEGER )

Sets how many milliseconds you have to hold your pointer over the widget before the tooltip will pop up. The default is 500 milliseconds (half a second).

set_colors ( background : GDK_COLOR; foreground : GDK_COLOR )

Set the foreground and background color of the tooltips.

And that's all the functions associated with tooltips. More than you'll ever want to know :-)

9.4 Progress Bars

Progress bars are used to show the status of an operation. They are pretty easy to use, as you will see with the code below. But first lets start out with the calls to create a new progress bar.

There are two creation procedures in the class GTK_PROGRESS_BAR. One simple that takes no arguments, and one that takes a GTK_ADJUSTMENT object as an argument. If the former is used, the progress bar creates its own adjustment object.

   make
   make_with_adjustment ( arg_adjustment : GTK_ADJUSTMENT )

The second method has the advantage that we can use the adjustment object to specify our own range parameters for the progress bar.

The adjustment of a progress object can be changed dynamically using:

set_adjustment ( arg_adjustment : GTK_ADJUSTMENT )

Now that the progress bar has been created we can use it.

update ( percentage : REAL ) is

The argument is the amount 'completed', meaning the amount the progress bar has been filled from 0-100%. This is passed to the function as a real number ranging from 0 to 1.

GTK v1.2 has added new functionality to the progress bar that enables it to display its value in different ways, and to inform the user of its current value and its range.

A progress bar may be set to one of a number of orientations using the function

set_orientation ( orientation : GTK_PROGRESS_BAR_ORIENTATION )

Where the orientation argument may take one of the following values to indicate the direction in which the progress bar moves:

When used as a measure of how far a process has progressed, the GTK_PROGRESS_BAR can be set to display its value in either a continuous or discrete mode. In continuous mode, the progress bar is updated for each value. In discrete mode, the progress bar is updated in a number of discrete blocks. The number of blocks is also configurable.

The style of a progress bar can be set using the following function.

set_bar_style ( style : GTK_PROGRESS_BAR_STYLE )

The style parameter can take one of two values:

The number of discrete blocks can be set by calling

set_discrete_blocks ( blocks : INTEGER )

As well as indicating the amount of progress that has occured, the progress bar may be set to just indicate that there is some activity. This can be useful in situations where progress cannot be measured against a value range. Activity mode is not effected by the bar style that is described above, and overrides it.This mode is selected by the following procedure.

set_activity_mode ( arg_activity_mode : BOOLEAN )

The step size of the activity indicator, and the number of blocks are set using the following procedures.

set_activity_step ( step : INTEGER )
set_activity_blocks ( blocks : INTEGER )

When in continuous mode, the progress bar can also display a configurable text string within its trough, using the following procedure.

set_format_string ( arg_format : STRING )

The format argument is similiar to one that would be used in a C printf statement. The following directives may be used within the format string:

Note that in Eiffel you have use double percent sign %% :-)

The displaying of this text string can be toggled using:

set_show_text ( show_text : BOOLEAN )

The appearance of the text can be modified further using:

set_text_alignment ( x_align : REAL; y_align : REAL )

The x_align and y_align arguments take a value between 0.0 and 1.0. Their value indicates the position of the text string within the trough. Values of 0.0 for both would place the string in the top left hand corner; values of 0.5 (the default) centres the text, and values of 1.0 places the text in the lower right hand corner.

The current text setting of a progress object can be retrieved using the current or a specified adjustment value using the following two functions.These functions return the formatted string that would be displayed within the trough.

   get_current_text : STRING
   get_text_from_value ( value : REAL ) : STRING

There is yet another way to change the range and value of a progress object using the following function:

configure ( value : REAL; min : REAL; max : REAL )

This function provides quite a simple interface to the range and value of a progress object.

The remaining procedures and functions can be used to get and set the current value of a progess object in various types and formats:

   set_percentage ( percentage : REAL )
   set_value ( value : REAL )

   get_value : REAL
   get_current_percentage : REAL
   get_percentage_from_value ( value : REAL ) : REAL

They are pretty self explanatory. The last function uses the adjustment of the specified progess object to compute the percentage value of the given range value.

Progress Bars are usually used with timeouts or other such functions (see section on Timeouts, I/O and Idle Functions) to give the illusion of multitasking. All will employ the update procedure in the same manner.

Here is an example of the progress bar, updated using timeouts. This code also shows you how to reset the Progress Bar.

-- example-start progressbar progressbar.e
class PROGRESSBAR
inherit
   VEGTK_MAIN
   VEGTK_CALLBACK_HANDLER
   GTK_CONSTANTS

creation
   make

feature -- Creation

   make is
      local
         button : GTK_BUTTON
         vbox : GTK_VBOX
         vbox2 : GTK_VBOX
         hbox : GTK_HBOX
         check_button : GTK_CHECK_BUTTON
         frame : GTK_FRAME
         tab : GTK_TABLE
         label : GTK_LABEL
         align : GTK_ALIGNMENT
         adj : GTK_ADJUSTMENT
         dialog : GTK_DIALOG
      do
         -- Our init, don't forget this!
         vegtk_init
         !!dialog.make

         window := dialog

         dialog.set_policy(false, false, true)

         signal_connect(dialog,"destroy",$destroy_window)

         dialog.set_title("GtkProgressBar")
         dialog.set_border_width(0)

         !!vbox.make(false, 5)
         vbox.set_border_width(10)
         dialog.vbox.pack_start(vbox, false, true, 0)

         !!frame.make("Progress")
         vbox.pack_start(frame, false, true, 0)

         !!vbox2.make(false, 5)
         frame.add(vbox2)

         !!align.make(0.5, 0.5, 0, 0)
         vbox2.pack_start(align, false, false, 5)

         !!adj.make(0, 1, 300, 0, 0, 0)
         signal_connect(adj,"value_changed",$progress_value_changed)

         !!pbar.make_with_adjustment(adj)
         pbar.set_format_string("%%v from [%%l,%%u] (=%%p%%%%)")
         align.add(pbar)
         !!timer.make(100,current,$timer_function,void)
         timer.start

         !!align.make(0.5, 0.5, 0, 0)
         vbox2.pack_start(align, false, false, 5)

         !!hbox.make(false, 5)
         align.add(hbox)
         !!label.make("Label updated by user :")
         hbox.pack_start(label, false, true, 0)
         !!user_label.make("")
         hbox.pack_start(user_label, false, true, 0)

         !!frame.make("Options")
         vbox.pack_start(frame, false, true, 0)

         !!vbox2.make(false, 5)
         frame.add(vbox2)

         !!tab.make(7, 2, false)
         vbox2.pack_start(tab, false, true, 0)

         !!label.make("Orientation :")
         tab.attach(label, 0, 1, 0, 1,
            Gtk_expand and Gtk_fill, Gtk_expand and Gtk_fill,
            5, 5)
         label.set_alignment(0, 0.5)

         omenu1 := build_option_menu (items1, 4, 0, Void)
         !!hbox.make(false, 0)
         tab.attach(hbox, 1, 2, 0, 1,
            Gtk_expand and Gtk_fill, Gtk_expand and Gtk_fill,
            5, 5)
         hbox.pack_start(omenu1, true, true, 0)

         !!check_button.make_with_label("Show text")
         signal_connect(check_button,"clicked",$toggle_show_text)
         tab.attach(check_button, 0, 1, 1, 2,
            Gtk_expand and Gtk_fill, Gtk_expand and Gtk_fill,
            5, 5)

         !!hbox.make(false, 0)
         tab.attach(hbox, 1, 2, 1, 2,
            Gtk_expand and Gtk_fill, Gtk_expand and Gtk_fill,
            5, 5)

         !!label.make("Format : ")
         hbox.pack_start(label, false, true, 0)

         !!entry.make
         signal_connect(entry,"changed",$entry_changed)
         hbox.pack_start(entry, true, true, 0)
         entry.set_text("%%v from [%%l,%%u] (=%%p%%%%)")
         entry.set_usize(100, -1)
         entry.set_sensitive(false)

         !!label.make("Text align :")
         tab.attach(label, 0, 1, 2, 3,
            Gtk_expand and Gtk_fill, Gtk_expand and Gtk_fill,
            5, 5)
         label.set_alignment(0, 0.5)

         !!hbox.make(false, 0)
         tab.attach(hbox, 1, 2, 2, 3,
            Gtk_expand and Gtk_fill, Gtk_expand and Gtk_fill,
            5, 5)

         !!label.make("x :")
         hbox.pack_start(label, false, true, 5)

         !!adj.make(0.5, 0, 1, 0.1, 0.1, 0)
         !!x_align_spin.make(adj, 0, 1)
         signal_connect(adj,"value_changed",$adjust_align)
         hbox.pack_start(x_align_spin, false, true, 0)
         x_align_spin.set_sensitive(false)

         !!label.make("y :")
         hbox.pack_start(label, false, true, 5)

         !!adj.make(0.5, 0, 1, 0.1, 0.1, 0)
         !!y_align_spin.make(adj, 0, 1)
         signal_connect(adj,"value_changed",$adjust_align)
         hbox.pack_start(y_align_spin, false, true, 0)
         y_align_spin.set_sensitive(false)

         !!label.make("Bar Style :")
         tab.attach(label, 0, 1, 3, 4,
            Gtk_expand and Gtk_fill, Gtk_expand and Gtk_fill,
            5, 5)
         label.set_alignment(0, 0.5)

         omenu2 := build_option_menu (items2, 2, 0, Void)
         !!hbox.make(false, 0)
         tab.attach(hbox, 1, 2, 3, 4,
            Gtk_expand and Gtk_fill, Gtk_expand and Gtk_fill,
            5, 5)
         hbox.pack_start(omenu2, true, true, 0)

         !!label.make("Block count :")
         tab.attach(label, 0, 1, 4, 5,
            Gtk_expand and Gtk_fill, Gtk_expand and Gtk_fill,
            5, 5)
         label.set_alignment(0, 0.5)

         !!hbox.make(false, 0)
         tab.attach(hbox, 1, 2, 4, 5,
            Gtk_expand and Gtk_fill, Gtk_expand and Gtk_fill,
            5, 5)
         !!adj.make(10, 2, 20, 1, 5, 0)
         !!block_spin.make(adj, 0, 0)
         signal_connect(adj,"value_changed",$adjust_blocks)
         hbox.pack_start(block_spin, false, true, 0)
         block_spin.set_sensitive(false)

         !!check_button.make_with_label("Activity mode")
         signal_connect(check_button,"clicked",$toggle_activity_mode)
         tab.attach(check_button, 0, 1, 5, 6,
            Gtk_expand and Gtk_fill, Gtk_expand and Gtk_fill,
            5, 5)

         !!hbox.make(false, 0)
         tab.attach(hbox, 1, 2, 5, 6,
            Gtk_expand and Gtk_fill, Gtk_expand and Gtk_fill,
            5, 5)
         !!label.make("Step size : ")
         hbox.pack_start(label, false, true, 0)
         !!adj.make(3, 1, 20, 1, 5, 0)
         !!step_spin.make(adj, 0, 0)
         signal_connect(adj,"value_changed",$adjust_step)
         hbox.pack_start(step_spin, false, true, 0)
         step_spin.set_sensitive(false)

         !!hbox.make(false, 0)
         tab.attach(hbox, 1, 2, 6, 7,
            Gtk_expand and Gtk_fill, Gtk_expand and Gtk_fill,
            5, 5)
         !!label.make("Blocks :     ")
         hbox.pack_start(label, false, true, 0)
         !!adj.make(5, 2, 10, 1, 5, 0)
         !!act_blocks_spin.make(adj, 0, 0)
         signal_connect(adj,"value_changed",$adjust_act_blocks)
         hbox.pack_start(act_blocks_spin, false, true,0)
         act_blocks_spin.set_sensitive(false)

         !!button.make_with_label("close")
         signal_connect(button,"clicked",$destroy_window)
         button.set_flags(Gtk_can_default)
         dialog.action_area.pack_start(button, true, true, 0)
         button.grab_default
         dialog.show_all

         gtk_main
      end -- make

feature {NONE} -- implementations


   pbar : GTK_PROGRESS_BAR
   block_spin : GTK_SPIN_BUTTON
   x_align_spin : GTK_SPIN_BUTTON
   y_align_spin : GTK_SPIN_BUTTON
   step_spin : GTK_SPIN_BUTTON
   act_blocks_spin : GTK_SPIN_BUTTON
   user_label : GTK_LABEL
   omenu1 : GTK_OPTION_MENU
   omenu2 : GTK_OPTION_MENU
   entry : GTK_ENTRY
   timer : GTK_TIMER
   window : GTK_WINDOW;

   items1 : ARRAY[OPTION_MENU_ITEM] is
      local
         item : OPTION_MENU_ITEM
      do
         !!Result.make(0,3)
         !!item.make("Left-Right", $progressbar_toggle_orientation)
         Result.put(item,0)
         !!item.make("Right-Left", $progressbar_toggle_orientation)
         Result.put(item,1)
         !!item.make("Bottom-Top", $progressbar_toggle_orientation)
         Result.put(item,2)
         !!item.make("Top-Bottom", $progressbar_toggle_orientation)
         Result.put(item,3)
      end -- items1

   items2 : ARRAY[OPTION_MENU_ITEM] is
      local
         item : OPTION_MENU_ITEM
      do
         !!Result.make(0,1)
         !!item.make("Continuous", $progressbar_toggle_bar_style)
         Result.put(item,0)
         !!item.make("Discrete",   $progressbar_toggle_bar_style)
         Result.put(item,1)
      end -- items2

   timer_function : INTEGER is
      local
         new_val : REAL
      do
         new_val := pbar.adjustment.value + 1
         if new_val > pbar.adjustment.upper then
            new_val := pbar.adjustment.lower
         end -- if

         pbar.set_value(new_val)

         Result := 1;
      end -- timer_function

   destroy_window is
      do
         timer.stop
         window.destroy
         gtk_main_quit
      end -- destroy_window

   progressbar_toggle_orientation(data : ANY; cb_data : VEGTK_CALLBACK_DATA) is
      local
         widget : GTK_WIDGET
         i : INTEGER
         orient : GTK_PROGRESS_BAR_ORIENTATION
      do
         widget ?= cb_data.get_widget

         if widget.mapped then
            i := radio_menu_toggled(omenu1.menu_item.group)
            !!orient.make(3 - i)
            pbar.set_orientation(orient)
         end -- if
      end -- progressbar_toggle_orientation

   toggle_show_text(data : ANY; cb_data : VEGTK_CALLBACK_DATA) is
      local
         button : GTK_TOGGLE_BUTTON
      do
         button ?= cb_data.get_widget

         pbar.set_show_text(button.get_active)
         entry.set_sensitive(button.get_active)
         x_align_spin.set_sensitive(button.get_active)
         y_align_spin.set_sensitive(button.get_active)
      end -- toggle_show_text

   progressbar_toggle_bar_style(data : ANY; cb_data : VEGTK_CALLBACK_DATA) is
      local
         widget : GTK_WIDGET
         i : INTEGER
         style : GTK_PROGRESS_BAR_STYLE
      do
         widget ?= cb_data.get_widget

         if widget.mapped then
            i := radio_menu_toggled(omenu2.menu_item.group)

            i := 1 - i

            if i = 1 then
               block_spin.set_sensitive(true)
            else
               block_spin.set_sensitive(false)
            end -- if

            !!style.make(i)
            pbar.set_bar_style(style)
         end -- if
      end -- progressbar_toggle_bar_style

   progress_value_changed is
      local
         buf : STRING
      do
         if pbar.activity_mode then
            buf := "???"
         else
            !!buf.make_from_string((100.0*
               pbar.get_current_percentage).out)
         end -- if
         user_label.set_text(buf)
      end -- progress_value_changed

   adjust_blocks is
      do
         pbar.set_percentage(0)
         pbar.set_discrete_blocks(block_spin.get_value_as_int)
      end -- adjust_blocks

   adjust_step is
      do
         pbar.set_activity_step(step_spin.get_value_as_int)
      end -- adjust_step

   adjust_act_blocks is
      do
         pbar.set_activity_blocks(act_blocks_spin.get_value_as_int)
      end -- adjust_act_blocks

   adjust_align is
      do
         pbar.set_text_alignment(x_align_spin.get_value_as_float,
            y_align_spin.get_value_as_float)
      end -- adjust_align

   toggle_activity_mode(data : ANY; cb_data : VEGTK_CALLBACK_DATA) is
      local
         button : GTK_TOGGLE_BUTTON
      do
         button ?= cb_data.get_widget

         pbar.set_activity_mode(button.get_active)
         step_spin.set_sensitive(button.get_active)
         act_blocks_spin.set_sensitive(button.get_active)
      end -- toggle_activity_mode

   entry_changed is
      do
         pbar.set_format_string(entry.get_text)
      end -- entry_changed
   build_option_menu(arg_items : ARRAY[OPTION_MENU_ITEM];
                   num_items : INTEGER;
                   history : INTEGER;
                   data : ANY) : GTK_OPTION_MENU is
      local
         omenu : GTK_OPTION_MENU
         menu : GTK_MENU
         menu_item : GTK_RADIO_MENU_ITEM
         group : G_SLIST
         i : INTEGER
      do
         !!omenu.make

         !!menu.make
         group := Void -- is this needed in eiffel?

         from
            i := 0
         until
            i = num_items
         loop
            if group=Void then
               !!menu_item.make_alone_with_label(arg_items.item(i).name)
            else
               !!menu_item.make_with_label(group,arg_items.item(i).name)
            end -- if
            signal_connect_with_data(menu_item,"activate",
                          arg_items.item(i).func, data)
            group := menu_item.group
            menu.append(menu_item)
            if i = history then
               menu_item.set_active(true)
            end -- if
            menu_item.show
            i := i+1
         end -- loop

         omenu.set_menu(menu)
         omenu.set_history(history)

         Result := omenu
      end -- build_option_menu

   radio_menu_toggled(group : G_SLIST) : INTEGER is
      local
         item : GTK_RADIO_MENU_ITEM
         i : INTEGER
      do
         from
            i := 0
            !!item.from_external(group.nth_data(i))
         until
            item.active or item.to_external = default_pointer
         loop
            !!item.from_external(group.nth_data(i))
            i := i+1
         end -- loop
         if i>0 then
            i:=i-1
         end -- if
         Result := i
      end
end -- class PROGRESSBAR
-- example-end

9.5 Dialogs

The Dialog widget is very simple, and is actually just a window with a few things pre-packed into it for you. They are

vbox : GTK_VBOX;
separator : GTK_HSEPARATOR;
action_area : GTK_HBOX;

So you see, it simply creates a window, and then packs a vbox into the top, then a separator, and then an hbox for the "action_area".

The GTK_DIALOG widget can be used for pop-up messages to the user, and other similar tasks. It is really basic. It's creation procedure doesn't take any parameter.

make

You could pack a button in the action_area by doing something like this:

    !!button.make
    dialog.action_area.pack_start (button, True, True, 0);
    button.show;

And you could add to the vbox area by packing, for instance, a label in it, try something like this:

    label.make ("Dialogs are groovy");
    dialog.vbox.pack_start (label, True, True, 0);
    label.show;

As an example in using the dialog box, you could put two buttons in the action_area, a Cancel button and an Ok button, and a label in the vbox area, asking the user a question or giving an error etc. Then you could attach a different signal to each of the buttons and perform the operation the user selects.

If the simple functionality provided by the default vertical and horizontal boxes in the two areas does't give you enough control for your application, then you can simply pack another layout widget into the boxes provided. For example, you could pack a table into the vertical box.

9.6 Pixmaps

Pixmaps are data structures that contain pictures. These pictures can be used in various places, but most visibly as icons on the X-Windows desktop, or as cursors.

A pixmap which only has 2 colors is called a bitmap, and there are a few additional routines for handling this common special case.

To understand pixmaps, it would help to understand how X-windows works. Under X-windows, applications do not need to be running on the same computer that is interacting with the user. Instead, the various applications, called "clients", all communicate with a program which displays the graphics and handles the keyboard and mouse. This program which interacts directly with the user is called a "display server" or "X server." Since the communication might take place over a network, it's important to keep some information with the X server. Pixmaps, for example, are stored in the memory of the X server. This means that once pixmap values are set, they don't need to keep getting transmitted over the network; instead a command is sent to "display pixmap number XYZ here." Even if you aren't using X-windows with GTK currently, using constructs such as Pixmaps will make your programs work acceptably under X-windows.

The rest is unfinished, sorry.

9.7 Rulers

Ruler widgets are used to indicate the location of the mouse pointer in a given window. A window can have a vertical ruler spanning across the width and a horizontal ruler spanning down the height. A small triangular indicator on the ruler shows the exact location of the pointer relative to the ruler.

A ruler must first be created. The creation procedure of the both classes GTK_HRULER and GTK_VRULER are idential

   make

Once a ruler is created, we can define the unit of measurement. Units of measure for rulers can be GTK_PIXELS, GTK_INCHES or GTK_CENTIMETERS. This is set using

set_metric ( metric : GTK_METRIC_TYPE )

The default measure is Gtk_pixels.

Other important characteristics of a ruler are how to mark the units of scale and where the position indicator is initially placed. These are set for a ruler using

set_range ( lower : REAL; upper : REAL; position : REAL;
            max_size : REAL )

The lower and upper arguments define the extent of the ruler, and max_size is the largest possible number that will be displayed. Position defines the initial position of the pointer indicator within the ruler.

A vertical ruler can span an 800 pixel wide window thus

    vruler.set_range (0, 800, 0, 800);

The markings displayed on the ruler will be from 0 to 800, with a number for every 100 pixels. If instead we wanted the ruler to range from 7 to 16, we would code

    vruler.set_range (7, 16, 0, 20);

The indicator on the ruler is a small triangular mark that indicates the position of the pointer relative to the ruler. If the ruler is used to follow the mouse pointer, the motion_notify_event signal should be connected to the motion_notify_event method of the ruler.

The following example creates a window with a horizontal ruler on the top and a vertical ruler on the left side of it. The size of the window is 300 pixels wide by 300 pixels high. The horizontal ruler spans from 7 to 13 with a mark every 100 pixels, while the vertical ruler spans from 0 to 400 with a mark every 100 pixels. Placement of the drawing area and the rulers is done using a table.

-- example-start rulers rulers.e
class RULERS
inherit
   GTK_CONSTANTS
   VEGTK_MAIN
   VEGTK_CALLBACK_HANDLER
creation
   make

feature -- Creation

   make is
   local
      table : GTK_TABLE
   do
      vegtk_init
      !!window.make(Gtk_window_toplevel)
      signal_connect(window,"destroy",$destroy_window)
      window.set_title("rulers")
      window.set_usize(300, 300)
      window.set_events(Gdk_pointer_motion_mask
                        and Gdk_pointer_motion_hint_mask)
      window.set_border_width(0)

      !!table.make(2, 2, False)
      window.add(table)
      table.show

      !!hruler.make
      hruler.set_metric(Gtk_centimeters)
      hruler.set_range(0,100,0, 100)

      signal_connect(window,"motion_notify_event",$motion_event)

      table.attach(hruler, 1, 2, 0, 1,
                   Gtk_expand and Gtk_fill, Gtk_fill, 0, 0)
      hruler.show

      !!vruler.make
      vruler.set_range(5, 15, 0, 20)
      table.attach(vruler, 0, 1, 1, 2,
                   Gtk_fill, Gtk_expand and Gtk_fill, 0, 0)
      vruler.show
      window.show
      gtk_main;
   end -- make

feature {NONE} -- Implementations

   hruler : GTK_HRULER
   vruler : GTK_VRULER
   window : GTK_WINDOW

   motion_event(data : ANY; cb_data : VEGTK_CALLBACK_DATA) is
   local
      event : GDK_EVENT_MOTION
   do
      event ?= cb_data.get_event
      hruler.motion_notify_event(event)
      vruler.motion_notify_event(event)
   end -- motion_event
   destroy_window is
   do
      window.destroy
      gtk_main_quit;
   end -- destroy_window

end -- class RULERS
-- example-end

9.8 Statusbars

Statusbars are simple widgets used to display a text message. They keep a stack of the messages pushed onto them, so that popping the current message will re-display the previous text message.

In order to allow different parts of an application to use the same statusbar to display messages, the statusbar widget issues Context Identifiers which are used to identify different 'users'. The message on top of the stack is the one displayed, no matter what context it is in. Messages are stacked in last-in-first-out order, not context identifier order.

The creation of the class GTK_STATUSBAR follows.

make;

A new Context Identifier is requested using a call to the following function with a short textual description of the context:

get_context_id ( context_description : STRING ) : INTEGER

There are function ans procedures that can operate on statusbars:

push ( context_id : INTEGER; text : STRING ) : INTEGER
pop ( context_id : INTEGER )
remove ( context_id : INTEGER; message_id : INTEGER )

The first, push, is used to add a new message to the statusbar. It returns a Message Identifier, which can be passed later to the procedure remove to remove the message with the given Message and Context Identifiers from the statusbar's stack.

The procedure pop removes the message highest in the stack with the given Context Identifier.

The following example creates a statusbar and two buttons, one for pushing items onto the statusbar, and one for popping the last item back off.

-- example-start statusbar statusbar.e
class STATUSBAR
inherit
   GTK_CONSTANTS
   VEGTK_MAIN
   VEGTK_CALLBACK_HANDLER
creation
   make

feature -- Creation

   make is
   local
      vbox : GTK_VBOX
      button : GTK_BUTTON;
   do
      vegtk_init

      !!window.make(Gtk_window_toplevel)
      window.set_usize( 200, 100);
      window.set_title("GTK Statusbar Example");
      signal_connect(window,"delete_event",$destroy_window)

      !!vbox.make (False, 1);
      window.add(vbox);
      vbox.show;

      !!statusbar.make;
      vbox.pack_start (statusbar, True, True, 0);
      statusbar.show;

      context_id := statusbar.get_context_id("Statusbar example");

      !!button.make_with_label ("push item");
      signal_connect(button, "clicked",$push_item);
      vbox.pack_start(button, True, True, 2);
      button.show;

      !!button.make_with_label ("pop last item");
      signal_connect(button, "clicked",$pop_item);
      vbox.pack_start(button, True, True, 2);
      button.show;

      -- always display the window as the last step so it all splashes on
      -- the screen at once. */
      window.show;

      gtk_main;
   end
feature {NONE} -- implementation
   push_item is
   local
      str : STRING;
   do
     statusbar_counter := statusbar_counter+1;
     !!str.make_from_string("something ")
     str.append(statusbar_counter.out)
     statusbar.push(context_id,str)
   end

   pop_item is
   do
     statusbar.pop(context_id);
   end
   destroy_window is
   do
--      window.destroy
      gtk_main_quit;
   end -- destroy_window

   window : GTK_WINDOW;
   statusbar : GTK_STATUSBAR;
   statusbar_counter : INTEGER;
   context_id : INTEGER;
end -- class STATUSBAR
-- example-end

9.9 Text Entries

The Entry widget allows text to be typed and displayed in a single line text box. The text may be set with function calls that allow new text to replace, prepend or append the current contents of the Entry widget.

There are two creation procedures in the class GTK_ENTRY:

make
make_with_max_length ( max : INTEGER )

The first just creates a new Entry widget, while the second creates a new Entry and sets a limit on the length of the text within the Entry.

There are several procedures for altering the text which is currently within the Entry widget.

set_text ( text : STRING )
append_text ( text : STRING )
prepend_text ( text : STRING )

The procedure set_text sets the contents of the Entry widget, replacing the current contents. The procedures append_text and prepend_text allow the current contents to be appended and prepended to.

The next procedure allows the current insertion point to be set.

set_position ( position : INTEGER )

The contents of the Entry can be retrieved by using a call to the following function. This is useful in the callback functions described below.

get_text : STRING

If we don't want the contents of the Entry to be changed by someone typing into it, we can change its editable state.

set_editable ( editable : BOOLEAN )

The procedure above allows us to toggle the editable state of the Entry widget by passing in a True or False value for the editable argument.

If we are using the Entry where we don't want the text entered to be visible, for example when a password is being entered, we can use the following procedure, which also takes a boolean flag.

set_visibility ( arg_visible : BOOLEAN )

A region of the text may be set as selected by using the following procedure. This would most often be used after setting some default text in an Entry, making it easy for the user to remove it.

select_region ( start : INTEGER; upper : INTEGER )

If we want to catch when the user has entered text, we can connect to the activate or changed signal. Activate is raised when the user hits the enter key within the Entry widget. Changed is raised when the text changes at all, e.g. for every character entered or removed.

The following code is an example of using an Entry widget.

-- example-start entry entry.e
class ENTRY
inherit
   GTK_CONSTANTS
   VEGTK_MAIN
   VEGTK_CALLBACK_HANDLER
creation
   make

feature -- Creation

   make is
   local
      vbox : GTK_VBOX;
      hbox : GTK_HBOX;
      button : GTK_BUTTON;
      checkb  : GTK_CHECK_BUTTON;
   do
      vegtk_init

      !!window.make(Gtk_window_toplevel)
      window.set_usize( 200, 100);
      window.set_title("GTK Entry");
      signal_connect(window,"destroy",$destroy_window)

      !!vbox.make (False, 0);
      window.add(vbox);
      vbox.show;

      !!entry.make_with_max_length (50);
      signal_connect(entry, "activate",$enter_callback);
      entry.set_text ("hello");
      entry.append_text (" world");
      entry.select_region (0, entry.text_length);
      vbox.pack_start (entry, True, True, 0);
      entry.show;

      !!hbox.make(False, 0);
      vbox.add (hbox);
      hbox.show;

      !!checkb.make_with_label("Editable");
      hbox.pack_start (checkb, True, True, 0);
      signal_connect (checkb, "toggled",$entry_toggle_editable);
      checkb.set_active(True);
      checkb.show;

      !!checkb.make_with_label("Visible");
      hbox.pack_start (checkb, True, True, 0);
      signal_connect (checkb, "toggled",$entry_toggle_visibility);
      checkb.set_active(True);
      checkb.show;

      !!button.make_with_label ("Close");
      signal_connect (button, "clicked",$close_application);
      vbox.pack_start (button, True, True, 0);
      button.set_flags(Gtk_can_default);
      button.grab_default;
      button.show;

      window.show;

      gtk_main
   end -- make
feature {NONE} -- Implementation

   window : GTK_WINDOW;
   entry  : GTK_ENTRY;
   destroy_window is
   do
      gtk_main_quit;
   end -- destroy_window

   enter_callback is
   do
     print ("Entry contents: ");
     print (entry.get_text);
     print("%N");
   end

   entry_toggle_editable (data : ANY; cb_data : VEGTK_CALLBACK_DATA) is
   local
      checkbutton : GTK_CHECK_BUTTON;
   do
      checkbutton ?= cb_data.get_widget;
      entry.set_editable(checkbutton.get_active);
   end

   entry_toggle_visibility (data : ANY; cb_data : VEGTK_CALLBACK_DATA) is
   local
      checkbutton : GTK_CHECK_BUTTON;
   do
     checkbutton ?= cb_data.get_widget;
     entry.set_visibility(checkbutton.get_active);
   end
   close_application is
   -- This callback quits the program
   do
       window.destroy;
   end

end -- class ENTRY
-- example-end

9.10 Spin Buttons

The Spin Button widget is generally used to allow the user to select a value from a range of numeric values. It consists of a text entry box with up and down arrow buttons attached to the side. Selecting one of the buttons causes the value to 'spin' up and down the range of possible values. The entry box may also be edited directly to enter a specific value.

The Spin Button allows the value to have zero or a number of decimal places and to be incremented/decremented in configurable steps. The action of holding down one of the buttons optionally result in an acceleration of change in the value according to how long it is depressed.

The Spin Button uses an Adjustment object to hold information about the range of values that the spin button can take. This makes for a powerful Spin Button widget.

Recall that an adjustment widget is created with the following creation procedure, which illustrates the information that it holds:

make ( value : REAL;
       lower : REAL;
       upper : REAL;
       step_increment : REAL;
       page_increment : REAL;
       page_size : REAL )

These attributes of an Adjustment are used by the Spin Button in the following way:

Additionally, mouse button 3 can be used to jump directly to the upper or lower values when used to select one of the buttons. Lets look at the creation procedure of the class GTK_SPIN_BUTTON:

make ( adjustment : GTK_ADJUSTMENT; climb_rate : REAL; digits : INTEGER )

The climb_rate argument take a value between 0.0 and 1.0 and indicates the amount of acceleration that the Spin Button has. The digits argument specifies the number of decimal places to which the value will be displayed.

A Spin Button can be reconfigured after creation using the following procedure:

configure ( adjustment : GTK_ADJUSTMENT; climb_rate : REAL; digits : INTEGER )

The adjustment can be set and retrieved independantly:

set_adjustment ( adjustment : GTK_ADJUSTMENT )
get_adjustment : GTK_ADJUSTMENT

The number of decimal places can also be altered using:

set_digits ( digits : INTEGER )

The value that a Spin Button is currently displaying can be changed using the following procedure:

set_value ( value : REAL )

The current value of a Spin Button can be retrieved as either a floating point or integer value with the following functions:

get_value_as_float : REAL
get_value_as_int : INTEGER

If you want to alter the value of a Spin Value relative to its current value, then the following procededure can be used:

spin ( direction : GTK_SPIN_TYPE; increment : REAL ) is

The direction parameter can take one of the following values:

This procedure packs in quite a bit of functionality, which I will attempt to clearly explain. Many of these settings use values from the Adjustment object that is associated with a Spin Button.

Gtk_spin_step_forward and Gtk_spin_step_backward change the value of the Spin Button by the amount specified by increment, unless increment is equal to 0, in which case the value is changed by the value of step_increment in theAdjustment.

Gtk_spin_page_forward and Gtk_spin_page_backward simply alter the value of the Spin Button by increment.

Gtk_spin_home sets the value of the Spin Button to the bottom of the Adjustments range.

Gtk_spin_end sets the value of the Spin Button to the top of the Adjustments range.

Gtk_spin_user_defined simply alters the value of the Spin Button by the specified amount.

We move away from functions for setting and retreving the range attributes of the Spin Button now, and move onto procedrues that effect the appearance and behaviour of the Spin Button widget itself.

The first of these procedures is used to constrain the text box of the Spin Button such that it may only contain a numric value. This prevents a user from typing anything other than numeric values into the text box of a Spin Button:

set_numeric ( numeric : BOOLEAN )

You can set whether a Spin Button will wrap around between the upper and lower range values with the following procedure:

set_wrap ( wrap : BOOLEAN ) is

You can set a Spin Button to round the value to the nearest step_increment, which is set within the Adjustment object used with the Spin Button. This is accomplished with the following procedure:

set_snap_to_ticks ( snap_to_ticks : BOOLEAN )

The update policy of a Spin Button can be changed with the following procedure:

set_update_policy ( policy : GTK_SPIN_BUTTON_UPDATE_POLICY )

The possible values of policy are either Gtk_update_always or Gtk_update_if_valid.

These policies affect the behavior of a Spin Button when parsing inserted text and syncing it's value with the values of the Adjustment.

In the case of Gtk_update_if_valid the Spin Button only value gets changed if the text input is a numeric value that is within the range specified by the Adjustment. Otherwise the text is reset to the current value.

In case of Gtk_update_always we ignore errors while converting text into a numeric value.

The appearance of the buttons used in a Spin Button can be changed using the following procedure:

set_shadow_type ( shadow_type : GTK_SHADOW_TYPE )

As usual, the shadow_type can be one of:

Finally, you can explicitly request that a Spin Button update itself:

update

It's example time again.

-- example-start spinbutton spinbutton.e
class SPINBUTTON
inherit
   GTK_CONSTANTS
   VEGTK_MAIN
   VEGTK_CALLBACK_HANDLER
creation
   make
feature {ANY} -- Creation
   make is
   local
      frame : GTK_FRAME;
      hbox : GTK_HBOX;
      main_vbox, vbox, vbox2 : GTK_VBOX;
      spinner, spinner2 : GTK_SPIN_BUTTON;
      button : GTK_BUTTON;
      check_button : GTK_CHECK_BUTTON;
      label : GTK_LABEL;
      adj : GTK_ADJUSTMENT;
   do
      -- Initialise GTK
      vegtk_init

      !!window.make (GTK_WINDOW_TOPLEVEL);

      signal_connect (window, "destroy",$destroy);

      window.set_title ("Spin Button");

      !!main_vbox.make (False, 5);
      main_vbox.set_border_width (10);
      window.add (main_vbox);

      !!frame.make ("Not accelerated");
      main_vbox.pack_start (frame, True, True, 0);

      !!vbox.make (False, 0);
      vbox.set_border_width (5);
      frame.add (vbox);

      -- Day, month, year spinners

      !!hbox.make (False, 0);
      vbox.pack_start (hbox, True, True, 5);

      !!vbox2.make (False, 0);
      hbox.pack_start (vbox2, True, True, 5);

      !!label.make ("Day :");
      label.set_alignment (0, 0.5);
      vbox2.pack_start (label, False, True, 0);

      !!adj.make (1.0, 1.0, 31.0, 1.0, 5.0, 0.0);
      !!spinner.make (adj, 0, 0);
      spinner.set_wrap (True);
      spinner.set_shadow_type (GTK_SHADOW_OUT);
      vbox2.pack_start (spinner, False, True, 0);

      !!vbox2.make (False, 0);
      hbox.pack_start (vbox2, True, True, 5);

      !!label.make ("Month :");
      label.set_alignment (0, 0.5);
      vbox2.pack_start (label, False, True, 0);

      !!adj.make (1.0, 1.0, 12.0, 1.0, 5.0, 0.0);
      !!spinner.make (adj, 0, 0);
      spinner.set_wrap (True);
      spinner.set_shadow_type (GTK_SHADOW_ETCHED_IN);
      vbox2.pack_start (spinner, False, True, 0);

      !!vbox2.make (False, 0);
      hbox.pack_start (vbox2, True, True, 5);

      !!label.make ("Year :");
      label.set_alignment (0, 0.5);
      vbox2.pack_start (label, False, True, 0);

      !!adj.make (1998.0, 0.0, 2100.0, 1.0, 100.0, 0.0);
      !!spinner.make (adj, 0, 0);
      spinner.set_wrap (False);
      spinner.set_shadow_type (GTK_SHADOW_IN);
      spinner.set_usize (55, 0);
      vbox2.pack_start (spinner, False, True, 0);

      !!frame.make ("Accelerated");
      main_vbox.pack_start (frame, True, True, 0);

      !!vbox.make (False, 0);
      vbox.set_border_width (5);
      frame.add (vbox);

      !!hbox.make (False, 0);
      vbox.pack_start (hbox, False, True, 5);

      !!vbox2.make (False, 0);
      hbox.pack_start (vbox2, True, True, 5);

      !!label.make ("Value :");
      label.set_alignment (0, 0.5);
      vbox2.pack_start (label, False, True, 0);

      !!adj.make (0.0, -10000.0, 10000.0, 0.5, 100.0, 0.0);
      !!spinner1.make (adj, 1.0, 2);
      spinner1.set_wrap (True);
      spinner1.set_usize (100, 0);
      vbox2.pack_start (spinner1, False, True, 0);

      !!vbox2.make (False, 0);
      hbox.pack_start (vbox2, True, True, 5);

      !!label.make ("Digits :");
      label.set_alignment (0, 0.5);
      vbox2.pack_start (label, False, True, 0);

      !!adj.make (2, 1, 5, 1, 1, 0);
      !!spinner2.make (adj, 0.0, 0);
      spinner2.set_wrap (True);
      signal_connect_with_data (adj, "value_changed",$change_digits,spinner2);
      vbox2.pack_start (spinner2, False, True, 0);

      !!hbox.make (False, 0);
      vbox.pack_start (hbox, False, True, 5);

      !!check_button.make_with_label ("Snap to 0.5-ticks");
      signal_connect_with_data (check_button, "clicked", $toggle_snap,spinner1);
      vbox.pack_start (check_button, True, True, 0);
      check_button.set_active (True);

      !!check_button.make_with_label ("Numeric only input mode");
      signal_connect_with_data (check_button, "clicked", $toggle_numeric, spinner1);
      vbox.pack_start (check_button, True, True, 0);
      check_button.set_active (True);

      !!val_label.make ("");

      !!hbox.make (False, 0);
      vbox.pack_start (hbox, False, True, 5);
      !!button.make_with_label ("Value as Int");
--      gtk_object_set_user_data (GTK_OBJECT (button), val_label);
      signal_connect_with_data (button, "clicked",$get_value,1);
      hbox.pack_start (button, True, True, 5);

      !!button.make_with_label ("Value as Float");
--      gtk_object_set_user_data (GTK_OBJECT (button), val_label);
      signal_connect_with_data (button, "clicked",$get_value,2.0);
      hbox.pack_start (button, True, True, 5);

      vbox.pack_start (val_label, True, True, 0);
      val_label.set_text ("0");

      !!hbox.make (False, 0);
      main_vbox.pack_start (hbox, False, True, 0);

      !!button.make_with_label ("Close");
      signal_connect_with_data (button, "clicked", $quit_app,window);

      hbox.pack_start (button, True, True, 5);

      window.show_all;

      -- Enter the event loop
      gtk_main;
   end

feature {NONE} -- Callbacks
   destroy is
   do
      gtk_main_quit;
   end
   quit_app is
   do
      window.destroy;
   end

   toggle_snap (data : ANY; cb_data : VEGTK_CALLBACK_DATA) is
   local
      widget : GTK_TOGGLE_BUTTON;
      spin : GTK_SPIN_BUTTON;
   do
      widget ?= cb_data.get_widget;
      spin ?= data;
      spin.set_snap_to_ticks (widget.get_active);
   end

   toggle_numeric (data : ANY; cb_data : VEGTK_CALLBACK_DATA) is
   local
      widget : GTK_TOGGLE_BUTTON;
      spin : GTK_SPIN_BUTTON;
   do
      widget ?= cb_data.get_widget;
      spin ?= data;
      spin.set_numeric(widget.get_active);
   end

   change_digits (data : ANY; cb_data : VEGTK_CALLBACK_DATA) is
   local
      spin : GTK_SPIN_BUTTON;
   do
      spin ?= data;
      spinner1.set_digits(spin.get_value_as_int );
   end

   get_value (data : ANY) is
   local
      f_value : REAL_REF;
      i_value : INTEGER_REF;
      buf : STRING;
   do
      i_value  ?= data;
      if i_value /= Void then
         buf := spinner1.get_value_as_int.out;
      else
         f_value  ?= data;
         if f_value /= Void then
            buf := spinner1.get_value_as_float.out;
         else
            buf := "";
         end
      end
      val_label.set_text (buf);
   end
feature {NONE} -- mplementation
   window : GTK_WINDOW;
   spinner1 : GTK_SPIN_BUTTON;
   val_label : GTK_LABEL;
end -- class SPINBUTTON
-- example-end
It's unfinished yet.

9.11 Combo Box

The combo box is another fairly simple widget that is really just a collection of other widgets. From the users point of view, the widget consists of a text entry box and a pull down menu from which the user can select one of a set of predefined entries. Alternatively, the user can type a different option directly into the text box.

The creation procedure of the class GTK_COMBO is simple:

make

Now, if you want to set the string in the entry section of the combo box, this is done by manipulating the entry widget directly:

   ...
   local
      combo : GTK_COMBO
   do
    combo.entry.set_text("My String.");

To set the values in the popdown list, one uses the procedure:

set_popdown_strings ( strings : ARRAY[STRING] )

At this point you have a working combo box that has been set up. There are a few aspects of its behavior that you can change. These are accomplished with the procedures:

set_use_arrows ( val : BOOLEAN )
set_use_arrows_always ( val : BOOLEAN )
set_case_sensitive ( val : BOOLEAN )

set_use_arrows() lets the user change the value in the entry using the up/down arrow keys. This doesn't bring up the list, but rather replaces the current text in the entry with the next list entry (up or down, as your key choice indicates). It does this by searching in the list for the item corresponding to the current value in the entry and selecting the previous/next item accordingly. Usually in an entry the arrow keys are used to change focus (you can do that anyway using TAB). Note that when the current item is the last of the list and you press arrow-down it changes the focus (the same applies with the first item and arrow-up).

If the current value in the entry is not in the list, then the function of set_use_arrows() is disabled.

set_use_arrows_always() similarly allows the use the the up/down arrow keys to cycle through the choices in the dropdown list, except that it wraps around the values in the list, completely disabling the use of the up and down arrow keys for changing focus.

set_case_sensitive() toggles whether or not GTK searches for entries in a case sensitive manner. This is used when the Combo widget is asked to find a value from the list using the current entry in the text box. This completion can be performed in eother a case sensitive or insensitive manner, depending upon the use of this function. The Combo widget can also simply complete the current entry if the user presses the key combination MOD-1 and 'Tab'. MOD-1 is often mapped to the 'Alt' key. Note, however that some Window managers also use this key combination, which will override it's use within GTK.

Now that we have a combo box, tailored to look and act how we want it, all that remains is being able to get data from the combo box. This is relatively straight forward. The majority of the time, all you are going to care about getting data from is the entry. The entry is accessed simply by combo.entry). The two principle things that you are going to want to do with it are attach to the activate signal, which indicates that the user has pressed the Return or Enter key,and read the text. The first is accomplished using something like:

    signal_connect_with_data(combo.entry, "activate",$my_callback_function,
                             my_data);

Getting the text at any arbitrary time is accomplished by simply using the function GTK_ENTRY::get_text:

Such as:

   ...
local
   get_text : STRING;
   combo : GTK_COMBO;
   ...
do
   string := combo.entry.get_text;

That's about all there is to it. There is a procedure

disable_activate

that will disable the activate signal on the entry widget in the combo box. Personally, I can't think of why you'd want to use it, but it does exist.

9.12 Color Selection

The color selection widget is, not surprisingly, a widget for interactive selection of colors. This composite widget lets the user select a color by manipulating RGB (Red, Green, Blue) and HSV (Hue, Saturation, Value) triples. This is done either by adjusting single values with sliders or entries, or by picking the desired color from a hue-saturation wheel/value bar. Optionally, the opacity of the color can also be set.

The color selection widget currently emits only one signal, "color_changed", which is emitted whenever the current color in the widget changes, either when the user changes it or if it's set explicitly through set_color().

Lets have a look at what the color selection widget has to offer us. The widget comes in two flavours: GTK_COLOR_SELECTION and GTK_COLOR_SELECTION_DIALOG.

GTK_COLOR_SELECTION You'll probably not be using GTK_COLOR_SELECTION directly. It creates an orphan widget which you'll have to parent yourself. The GTK_COLOR_SELECTION widget inherits from the GtkVBox widget.

GTK_COLOR_SELECTION_DIALOG
creation:
   make (title : STRING)

This is the most common color selection widget.It consists of a GTK_FRAME containing a GTK_COLOR_SELECTION widget, a GTK_HSEPARATOR and a GTK_HBOX with three buttons, "Ok", "Cancel" and "Help". You can reach these buttons by accessing the "ok_button", "cancel_button" and "help_button" features in the GTK_COLOR_SELECTION_DIALOG, (i.e. colorseldialog.ok_button).

Let's return again to procedures and functions of GTK_COLOR_SELECTION

set_update_policy ( policy : GTK_UPDATE_TYPE )

This procedure sets the update policy. The default policy is GTK_UPDATE_CONTINUOUS which means that the current color is updated continuously when the user drags the sliders or presses the mouse and drags in the hue-saturation wheel or value bar. If you experience performance problems, you may want to set the policy to GTK_UPDATE_DISCONTINUOUS or GTK_UPDATE_DELAYED.

set_opacity ( use_opacity : BOOLEAN )

The color selection widget supports adjusting the opacity of a color (also known as the alpha channel). This is disabled by default. Calling this procedure with use_opacity set to True enables opacity. Likewise, use_opacity set to False will disable opacity.

set_color ( color : ARRAY[DOUBLE] )

You can set the current color explicitly by calling this procedure with an array of colors. The length of the array depends on whether opacity is enabled or not. Position 1 contains the red component, 2 is green, 3 is blue and opacity is at position 4 (only if opacity is enabled, see set_opacity()). All values are between 0.0 and 1.0.

get_color : ARRAY[DOUBLE] is

When you need to query the current color, typically when you've received a "color_changed" signal, you use this function. See the set_color() procedure for the description of this array.

Here's a simple example demonstrating the use of the GTK_COLOR_SELECTION_DIALOG. The program displays a window containing a drawing area. Clicking on it opens a color selection dialog, and changing the color in the color selection dialog changes the background color.

-- example-start colorsel colorsel.e
class COLORSEL
inherit
   GTK_CONSTANTS
   VEGTK_MAIN
   VEGTK_CALLBACK_HANDLER
creation
   make

feature -- Creation

   make is
   do
      -- Initialize the toolkit, remove gtk-related commandline stuff
      vegtk_init

      -- Create toplevel window, set title and policies
      !!window.make(Gtk_window_toplevel)
      window.set_title("Color selection test");
      window.set_policy (True, True, True);
      -- Attach to the "delete" and "destroy" events so we can exit
      signal_connect(window,"destroy",$destroy_window)
      -- Create drawingarea, set size and catch button events

      !!drawingarea.make;

      drawingarea.size (200, 200);

      drawingarea.set_events (Gdk_button_press_mask);

      signal_connect (drawingarea, "event",$area_event);

      -- Add drawingarea to window, then show them both

      window.add (drawingarea);

      drawingarea.show;
      window.show;

      -- Enter the gtk main loop (this never returns

      gtk_main;

   end
feature {NONE} -- Implementation
   window : GTK_WINDOW;
   colorseldlg : GTK_COLOR_SELECTION_DIALOG;
   drawingarea : GTK_DRAWING_AREA;
feature {NONE} -- Callbacks

   color_changed_cb (data : ANY; cb_data : VEGTK_CALLBACK_DATA) is
      -- Color changed handler
   local
     color : ARRAY[DOUBLE];
     gdk_color : GDK_COLOR;
     colormap : GDK_COLORMAP;
     colorsel : GTK_COLOR_SELECTION;
     dummy : BOOLEAN;
   do
     colorsel ?= cb_data.get_widget;
     -- Get drawingarea colormap

     colormap := drawingarea.window.get_colormap;

     -- Get current color

     color := colorsel.get_color;

     -- Fit to a unsigned 16 bit integer (0..65535) and
     -- insert into the GdkColor structure */
     !!gdk_color.make_with_values((color.item(1)*65535).rounded,
                                  (color.item(2)*65535).rounded,
                                  (color.item(3)*65535).rounded);

     -- Allocate color

     dummy := colormap.alloc_color (gdk_color,True,True);

     -- Set window background color

     drawingarea.window.set_background (gdk_color);

     -- Clear window

     drawingarea.window.clear;
   end


   area_event (data : ANY; cb_data : VEGTK_CALLBACK_DATA) is
      -- Drawingarea event handler
   local
     handled  : BOOLEAN;
     event : GDK_EVENT_BUTTON;
     colorsel : GTK_COLOR_SELECTION;
   do
      -- Check if we've received a button pressed event
      event ?= cb_data.get_event
      if event /= Void and then colorseldlg = Void then
          -- Yes, we have an event and there's no colorseldlg yet!

          handled := True;

          -- Create color selection dialog

          !!colorseldlg.make("Select background color");

          -- Get the GtkColorSelection widget

          colorsel := colorseldlg.colorsel;

          -- Connect to the "color_changed" signal

          signal_connect(colorsel, "color_changed",$color_changed_cb);

          -- Show the dialog

          colorseldlg.show;
        end -- if
    --  return handled;
   end


   destroy_window is
   -- Close down and exit handler
   do
      gtk_main_quit;
   end
end -- class COLORSEL
-- example-end

9.13 File Selections

The file selection widget is a quick and simple way to display a File dialog box. It comes complete with Ok, Cancel, and Help buttons, a great way to cut down on programming time.

Creation procedure of the class GTK_FILE_SELECTION is :

make ( title : STRING )

To set the filename, for example to bring up a specific directory, or give a default filename, use this procedure:

set_filename ( filename : STRING )

To grab the text that the user has entered or clicked on, use this function:

get_filename : STRING

There are also widgets contained within the file selection widget. These are:

Most likely you will want to use the ok_button, cancel_button, and help_button pointers in signaling their use.

Included here is an example stolen from testgtk, modified to run on its own. As you will see, there is nothing much to creating a file selection widget. While in this example the Help button appears on the screen, it does nothing as there is not a signal attached to it.

-- example-start filesel filesel.e
class FILESEL
inherit
   GTK_CONSTANTS
   VEGTK_MAIN
   VEGTK_CALLBACK_HANDLER
creation
   make

feature -- Creation

   make is
   do
      -- Initialize the toolkit, remove gtk-related commandline stuff
      vegtk_init

      -- Create a new file selection widget
      !!filew.make("File selection")
      signal_connect (filew, "destroy",$destroy);
      -- Connect the ok_button to file_ok_sel function
      signal_connect (filew.ok_button,"clicked",$file_ok_sel);

      -- Connect the cancel_button to destroy the widget
      signal_connect (filew.cancel_button,"clicked", $close_application);

      -- Lets set the filename, as if this were a save dialog,
      -- and we are giving a default filename
      filew.set_filename ("penguin.png");

      filew.show;
      gtk_main ;
   end
feature {NONE} -- Implementation
   filew : GTK_FILE_SELECTION;
feature {NONE} -- Callbacks
   close_application is
   -- This callback quits the program
   do
       filew.destroy;
   end

   file_ok_sel is
      -- Get the selected filename and print it to the console
   do
       print (filew.get_filename);
       print("%N");
   end

   destroy is
   do
       gtk_main_quit;
   end
end -- class FILESEL
-- example-end


Next Previous Contents