Multiline Text Editing Widget

Vijay Kumar B. vijaykumar@bravegnu.org

$Name: $ $Id: gtktext.docbook,v 1.9 2007/01/07 11:17:21 vijay Exp $

Other Formats: [DocBook Tar ball] [HTML Tar ball] [Single HTML] [TXT] [PDF]

Revision History
Revision 0.47 Jan 2007Revised by: bertrand
Added GtkSourceView example.
Revision 0.315 June 2006Revised by: vijay
Fixed document style and typos.
Revision 0.223 May 2006Revised by: vijay
Fixed spelling mistakes and typos.
Revision 0.116 May 2006Revised by: vijay
Initial revision.

Table of Contents
1. Simple Multiline Text Widget
1.1. More on Iterators
1.2. UTF-8 and GTK+
1.3. Other Editing Functions
1.4. Other Functions to Obtain Iters
2. Formatted Text in GtkTextView
2.1. More Functions for Applying and Removing tags
2.2. Formatting the Entire Widget
3. Cut, Copy and Paste
4. Searching
4.1. Simple Search
4.2. Continuing your Search
4.2.1. Marks
4.3. The Scrolling Problem
4.4. More on Marks
4.5. Built-in Marks
5. Examining and Modifying Text
5.1. More functions to Examine and Modify Text
6. Images/Widgets
6.1. Inserting Images
6.2. Retrieving Images
6.3. Inserting Widgets
6.4. Retrieving Widgets
7. Buffer and Window Coordinates
7.1. Tooltips under Text Cursor
7.2. More on Buffer and Window Coordinates
8. Final Notes
8.1. GtkTextView in gtk-demo
8.2. GtkSourceView
9. Credits
10. Tutorial Copyright and Permissions Notice

1. Simple Multiline Text Widget

In this section, you will learn how to create a simple multiline text widget, using GtkTextView, for data entry.

The widget itself is created using


GtkWidget *gtk_text_view_new( void );

When the GtkTextView widget is created this way, a GtkTextBuffer object associated with this widget is also created. The GtkTextBuffer is responsible for storing the text and associated attributes, while the GtkTextView widget is responsible for displaying the text ie. it provides an I/O interface to buffer.

All text modification operations are related to the buffer object. The buffer associated with a GtkTextView widget can be obtained using


GtkTextBuffer *gtk_text_view_get_buffer( GtkTextView *text_view );

Some common operations performed on a simple multiline text widget are, setting the entire text and reading the entire text from the buffer. The entire text of the buffer can be set using


void gtk_text_buffer_set_text( GtkTextBuffer *buffer, 
                               const gchar   *text,
                               gint          len );
The len should be specified when the text contains '\0'. When the text does not contain '\0'and is terminated by a '\0', len could be -1.

Getting the entire text of a buffer, could be a little more complicated than setting the entire text of the buffer. You will have to understand iterators. Iterators are objects that represent positions between two characters in a buffer. Iters in a buffer can be obtained using many different functions, but for our simple case the following functions can be used to get the iters at the start and end of the buffer.


void gtk_text_buffer_get_start_iter( GtkTextBuffer *buffer,
                                     GtkTextIter   *iter );

void gtk_text_buffer_get_end_iter( GtkTextBuffer *buffer,
                                   GtkTextIter   *iter );

Unlike other objects which are created using constructor like functions returning pointers to the objects, the GtkTextIter objects are created by instantiating the structure itself i.e they are allocated on the stack. So new GtkTextIter objects are created as follows


GtkTextIter start_iter;
GtkTextIter end_iter;
Pointers to the iterators are then passed to the above functions for initialization.

The initialized iters, can be used in the following function to retrieve the entire contents of the buffer.


gchar *gtk_text_buffer_get_text( GtkTextBuffer     *buffer, 
                                 const GtkTextIter *start,
                                 const GtkTextIter *end,
                                 gboolean          include_hidden_chars );
The include_hidden_chars is used to specify whether are not to include text that has the invisible attribute set. Text attributes and how to set them will be discussed later in this tutorial. The returned string is dynamically allocated and should be freed using g_free.

Below is a sample program, that implements the simple multiline text widget. The program assigns a default text to the buffer. The text in the buffer can be modified by the user and when the close button is pressed it prints the contents of the buffer and quits.


