Monday, April 15, 2013

TextView Widget in GTK+

TextView Widget in GTK+

In this part of the GTK+ programming tutorial, we will work with a GtkTextView widget.
GtkTextView widget is used for displaying and editing multiline text. GtkTextView widget has also the MVC design. The GtkTextView represents the view component and GtkTextBuffer represents the model component. The GtkTextBuffer is used to manipulate the text data. GtkTextTag is an attribute that can be applied to the text. The GtkTextIter represents a position between two characters in the text. All manipulation with the text is done using text iterators.

Simple example

In our first example, we show some of the GtkTextView's functionality. We show how to apply various text tags to the text data in the GtkTextView.
#include <gtk/gtk.h>


int main( int argc, char *argv[])
{

GtkWidget *window;
GtkWidget *view;
GtkWidget *vbox;

GtkTextBuffer *buffer;
GtkTextIter start, end;
GtkTextIter iter;

gtk_init(&argc, &argv);

window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_default_size(GTK_WINDOW(window), 250, 200);
gtk_window_set_title(GTK_WINDOW(window), "TextView");
gtk_container_set_border_width(GTK_CONTAINER(window), 5);
GTK_WINDOW(window)->allow_shrink = TRUE;

vbox = gtk_vbox_new(FALSE, 0);
view = gtk_text_view_new();
gtk_box_pack_start(GTK_BOX(vbox), view, TRUE, TRUE, 0);

buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(view));

gtk_text_buffer_create_tag(buffer, "gap",
"pixels_above_lines", 30, NULL);

gtk_text_buffer_create_tag(buffer, "lmarg",
"left_margin", 5, NULL);
gtk_text_buffer_create_tag(buffer, "blue_fg",
"foreground", "blue", NULL);
gtk_text_buffer_create_tag(buffer, "gray_bg",
"background", "gray", NULL);
gtk_text_buffer_create_tag(buffer, "italic",
"style", PANGO_STYLE_ITALIC, NULL);
gtk_text_buffer_create_tag(buffer, "bold",
"weight", PANGO_WEIGHT_BOLD, NULL);

gtk_text_buffer_get_iter_at_offset(buffer, &iter, 0);

gtk_text_buffer_insert(buffer, &iter, "Plain text\n", -1);
gtk_text_buffer_insert_with_tags_by_name(buffer, &iter,
"Colored Text\n", -1, "blue_fg", "lmarg", NULL);
gtk_text_buffer_insert_with_tags_by_name (buffer, &iter,
"Text with colored background\n", -1, "lmarg", "gray_bg", NULL);

gtk_text_buffer_insert_with_tags_by_name (buffer, &iter,
"Text in italics\n", -1, "italic", "lmarg", NULL);

gtk_text_buffer_insert_with_tags_by_name (buffer, &iter,
"Bold text\n", -1, "bold", "lmarg", NULL);

gtk_container_add(GTK_CONTAINER(window), vbox);

g_signal_connect_swapped(G_OBJECT(window), "destroy",
G_CALLBACK(gtk_main_quit), G_OBJECT(window));

gtk_widget_show_all(window);

gtk_main();

return 0;
}
The example shows some text with different GtkTextTags applied.
 view = gtk_text_view_new();
The GtkTextView is created.
 gtk_text_buffer_create_tag(buffer, "blue_fg", 
"foreground", "blue", NULL);
This is an example of a GtkTextTag. The tag changes the color of the text to blue.
 gtk_text_buffer_insert_with_tags_by_name(buffer, &iter, 
"Colored Text\n", -1, "blue_fg", "lmarg", NULL);
This code inserts some text with a specific blue_fg text tag.

Simple TextView
Figure: Simple TextView

Lines and Columns

The following example will display the current line and column of the text cursor.
#include <gtk/gtk.h>

update_statusbar(GtkTextBuffer *buffer,
GtkStatusbar *statusbar)
{
gchar *msg;
gint row, col;
GtkTextIter iter;

gtk_statusbar_pop(statusbar, 0);

gtk_text_buffer_get_iter_at_mark(buffer,
&iter, gtk_text_buffer_get_insert(buffer));

row = gtk_text_iter_get_line(&iter);
col = gtk_text_iter_get_line_offset(&iter);

msg = g_strdup_printf("Col %d Ln %d", col+1, row+1);

gtk_statusbar_push(statusbar, 0, msg);

g_free(msg);
}

