2. Formatted Text in GtkTextView

GtkTextView can also be used to display formatted text. This usually involves creating tags which represent a group of attributes and then applying them to a range of text.

Tag objects are associated with a buffer and are created using the following function,


GtkTextTag* gtk_text_buffer_create_tag( GtkTextBuffer *buffer,
                                        const gchar   *tag_name,
                                        const gchar   *first_prop_name,
                                        ... );
Tags can be optionally associated with a name tag_name. Thus, the tag could be referred using the returned pointer or using the tag_name. For anonymous tags, NULL is passed to tag_name. The group of properties represented by this tag is listed as name/value pairs after the tag_name. The list of property/value pairs is terminated with a NULL pointer. "style", "weight", "editable", "justification" are some common property names. The following table lists their meaning and assignable values.

Table 1. Common properties used for creating tags

PropertyMeaningValues
"style"Font style as PangoStyle.

PANGO_STYLE_NORMAL
PANGO_STYLE_OBLIQUE
PANGO_STYLE_ITALIC

"weight"Font weight as integer.

PANGO_WEIGHT_NORMAL
PANGO_WEIGHT_BOLD

"editable"Text modifiable by user.

TRUE
FALSE

"justification"Justification of text.

GTK_JUSTIFY_LEFT
GTK_JUSTIFY_RIGHT
GTK_JUSTIFY_CENTER
GTK_JUSTIFY_FILL

"foreground"Foreground color of text."#RRGGBB"
"background"Background color of text."#RRGGBB"
"wrap-mode"Text wrapping mode

GTK_WRAP_NONE - Don't wrap text
GTK_WRAP_CHAR - Wrap text, breaking in between characters
GTK_WRAP_WORD - Wrap text, breaking in between words
GTK_WRAP_WORD_CHAR - Wrap text, breaking in words, or if that is not enough, also between characters

"font"Text font specified by font description string. "[FAMILY-LIST] [STYLE-OPTIONS] [SIZE]"

Here is a brief description of the font description string from the GTK+ manual.

""[FAMILY-LIST] [STYLE-OPTIONS] [SIZE]", where FAMILY-LIST is a comma separated list of families optionally terminated by a comma, STYLE_OPTIONS is a whitespace separated list of words where each WORD describes one of style, variant, weight, or stretch, and SIZE is an decimal number (size in points). Any one of the options may be absent. If FAMILY-LIST is absent, then the family_name field of the resulting font description will be initialized to NULL. If STYLE-OPTIONS is missing, then all style options will be set to the default values. If SIZE is missing, the size in the resulting font description will be set to 0."

See the GTK+ manual, for a complete list of properties and their corresponding values.

The created tag can then be applied to a range of text using the following functions,


void gtk_text_buffer_apply_tag( GtkTextBuffer     *buffer,
                                GtkTextTag        *tag,
                                const GtkTextIter *start,
                                const GtkTextIter *end );

void gtk_text_buffer_apply_tag_by_name( GtkTextBuffer     *buffer,
                                        const gchar       *name
                                        const GtkTextIter *start,
                                        const GtkTextIter *end );
The first function specifies the tag to be applied by a tag object and the second function specifies the tag by it's name. The range of text over with the tag is to applies is specified by the start and end iters.

Below is an extension of the previous example, that has a tool-bar to apply different tags to selected regions of text.


#include <gtk/gtk.h>

void
on_window_destroy (GtkWidget *widget, gpointer data)
{
  gtk_main_quit ();
}

/* Callback for buttons in the toolbar. */
void
on_format_button_clicked (GtkWidget *button, GtkTextBuffer *buffer)
{
  GtkTextIter start, end;
  gchar *tag_name;

  /* Get iters at the beginning and end of current selection. */
  gtk_text_buffer_get_selection_bounds (buffer, &start, &end);
  /* Find out what tag to apply. The key "tag" is set to the
     appropriate tag name when the button widget was created. */
  tag_name = g_object_get_data (G_OBJECT (button), "tag");
  /* Apply the tag to the selected text. */
  gtk_text_buffer_apply_tag_by_name (buffer, tag_name, &start, &end);
}

/* Callback for the close button. */
void
on_close_button_clicked (GtkWidget *button, GtkTextBuffer *buffer)
{
  GtkTextIter start;
  GtkTextIter end;

  gchar *text;

  /* Get iters at the beginning and end of the buffer. */
  gtk_text_buffer_get_bounds (buffer, &start, &end);
  /* Retrieve the text. */
  text = gtk_text_buffer_get_text (buffer, &start, &end, FALSE);
  /* Print the text. */
  g_print ("%s", text);

  g_free (text);

  gtk_main_quit ();
}