#include <gtk/gtk.h>

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

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

  gchar *text;

  /* Obtain iters for the start and end of points of the buffer */
  gtk_text_buffer_get_start_iter (buffer, &start);
  gtk_text_buffer_get_end_iter (buffer, &end);

  /* Get the entire buffer 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 *text_view;
  GtkWidget *button;
  GtkTextBuffer *buffer;
  
  gtk_init (&argc, &argv);

  /* Create a Window. */
  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  gtk_window_set_title (GTK_WINDOW (window), "Simple Multiline Text Input");

  /* Set a decent default size for the window. */
  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 multiline text widget. */
  text_view = gtk_text_view_new ();
  gtk_box_pack_start (GTK_BOX (vbox), text_view, 1, 1, 0);

  /* Obtaining the buffer associated with the widget. */
  buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (text_view));
  /* Set the default buffer text. */ 
  gtk_text_buffer_set_text (buffer, "Hello Text View!", -1);
  
  /* Create a close button. */
  button = gtk_button_new_with_label ("Close");
  gtk_box_pack_start (GTK_BOX (vbox), button, 0, 0, 0);
  g_signal_connect (G_OBJECT (button), "clicked", 
                    G_CALLBACK (on_button_clicked),
                    buffer);
  
  gtk_widget_show_all (window);

  gtk_main ();
  return 0;
}

1.1. More on Iterators

A point to note about text iterators - iterators are not valid indefinitely. Whenever the buffer is modified in a way that affects the number of characters in the buffer, all outstanding iterators become invalid. You will have to re-obtain iterators to use them. To preserve positions across buffer modifications the GtkTextMark can be used. Text marks will be discussed later in this tutorial.

1.2. UTF-8 and GTK+

GTK+ handles text in UTF-8 format. For the uninitiated, the UTF-8 is an ASCII compatible multi-byte unicode encoding. The thing to be noted is that one character can be encoded as multiple bytes. The GTK+ manual uses the term offset for character counts, and uses the term index for byte counts. The len argument of the gtk_text_buffer_set_text function is the length of the text in bytes.

1.3. Other Editing Functions

The following function can be used to delete text from a buffer.


void gtk_text_buffer_delete( GtkTextBuffer *buffer,
                             GtkTextIter   *start,
                             GtkTextIter   *end );
Since this function modifies the buffer, all outstanding iterators become invalid after a call to this function. But, the iterators passed to the function are re-initialized to point to the location where the text was deleted.

The following function can be used to insert text into a buffer at a position specified by an iterator.


void gtk_text_buffer_insert( GtkTextBuffer *buffer,
                             GtkTextIter   *iter,
                             const gchar   *text,
                             gint          len );
The len argument is similar to the len argument in gtk_text_buffer_set_text function. As with gtk_text_buffer_delete, the buffer is modified and hence all outstanding iterators become invalid, and start and end are re-initialized. Hence the same iterator can be used for a series of consecutive inserts.

The following function can be used to insert text at the current cursor position.


void gtk_text_buffer_insert_at_cursor( GtkTextBuffer *buffer,
                                       const gchar   *text,
                                       gint          len );

1.4. Other Functions to Obtain Iters

The following function can be used to get iterators at the beginning and end of the buffer in one go.


void gtk_text_buffer_get_bounds( GtkTextBuffer *buffer,
                                 GtkTextIter   *start,
                                 GtkTextIter   *end );

A variant of the above function can be used to obtain iterators at the beginning and end of the current selection.


void gtk_text_buffer_selection_bounds( GtkTextBuffer *buffer,
                                       GtkTextIter   *start,
                                       GtkTextIter   *end );

The following functions can be used to obtain an iterator at a specified character offset into the buffer, at the start of the given line, at an character offset into a given line, or at a byte offset into a given line, respectively.


void gtk_text_buffer_get_iter_at_offset( GtkTextBuffer *buffer,
                                         GtkTextIter   *iter,
                                         gint          char_offset);

void gtk_text_buffer_get_iter_at_line( GtkTextBuffer *buffer,
                                       GtkTextIter   *iter,
                                       gint          line_number);

void gtk_text_buffer_get_iter_at_line_offset( GtkTextBuffer *buffer,
                                              GtkTextIter   *iter,
                                              gint          line_no,
                                              gint          offset );

void gtk_text_buffer_get_iter_at_line_index( GtkTextBuffer *buffer,
                                             GtkTextIter   *iter,
                                             gint          line_no,
                                             gint          index );