static void
mark_set_callback(GtkTextBuffer *buffer,
const GtkTextIter *new_location, GtkTextMark *mark,
gpointer data)
{
update_statusbar(buffer, GTK_STATUSBAR(data));
}


int main( int argc, char *argv[])
{

GtkWidget *window;
GtkWidget *vbox;

GtkWidget *toolbar;
GtkWidget *view;
GtkWidget *statusbar;
GtkToolItem *exit;
GtkTextBuffer *buffer;

gtk_init(&argc, &argv);

window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_default_size(GTK_WINDOW(window), 250, 200);
gtk_window_set_title(GTK_WINDOW(window), "lines & cols");

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

toolbar = gtk_toolbar_new();
gtk_toolbar_set_style(GTK_TOOLBAR(toolbar), GTK_TOOLBAR_ICONS);

exit = gtk_tool_button_new_from_stock(GTK_STOCK_QUIT);
gtk_toolbar_insert(GTK_TOOLBAR(toolbar), exit, -1);

gtk_box_pack_start(GTK_BOX(vbox), toolbar, FALSE, FALSE, 5);

view = gtk_text_view_new();
gtk_box_pack_start(GTK_BOX(vbox), view, TRUE, TRUE, 0);
gtk_widget_grab_focus(view);

buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(view));

statusbar = gtk_statusbar_new();
gtk_box_pack_start(GTK_BOX(vbox), statusbar, FALSE, FALSE, 0);

g_signal_connect(G_OBJECT(exit), "clicked",
G_CALLBACK(gtk_main_quit), NULL);

g_signal_connect(buffer, "changed",
G_CALLBACK(update_statusbar), statusbar);

g_signal_connect_object(buffer, "mark_set",
G_CALLBACK(mark_set_callback), statusbar, 0);

g_signal_connect_swapped(G_OBJECT(window), "destroy",
G_CALLBACK(gtk_main_quit), NULL);

gtk_widget_show_all(window);

update_statusbar(buffer, GTK_STATUSBAR (statusbar));

gtk_main();

return 0;
}
In this code example, we show the current position of the text cursor in the statusbar.
 view = gtk_text_view_new();
The GtkTextView widget is created.
 g_signal_connect(buffer, "changed",
G_CALLBACK(update_statusbar), statusbar);
When we change the text, we call the update_statusbar() handler.
 g_signal_connect_object(buffer, "mark_set", 
G_CALLBACK(mark_set_callback), statusbar, 0);
The mark_set signal is emitted, when the cursor moves.
 gtk_statusbar_pop(statusbar, 0); 
This code line clears any previous message from the statusbar.
 gtk_text_buffer_get_iter_at_mark(buffer,
&iter, gtk_text_buffer_get_insert(buffer));

row = gtk_text_iter_get_line(&iter);
col = gtk_text_iter_get_line_offset(&iter);
These lines determine the current line/row and column.
 msg = g_strdup_printf("Col %d Ln %d", col+1, row+1);
This code builds the text to be displayed on the statusbar.
 gtk_statusbar_push(statusbar, 0, msg);
We show the text on the statusbar.

Lines & Columns
Figure: Lines and Columns

Search & Highlight

In the next example, we will do some searching in the GtkTextBuffer. We will highlight some text patterns in the text buffer.
#include <gtk/gtk.h>
#include <gdk/gdkkeysyms.h>



