[Previous] [Contents] [Next]
9. Miscellaneous Widgets
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, place it inside a EventBox widget or a Button widget.
To create a new label, use:
FUNCTION gtk_label_new(
str : pchar ): pGtkWidget;
The sole argument is the string you wish the label to display. To change the label's text after creation, use the function:
PROCEDURE gtk_label_set_text(
a_label : pGtkLabel ; str : pchar );
The first argument is the label you created previously (cast using the GTK_LABEL() macro), and the second is the new
string. The space needed for the new string will be automatically adjusted if needed. You can produce multi-line labels
by putting line breaks in the label string.
To retrieve the current string, use:
PROCEDURE gtk_label_get(
a_label : pGtkLabel ; str : ppchar );
The first argument is the label you've created, and the second, the return for the string. Do not free the return string,
as it is used internally by GTK. The label text can be justified using:
PROCEDURE gtk_label_set_justify(
a_label : pGtkLabel ; j_type : LONGINT );
Values for j_type are:
| GTK_JUSTIFY_LEFT | GTK_JUSTIFY_RIGHT | GTK_JUSTIFY_CENTER (the default) | GTK_JUSTIFY_FILL |
The label widget is also capable of line wrapping the text automatically. This can be activated using:
PROCEDURE gtk_label_set_line_wrap(
a_label : pGtkLabel ; wrap : boolean );
The wrap argument takes a TRUE or FALSE value.
If you want your label underlined, then you can set a pattern on the label:
PROCEDURE gtk_label_set_pattern(
a_label : pGtkLabel ; a_pattern : pgchar );
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 eight and ninth characters.
Below is a short example to illustrate these functions. This example makes use of the Frame widget to better
demonstrate the label styles. You can ignore this aspect of the program for now as the Frame widget is explained
later on.
PROGRAM label_example;
USES gtk;
CONST
window : pGtkWidget = NIL;
{ -------------------------------------Global Variables---------------------------- }
VAR
hbox, vbox, frame, a_label : pGtkWidget;
{ -------------------------------------Main Program---------------------------- }
BEGIN
gtk_init(@argc, @argv);
{ Initialise GTK }
window := gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_signal_connect(GTK_OBJECT(window),
'destroy',
GTK_SIGNAL_FUNC(@gtk_main_quit), NIL);
gtk_window_set_title(GTK_WINDOW(window), 'Label');
vbox := gtk_vbox_new(FALSE, 5);
hbox := gtk_hbox_new(FALSE, 5);
gtk_container_add(GTK_CONTAINER(window), hbox);
gtk_box_pack_start(GTK_BOX(hbox), vbox, FALSE,
FALSE, 0);
gtk_container_set_border_width(GTK_CONTAINER(window), 5);
frame := gtk_frame_new('Normal Label');
a_label := gtk_label_new('This is a Normal label');
gtk_container_add(GTK_CONTAINER(frame), a_label);
gtk_box_pack_start(GTK_BOX(vBox), frame, FALSE,
FALSE, 0);
frame := gtk_frame_new('Multi-line Label');
{ A new line is acheived with '#10' in the same way that \n is used in C }
a_label := gtk_label_new('This is a Multi-line
label.'#10'Second
line'#10'Third line');
gtk_container_add(GTK_CONTAINER(frame), a_label);
gtk_box_pack_start(GTK_BOX(vBox), frame, FALSE,
FALSE, 0);
frame := gtk_frame_new('Left Justified Label');
a_label := gtk_label_new('This is a
Left-Justified'#10'Multi-line
label.'#10'Third line');
gtk_label_set_justify(GTK_LABEL(a_label), GTK_JUSTIFY_LEFT);
gtk_container_add(GTK_CONTAINER(frame), a_label);
gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE,
FALSE, 0);
frame := gtk_frame_new('Right Justified Label');
{ Remember that in Pascal a constant string must be all on one
- possibly word-wrapped - line in your text editor }
a_label := gtk_label_new('This is a'#10'
Right-Justified'#10'Multi-line
label.'#10'Fourth line, (j/k)');
gtk_label_set_justify(GTK_LABEL(a_label), GTK_JUSTIFY_RIGHT);
gtk_container_add(GTK_CONTAINER(frame), a_label);
gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE,
FALSE, 0);
{ Add a new vertical box for the next three labels }
vBox := gtk_vbox_new(FALSE, 5);
gtk_box_pack_start(GTK_BOX(hbox), vbox, FALSE,
FALSE, 0);
frame := gtk_frame_new('Line wrapped label');
{ The Free Pascal Compiler doesn't allow constant strings to be longer than
255 characters long.
Also remember that an apostrophe in a string is represented
by two apostrophes in succession: '' }
a_label := gtk_label_new('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 sixth sheik''s six sheep''s sick.'#10' It supports multiple
paragraphs correctly, and adds extra spaces.');
gtk_label_set_line_wrap(GTK_LABEL(a_label), TRUE);
gtk_container_add(GTK_CONTAINER(frame), a_label);
gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE,
FALSE, 0);
frame := gtk_frame_new('Filled, wrapped label');
a_label := gtk_label_new('This is an example of a line-wrapped,
filled label. It should be taking up the entire width allocated to it. Here is a sentence to prove
my point. Here is another sentence. Here comes the sun, do de do de
do.'#10'This is a new
paragraph.'#10'Paragraph.');
gtk_label_set_justify(GTK_LABEL(a_label), GTK_JUSTIFY_FILL);
gtk_label_set_line_wrap(GTK_LABEL(a_label), TRUE);
gtk_container_add(GTK_CONTAINER(frame), a_label);
gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE,
FALSE, 0);
frame := gtk_frame_new('Underlined label');
a_label := gtk_label_new('This label is
underlined!'#10'This one is underlined in quite a funky fashion');
gtk_label_set_justify(GTK_LABEL(a_label), GTK_JUSTIFY_LEFT);
gtk_label_set_pattern(GTK_LABEL(a_label),
'_________________________ _____ _________ _ ______ __ _______ ___');
gtk_container_add(GTK_CONTAINER(frame), a_label);
gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE,
FALSE, 0);
gtk_widget_show_all(window);
gtk_main();
END.
{ -------------------------------------Main Program---------------------------- }
[Previous] [Contents] [Next]
The 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. Like the Label widget, it emits no
signals. There are only two functions for manipulating an Arrow widget:
FUNCTION gtk_arrow_new( arrow_type :
LONGINT ; shadow_type :
LONGINT ) :
pGtkWidget;
PROCEDURE gtk_arrow_set( arrow :
pGtkArrow ; arrow_type :
LONGINT ;
shadow_type : LONGINT );
The first creates a new arrow widget with the indicated type and appearance. The second allows these values to be altered
retrospectively. The arrow_type argument may take one of the following values:
| GTK_ARROW_UP | GTK_ARROW_DOWN | GTK_ARROW_LEFT | GTK_ARROW_RIGHT |
These values obviously indicate the direction in which the arrow will point. The shadow_type argument may take one of
these values:
| GTK_SHADOW_IN | GTK_SHADOW_OUT* | GTK_SHADOW_ETCHED_IN | GTK_SHADOW_ETCHED_OUT |
* the default
Here's a brief example to illustrate their use.
PROGRAM arrow_example;
USES gtk;
{ --------------------------------create_arrow_button----------------------------- }
FUNCTION create_arrow_button( arrow_type : LONGINT ;
shadow_type : LONGINT ) :
pGtkWidget;
{ Create an Arrow widget with the specified parameters and pack it into a button }
VAR
button, arrow : pGtkWidget;
BEGIN
button := gtk_button_new();
arrow := gtk_arrow_new(arrow_type, shadow_type);
gtk_container_add(GTK_CONTAINER(button), arrow);
gtk_widget_show(button);
gtk_widget_show(arrow);
create_arrow_button := button;
{ Return new arrow button to caller }
END;
{ --------------------------------create_arrow_button----------------------------- }
{ --------------------------------Global Variables------------------------------- }
VAR
window, button, box : pGtkWidget;
{ -----------------------------------Main Program------------------------------- }
BEGIN
gtk_init(@argc, @argv); { Initialise GTK }
window := gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(window), 'Arrow Buttons');
gtk_signal_connect(GTK_OBJECT(window), 'destroy',
GTK_SIGNAL_FUNC(@gtk_main_quit),
NIL);
{ Set the border width of the window: }
gtk_container_set_border_width(GTK_CONTAINER(window), 10);
{ Create a box to hold the arrows/buttons: }
box := gtk_hbox_new(FALSE, 0);
gtk_container_set_border_width(GTK_CONTAINER(box), 2);
gtk_container_add(GTK_CONTAINER(window), box);
{ Pack and show all our widgets: }
gtk_widget_show(box);
{ Create four variations of the arrow button and add them to the box }
button := create_arrow_button(GTK_ARROW_UP, GTK_SHADOW_IN);
gtk_box_pack_start(GTK_BOX(box), button, FALSE,
FALSE, 3);
button := create_arrow_button(GTK_ARROW_DOWN, GTK_SHADOW_OUT);
gtk_box_pack_start(GTK_BOX(box), button, FALSE,
FALSE, 3);
button := create_arrow_button(GTK_ARROW_LEFT, GTK_SHADOW_ETCHED_IN);
gtk_box_pack_start(GTK_BOX(box), button, FALSE,
FALSE, 3);
button := create_arrow_button(GTK_ARROW_RIGHT, GTK_SHADOW_ETCHED_OUT);
gtk_box_pack_start(GTK_BOX(box), button, FALSE,
FALSE, 3);
gtk_widget_show(window);
gtk_main();
END.
{ ---------------------------------Main Program----------------------------- }
[Previous] [Contents] [Next]
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 the example in the 10.11 Toolbar section.
Widgets that don't receive events (widgets that don't have their own window) won't 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
GtkTooltips object this function returns can be used to create multiple tooltips.
FUNCTION gtk_tooltips_new()
: pGtkToolTips;
Once you have created a new tooltip, and the widget you wish to use it on, simply use this call to set it:
PROCEDURE gtk_tooltips_set_tip( tooltips :
pGtkTooltips ; widget : pGtkWidget ;
tip_text pgchar ;
tip_private : pgchar );
The first argument is the tooltip you've already created, followed by 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
GtkTipsQuery() to implement context sensitive help. For now, you can set it to
NIL. Here's a short example:
VAR
tooltips : pGtkTooltips;
button : pGtkWidget;
.
.
BEGIN
tooltips := gtk_tooltips_new();
button := gtk_button_new_with_label('button 1');
.
.
gtk_tooltips_set_tip(tooltips, button, 'This is button 1',
NIL);
END.
There are other calls that can be used with tooltips. I will just list them with a brief description of what they do.
PROCEDURE gtk_tooltips_enable( tooltips
: pGtkTooltips );
- enable a disabled set of tooltips.
PROCEDURE gtk_tooltips_disable( tooltips
: pGtkTooltips );
- disable an enabled set of tooltips.
PROCEDURE gtk_tooltips_set_delay( tooltips
: pGtkTooltips ; delay : gint );
- 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).
PROCEDURE gtk_tooltips_set_colors( tooltips
: pGtkTooltips ; background :
pGdkColor ;
foreground : pGdkColor );
- set the foreground and background colour of the tooltips.
And that's all the functions associated with tooltips. More than you'll ever want to know :-)
[Previous] [Contents] [Next]
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 ways to create a progress bar, one simple one that takes no arguments, and one that takes an
Adjustment object as an argument. If the former is used, the progress bar creates its own adjustment object.
FUNCTION gtk_progress_bar_new()
: pGtkToolTips;
FUNCTION gtk_progress_bar_new_with_adjustment( adjustment :
pGtkAdjustment ) :
pGtkToolTips;
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:
PROCEDURE gtk_progress_set_adjustment(
progress : pGtkProgress ;
adjustment : pGtkAdjustment );
Now that the progress bar has been created we can use it.
PROCEDURE gtk_progress_bar_update(
p_bar : pGtkProgressBar ;
percentage : gfloat );
The first argument is the progress bar you wish to operate on, and the second argument is the amount completed,
meaning the amount the progress bar has been filled from 0-100%. This is passed to the procedure 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 procedure:
PROCEDURE gtk_progress_bar_set_orientation(
p_bar : pGtkProgressBar ;
orientation : LONGINT );
The orientation argument may take one of the following values to indicate the direction in which the progress bar moves:
| GTK_PROGRESS_LEFT_TO_RIGHT | GTK_PROGRESS_RIGHT_TO_LEFT |
| GTK_PROGRESS_BOTTOM_TO_TOP | GTK_PROGRESS_TOP_TO_BOTTOM |
When used as a measure of how far a process has progressed, the ProgressBar 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 procedure:
PROCEDURE gtk_progress_bar_set_bar_style(
p_bar : pGtkProgressBar ;
style : LONGINT );
The style parameter can take one of two values:
| GTK_PROGRESS_CONTINUOUS | GTK_PROGRESS_DISCRETE |
The number of discrete blocks can be set by calling
PROCEDURE gtk_progress_bar_set_discrete_blocks(
p_bar : pGtkProgressBar ;
blocks : guint );
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 affected by the bar style that is described above, it overrides it. This mode is either
TRUE or FALSE, and is selected by the following procedure.
PROCEDURE gtk_progress_set_activity_mode(
progress : pGtkProgress ;
activity_mode : guint );
The step size of the activity indicator, and the number of blocks are set using the following procedures:
PROCEDURE gtk_progress_bar_set_activity_step(
p_bar : pGtkProgressBar ;
step : guint );
PROCEDURE gtk_progress_bar_set_activity_blocks(
p_bar : pGtkProgressBar ;
blocks : guint );
When in continuous mode, the progress bar can also display a configurable text string within its trough, using
the following procedure.
PROCEDURE gtk_progress_set_format_string(
progress : pGtkProgress ;
format : pgchar );
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:
| %p - percentage | %v - value | %l - lower range value |
%u - upper range value |
The displaying of this text string can be toggled using:
PROCEDURE gtk_progress_set_show_text(
progress : pGtkProgress ;
show_text : gint );
The show_text argument is a boolean
TRUE/FALSE value. The appearance of the text can be modified further
using:
PROCEDURE gtk_progress_set_text_alignment(
progress : pGtkProgress ;
x_align : gfloat ;
y_align : gfloat );
The x_align and y_align arguments take values between 0.0 and 1.0. Their values indicate 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 place 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. The character string returned by these functions should be freed by the application (using
the g_free() function). These functions return the formatted string that would be displayed
within the trough.
FUNCTION gtk_progress_get_current_text(
progress : pGtkProgress ) : pgchar;
FUNCTION gtk_progress_get_text_from_value(
progress : pGtkProgress ;
value : gfloat ) :
pgchar;
There is yet another way to change the range and value of a progress object using the following procedure:
PROCEDURE gtk_progress_configure(
progress : pGtkProgress ;
value : gfloat ;
min : gfloat ; max : gfloat );
This procedure provides quite a simple interface to the range and value of a progress object. The remaining two procedures
and three functions can be used to get and set the current value of a progess object in various types and formats:
PROCEDURE gtk_progress_set_percentage(
progress : pGtkProgress ;
percentage : gfloat ) ;
PROCEDURE gtk_progress_set_value(
progress : pGtkProgress ;
value : gfloat );
FUNCTION gtk_progress_get_value(
progress : pGtkProgress ) : gfloat;
FUNCTION gtk_progress_get_current_percentage(
progress : pGtkProgress )
: gfloat;
FUNCTION gtk_progress_get_percentage_from_value(
progress : pGtkProgress ;
value : gfloat ) :
gfloat;
These functions/procedures are pretty self explanatory. The last function uses the 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, IO and Idle Functions) to give the illusion of multitasking. All will employ the
gtk_progress_bar_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.
{ For a slightly different translation of this (by Stefan Hille) see the examples included
with the FPC documentation }
PROGRAM progressbars;
{$mode objfpc}
USES glib, gtk;
TYPE
tProgressData = RECORD
window : pGtkWindow;
p_bar : pGtkWidget;
timer : Integer;
END;
ptProgressData = ^tProgressData;
{ ---------------------------------progress_timeout------------------------------- }
FUNCTION progress_timeout( data : pgpointer ) :
LONGINT; cdecl;
{ Update the value of the progress bar so that we get some movement }
VAR
new_val : gfloat;
adj : pGtkAdjustment;
{ Calculate the value of the progress bar using the pointer value range set in the
adjustment object }
BEGIN
new_val := gtk_progress_get_value(GTK_PROGRESS(data)) + 1;
adj := GTK_PROGRESS(Data)^.adjustment;
IF (new_val > adj^.upper) THEN
new_val := adj^.lower;
{ Set the new value }
gtk_progress_set_value(GTK_PROGRESS(data), new_val);
progress_timeout := 1; { i.e. TRUE }
{ As this is a timeout function, return TRUE so that it continues to get called }
END;
{ ------------------------------ progress_timeout ----------------------------------- }
{ -----------------------------toggle_show_text-------------------------------- }
PROCEDURE toggle_show_text( widget : pGtkWidget ;
p_data : ptProgressData );
{ Callback that toggles the text display within the progress bar trough }
BEGIN
gtk_progress_set_show_text(GTK_PROGRESS(p_data^.p_bar),
active(GTK_TOGGLE_BUTTON(widget)^));
END;
{ --------------------------------toggle_show_text---------------------------- }
{ --------------------------------toggle_activity_mode------------------------ }
PROCEDURE toggle_activity_mode( widget : pGtkWidget ;
p_data : ptProgressData );
{ Callback that toggles the activity mode of the progress bar }
BEGIN
gtk_progress_set_activity_mode(GTK_PROGRESS(p_data^.p_bar),
active(GTK_TOGGLE_BUTTON(widget)^));
END;
{ ---------------------------------toggle_activity_mode---------------------------- }
{ -------------------------------set_continuous_mode---------------------------- }
PROCEDURE set_continuous_mode( widget : pGtkWidget ;
p_data : ptProgressData );
{ Callback that toggles the continuous mode of the progress bar }
BEGIN
gtk_progress_bar_set_bar_style(GTK_PROGRESS_BAR(p_data^.p_bar),
GTK_PROGRESS_CONTINUOUS);
END;
{ --------------------------------set_continuous_mode---------------------------- }
{ ---------------------------set_discrete_mode------------------------- }
PROCEDURE set_discrete_mode( widget : pGtkWidget ;
p_data : ptProgressData );
{ Callback that toggles the discrete mode of the progress bar }
BEGIN
gtk_progress_bar_set_bar_style(GTK_PROGRESS_BAR(p_data^.p_bar),
GTK_PROGRESS_DISCRETE);
END;
{ ------------------------------set_discrete_mode---------------------------- }
{ ----------------------------destroy_progress------------------------- }
PROCEDURE destroy_progress( widget : pGtkObject ;
p_data : ptProgressData ); cdecl;
{ Clean up allocated memory and remove the timer }
BEGIN
gtk_timeout_remove(p_data^.timer);
p_data^.timer := 0;
p_data^.window := NIL;
dispose(p_data);
gtk_main_quit();
END;
{ ------------------------------destroy_progress---------------------------- }
{ -----------------------------------Global Variables---------------------------- }
VAR
p_data : ptProgressData;
align, separator, table, button, check, vbox : pGtkWidget;
adj : pGtkObject;
{ ----------------------------------Main Program---------------------------- }
BEGIN
gtk_init(@argc, @argv);
p_data := NEW(pTProgressData);
{ Allocate memory for the data that is passed to the callbacks }
WITH p_data^ DO
BEGIN
window := pGtkWindow(gtk_window_new(GTK_WINDOW_TOPLEVEL));
gtk_window_set_policy(GTK_WINDOW(window), 0, 0, 1);
gtk_signal_connect(pGtkObject(window), 'destroy',
GTK_SIGNAL_FUNC(@destroy_progress), p_data);
gtk_window_set_title(GTK_WINDOW(window),
'GtkProgressBar');
gtk_container_set_border_width(GTK_CONTAINER(window), 0);
vbox := gtk_vbox_new(FALSE, 5);
gtk_container_set_border_width(GTK_CONTAINER(vbox), 10);
gtk_container_add(GTK_CONTAINER(window), vbox);
gtk_widget_show(vbox);
align := gtk_alignment_new(0.5, 0.5, 0, 0);
{ Create a centering alignment object }
gtk_box_pack_start(GTK_BOX(vbox), align, FALSE,
FALSE, 5);
gtk_widget_show(align);
{ Create a Adjusment object to hold the range of the progress bar }
adj := gtk_adjustment_new(0, 1, 150, 0, 0, 0);
{ Create the GtkProgressBar using the new adjustment }
p_bar := gtk_progress_bar_new_with_adjustment(GTK_ADJUSTMENT(adj));
{ Set the format of the string that can be displayed in the trough of the
progress bar:
%p - percentage, %v - value, %l - lower range value, %u - upper range value }
gtk_progress_set_format_string(GTK_PROGRESS(p_bar),
'%v from [%l-%u] (=%p%%)');
gtk_container_add(GTK_CONTAINER(align), p_bar);
gtk_widget_show(p_bar);
{ Add a timer callback to update the value of the progress bar }
timer := gtk_timeout_add(100, @progress_timeout, p_bar);
separator := gtk_hseparator_new();
gtk_box_pack_start(GTK_BOX(vbox), separator, FALSE,
FALSE, 0);
gtk_widget_show(separator);
table := gtk_table_new(2, 3, FALSE);
{ rows, columns, homogeneous }
gtk_box_pack_start(GTK_BOX(vbox), table, FALSE,
TRUE, 0);
gtk_widget_show(table);
{ Add a check button to select displaying of the trough text }
check := gtk_check_button_new_with_label('Show text');
gtk_table_attach(GTK_TABLE(table), check, 0, 1, 0, 1,
GTK_EXPAND OR GTK_FILL,
GTK_EXPAND OR GTK_FILL, 5, 5);
gtk_signal_connect(GTK_OBJECT(check), 'clicked',
GTK_SIGNAL_FUNC(@toggle_show_text), p_data);
gtk_widget_show(check);
{ Add a check button to toggle activity mode }
check := gtk_check_button_new_with_label('Activity mode');
gtk_table_attach(GTK_TABLE(table), check, 0, 1, 1, 2,
GTK_EXPAND OR GTK_FILL,
GTK_EXPAND OR GTK_FILL, 5, 5);
gtk_signal_connect(GTK_OBJECT(check), 'clicked',
GTK_SIGNAL_FUNC(@toggle_activity_mode), p_data);
gtk_widget_show(check);
separator := gtk_vseparator_new();
gtk_table_attach(GTK_TABLE(table), separator, 1, 2, 0, 2,
GTK_EXPAND OR GTK_FILL,
GTK_EXPAND OR GTK_FILL, 5, 5);
gtk_widget_show(separator);
{ Add a radio button to select continuous display mode }
button := gtk_radio_button_new_with_label(NIL,
'Continuous');
gtk_table_attach(GTK_TABLE(table), button, 2, 3, 0, 1,
GTK_EXPAND OR GTK_FILL,
GTK_EXPAND OR GTK_FILL, 5, 5);
gtk_signal_connect(GTK_OBJECT(button), 'clicked',
GTK_SIGNAL_FUNC(@set_continuous_mode), p_data);
gtk_widget_show(button);
{ Add a radio button to select discrete display mode }
button := gtk_radio_button_new_with_label(
gtk_radio_button_group(GTK_RADIO_BUTTON(button)),
'Discrete');
gtk_table_attach(GTK_TABLE(table), button, 2, 3, 1, 2,
GTK_EXPAND OR GTK_FILL,
GTK_EXPAND OR GTK_FILL, 5, 5);
gtk_signal_connect(GTK_OBJECT(button), 'clicked',
GTK_SIGNAL_FUNC(@set_discrete_mode), p_data);
gtk_widget_show(button);
separator := gtk_hseparator_new();
gtk_box_pack_start(GTK_BOX(vbox), separator, FALSE,
FALSE, 0);
gtk_widget_show(separator);
{ Add a button to exit the program }
button := gtk_button_new_with_label('close');
gtk_signal_connect_object(GTK_OBJECT(button),
'clicked',
GTK_SIGNAL_FUNC(@gtk_widget_destroy),
GTK_OBJECT(window));
gtk_box_pack_start(GTK_BOX(vbox), button, FALSE,
FALSE, 0);
{ This makes it so the button is the default. }
GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
{ This grabs this button to be the default button. Simply hitting the Enter
key will cause this button to activate. }
gtk_widget_grab_default(button);
gtk_widget_show(button);
gtk_widget_show(pGtkWidget(window));
gtk_main();
END;
{ ------------------------------WITH p_data^ DO---------------------------- }
END.
{ -------------------------------------Main Program---------------------------- }
[Previous] [Contents] [Next]
The Dialog widget is very simple, and is actually just a window with a few things pre-packed into it for you.
The structure of a Dialog is:
TYPE
GtkDialog = RECORD
window : GtkWindow;
vbox : pGtkWidget;
action_area : pGtkWidget;
END;
So you see, it simply creates a window, and then packs a vbox into the top, which contains a separator and then an hbox
called the action_area.
The Dialog widget can be used for pop-up messages to the user, and other similar tasks. It is really basic, and there
is only one function for the dialog box, which is:
FUNCTION gtk_dialog_new() : pGtkWidget;
So to create a new dialog box, use,
VAR
window : pGtkWidget;
BEGIN
window := gtk_dialog_new();
END;
This will create the dialog box, and it is now up to you to use it. You could pack a button in the action_area
by doing something like this:
button:= ...
gtk_box_pack_start(GTK_BOX(GTK_DIALOG(window)^.action_area), button,
TRUE, TRUE, 0);
gtk_widget_show(button);
And you could add to the vbox area by packing, for instance, a label in it, try something like this:
a_label := gtk_label_new('Dialogs are groovy');
gtk_box_pack_start(GTK_BOX(GTK_DIALOG(window)^.vbox), a_label,
TRUE, TRUE, 0);
gtk_widget_show(a_label);
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 doesn'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.
[Previous] [Contents] [Next]
Pixmaps are data structures that contain pictures. These pictures can be used in various places, but most commonly as
icons on the X desktop, or as cursors.
A pixmap which only has two 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 window system works. Under X, 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
with GTK currently, using constructs such as Pixmaps will make your programs work acceptably under X.
To use pixmaps in GTK, we must first build a GdkPixmap structure using routines from the GDK layer. Pixmaps can
either be created from in-memory data, or from data read from a file. We'll go through each of the calls to create a
pixmap.
FUNCTION gdk_bitmap_create_from_data( window :
pGdkWindow ; data : pgchar ;
width : gint ;
height : gint ) : pGdkPixmap;
This routine is used to create a single-plane pixmap (two colors) from data in memory. Each bit of the data represents
whether that pixel is off or on. Width and height are in pixels. The GdkWindow pointer is to the current window,
since a pixmap's resources are meaningful only in the context of the screen where it is to be displayed.
FUNCTION gdk_pixmap_create_from_data( window :
pGdkWindow ; data : pgchar ;
width : gint ;
height : gint ; depth : gint ;
fg : pGdkColor ;
bg : pGdkColor ) : pGdkPixmap;
This is used to create a pixmap of the given depth (number of colours) from the bitmap data specified. fg and
bg are the foreground and background colour to use.
FUNCTION gdk_pixmap_create_from_xpm( window :
pGdkWindow ; mask^ pGdkBitmap ;
transparent_colour : pGdkColor ;
filename : pgchar ) : pGdkPixmap;
The XPM format is a readable pixmap representation for the X Window System. It is widely used and many different
utilities are available for creating image files in this format. The file specified by filename must contain an
image in that format and it is loaded into the pixmap structure. The mask specifies which bits of the pixmap are opaque.
All other bits are coloured using the colour specified by transparent_colour. An example using this follows below.
FUNCTION gdk_pixmap_create_from_xpm_d( window :
pGdkWindow ; mask^ pGdkBitmap ;
transparent_colour : pGdkColor ;
data^ pgchar ) : pGdkPixmap;
Small images can be incorporated into a program as data in the XPM format. A pixmap is created using this data, instead of
reading it from a file. An example of such data is
{ XPM data }
CONST
xpm_data : ARRAY[0..19] OF pchar =
( '16 16 3 1',
' c None',
'. c #000000000000',
'X c #FFFFFFFFFFFF',
' ',
' ...... ',
' .XXX.X. ',
' .XXX.XX. ',
' .XXX.XXX. ',
' .XXX..... ',
' .XXXXXXX. ',
' .XXXXXXX. ',
' .XXXXXXX. ',
' .XXXXXXX. ',
' .XXXXXXX. ',
' .XXXXXXX. ',
' .XXXXXXX. ',
' ......... ',
' ',
' ' );
You should see the resemblence between the above and the image it produces on this button:

