1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
|
<refentry id="TextWidget" revision="18 Oct 2000">
<refmeta>
<refentrytitle>Text Widget Overview</refentrytitle>
<manvolnum>3</manvolnum>
<refmiscinfo>GTK Library</refmiscinfo>
</refmeta>
<refnamediv>
<refname>Text Widget Overview</refname><refpurpose>Overview of <link linkend="GtkTextBuffer">GtkTextBuffer</link>, <link linkend="GtkTextView">GtkTextView</link>, and friends</refpurpose>
</refnamediv>
<refsect1>
<title>Conceptual Overview</title>
<para>
GTK+ has an extremely powerful framework for multiline text editing. The
primary objects involved in the process are <link
linkend="GtkTextBuffer">GtkTextBuffer</link>, which represents the text being
edited, and <link linkend="GtkTextView">GtkTextView</link>, a widget which can
display a <link linkend="GtkTextBuffer">GtkTextBuffer</link>. Each buffer can be
displayed by any number of views.
</para>
<para>
One of the important things to remember about text in GTK+ is that it's in the
UTF-8 encoding. This means that one character can be encoded as multiple
bytes. Character counts are usually referred to as
<firstterm>offsets</firstterm>, while byte counts are called
<firstterm>indexes</firstterm>. If you confuse these two, things will work fine
with ASCII, but as soon as your buffer contains multibyte characters, bad things
will happen.
</para>
<para>
Text in a buffer can be marked with <firstterm>tags</firstterm>. A tag is an
attribute that can be applied to some range of text. For example, a tag might be
called "bold" and make the text inside the tag bold. However, the tag concept is
more general than that; tags don't have to affect appearance. They can instead
affect the behavior of mouse and key presses, "lock" a range of text so
the user can't edit it, or countless other things. A tag is represented by a
<link linkend="GtkTextTag">GtkTextTag</link> object. One <link
linkend="GtkTextTag">GtkTextTag</link> can be applied to any number of text
ranges in any number of buffers.
</para>
<para>
Each tag is stored in a <link
linkend="GtkTextTagTable">GtkTextTagTable</link>. A tag table defines a set of
tags that can be used together. Each buffer has one tag table associated with
it; only tags from that tag table can be used with the buffer. A single tag
table can be shared between multiple buffers, however.
</para>
<para>
Tags can have names, which is convenient sometimes (for example, you can name
your tag that makes things bold "bold"), but they can also be anonymous (which
is convenient if you're creating tags on-the-fly).
</para>
<para>
Most text manipulation is accomplished with <firstterm>iterators</firstterm>,
represented by a <link linkend="GtkTextIter">GtkTextIter</link>. An iterator
represents a position between two characters in the text buffer. <link
linkend="GtkTextIter">GtkTextIter</link> is a struct designed to be allocated on
the stack; it's guaranteed to be copiable by value and never contain any
heap-allocated data. 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. (Note that deleting 5 characters and then
reinserting 5 still invalidates iterators, though you end up with the same
number of characters you pass through a state with a different number).
</para>
<para>
Because of this, iterators can't be used to preserve positions across buffer
modifications. To preserve a position, the <link
linkend="GtkTextMark">GtkTextMark</link> object is ideal. You can think of a
mark as an invisible cursor or insertion point; it floats in the buffer, saving
a position. If the text surrounding the mark is deleted, the mark remains in the
position the text once occupied; if text is inserted at the mark, the mark ends
up either to the left or to the right of the new text, depending on its
<firstterm>gravity</firstterm>. The standard text cursor in left-to-right
languages is a mark with right gravity, because it stays to the right of
inserted text.
</para>
<para>
Like tags, marks can be either named or anonymous. There are two marks built-in
to <link linkend="GtkTextBuffer">GtkTextBuffer</link>; these are named
<literal>"insert"</literal> and <literal>"selection_bound"</literal> and refer
to the insertion point and the boundary of the selection which is not the
insertion point, respectively. If no text is selected, these two marks will be
in the same position. You can manipulate what is selected and where the cursor
appears by moving these marks around.
<footnote>
<para>
If you want to place the cursor in response to a user action, be sure to use
<function>gtk_text_buffer_place_cursor()</function>, which moves both at once
without causing a temporary selection (moving one then the other temporarily
selects the range in between the old and new positions).
</para>
</footnote>
</para>
<para>
Text buffers always contain at least one line, but may be empty (that
is, buffers can contain zero characters). The last line in the text
buffer never ends in a line separator (such as newline); the other
lines in the buffer always end in a line separator. Line separators
count as characters when computing character counts and character
offsets. Note that some Unicode line separators are represented with
multiple bytes in UTF-8, and the two-character sequence "\r\n" is also
considered a line separator.
</para>
</refsect1>
<refsect1>
<title>Simple Example</title>
<para>
The simplest usage of <link linkend="GtkTextView">GtkTextView</link>
might look like this:
<informalexample><programlisting>
GtkWidget *view;
GtkTextBuffer *buffer;
view = gtk_text_view_new ();
buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
gtk_text_buffer_set_text (buffer, "Hello, this is some text", -1);
/* Now you might put the view in a container and display it on the
* screen; when the user edits the text, signals on the buffer
* will be emitted, such as "changed", "insert_text", and so on.
*/
</programlisting></informalexample>
In many cases it's also convenient to first create the buffer with
<function>gtk_text_buffer_new()</function>, then create a widget for that
buffer with <function>gtk_text_view_new_with_buffer()</function>. Or you can
change the buffer the widget displays after the widget is created with
<function>gtk_text_view_set_buffer()</function>.
</para>
</refsect1>
<refsect1>
<title>Example of Changing Text Attributes</title>
<para>
There are two ways to affect text attributes in
<link linkend="GtkTextView">GtkTextView</link>.
You can change the default attributes for a given
<link linkend="GtkTextView">GtkTextView</link>, and you can
apply tags that change the attributes for a region of text.
For text features that come from the theme — such as
font and foreground color &mdash use standard
<link linkend="GtkWidget">GtkWidget</link>
functions such as
<link linkend="gtk-widget-modify-font">gtk_widget_modify_font()</link>
or
<link linkend="gtk-widget-modify-fg">gtk_widget_modify_fg()</link>.
For other attributes there are dedicated methods on
<link linkend="GtkTextView">GtkTextView</link> such as
<link linkend="gtk-text-view-set-tabs">gtk_text_view_set_tabs()</link>.
<informalexample><programlisting>
GtkWidget *view;
GtkTextBuffer *buffer;
GtkTextIter start, end;
PangoFontDescription *font_desc;
GdkColor color;
GtkTextTag *tag;
view = gtk_text_view_new ();
buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
gtk_text_buffer_set_text (buffer, "Hello, this is some text", -1);
/* Change default font throughout the widget */
font_desc = pango_font_description_from_string ("Serif 15");
gtk_widget_modify_font (view, font_desc);
pango_font_description_free (font_desc);
/* Change default color throughout the widget */
gdk_color_parse ("green", &color);
gtk_widget_modify_text (view, GTK_STATE_NORMAL, &color);
/* Change left margin throughout the widget */
gtk_text_view_set_left_margin (GTK_TEXT_VIEW (view), 30);
/* Use a tag to change the color for just one part of the widget */
tag = gtk_text_buffer_create_tag (buffer, "blue_foreground",
"foreground", "blue", NULL);
gtk_text_buffer_get_iter_at_offset (buffer, &start, 7);
gtk_text_buffer_get_iter_at_offset (buffer, &end, 12);
gtk_text_buffer_apply_tag (buffer, tag, &start, &end);
</programlisting></informalexample>
</para>
<para>
The <application>gtk-demo</application> application that comes with
GTK+ contains more example code for <link
linkend="GtkTextView">GtkTextView</link>.
</para>
</refsect1>
</refentry>
|