gboolean key_pressed(GtkWidget * window,
GdkEventKey* event, GtkTextBuffer *buffer) {

GtkTextIter start_sel, end_sel;
GtkTextIter start_find, end_find;
GtkTextIter start_match, end_match;
gboolean selected;
gchar *text;



if ((event->type == GDK_KEY_PRESS) &&
(event->state & GDK_CONTROL_MASK)) {

switch (event->keyval)
{
case GDK_m :
selected = gtk_text_buffer_get_selection_bounds(buffer,
&start_sel, &end_sel);
if (selected) {
gtk_text_buffer_get_start_iter(buffer, &start_find);
gtk_text_buffer_get_end_iter(buffer, &end_find);

gtk_text_buffer_remove_tag_by_name(buffer, "gray_bg",
&start_find, &end_find);
text = (char *) gtk_text_buffer_get_text(buffer, &start_sel,
&end_sel, FALSE);

while ( gtk_text_iter_forward_search(&start_find, text,
GTK_TEXT_SEARCH_TEXT_ONLY |
GTK_TEXT_SEARCH_VISIBLE_ONLY,
&start_match, &end_match, NULL) ) {

gtk_text_buffer_apply_tag_by_name(buffer, "gray_bg",
&start_match, &end_match);
int offset = gtk_text_iter_get_offset(&end_match);
gtk_text_buffer_get_iter_at_offset(buffer,
&start_find, offset);
}

g_free(text);
}

break;

case GDK_r:
gtk_text_buffer_get_start_iter(buffer, &start_find);
gtk_text_buffer_get_end_iter(buffer, &end_find);

gtk_text_buffer_remove_tag_by_name(buffer, "gray_bg",
&start_find, &end_find);
break;
}
}

return FALSE;
}


int main( int argc, char *argv[])
{

GtkWidget *window;
GtkWidget *view;
GtkWidget *vbox;

GtkTextBuffer *buffer;
GtkTextIter start, end;
GtkTextIter iter;

gtk_init(&argc, &argv);

window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_default_size(GTK_WINDOW(window), 250, 200);
gtk_window_set_title(GTK_WINDOW(window), "Search & Highlight");
gtk_container_set_border_width(GTK_CONTAINER(window), 5);
GTK_WINDOW(window)->allow_shrink = TRUE;

vbox = gtk_vbox_new(FALSE, 0);
view = gtk_text_view_new();
gtk_widget_add_events(view, GDK_BUTTON_PRESS_MASK);
gtk_box_pack_start(GTK_BOX(vbox), view, TRUE, TRUE, 0);

buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(view));
gtk_text_buffer_create_tag(buffer, "gray_bg",
"background", "gray", NULL);
gtk_container_add(GTK_CONTAINER(window), vbox);

g_signal_connect_swapped(G_OBJECT(window), "destroy",
G_CALLBACK(gtk_main_quit), G_OBJECT(window));

g_signal_connect(G_OBJECT(window), "key-press-event",
G_CALLBACK(key_pressed), buffer);

gtk_widget_show_all(window);

gtk_main();

return 0;
}
In our code example we use keyboard shortcuts. The Ctrl + M shortcut highlights the all occurrences of currently selected text. The Ctrl + R removes the highlights from the text.
 gtk_text_buffer_create_tag(buffer, "gray_bg", 
"background", "gray", NULL);
This is the GtkTextTag that we use in our example. The tag makes the background of the text gray.
 selected = gtk_text_buffer_get_selection_bounds(buffer, 
&start_sel, &end_sel);
Here we get the start and end positions of the selected text.
gtk_text_buffer_get_start_iter(buffer, &start_find);
gtk_text_buffer_get_end_iter(buffer, &end_find);
We get the first and last position in the text buffer.
gtk_text_buffer_remove_tag_by_name(buffer, "gray_bg", 
&start_find, &end_find);
We remove any previous text tag.
 text = (char *) gtk_text_buffer_get_text(buffer, &start_sel,
&end_sel, FALSE);
We obtain the selected text. It is the text, we are going to search for.
while ( gtk_text_iter_forward_search(&start_find, text, 
GTK_TEXT_SEARCH_TEXT_ONLY |
GTK_TEXT_SEARCH_VISIBLE_ONLY,
&start_match, &end_match, NULL) ) {

gtk_text_buffer_apply_tag_by_name(buffer, "gray_bg",
&start_match, &end_match);
int offset = gtk_text_iter_get_offset(&end_match);
gtk_text_buffer_get_iter_at_offset(buffer,
&start_find, offset);
}
This code searches for all occurences of our selected text. If we find any match, we apply the text tag. After the match, the ending point of the word becomes the starting point for the next search.

Search & Highlight
Figure: Search & Highlight


In this chapter we covered GtkTextView widget.

No comments:

Post a Comment