This is the pixmap button produced by the example program that follows shortly.
When we're done using a pixmap and not likely to reuse it again soon, it is a good idea to release the resource using
gdk_pixmap_unref(). Pixmaps should be considered a precious resource, because they take up
memory in the end-user's X server process. Even though the X client you write may run on a powerful server
computer, the user may be running the X server on a small personal computer.
Once we've created a pixmap, we can display it as a GTK widget. We must create a GTK pixmap widget to contain the GDK
pixmap. This is done using
FUNCTION gtk_pixmap_new( pixmap :
pGdkPixmap ; mask : pGdkPixmap ) :
pGtkWidget;
The other pixmap widget calls are
FUNCTION gtk_pixmap_get_type() :
guint;
PROCEDURE gtk_pixmap_set( pixmap :
pGdkPixmap ; val : pGdkPixmap ;
mask : pGdkPixmap );
PROCEDURE gtk_pixmap_get( pixmap :
pGdkPixmap ; val^ : pGdkPixmap ;
mask^ : pGdkPixmap );
gtk_pixmap_set() is used to change the pixmap that the widget is currently managing.
val is the pixmap created using GDK.
The following is an example of using a pixmap in a button.
{ Converted from C to Pascal by Frank Loemker <floemker@techfak.uni-bielefeld.de> }
PROGRAM pixmap;
USES glib, gdk, gtk;
{ XPM data of Open-File icon }
CONST
xpm_data : ARRAY[0..19] OF
pchar =
( '16 16 3 1',
' c None',
'. c #000000000000',
'X c #FFFFFFFFFFFF',
' ',
' ...... ',
' .XXX.X. ',
' .XXX.XX. ',
' .XXX.XXX. ',
' .XXX..... ',
' .XXXXXXX. ',
' .XXXXXXX. ',
' .XXXXXXX. ',
' .XXXXXXX. ',
' .XXXXXXX. ',
' .XXXXXXX. ',
' .XXXXXXX. ',
' ......... ',
' ',
' ' );
{ -------------------------------close_application----------------------------- }
PROCEDURE close_application( widget : pGtkWidget ;
event : pGdkEvent ;
data : pgpointer );
cdecl;
{ when invoked (via signal delete_event), terminates the application. }
BEGIN
gtk_main_quit();
END;
{ ---------------------------------close_application----------------------------}
{ -------------------------------button_clicked------------------------------- }
PROCEDURE button_clicked( widget : pGtkWidget ;
data : pgpointer ); cdecl;
{ is invoked when the button is clicked. It just prints a message. }
BEGIN
writeln( 'button clicked' );
END;
{ ------ ------------------------button_clicked------------------------------- }
{ ---------------------------Global Variables--------------------------------- }
VAR
window, pixmapwid, button : pGtkWidget;
{ GtkWidget is the storage type for widgets }
thepixmap : pGdkPixmap;
mask : pGdkBitmap;
style : pGtkStyle;
{ ---------------------------------Main Program--------------------------------- }
BEGIN
{ create the main window, and attach delete_event signal to terminating the application }
gtk_init( @argc, @argv );
gtk_rc_init;
window := gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_signal_connect(pGTKOBJECT(window), 'delete_event',
GTK_SIGNAL_FUNC(@close_application), NIL );
gtk_container_set_border_width(pGTKCONTAINER(window), 10 );
gtk_widget_show(window);
{ now for the pixmap from gdk }
style := gtk_widget_get_style(window);
thepixmap := gdk_pixmap_create_from_xpm_d( window^.window, @mask,
@style^.bg[GTK_STATE_NORMAL], ppgchar(xpm_data));
{ a pixmap widget to contain the pixmap }
pixmapwid := gtk_pixmap_new(thepixmap, mask);
gtk_widget_show(pixmapwid);
{ a button to contain the pixmap widget }
button := gtk_button_new();
gtk_container_add(pGTKCONTAINER(button), pixmapwid);
gtk_container_add(pGTKCONTAINER(window), button);
gtk_widget_show(button);
gtk_signal_connect(pGTKOBJECT(button), 'clicked',
GTK_SIGNAL_FUNC(@button_clicked), NIL );
{ show the window }
gtk_widget_show(window);
gtk_main();
END.
{ ----------------------------------Main Program-------------------------------- }
To load a file from an XPM data file called icon0.xpm in the current directory, we would have created the pixmap thus
{load a pixmap from a file}
pixmap := gdk_pixmap_create_from_xpm(window^.window, @mask,
@style^.bg[GTK_STATE_NORMAL], './icon0.xpm' );
pixmapwid := gtk_pixmap_new( pixmap, mask );
gtk_widget_show( pixmapwid );
gtk_container_add(GTK_CONTAINER(window), pixmapwid);
A disadvantage of using pixmaps is that the displayed object is always rectangular, regardless of the image. We would like
to create desktops and applications with icons that have more natural shapes. For example, for a game interface, we would
like to have round buttons to push. The way to do this is using shaped windows.
A shaped window is simply a pixmap where the background pixels are transparent. This way, when the background image is
multi-coloured, we don't overwrite it with a rectangular, non-matching border around our icon. The following example
displays a full wheelbarrow image on the desktop. This is what it looks like on a white desktop:
PROGRAM wheelbarrow;
USES
glib, gdk, gtk;
{ XPM data of Open-File icon }
CONST
WheelbarrowFull_xpm : array[0..112] of
pchar =
('48 48 64 1',
' c None',
'. c #DF7DCF3CC71B',
'X c #965875D669A6',
'o c #71C671C671C6',
'O c #A699A289A699',
'+ c #965892489658',
'@ c #8E38410330C2',
'# c #D75C7DF769A6',
'$ c #F7DECF3CC71B',
'% c #96588A288E38',
'& c #A69992489E79',
'* c #8E3886178E38',
'= c #104008200820',
'- c #596510401040',
'; c #C71B30C230C2',
': c #C71B9A699658',
'> c #618561856185',
', c #20811C712081',
'< c #104000000000',
'1 c #861720812081',
'2 c #DF7D4D344103',
'3 c #79E769A671C6',
'4 c #861782078617',
'5 c #41033CF34103',
'6 c #000000000000',
'7 c #49241C711040',
'8 c #492445144924',
'9 c #082008200820',
'0 c #69A618611861',
'q c #B6DA71C65144',
'w c #410330C238E3',
'e c #CF3CBAEAB6DA',
'r c #71C6451430C2',
't c #EFBEDB6CD75C',
'y c #28A208200820',
'u c #186110401040',
'i c #596528A21861',
'p c #71C661855965',
'a c #A69996589658',
's c #30C228A230C2',
'd c #BEFBA289AEBA',
'f c #596545145144',
'g c #30C230C230C2',
'h c #8E3882078617',
'j c #208118612081',
'k c #38E30C300820',
'l c #30C2208128A2',
'z c #38E328A238E3',
'x c #514438E34924',
'c c #618555555965',
'v c #30C2208130C2',
'b c #38E328A230C2',
'n c #28A228A228A2',
'm c #41032CB228A2',
'M c #104010401040',
'N c #492438E34103',
'B c #28A2208128A2',
'V c #A699596538E3',
'C c #30C21C711040',
'Z c #30C218611040',
'A c #965865955965',
'S c #618534D32081',
'D c #38E31C711040',
'F c #082000000820',
' ',
' .XoO ',
' +@#$%o& ',
' *=-;#::o+ ',
' >,<12#:34 ',
' 45671#:X3 ',
' +89<02qwo ',
'e* >,67;ro ',
'ty> 459@>+&& ',
'$2u+ ><ipas8* ',
'%$;=* *3:.Xa.dfg> ',
'Oh$;ya *3d.a8j,Xe.d3g8+ ',
' Oh$;ka *3d$a8lz,,xxc:.e3g54 ',
' Oh$;kO *pd$%svbzz,sxxxxfX..&wn> ',
' Oh$@mO *3dthwlsslszjzxxxxxxx3:td8M4 ',
' Oh$@g& *3d$XNlvvvlllm,mNwxxxxxxxfa.:,B* ',
' Oh$@,Od.czlllllzlmmqV@V#V@fxxxxxxxf:%j5& ',
' Oh$1hd5lllslllCCZrV#r#:#2AxxxxxxxxxcdwM* ',
' OXq6c.%8vvvllZZiqqApA:mq:Xxcpcxxxxxfdc9* ',
' 2r<6gde3bllZZrVi7S@SV77A::qApxxxxxxfdcM ',
' :,q-6MN.dfmZZrrSS:#riirDSAX@Af5xxxxxfevo',
' +A26jguXtAZZZC7iDiCCrVVii7Cmmmxxxxxx%3g',
' *#16jszN..3DZZZZrCVSA2rZrV7Dmmwxxxx&en',
' p2yFvzssXe:fCZZCiiD7iiZDiDSSZwwxx8e*>',
' OA1<jzxwwc:$d%NDZZZZCCCZCCZZCmxxfd.B ',
' 3206Bwxxszx%et.eaAp77m77mmmf3&eeeg* ',
' @26MvzxNzvlbwfpdettttttttttt.c,n& ',
' *;16=lsNwwNwgsvslbwwvccc3pcfu<o ',
' p;<69BvwwsszslllbBlllllllu<5+ ',
' OS0y6FBlvvvzvzss,u=Blllj=54 ',
' c1-699Blvlllllu7k96MMMg4 ',
' *10y8n6FjvllllB<166668 ',
' S-kg+>666<M<996-y6n<8* ',
' p71=4 m69996kD8Z-66698&& ',
' &i0ycm6n4 ogk17,0<6666g ',
' N-k-<> >=01-kuu666> ',
' ,6ky& &46-10ul,66, ',
' Ou0<> o66y<ulw<66& ',
' *kk5 >66By7=xu664 ',
' <<M4 466lj<Mxu66o ',
' *>> +66uv,zN666* ',
' 566,xxj669 ',
' 4666FF666> ',
' >966666M ',
' oM6668+ ',
' *4 ',
' ',
' ');
{ --------------------------------------------------------------close_application---------------------------------------------------------}
PROCEDURE close_application( widget : pGtkWidget ;
event : pGdkEvent ;
data : pgpointer ); cdecl;
{ when invoked (via signal delete_event), terminates the application. }
BEGIN
gtk_main_quit();
END;
{ --------------------------------------------------------------close_application---------------------------------------------------------}
{ --------------------------------------------------------------Global Variables---------------------------------------------------------}
VAR
window, pixmap, fixed : pGtkWidget;
{ GtkWidget is the storage type for widgets }
gdk_pixmap : pGdkPixmap;
mask : pGdkBitmap;
style : pGtkStyle;
{ ---------------------------------------------------------------------Main Program-------------------------------------------------------------------------- }
BEGIN
{ Create the main window, and attach delete_event signal to terminate the application.
Note that the main window will not have a titlebar since we're making it a popup.}
gtk_init(@argc, @argv);
gtk_rc_init;
window := gtk_window_new(GTK_WINDOW_POPUP);
gtk_signal_connect(pGTKOBJECT(window), 'delete_event',
GTK_SIGNAL_FUNC(@close_application), NIL );
gtk_widget_show(window);
{ Now for the pixmap and the pixmap widget}
style := gtk_widget_get_default_style();
gdk_pixmap := gdk_pixmap_create_from_xpm_d(window^.window,
@mask, @style^.bg[GTK_STATE_NORMAL], WheelbarrowFull_xpm);
pixmap := gtk_pixmap_new(gdk_pixmap, mask);
gtk_widget_show(pixmap);
{ To display the pixmap, we use a fixed widget to place the pixmap }
fixed := gtk_fixed_new();
gtk_widget_set_usize(fixed, 200, 200);
gtk_fixed_put(GTK_FIXED(fixed), pixmap, 0, 0);
gtk_container_add(GTK_CONTAINER(window), fixed);
gtk_widget_show(fixed);
{ This masks out everything except for the image itself }
gtk_widget_shape_combine_mask(window, mask, 0, 0);
{ show the window }
gtk_widget_set_uposition(window, 20, 400);
gtk_widget_show(window);
gtk_main();
END.
{ ----------------------------------Main Program------------------------------- }
As it stands you'll need to terminate the program with a command like CTRL+C. To make the wheelbarrow image
sensitive, we could attach the button press event signal to make it do something. The following line would make the
picture sensitive to a mouse button being pressed and make the application terminate.
gtk_signal_connect( GTK_OBJECT(window), 'button_press_event',
GTK_SIGNAL_FUNC(@close_application), NIL );
[Previous] [Contents] [Next]
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. Horizontal and vertical rulers are created using
FUNCTION gtk_hruler_new() : pGtkWidget;
{ horizontal ruler }
FUNCTION gtk_vruler_new() : pGtkWidget;
{ vertical ruler }
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
PROCEDURE gtk_ruler_set_metric( ruler :
pGtkRuler ; metric : LONGINT );
The default measure is GTK_PIXELS.
gtk_ruler_set_metric( GTK_RULER(ruler), 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
PROCEDURE gtk_ruler_set_range( ruler :
pGtkRuler ; lower : gfloat ;
upper : gfloat ;
position : gfloat ;
max_size : gfloat );
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
gtk_ruler_set_range(GTK_RULER(vruler), 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
gtk_ruler_set_range(GTK_RULER(vruler), 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. To follow all mouse movements within a window area, we would use
gtk_signal_connect_object( GTK_OBJECT(area),
'motion_notify_event',
Gtk_Signal_Func(GTK_WIDGET_CLASS(GTK_OBJECT(ruler)^.klass)^.motion_notify_event),
GTK_OBJECT(ruler));
The following example creates a drawing area with a horizontal ruler above it and a vertical ruler to the left of it.
The size of the drawing area is 600 pixels wide by 400 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. The following example program produces an output like this:
{ Convertion from C to Pascal (plus little additions) by Artur Bac
<arturbac@@poczta.onet.pl> Reda Poland }
{$MODE objfpc}
{$H+}
{$S+}
{$HINTS ON}
{$ifdef win32}
{$define extdecl := stdcall;}
{$APPTYPE GUI}
{$endif}
{$ifdef linux}
{$define extdecl := cdecl;}
{$endif}
PROGRAM Rulers;
USES glib,gdk,gtk;
CONST
XSIZE = 600;
YSIZE = 400;
{--------------------close_application------------------}
FUNCTION close_application( widget : pGtkWidget ;
event : pGdkEvent ;
data : gpointer ) :
boolean; cdecl;
{--- This routine gets control when the close button is clicked ---}
BEGIN
gtk_main_quit();
close_application := false;
{ if something goes wrong we will know that gtk didn't quit }
END;
{--------------------close_application------------------}
{--------------------Global Variables-------------------}
VAR
window, table, area, hrule, vrule : pGtkWidget;
{------------------------------Main Program------------------}
BEGIN
{ Initialize GTK and create the main window }
gtk_set_locale();
gtk_init(@argc, @argv);
gtk_rc_init;
window := gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_signal_connect(GTK_OBJECT(window), 'delete_event',
GTK_SIGNAL_FUNC(@close_application), NIL);
gtk_container_set_border_width(GTK_CONTAINER(window), 10);
{ Create a table for placing the ruler and the drawing area }
table := gtk_table_new(3, 2, FALSE);
gtk_container_add(GTK_CONTAINER(window), table);
area := gtk_drawing_area_new();
gtk_drawing_area_size(Gtk_Drawing_Area(area), XSIZE, YSIZE);
gtk_table_attach(GTK_TABLE(table), area, 1, 2, 1, 2,
GTK_EXPAND OR GTK_FILL, GTK_FILL, 0, 0);
gtk_widget_set_events(area, GDK_POINTER_MOTION_MASK
OR GDK_POINTER_MOTION_HINT_MASK);
{ The horizontal ruler goes on top. As the mouse moves across the drawing
area, a motion_notify_event is passed to the appropriate event handler for the ruler. }
hrule := gtk_hruler_new();
gtk_ruler_set_metric(GTK_RULER(hrule), GTK_PIXELS);
gtk_ruler_set_range(GTK_RULER(hrule), 7, 13, 0, 20);
gtk_signal_connect_object(GTK_OBJECT(area), 'motion_notify_event',
GTK_SIGNAL_FUNC(GTK_WIDGET_CLASS(GTK_OBJECT(hrule)^.klass)^.motion_notify_event),
GTK_OBJECT(hrule));
gtk_table_attach( GTK_TABLE(table), hrule, 1, 2, 0, 1,
GTK_EXPAND
or GTK_SHRINK or GTK_FILL, GTK_FILL, 0, 0 );
{ The vertical ruler goes on the left. As the mouse moves across the drawing area,
a motion_notify_event is passed to the appropriate event handler for the ruler. }
vrule := gtk_vruler_new();
gtk_ruler_set_metric(GTK_RULER(vrule), GTK_PIXELS);
gtk_ruler_set_range(GTK_RULER(vrule), 0, YSIZE, 10, YSIZE);
gtk_signal_connect_object(GTK_OBJECT(area), 'motion_notify_event',
GTK_SIGNAL_FUNC(GTK_WIDGET_CLASS(GTK_OBJECT(vrule)^.klass)^.motion_notify_event),
GTK_OBJECT(vrule));
gtk_table_attach(GTK_TABLE(table), vrule, 0, 1, 1, 2, GTK_FILL,
GTK_EXPAND
OR GTK_SHRINK OR GTK_FILL, 0, 0);
{ Now show everything }
gtk_widget_show_all(window);
{ This will show all children of window }
gtk_main();
END.
{------------------------------Main Program------------------}
[Previous] [Contents] [Next]
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.
A statusbar is created with a call to:
FUNCTION gtk_statusbar_new() :
pGtkWidget;
A new Context Identifier is requested using a call to the following function with a short textual description of the
context:
FUNCTION gtk_statusbar_get_context_id(
statusbar : pGtkStatusbar ;
context_description : pgpointer )
: guint;
There are two procedures and one function that can operate on statusbars:
FUNCTION gtk_statusbar_push(
statusbar : pGtkStatusbar ; context_id : guint ;
text : pgpointer ) : guint;
PROCEDURE gtk_statusbar_pop(
statusbar : pGtkStatusbar ; context_id : guint );
PROCEDURE gtk_statusbar_remove(
statusbar : pGtkStatusbar ; context_id : guint ;
message_id : guint );
The first, gtk_statusbar_push(), is used to add a new message to the statusbar. It returns a
Message Identifier, which can be passed later to the function gtk_statusbar_remove() to remove
the message with the given Message and Context Identifiers from the statusbar's stack.
The function gtk_statusbar_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. When compiled and run it should look like this:
{ Convertion from C to Pascal (plus little additions) by
Artur Bac <arturbac@poczta.onet.pl> Reda Poland }
{$MODE objfpc}
{$H+}
{$S+}
{$HINTS ON}
{$ifdef win32}
{$define extdecl := stdcall;}
{$APPTYPE GUI}
{$endif}
{$ifdef linux}
{$define extdecl := cdecl;}
{$endif}
PROGRAM statusbar;
USES glib,gtk;
TYPE
pgint = ^gint;
VAR
status_bar : pGtkWidget;
count : LONGINT;
{----------------------------push_item--------------------------}
PROCEDURE push_item( widget : pGtkWidget ;
data : pgint ); cdecl;
VAR
buff : ansistring;
ptr_buff : pGChar;
BEGIN
Inc(count);
Str(count,buff);
buff := 'Item ' + buff;
ptr_buff := pChar(buff); { changing type from ansistring to pGChar == pChar }
gtk_statusbar_push(GTK_STATUSBAR(status_bar), data^, ptr_buff);
END;
{-------------------------------- push_item ------------------------- }
{----------------------------pop_item----------------------------}
PROCEDURE pop_item( widget : pGtkWidget;
data : pgint); cdecl;
{ used pointer to gint, not gpointer becouse we can read value directly from an
address specified in data }
BEGIN
gtk_statusbar_pop(GTK_STATUSBAR(status_bar), data^);
END;
{-------------------------------- pop_item -------------------------- }
{------------------------------------Global Variables--------------------------------}
VAR
window, vbox, button1, button2 : pGtkWidget;
context_id : gint;
{-----------------------------------Main Program-------------------------------------}
BEGIN
gtk_set_locale();
gtk_init( @argc, @argv );
gtk_rc_init;
count := 1;
{ create a new window }
window := gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_widget_set_usize(GTK_WIDGET(window), 200, 100);
{ GTK_WIDGET == pGtkWidget }
gtk_window_set_title(GTK_WINDOW(window),
'GTK Statusbar Example');
vbox := gtk_vbox_new( FALSE, 1 );
gtk_container_add( GTK_CONTAINER(window), vbox );
status_bar := gtk_statusbar_new();
gtk_box_pack_start( GTK_BOX(vbox), status_bar, TRUE,
TRUE, 0 );
context_id := gtk_statusbar_get_context_id(GTK_STATUSBAR(status_bar),
'Statusbar example');
button1 := gtk_button_new_with_label('push item');
gtk_box_pack_start( GTK_BOX(vbox), button1, TRUE,
TRUE, 2 );
button2 := gtk_button_new_with_label( 'pop last item');
gtk_box_pack_start( GTK_BOX(vbox), button2, TRUE,
TRUE, 2);
gtk_signal_connect(GTK_OBJECT(window), 'delete_event',
GTK_SIGNAL_FUNC(@gtk_exit), NIL);
gtk_signal_connect(GTK_OBJECT(button1), 'clicked',
GTK_SIGNAL_FUNC(@push_item), @context_id );
gtk_signal_connect(GTK_OBJECT(button2), 'clicked',
GTK_SIGNAL_FUNC(@pop_item), @context_id );
gtk_widget_show_all(window);
gtk_main();
END.
{-------------------------------------Main Program------------------------------------}
[Previous] [Contents] [Next]
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 functions for creating Entry widgets:
FUNCTION gtk_entry_new() :
pGtkWidget;
FUNCTION gtk_entry_new_with_max_length( max :
guint16 ) : pGtkWidget;
The first just creates a new Entry widget, whilst the second creates a new Entry and sets a limit on the
length of the text within the Entry.
There are several functions for altering the text which is currently within the Entry widget.
PROCEDURE gtk_entry_set_text( entry :
pGtkEntry ; sometext : gchar);
PROCEDURE gtk_entry_append_text( entry :
pGtkEntry ; sometext : gchar);
PROCEDURE gtk_entry_prepend_text( entry :
pGtkEntry ; sometext : gchar);
The function gtk_entry_set_text() sets the contents of the Entry widget, replacing the
current contents. The functions gtk_entry_append_text() and
gtk_entry_prepend_text() allow the current contents to be appended and prepended to.
The next function allows the current insertion point to be set.
PROCEDURE gtk_entry_set_position( entry :
pGtkEntry ; position : gint);
The contents of the Entry can be retrieved by using a call to the following function. This is useful in the
callbacks described below.
FUNCTION gtk_entry_get_text( entry :
pGtkEntry ) : pgchar;
The value returned by this function is used internally, and must not be freed using dispose().
If we don't want the contents of the Entry to be changed by someone typing into it, we can change its editable state.
PROCEDURE gtk_entry_set_editable( entry :
pGtkEntry ; editable : gboolean );
The function 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 function, which also takes a boolean argument.
PROCEDURE gtk_entry_set_visibility( entry :
pGtkEntry ; visible : gboolean );
A region of the text may be set as selected by using the following function. This would most often be used after setting
some default text in an Entry, making it easy for the user to remove it.
PROCEDURE gtk_entry_select_region( entry :
pGtkEntry ; start : gint ;
end : gint );
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. Compiled and run it should look like this:
{ Converted from C to Pascal by Frank Loemker <floemker@techfak.uni-bielefeld.de> }
PROGRAM entry;
USES glib, gdk, gtk;
{-------------------------------enter_callback-------------------------}
PROCEDURE enter_callback( widget, entry : pGtkWidget);
cdecl;
VAR
entry_text : pgchar;
BEGIN
entry_text := gtk_entry_get_text(pGtkEntry(entry));
writeln('Entry contents: ', entry_text);
END;
{--------------------------enter_callback-------------------------}
{-------------------------------entry_toggle_editable------------------------}
PROCEDURE entry_toggle_editable( checkbutton, entry:
pGtkWidget ); cdecl;
BEGIN
gtk_entry_set_editable(pGtkEntry(entry),
gboolean(active(pGtkToggleButton(checkbutton)^)));
END;
{---------------------------entry_toggle_editable----------------------}
{--------------------------------entry_toggle_visibility---------------}
PROCEDURE entry_toggle_visibility( checkbutton, entry :
pGtkWidget ); cdecl;
BEGIN
gtk_entry_set_visibility(pGtkENTRY(entry),
gboolean(active(PGtkToggleButton(checkbutton)^)));
END;
{---------------------------entry_toggle_visibility---------------------}
{--------------------------------Global Variables-----------------------}
VAR
window, vbox, hbox, fentry, button, check : pGtkWidget;
{--------------------------------Main Program---------------------------}
BEGIN
gtk_init( @argc, @argv );
{ create a new window }
window := gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_widget_set_usize(pGtkWIDGET(window), 200, 100);
gtk_window_set_title(pGtkWINDOW(window), 'Gtk Entry');
gtk_signal_connect(pGtkOBJECT(window), 'delete_event',
GTK_SIGNAL_FUNC(@gtk_exit), NIL);
vbox := gtk_vbox_new(FALSE, 0);
gtk_container_add(pGtkCONTAINER(window), vbox);
gtk_widget_show(vbox);
fentry := gtk_entry_new_with_max_length(50);
gtk_signal_connect(pGtkOBJECT(fentry), 'activate',
GTK_SIGNAL_FUNC(@enter_callback), fentry);
gtk_entry_set_text(pGtkENTRY(fentry), 'hello');
gtk_entry_append_text(pGtkENTRY(fentry), ' world');
gtk_entry_select_region(pGtkENTRY(fentry), 0, pGtkENTRY(fentry)^.text_length);
gtk_box_pack_start(pGtkBOX(vbox), fentry, TRUE,
TRUE, 0);
gtk_widget_show(fentry);
hbox := gtk_hbox_new(FALSE, 0);
gtk_container_add(pGtkCONTAINER(vbox), hbox);
gtk_widget_show(hbox);
check := gtk_check_button_new_with_label('Editable');
gtk_box_pack_start(pGtkBOX(hbox), check, TRUE,
TRUE, 0);
gtk_signal_connect(pGtkOBJECT(check), 'toggled',
GTK_SIGNAL_FUNC(@entry_toggle_editable), fentry);
gtk_toggle_button_set_active(pGtkTOGGLEBUTTON(check), TRUE);
gtk_widget_show(check);
check := gtk_check_button_new_with_label('Visible');
gtk_box_pack_start(pGtkBOX(hbox), check, TRUE,
TRUE, 0);
gtk_signal_connect(pGtkOBJECT(check), 'toggled',
GTK_SIGNAL_FUNC(@entry_toggle_visibility), fentry);
gtk_toggle_button_set_active(pGtkTOGGLEBUTTON(check), TRUE);
gtk_widget_show(check);
button := gtk_button_new_with_label('Close');
gtk_signal_connect_object(pGtkOBJECT(button), 'clicked',
GTK_SIGNAL_FUNC(@gtk_exit), pGtkOBJECT(window));
gtk_box_pack_start(pGtkBOX(vbox), button, TRUE,
TRUE, 0);
GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
gtk_widget_grab_default(button);
gtk_widget_show(button);
gtk_widget_show(window);
gtk_main();
END.
{ ------------------------Main Program----------------------- }
[Previous] [Contents] [Next]
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 results 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 function, which illustrates the information that it holds:
FUNCTION gtk_adjustment_new( value :
gfloat ; lower : gfloat ; upper :
gfloat ;
step_increment : gfloat ;
page_increment : gfloat ;
page_size : gfloat ) :
pGtkObject;
These attributes of an Adjustment are used by the Spin Button in the following way:
| value: | initial value for the Spin Button |
| lower: | lower range value |
| upper: | upper range value |
| step_increment: |
value to increment/decrement when pressing mouse button 1 on a button |
| page_increment: |
value to increment/decrement when pressing mouse button 2 on a button |
| page_size: | unused |
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 how to create a Spin Button:
FUNCTION gtk_spin_button_new(
adjustment : pGtkAdjustment ; climb_rate : gfloat ;
digits : guint )
: pGtkWidget
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 function:
PROCEDURE gtk_spin_button_configure(
spin_button : pGtkSpinButton ;
adjustment : pGtkAdjustment ;
climb_rate : gfloat ; digits : guint );
The spin_button argument specifies the Spin Button widget that is to be reconfigured. The other arguments are as
specified above.
The adjustment can be set and retrieved independently using the following procedure and function:
PROCEDURE gtk_spin_button_set_adjustment(
spin_button : pGtkSpinButton ;
adjustment : pGtkAdjustment );
FUNCTION gtk_spin_button_get_adjustment(
spin_button : pGtkSpinButton )
: pGtkAdjustment;
The number of decimal places can also be altered using:
PROCEDURE gtk_spin_button_set_digits(
spin_button : pGtkSpinButton ;
digits : guint ) ;
The value that a Spin Button is currently displaying can be changed using the following procedure:
PROCEDURE gtk_spin_button_set_value(
spin_button : pGtkSpinButton ;
value : gfloat );
The current value of a Spin Button can be retrieved as either a floating point or integer value with the following
functions:
FUNCTION gtk_spin_button_get_value_as_float(
spin_button : pGtkSpinButton )
: gfloat;
FUNCTION gtk_spin_button_get_value_as_int(
spin_button : pGtkSpinButton ) : gint;
If you want to alter the value of a Spin Value relative to its current value, then the following function can be used:
PROCEDURE gtk_spin_button_spin(
spin_button : pGtkSpinButton ;
direction : LONGINT ;
increment : gfloat );
The direction parameter can take one of the following values:
| GTK_SPIN_STEP_FORWARD | GTK_SPIN_STEP_BACKWARD |
| GTK_SPIN_PAGE_FORWARD | GTK_SPIN_PAGE_BACKWARD |
| GTK_SPIN_HOME | GTK_SPIN_END |
| GTK_SPIN_USER_DEFINED | |
This function 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 the
Adjustment.
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 retrieving the range attributes of the Spin Button now, and move on to
procedures 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 numeric
value. This prevents a user from typing anything other than numeric values into the text box of a Spin Button:
PROCEDURE gtk_spin_button_set_numeric(
spin_button : pGtkSpinButton ;
numeric : gboolean );
You can set whether a Spin Button will wrap around between the upper and lower range values with the following procedure:
PROCEDURE gtk_spin_button_set_wrap(
spin_button : pGtkSpinButton ;
wrap : gboolean );
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:
PROCEDURE gtk_spin_button_set_snap_to_ticks(
spin_button : pGtkSpinButton ;
snap_to_ticks : gboolean );
The update policy of a Spin Button can be changed with the following procedure:
PROCEDURE gtk_spin_button_set_update_policy(
spin_button : pGtkSpinButton ;
policy : LONGINT );
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 its 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:
PROCEDURE gtk_spin_button_set_shadow_type(
spin_button : pGtkSpinButton ;
shadow_type : LONGINT );
As usual, the shadow_type can be one of:
| GTK_SHADOW_IN | GTK_SHADOW_OUT |
| GTK_SHADOW_ETCHED_IN | GTK_SHADOW_ETCHED_OUT |
Finally, you can explicitly request that a Spin Button update itself:
PROCEDURE gtk_spin_button_update(
spin_button : pGtkSpinButton );
It's example time again. The program produces the output shown, but it's not a static output. Clicking the up/down
arrows increases/decreases the numbers as you'd expect. Not accelerated means that you can't type the numbers in from
the keyboard. The number 57 at the bottom of the Accelerated box is the result of pressing the Value as Int
button for the value 56.500.
{ Conversion from C to Pascal (plus little additions) by Artur Bac
<arturbac@poczta.onet.pl> Reda Poland }
{$MODE objfpc}
{$H+}
{$S+}
{$HINTS ON}
{$ifdef win32}
{$define extdecl := stdcall;}
{$APPTYPE GUI}
{$endif}
{$ifdef linux}
{$define extdecl := cdecl;}
{$endif}
PROGRAM spinbuttons;
USES glib,gtk;
TYPE
pgint = ^gint;
CONST
a : gint = 1;
b : gint = 2;
VAR
spinner1, spinner2 : pGtkWidget;
{-------------------------------GPOINTER_TO_INT-----------------------------}
FUNCTION GPOINTER_TO_INT(data : pgint) :
gint;
BEGIN
GPOINTER_TO_INT := data^;
END;
{-------------------------------GPOINTER_TO_INT-----------------------------}
{------------------------------toggle_snap----------------------------------}
PROCEDURE toggle_snap( widget : pGtkWidget ;
spin : pGtkSpinButton ); cdecl;
BEGIN
gtk_spin_button_set_snap_to_ticks(spin,
gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)));
END;
{-------------------------------toggle_snap-----------------------------}
{-------------------------------toggle_numeric-----------------------------}
PROCEDURE toggle_numeric( widget : pGtkWidget ;
spin : pGtkSpinButton ); cdecl;
BEGIN
gtk_spin_button_set_numeric(spin,
gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)));
END;
{-------------------------------toggle_numeric-----------------------------}
{-------------------------------change_digits-----------------------------}
PROCEDURE change_digits( widget : pGtkWidget ;
spin : pGtkSpinButton ); cdecl;
BEGIN
gtk_spin_button_set_digits(GTK_SPIN_BUTTON(spinner1), gtk_spin_button_get_value_as_int(spin));
END;
{-------------------------------change_digits-----------------------------}
{-------------------------------get_value-----------------------------}
PROCEDURE get_value( widget : pGtkWidget ;
data : gpointer ) ; cdecl;
VAR
Ptr_buf : pGchar;
buf : string;
label_l : pGtkLabel;
spin,spin2 : pGtkSpinButton;
BEGIN
spin := GTK_SPIN_BUTTON(spinner1);
spin2 := GTK_SPIN_BUTTON(spinner2);
label_l := GTK_LABEL(gtk_object_get_user_data(GTK_OBJECT(widget)));
IF (GPOINTER_TO_INT(data) = 1) THEN
str(gtk_spin_button_get_value_as_int(spin), buf)
ELSE
str(gtk_spin_button_get_value_as_float(spin)
:10:gtk_spin_button_get_value_as_int(spin2), buf);
{ The gtk_spin_button_get_value_as_int()
part checks how many digits we have }
Ptr_buf := pChar(buf); { We have to change ansistring to a pointer to char pChar == pGChar }
gtk_label_set_text(label_l, Ptr_buf);
END;
{-------------------------------get_value-----------------------------}
{----------------------------Global Variables--------------------------------------}
VAR
window, frame, hbox, main_vbox, vbox, vbox2, spinner, button, label_l, val_label : pGtkWidget;
adj : pGtkAdjustment;
{----------------------------Main Program------------------------------------------}
BEGIN
{ Initialise GTK }
gtk_set_locale();
gtk_init( @argc, @argv );
gtk_rc_init;
window := gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_signal_connect(GTK_OBJECT(window), 'destroy',
GTK_SIGNAL_FUNC(@gtk_main_quit), NIL );
gtk_window_set_title(GTK_WINDOW(window), 'Spin Button');
main_vbox := gtk_vbox_new(FALSE, 5);
gtk_container_set_border_width(GTK_CONTAINER(main_vbox), 10);
gtk_container_add(GTK_CONTAINER(window), main_vbox);
frame := gtk_frame_new('Not accelerated');
gtk_box_pack_start(GTK_BOX(main_vbox), frame, TRUE,
TRUE, 0);
vbox := gtk_vbox_new(FALSE, 0);
gtk_container_set_border_width(GTK_CONTAINER(vbox), 5);
gtk_container_add(GTK_CONTAINER(frame), vbox);
{ Day, month, year spinners }
hbox := gtk_hbox_new(FALSE, 0);
gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE,
TRUE, 5);
vbox2 := gtk_vbox_new(FALSE, 0);
gtk_box_pack_start(GTK_BOX(hbox), vbox2, TRUE,
TRUE, 5);
label_l := gtk_label_new('Day :');
gtk_misc_set_alignment(GTK_MISC(label_l), 0, 0.5);
gtk_box_pack_start(GTK_BOX(vbox2), label_l, FALSE,
TRUE, 0);
adj := pGtkAdjustment(gtk_adjustment_new(1.0, 1.0, 31.0, 1.0, 5.0, 0.0));
spinner := gtk_spin_button_new(adj, 0, 0);
gtk_spin_button_set_wrap(GTK_SPIN_BUTTON(spinner), TRUE);
gtk_spin_button_set_shadow_type(GTK_SPIN_BUTTON(spinner), GTK_SHADOW_OUT);
gtk_box_pack_start(GTK_BOX(vbox2), spinner, FALSE,
TRUE, 0);
vbox2 := gtk_vbox_new(FALSE, 0);
gtk_box_pack_start(GTK_BOX (hbox), vbox2, TRUE,
TRUE, 5);
label_l := gtk_label_new('Month :');
gtk_misc_set_alignment(GTK_MISC(label_l), 0, 0.5);
gtk_box_pack_start(GTK_BOX(vbox2), label_l, FALSE,
TRUE, 0);
adj := pGtkAdjustment(gtk_adjustment_new(1.0, 1.0, 12.0, 1.0, 5.0, 0.0));
spinner := gtk_spin_button_new(adj, 0, 0);
gtk_spin_button_set_wrap(GTK_SPIN_BUTTON(spinner), TRUE);
gtk_spin_button_set_shadow_type(GTK_SPIN_BUTTON(spinner), GTK_SHADOW_ETCHED_IN);
gtk_box_pack_start(GTK_BOX(vbox2), spinner, FALSE,
TRUE, 0);
vbox2 := gtk_vbox_new(FALSE, 0);
gtk_box_pack_start(GTK_BOX(hbox), vbox2, TRUE,
TRUE, 5);
label_l := gtk_label_new('Year :');
gtk_misc_set_alignment(GTK_MISC(label_l), 0, 0.5);
gtk_box_pack_start(GTK_BOX(vbox2), label_l, FALSE,
TRUE, 0);
adj := pGtkAdjustment(gtk_adjustment_new(1998.0, 0.0, 2100.0, 1.0, 100.0, 0.0));
spinner := gtk_spin_button_new(adj, 0, 0);
gtk_spin_button_set_wrap(GTK_SPIN_BUTTON(spinner), FALSE);
gtk_spin_button_set_shadow_type(GTK_SPIN_BUTTON(spinner), GTK_SHADOW_IN);
gtk_widget_set_usize(spinner, 55, 0);
gtk_box_pack_start(GTK_BOX(vbox2), spinner, FALSE,
TRUE, 0);
frame := gtk_frame_new('Accelerated');
gtk_box_pack_start(GTK_BOX(main_vbox), frame, TRUE,
TRUE, 0);
vbox := gtk_vbox_new(FALSE, 0);
gtk_container_set_border_width(GTK_CONTAINER(vbox), 5);
gtk_container_add(GTK_CONTAINER(frame), vbox);
hbox := gtk_hbox_new(FALSE, 0);
gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE,
TRUE, 5);
vbox2 := gtk_vbox_new(FALSE, 0);
gtk_box_pack_start(GTK_BOX(hbox), vbox2, TRUE,
TRUE, 5);
label_l := gtk_label_new('Value :');
gtk_misc_set_alignment(GTK_MISC(label_l), 0, 0.5);
gtk_box_pack_start(GTK_BOX(vbox2), label_l, FALSE,
TRUE, 0);
adj := pGtkAdjustment(gtk_adjustment_new(0.0, -10000.0, 10000.0, 0.5, 100.0, 0.0));
spinner1 := gtk_spin_button_new(adj, 1.0, 2);
gtk_spin_button_set_wrap(GTK_SPIN_BUTTON(spinner1), TRUE);
gtk_widget_set_usize(spinner1, 100, 0);
gtk_box_pack_start(GTK_BOX(vbox2), spinner1, FALSE,
TRUE, 0);
vbox2 := gtk_vbox_new(FALSE, 0);
gtk_box_pack_start(GTK_BOX(hbox), vbox2, TRUE,
TRUE, 5);
label_l := gtk_label_new('Digits :');
gtk_misc_set_alignment(GTK_MISC(label_l), 0, 0.5);
gtk_box_pack_start(GTK_BOX(vbox2), label_l, FALSE,
TRUE, 0);
adj := pGtkAdjustment(gtk_adjustment_new(2, 1, 5, 1, 1, 0));
spinner2 := gtk_spin_button_new(adj, 0.0, 0);
gtk_spin_button_set_wrap(GTK_SPIN_BUTTON(spinner2), TRUE);
gtk_signal_connect(GTK_OBJECT(adj), 'value_changed',
GTK_SIGNAL_FUNC(@change_digits), gpointer(spinner2));
gtk_box_pack_start(GTK_BOX(vbox2), spinner2, FALSE,
TRUE, 0);
hbox := gtk_hbox_new(FALSE, 0);
gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE,
TRUE, 5);
button := gtk_check_button_new_with_label('Snap to 0.5-ticks');
gtk_signal_connect(GTK_OBJECT(button), 'clicked',
GTK_SIGNAL_FUNC(@toggle_snap), spinner1);
gtk_box_pack_start(GTK_BOX(vbox), button, TRUE,
TRUE, 0);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), TRUE);
button := gtk_check_button_new_with_label('Numeric only input mode');
gtk_signal_connect(GTK_OBJECT(button), 'clicked',
GTK_SIGNAL_FUNC(@toggle_numeric), spinner1);
gtk_box_pack_start(GTK_BOX(vbox), button, TRUE,
TRUE, 0);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), TRUE);
val_label := gtk_label_new('');
hbox := gtk_hbox_new(FALSE, 0);
gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE,
TRUE, 5);
button := gtk_button_new_with_label('Value as Int');
gtk_object_set_user_data(GTK_OBJECT(button), val_label);
gtk_signal_connect(GTK_OBJECT(button), 'clicked',
GTK_SIGNAL_FUNC(@get_value), @a);
gtk_box_pack_start(GTK_BOX(hbox), button, TRUE,
TRUE, 5);
button := gtk_button_new_with_label('Value as Float');
gtk_object_set_user_data(GTK_OBJECT(button), val_label);
gtk_signal_connect(GTK_OBJECT(button), 'clicked',
GTK_SIGNAL_FUNC(@get_value), @b);
gtk_box_pack_start(GTK_BOX(hbox), button, TRUE,
TRUE, 5);
gtk_box_pack_start(GTK_BOX(vbox), val_label, TRUE,
TRUE, 0);
gtk_label_set_text(GTK_LABEL(val_label), '0');
hbox := gtk_hbox_new(FALSE, 0);
gtk_box_pack_start(GTK_BOX(main_vbox), hbox, FALSE,
TRUE, 0);
button := gtk_button_new_with_label('Close');
gtk_signal_connect_object(GTK_OBJECT(button), 'clicked',
GTK_SIGNAL_FUNC(@gtk_widget_destroy), GTK_OBJECT(window));
gtk_box_pack_start(GTK_BOX(hbox), button, TRUE,
TRUE, 5);
gtk_widget_show_all(window);
{ Enter the event loop }
gtk_main();
END.
{----------------------------Main Program------------------------------------------}
[Previous] [Contents] [Next]
The combo box is another fairly simple widget that is really just a collection of other widgets. From the user's 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 following extract from the structure that defines a Combo Box identifies several of the components:
TYPE
tGtkCombo = RECORD
hbox : GtkHBox;
entry, button, popup, popwin, list : pGtkWidget;
END;
As you can see, the Combo Box has two principal parts that you really care about: an entry and a list.
First of all, to create a combo box, use:
FUNCTION gtk_combo_new() : pGtkWidget;
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:
gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(combo)^.entry),
'My String.');
To set the values in the popdown list, one uses the procedure:
PROCEDURE gtk_combo_set_popdown_strings( combo :
pGtkCombo ; strings : pGList );
Before you can do this, you have to assemble a GList of the strings that you want. GList is a linked list implementation
that is part of GLib, a library supporting GTK. For the moment, the quick and dirty explanation is that you need
to set up a GList pointer, set it equal to NIL, then append strings to it with
FUNCTION g_list_append( glist :
pGList ; data : pGPointer ) :
pGList;
It is important that you set the initial GList pointer to NIL. The value returned from the
g_list_append() function must be used as the new pointer to the GList. Here's a typical code
segment for creating a set of options:
VAR
glist : pGList;
BEGIN
glist := NIL;
glist := g_list_append(glist, 'String 1');
glist := g_list_append(glist, 'String 2');
glist := g_list_append(glist, 'String 3');
glist := g_list_append(glist, 'String 4');
gtk_combo_set_popdown_strings(GTK_COMBO(combo), glist);
END;
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:
PROCEDURE gtk_combo_set_use_arrows( combo :
pGtkCombo ; val : gint );
PROCEDURE gtk_combo_set_use_arrows_always( combo :
pGtkCombo ; val : gint );
PROCEDURE gtk_combo_set_case_sensitive( combo :
pGtkCombo ; val : gint );
gtk_combo_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
gtk_combo_set_use_arrows() is disabled.
gtk_combo_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.
gtk_combo_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 either 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, by the xmodmap utility. Note, however that some
window managers also use this key combination, which will override its 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 straightforward. The majority of the time, all you are going to care about getting data from
is the entry. The entry is accessed simply by
GTK_ENTRY(GTK_COMBO(combo)^.entry);
The two principal 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:
gtk_signal_connect(GTK_OBJECT(GTK_COMB(combo)^.entry), 'activate',
GTK_SIGNAL_FUNC(@my_callback_function), @my_data);
Getting the text at any arbitrary time is accomplished by simply using the entry function:
gtk_entry_get_text( entry : pGtkEntry ) :
pGchar;
Such as:
VAR
a_string : pchar;
BEGIN
a_string := gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(combo)^.entry));
END;
That's all about comboboxes. There is a procedure which is declared as following:
PROCEDURE gtk_combo_disable_activate( combo :
pGtkCombo );
It 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.
[Previous] [Contents] [Next]
The colour selection widget is, not surprisingly, a widget for interactive selection of colours. This composite widget
lets the user select a colour 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 colour can also be set.
The colour selection widget currently emits only one signal, color_changed, which is emitted whenever the current
colour in the widget changes, either when the user changes it or if it's set explicitly through
gtk_color_selection_set_color().
Lets have a look at what the colour selection widget has to offer us. The widget comes in two flavours:
gtk_color_selection() and gtk_color_selection_dialog().
FUNCTION gtk_color_selection_new() :
pGtkWidget;
You'll probably not be using this constructor directly. It creates an orphan ColorSelection widget which you'll have
to parent yourself. The ColorSelection widget inherits from the VBox widget.
FUNCTION gtk_color_selection_dialog_new( title :
pgchar ) : pGtkWidget;
This is the most common colour selection constructor. It creates a ColorSelectionDialog. It consists of a
Frame containing a ColorSelection widget, an HSeparator and an HBox with three buttons, OK,
Cancel and Help. You can reach these buttons by accessing the ok_button, cancel_button and
help_button widgets in the ColorSelectionDialog record, (i.e.,
GTK_COLOR_SELECTION_DIALOG(colorseldialog)^.ok_button)).
PROCEDURE gtk_color_selection_set_update_policy(
coloursel :
pGtkColorSelection ;
policy : LONGINT );
This function sets the update policy. The default policy is GTK_UPDATE_CONTINUOUS which means that the current colour
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.
PROCEDURE gtk_color_selection_set_opacity(
coloursel : pGtkColorSelection ;
use_opacity : gint );
The colour selection widget supports adjusting the opacity of a colour (also known as the alpha channel). This is disabled
by default. Calling this function with use_opacity set to TRUE enables opacity. Likewise,
use_opacity set to FALSE will disable opacity.
PROCEDURE gtk_color_selection_set_color(
coloursel : pGtkColorSelection ;
colour : pgdouble );
You can set the current colour explicitly by calling this function with a pointer to an array of colours
(gdouble). The length of the array depends on whether opacity is enabled or not. Position 0
contains the red component, 1 is green, 2 is blue and opacity is at position 3 (only if opacity is enabled, see
gtk_color_selection_set_opacity() ). All values are between 0.0 and 1.0.
PROCEDURE gtk_color_selection_get_color( colour_sel :
pGtkColorSelection ;
colour : pgdouble );
When you need to query the current colour, typically when you've received a color_changed signal, you use this
function. Colour is a pointer to the array of colours to fill in. See the
gtk_color_selection_set_color() function for the description of this array.
Here's a simple example demonstrating the use of the ColorSelectionDialog. The program displays a window containing
a drawing area. Clicking on it opens a colour selection dialog, and changing the colour in the colour selection dialog
changes the background colour.
{ ------------------------colour select-------------------- }
PROGRAM coloursel
{$mode objfpc}
{h+}
USES glib, gdk, gtk;
{ ------------------------------Global Variables------------------------- }
VAR
Window : pGtkWidget;
ColourSelDlg : pGtkWidget;
DrawingArea : pGtkWidget;
{ ----------------------------------------colour_changed-------------------------- }
PROCEDURE colour_changed( Widget : pGtkWidget ;
data : gpointer ); cdecl;
VAR
Colour : ARRAY[0..3] OF GDouble;
Result : pGdkColor;
ColourMap : pGdkColormap;
BEGIN
{ Get drawingarea colourmap }
ColourMap := gdk_window_get_colormap( DrawingArea^.Window );
{ Get current color }
gtk_color_selection_get_color(
GTK_COLOR_SELECTION(GTK_COLOR_SELECTION_DIALOG(ColourSelDlg)^.colorsel),
@Colour );
Result := NEW(pGdkColor);
{ Fit to a unsigned 16 bit integer (0..65535) and insert into the GdkColor structure}
WITH Result ^ DO
BEGIN
pixel := 0;
writeln ('The colour selected was:');
red := Round(Colour[0] * 65535);
writeln ('red: ', red);
green := Round(Colour[1] * 65535);
writeln ('green: ', green);
blue := Round(Colour[2] * 65535);
writeln ('blue: ', blue);
END;
gdk_color_alloc( ColourMap, Result ); { Allocate color }
{ Set window background color }
gdk_window_set_background( DrawingArea^.Window, Result );
gdk_window_clear( Drawingarea^.Window ); { Clear window }
END;
{ ---------------------colour_changed----------------------- }
{ -------------------------------dialog_closed (Colour Selection)---------------------- }
PROCEDURE dialog_closed(Widget : pGtkWidget ; Data :
gpointer ); cdecl;
BEGIN
gtk_widget_destroy(ColourSelDlg);
gtk_main_quit();
END;
{ -----------------------------------------dialog_closed--------------------------------- }
{ -----------------------------------------area_event-------------------------------- }
FUNCTION area_event( Widget : pGtkWidget ; Event :
pGdkEvent ; data : gpointer ) :
Integer; cdecl;
VAR
handled : Integer;
BEGIN
handled := 0; { i.e. FALSE }
IF ( ColourSelDlg = NIL) THEN
BEGIN
{ Yes, we have an event and there's no colorseldlg yet! }
handled := 1; { i.e. TRUE }
{ Create color selection dialog }
ColourSelDlg := gtk_color_selection_dialog_new('Select background color' );
gtk_signal_connect(
GTK_OBJECT(GTK_COLOR_SELECTION_DIALOG(ColourSelDlg)^.ok_button),
'clicked',
GTK_SIGNAL_FUNC(@colour_changed), NIL);
gtk_signal_connect(
GTK_OBJECT(GTK_COLOR_SELECTION_DIALOG(ColourSelDlg)^.cancel_button),
'clicked',
GTK_SIGNAL_FUNC(@dialog_closed), NIL);
gtk_widget_show(ColourSelDlg);
{ Show the dialog }
END;
Result := handled;
END;
{ ----------------------------------area_event--------------------------- }
{ -----------------------------------------destroy_window-----------------------------}
PROCEDURE destroy_window( Widget : pGtkWidget ; Data :
gpointer ); cdecl;
{ Close down and exit handler }
BEGIN
gtk_main_quit();
END;
{ ------------------------------------destroy_window-------------------------- }
{ -------------------------------Main Program--------------------------- }
BEGIN
{ Initialize the toolkit, remove gtk-related commandline stuff }
gtk_init( @argc, @argv );
ColourSelDlg := NIL;
{ Create toplevel window, set title and policies. }
Window := gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(Window), 'Colour selection test');
gtk_window_set_policy(GTK_WINDOW(Window), 1, 1, 1);
{ Attach to the delete and destroy events so we can exit }
gtk_signal_connect(GTK_OBJECT(Window), 'delete_event',
GTK_SIGNAL_FUNC(@destroy_window), NIL);
gtk_signal_connect(GTK_OBJECT(Window), 'destroy',
GTK_SIGNAL_FUNC(@destroy_window), NIL);
{ Create drawingarea, set size and catch button events }
DrawingArea := gtk_drawing_area_new();
gtk_drawing_area_size(GTK_DRAWING_AREA(DrawingArea), 200, 200);
gtk_widget_set_events(DrawingArea, GDK_BUTTON_PRESS_MASK);
gtk_signal_connect(GTK_OBJECT(DrawingArea), 'button_press_event',
GTK_SIGNAL_FUNC(@area_event), NIL);
{ Add drawingarea to window, then show them both }
gtk_container_add(GTK_CONTAINER(Window), DrawingArea);
gtk_widget_show(DrawingArea);
gtk_widget_show(Window);
gtk_main();
{ Enter the gtk_main loop }
END.
{ ------------------------------Main Program------------------------------- }
[Previous] [Contents] [Next]
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.
To create a new file selection box use:
FUNCTION gtk_file_selection_new( title :
pgchar ) : pGtkWidget;
To set the filename, for example to bring up a specific directory, or give a default filename, use this procedure:
PROCEDURE gtk_file_selection_set_filename( filesel :
pGtkFileSelection ;
filename : pgchar );
To grab the text that the user has entered or clicked on, use this function:
FUNCTION gtk_file_selection_get_filename( filesel :
pGtkFileSelection ) : pgchar;
There are also pointers to the widgets contained within the file selection widget. These are:
| dir_list | file_list | selection_entry |
selection_text |
| main_vbox | ok_button | cancel_button |
help_button |
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.c, modified to run on its own and translated into Pascal. As you will see,
there is nothing much to creating a file selection widget. If you compile and run the program you'll find that the buttons
across the top (Create Dir etc.) work even though our program doesn't handle their signals.
{ Converted from C to Pascal by Javier Ros <jros@unavarra.es> }
PROGRAM filesel;
USES glib, gdk, gtk;
{ ----------------------------file_ok_sel------------------------ }
PROCEDURE file_ok_sel( w : pGtkWidget ; fs :
pGtkFileSelection ); cdecl;
(* Get the selected filename and print it to the console *)
BEGIN
writeln( gtk_file_selection_get_filename(GTK_FILE_SELECTION(fs)) );
END;
{ -------------------------file_ok_sel------------------------}
{ ---------------------------destroy----------------------------- }
PROCEDURE destroy( widget : pGtkWidget; data :
gpointer ); cdecl;
BEGIN
gtk_main_quit();
END;
{ ------------------------destroy------------------------------}
{ ---------------------------Global Variables-------------------- }
VAR
filew : pGtkWidget ;
{ ---------------------------Main Program------------------------ }
BEGIN
gtk_init(@argc, @argv);
(* Create a new file selection widget *)
filew := gtk_file_selection_new('File selection');
gtk_signal_connect(GTK_OBJECT(filew), 'destroy',
GTK_SIGNAL_FUNC(@destroy), @filew);
(* Connect the ok_button to file_ok_sel function *)
gtk_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION (filew)^.ok_button),
'clicked',
GTK_SIGNAL_FUNC(@file_ok_sel), filew );
(* Connect the cancel_button to destroy the widget *)
gtk_signal_connect_object(GTK_OBJECT(GTK_FILE_SELECTION(filew)^.cancel_button),
'clicked',
GTK_SIGNAL_FUNC(@gtk_widget_destroy), GTK_OBJECT(filew));
(* Lets set the filename, as if this were a save dialog, and we are giving
a default filename *)
gtk_file_selection_set_filename(GTK_FILE_SELECTION(filew),
'filesel.pas');
gtk_widget_show(filew);
gtk_main();
END.
{ ----------------Main Program--------------------- }
[Previous] [Contents] [Next]