int 
main(int argc, char *argv[])
{
  GtkWidget *window;
  GtkWidget *vbox;
  GtkWidget *bbox;

  GtkWidget *bold_button;
  GtkWidget *italic_button;
  GtkWidget *font_button;

  GtkWidget *text_view;
  GtkTextBuffer *buffer;

  GtkWidget *close_button;
  
  gtk_init (&argc, &argv);

  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  gtk_window_set_title (GTK_WINDOW (window), "Formatted multiline text widget");
  gtk_window_set_default_size (GTK_WINDOW (window), 200, 200);
  g_signal_connect (G_OBJECT (window), "destroy", 
                    G_CALLBACK (on_window_destroy),
                    NULL);

  vbox = gtk_vbox_new (FALSE, 2);
  gtk_container_add (GTK_CONTAINER (window), vbox);

  /* Create a button box that will serve as a toolbar. */
  bbox = gtk_hbutton_box_new ();
  gtk_box_pack_start (GTK_BOX (vbox), bbox, 0, 0, 0);

  /* Create the text view widget and set some default text. */
  text_view = gtk_text_view_new ();
  gtk_box_pack_start (GTK_BOX (vbox), text_view, 1, 1, 0);
  buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (text_view));
  gtk_text_buffer_set_text (buffer, "Hello Text View!", -1);

  /* Create tags associated with the buffer. */
  /* Tag with weight bold and tag name "bold" . */
  gtk_text_buffer_create_tag (buffer, "bold", 
                              "weight", PANGO_WEIGHT_BOLD, 
                              NULL);
  /* Tag with style italic and tag name "italic". */
  gtk_text_buffer_create_tag (buffer, "italic",
                              "style", PANGO_STYLE_ITALIC,
                              NULL);
  /* Tag with font fixed and tag name "font". */
  gtk_text_buffer_create_tag (buffer, "font",
                              "font", "fixed", 
                              NULL); 

  /* Create button for bold and add them the to the tool bar. */
  bold_button = gtk_button_new_with_label ("Bold");
  gtk_container_add (GTK_CONTAINER (bbox), bold_button);
  /* Connect the common signal handler on_format_button_clicked. This
     signal handler is common to all the buttons. A key called "tag"
     is associated with the buttons, which speicifies the tag name
     that the button is supposed to apply. The handler reads this key
     and applies the appropriate tag. Thus only one handler is needed
     for any number of buttons in the toolbar. */
  g_signal_connect (G_OBJECT (bold_button), "clicked",
                    G_CALLBACK (on_format_button_clicked),
                    buffer);
  g_object_set_data (G_OBJECT (bold_button), "tag", "bold");

  /* Create button for italic. */
  italic_button = gtk_button_new_with_label ("Italic");
  gtk_container_add (GTK_CONTAINER (bbox), italic_button);
  g_signal_connect (G_OBJECT (italic_button), "clicked",
                    G_CALLBACK (on_format_button_clicked),
                    buffer);
  g_object_set_data (G_OBJECT (italic_button), "tag", "italic");

  /* Create button for fixed font. */
  font_button = gtk_button_new_with_label ("Font Fixed");
  gtk_container_add (GTK_CONTAINER (bbox), font_button);
  g_signal_connect (G_OBJECT (font_button), "clicked",
                    G_CALLBACK (on_format_button_clicked),
                    buffer);
  g_object_set_data (G_OBJECT (font_button), "tag", "font");
  
  /* Create the close button. */
  close_button = gtk_button_new_with_label ("Close");
  gtk_box_pack_start (GTK_BOX (vbox), close_button, 0, 0, 0);
  g_signal_connect (G_OBJECT (close_button), "clicked", 
                    G_CALLBACK (on_close_button_clicked),
                    buffer);

  gtk_widget_show_all (window);

  gtk_main ();
  return 0;
}



2.1. More Functions for Applying and Removing tags

In the previous section, the gtk_text_buffer_insert function was introduced. A variant of this function can be used to insert text with tags applied.


void gtk_text_buffer_insert_with_tags( GtkTextBuffer *buffer,
                                       GtkTextIter   *iter,
                                       const gchar   *text,
                                       gint          len,
                                       GtkTextTag    *first_tag,
                                       ... );
The tags argument list is terminated by a NULL pointer. The _by_name suffixed variants are also available, in which the tags to be applied are specified by the tag names.

Tags applied to a range of text can be removed by using the following function


void gtk_text_buffer_remove_tag( GtkTextBuffer     *buffer,
                                 GtkTextTag        *tag,
                                 const GtkTextIter *start,
                                 const GtkTextIter *end );
This function also has the _by_name prefixed variant.

All tags on a range of text can be removed in one go using the following function


void gtk_text_buffer_remove_all_tags( GtkTextBuffer     *buffer,
                                      const GtkTextIter *start,
                                      const GtkTextIter *end );

2.2. Formatting the Entire Widget

The above functions apply attributes to portions of text in a buffer. If attributes have to be applied for the entire GtkTextView widget, the gtk_text_view_set_* functions can be used. For example, the following function makes text_view editable/non-editable.


void gtk_text_view_set_editable( GtkTextView *text_view,
                                 gboolean setting );
See the GTK+ manual for a complete list of available functions.

The attributes set by these functions, on the entire widget, can be overridden by applying tags to portions of text in the buffer.