The Text widget allows multiple lines of text to be displayed and edited. It supports both multi-coloured and multi-font text, allowing them to be mixed in any combination. It also has a wide set of key based text editing commands, which are compatible with Emacs.
The text widget supports full cut-and-paste facilities, including the use of double- and triple-click to select a word and a whole line, respectively.
The constructor for creating a new Text widget is:
Gtk::Text(Gtk::Adjustment &hadj,
Gtk::Adjustment &vadj);
The arguments allow us to give the Text widget pointers to Adjustments that can be used to track the viewing position of the widget. Leaving out the arguments will cause the constructor to create its own Adjustments.
void Gtk::Text::set_adjustments(Gtk::Adjustment &hadj,
Gtk::Adjustment &vadj);
The above function allows the horizontal and vertical adjustments of a text widget to be changed at any time.
The text widget will not automatically create its own scrollbars when the amount of text to be displayed is too long for the display window. We therefore have to create and add them to the display layout ourselves.
Gtk::VScrollbar vscrollbar = Gtk::VScrollbar(text.get_vadjustment());
hbox.pack_start(vscrollbar, false, false, 0);
vscrollbar.show();
The above code snippet creates a new vertical scrollbar, and attaches
it to the vertical adjustment of the text widget, text
. It then
packs it into a box, hbox
, in the normal way.
Unfortunately, Gtk::Text
does not currently support horizontal
scrollbars.
There are two main ways in which a Text widget can be used: to allow the user to edit a body of text, or to allow us to display multiple lines of text to the user. In order for us to switch between these modes of operation, the text widget has the following function:
void Gtk::Editable::set_editable(gboolean editable);
The editable
argument is a TRUE or FALSE value that specifies
whether the user is permitted to edit the contents of the Text
widget. When the text widget is editable, it will display a cursor at
the current insertion point.
You are not, however, restricted to just using the text widget in these two modes. You can toggle the editable state of the text widget at any time, and can insert text at any time.
The text widget wraps lines of text that are too long to fit onto a single line of the display window. Its default behaviour is to break words across line breaks. This can be changed using the next function:
void Gtk::Text::set_line_wrap(gboolean line_wrap);
Using this function allows us to specify that the text widget should
wrap long lines on word boundaries. The word_wrap
argument is a
TRUE or FALSE value.
The current insertion point of a Text widget can be set using
void Gtk::Text::set_point(guint index);
where index
is the position to set the insertion point.
Analogous to this is the function for getting the current insertion point:
guint Gtk::Text::get_point();
A function that is useful in combination with the above two functions is
guint Gtk::Text::get_length();
which returns the current length of the text in the Text widget. The length is the number of characters that are within the text block of the widget, including characters such as newlines, which mark the ends of lines.
In order to insert text at the current insertion point of a Text
widget, the insert()
method is used; it also allows us to
specify background and foreground colors and a font for the text.
void Gtk::Text::insert(const Gdk_Font& font,
const Gdk_Color& fore,
const Gdk_Color& back,
nstring& chars,
gint length);
The font and colours of the text can also be specified using a context object:
void Gtk::Text_Helpers::Context();
The font and colours of the context can be set using:
void Gtk::Text_Helpers::Context::set_foreground(const Gdk_Color& color)
void Gtk::Text_Helpers::Context::set_background(const Gdk_Color& color)
void Gtk::Text_Helpers::Context::set_font(const Gdk_Font& font)
These values can be cleared by calling the same methods without arguments. Corresponding "get" methods also exist for retrieving the values.
The default context of the text widget can be set using:
void Gtk::Text::set_context(const Context& gc);
Again, the value can be cleared by calling with no argument. To insert text using a context, use:
void Gtk::Text::insert(const Context& gc, const string& text);
or simply:
void Gtk::Text::insert(const string& text);
to insert using the text widget's current context. If the widget's context is cleared, text with no specified style is inserted. So if you want a plain text widget you only have to use this member.
The text widget is one of the few within GTK-- that redraws itself dynamically. This means that all changes to the contents of the text widget take effect immediately. This may be undesirable when performing multiple changes to the text widget. In order to allow us to perform multiple updates to the text widget without it continuously redrawing, we can "freeze" the widget, which temporarily stops it from automatically redrawing itself every time it is changed. We can then "thaw" the widget after our updates are complete.
The following two functions perform this freeze and thaw action:
void Gtk::Text::freeze();
void Gtk::Text::thaw();
Text is deleted from the text widget relative to the current insertion point by the following two functions. The return value is a TRUE or FALSE indicator of whether the operation was successful.
gint Gtk::Text::backward_delete(guint nchars);
gint Gtk::Text::forward_delete(guint nchars);
To retrieve blocks of text from the text widget, we can use the function
string Gtk::Editable::get_chars(gint start_pos,
gint end_pos);
This is a member of the parent class of the text widget. A value of
-1 as end_pos
signifies the end of the text. The index of the
text starts at 0.
The text widget has a number of pre-installed keyboard shortcuts for common editing, motion and selection functions. These are accessed using Control and Alt key combinations.
In addition to these, holding down the Control key whilst using cursor key movement will move the cursor by words rather than characters. Holding down Shift whilst using cursor movement will extend the selection.
Any resemblance to the Emacs keybindings is purely coincidental. :)
Source location: examples/text/text.cc
#include <stdio.h>
#include <gtk--/text.h>
#include <gtk--/main.h>
#include <gtk--/window.h>
#include <gtk--/box.h>
#include <gtk--/button.h>
#include <gtk--/checkbutton.h>
#include <gtk--/buttonbox.h>
#include <gtk--/table.h>
#include <gtk--/scrollbar.h>
#include <gtk--/separator.h>
class AppWindow : public Gtk::Window {
Gtk::Text text;
Gtk::CheckButton edit_check;
Gtk::CheckButton wrap_check;
public:
AppWindow();
~AppWindow();
void text_toggle_editable () {
text.set_editable(edit_check.get_active());
}
void text_toggle_word_wrap () {
text.set_word_wrap(wrap_check.get_active());
}
gint delete_event_impl (GdkEventAny*) {
Gtk::Main::quit();
return 0;
}
};
void close_application( GtkWidget *widget, gpointer data )
{
gtk_main_quit();
}
AppWindow::AppWindow() :
Gtk::Window(GTK_WINDOW_TOPLEVEL), edit_check("Editable"),
wrap_check("Wrap Words")
{
Gtk::VBox *box1;
Gtk::VBox *box2;
Gtk::HButtonBox *hbox;
Gtk::Button *button;
Gtk::HSeparator *separator;
Gtk::Table *table;
Gtk::VScrollbar *vscrollbar;
FILE *infile;
set_usize(600, 500);
set_policy ( true, true, false);
set_title ("Text Widget Example");
set_border_width (0);
box1 = manage( new Gtk::VBox (false, 0) );
add(*box1);
box2 = manage( new Gtk::VBox (false, 10) );
box2->set_border_width (10);
box1->pack_start (*box2, true, true, 0);
table = manage( new Gtk::Table (2, 2, false) );
table->set_row_spacing (0, 2);
table->set_col_spacing (0, 2);
box2->pack_start (*table, true, true, 0);
/* Create the GtkText widget */
text.set_editable (true);
table->attach (text, 0, 1, 0, 1,
GTK_EXPAND | GTK_SHRINK | GTK_FILL,
GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0);
/* Add a vertical scrollbar to the GtkText widget */
vscrollbar = manage( new Gtk::VScrollbar (*text.get_vadjustment()));
table->attach ( *vscrollbar, 1, 2, 0, 1,
GTK_FILL, GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0);
/* Get the system colour map and allocate the colour red */
Gdk_Color red("red");
Gdk_Color white("white");
Gdk_Color black("black");
/* Load a fixed font */
Gdk_Font fixed_font("-misc-fixed-medium-r-*-*-*-140-*-*-*-*-*-*");
/* Realizing a widget creates a window for it, ready for us to insert some text */
text.realize ();
/* Freeze the text widget, ready for multiple updates */
text.freeze ();
/* Insert some coloured text */
text.insert ( Gdk_Font(), black, white, "Supports ", -1);
text.insert ( Gdk_Font(), red, white, "colored ", -1);
text.insert ( Gdk_Font(), black, white, "text and different ", -1);
text.insert ( fixed_font, black, red , "fonts\n\n", -1);
/* Load the file text.c into the text window */
infile = fopen("text.cc", "r");
if (infile) {
char buffer[1024];
int nchars;
Gtk::Text::Context cx;
cx.set_font(fixed_font);
while (1)
{
nchars = fread(buffer, 1, 1024, infile);
buffer[nchars]='\0';
text.insert ( cx, buffer);
if (nchars < 1024)
break;
}
fclose (infile);
}
/* Thaw the text widget, allowing the updates to become visible */
text.thaw ();
hbox = manage( new Gtk::HButtonBox () );
box2->pack_start ( *hbox, false, false, 0);
hbox->pack_start ( edit_check, false, false, 0);
edit_check.toggled.connect(slot(this, &AppWindow::text_toggle_editable));
edit_check.set_active(true);
hbox->pack_start (wrap_check, false, true, 0);
wrap_check.toggled.connect(slot(this, &AppWindow::text_toggle_word_wrap));
wrap_check.set_active(false);
separator = manage( new Gtk::HSeparator () );
box1->pack_start ( *separator, false, true, 0);
box2 = manage( new Gtk::VBox (false, 10) );
box2->set_border_width (10);
box1->pack_start ( *box2, false, true, 0);
button = manage( new Gtk::Button ("close") );
button->clicked.connect( Gtk::Main::quit.slot() );
box2->pack_start ( *button, true, true, 0);
button->set_flags( GTK_CAN_DEFAULT);
button->grab_default ();
show_all ();
}
AppWindow::~AppWindow() {}
int main (int argc, char *argv[])
{
Gtk::Main m(argc, argv);
AppWindow app;
Gtk::Main::run();
return(0);
}