]> Pileus Git - ~andy/gtk/blob - demos/gtk-demo/textview.c
f17f880916b6fd113e0c2fb461ed9c76748c9ae8
[~andy/gtk] / demos / gtk-demo / textview.c
1 /* Text Widget/Multiple Views
2  *
3  * The GtkTextView widget displays a GtkTextBuffer. One GtkTextBuffer
4  * can be displayed by multiple GtkTextViews. This demo has two views
5  * displaying a single buffer, and shows off the widget's text
6  * formatting features.
7  *
8  */
9
10 #include <gtk/gtk.h>
11 #include <stdlib.h> /* for exit() */
12
13 #include "demo-common.h"
14
15 static void easter_egg_callback (GtkWidget *button, gpointer data);
16
17 #define gray50_width 2
18 #define gray50_height 2
19 static char gray50_bits[] = {
20   0x02, 0x01
21 };
22
23 static void
24 create_tags (GtkTextBuffer *buffer)
25 {
26   GdkBitmap *stipple;
27
28   /* Create a bunch of tags. Note that it's also possible to
29    * create tags with gtk_text_tag_new() then add them to the
30    * tag table for the buffer, gtk_text_buffer_create_tag() is
31    * just a convenience function. Also note that you don't have
32    * to give tags a name; pass NULL for the name to create an
33    * anonymous tag.
34    *
35    * In any real app, another useful optimization would be to create
36    * a GtkTextTagTable in advance, and reuse the same tag table for
37    * all the buffers with the same tag set, instead of creating
38    * new copies of the same tags for every buffer.
39    *
40    * Tags are assigned default priorities in order of addition to the
41    * tag table.  That is, tags created later that affect the same text
42    * property affected by an earlier tag will override the earlier
43    * tag.  You can modify tag priorities with
44    * gtk_text_tag_set_priority().
45    */
46
47   gtk_text_buffer_create_tag (buffer, "heading",
48                               "weight", PANGO_WEIGHT_BOLD,
49                               "size", 15 * PANGO_SCALE,
50                               NULL);
51   
52   gtk_text_buffer_create_tag (buffer, "italic",
53                               "style", PANGO_STYLE_ITALIC, NULL);
54
55   gtk_text_buffer_create_tag (buffer, "bold",
56                               "weight", PANGO_WEIGHT_BOLD, NULL);  
57   
58   gtk_text_buffer_create_tag (buffer, "big",
59                               /* points times the PANGO_SCALE factor */
60                               "size", 20 * PANGO_SCALE, NULL);
61
62   gtk_text_buffer_create_tag (buffer, "xx-small",
63                               "scale", PANGO_SCALE_XX_SMALL, NULL);
64
65   gtk_text_buffer_create_tag (buffer, "x-large",
66                               "scale", PANGO_SCALE_X_LARGE, NULL);
67   
68   gtk_text_buffer_create_tag (buffer, "monospace",
69                               "family", "monospace", NULL);
70   
71   gtk_text_buffer_create_tag (buffer, "blue_foreground",
72                               "foreground", "blue", NULL);  
73
74   gtk_text_buffer_create_tag (buffer, "red_background",
75                               "background", "red", NULL);
76
77   stipple = gdk_bitmap_create_from_data (NULL,
78                                          gray50_bits, gray50_width,
79                                          gray50_height);
80   
81   gtk_text_buffer_create_tag (buffer, "background_stipple",
82                               "background_stipple", stipple, NULL);
83
84   gtk_text_buffer_create_tag (buffer, "foreground_stipple",
85                               "foreground_stipple", stipple, NULL);
86
87   g_object_unref (stipple);
88
89   gtk_text_buffer_create_tag (buffer, "big_gap_before_line",
90                               "pixels_above_lines", 30, NULL);
91
92   gtk_text_buffer_create_tag (buffer, "big_gap_after_line",
93                               "pixels_below_lines", 30, NULL);
94
95   gtk_text_buffer_create_tag (buffer, "double_spaced_line",
96                               "pixels_inside_wrap", 10, NULL);
97
98   gtk_text_buffer_create_tag (buffer, "not_editable",
99                               "editable", FALSE, NULL);
100   
101   gtk_text_buffer_create_tag (buffer, "word_wrap",
102                               "wrap_mode", GTK_WRAP_WORD, NULL);
103
104   gtk_text_buffer_create_tag (buffer, "char_wrap",
105                               "wrap_mode", GTK_WRAP_CHAR, NULL);
106
107   gtk_text_buffer_create_tag (buffer, "no_wrap",
108                               "wrap_mode", GTK_WRAP_NONE, NULL);
109   
110   gtk_text_buffer_create_tag (buffer, "center",
111                               "justification", GTK_JUSTIFY_CENTER, NULL);
112
113   gtk_text_buffer_create_tag (buffer, "right_justify",
114                               "justification", GTK_JUSTIFY_RIGHT, NULL);
115
116   gtk_text_buffer_create_tag (buffer, "wide_margins",
117                               "left_margin", 50, "right_margin", 50,
118                               NULL);
119   
120   gtk_text_buffer_create_tag (buffer, "strikethrough",
121                               "strikethrough", TRUE, NULL);
122   
123   gtk_text_buffer_create_tag (buffer, "underline",
124                               "underline", PANGO_UNDERLINE_SINGLE, NULL);
125
126   gtk_text_buffer_create_tag (buffer, "double_underline",
127                               "underline", PANGO_UNDERLINE_DOUBLE, NULL);
128
129   gtk_text_buffer_create_tag (buffer, "superscript",
130                               "rise", 10 * PANGO_SCALE,   /* 10 pixels */
131                               "size", 8 * PANGO_SCALE,    /* 8 points */
132                               NULL);
133   
134   gtk_text_buffer_create_tag (buffer, "subscript",
135                               "rise", -10 * PANGO_SCALE,   /* 10 pixels */
136                               "size", 8 * PANGO_SCALE,     /* 8 points */
137                               NULL);
138
139   gtk_text_buffer_create_tag (buffer, "rtl_quote",
140                               "wrap_mode", GTK_WRAP_WORD,
141                               "direction", GTK_TEXT_DIR_RTL,
142                               "indent", 30,
143                               "left_margin", 20,
144                               "right_margin", 20,
145                               NULL);
146 }
147
148 static void
149 insert_text (GtkTextBuffer *buffer)
150 {
151   GtkTextIter iter;
152   GtkTextIter start, end;
153   GdkPixbuf *pixbuf;
154   GdkPixbuf *scaled;
155   GtkTextChildAnchor *anchor;
156   char *filename;
157
158   /* demo_find_file() looks in the current directory first,
159    * so you can run gtk-demo without installing GTK, then looks
160    * in the location where the file is installed.
161    */
162   pixbuf = NULL;
163   filename = demo_find_file ("gtk-logo-rgb.gif", NULL);
164   if (filename)
165     {
166       pixbuf = gdk_pixbuf_new_from_file (filename, NULL);
167       g_free (filename);
168     }
169
170   if (pixbuf == NULL)
171     {
172       g_printerr ("Failed to load image file gtk-logo-rgb.gif\n");
173       exit (1);
174     }
175
176   scaled = gdk_pixbuf_scale_simple (pixbuf, 32, 32, GDK_INTERP_BILINEAR);
177   g_object_unref (pixbuf);
178   pixbuf = scaled;
179   
180   /* get start of buffer; each insertion will revalidate the
181    * iterator to point to just after the inserted text.
182    */
183   gtk_text_buffer_get_iter_at_offset (buffer, &iter, 0);
184
185   gtk_text_buffer_insert (buffer, &iter, "The text widget can display text with all kinds of nifty attributes. It also supports multiple views of the same buffer; this demo is showing the same buffer in two places.\n\n", -1);
186
187   gtk_text_buffer_insert_with_tags_by_name (buffer, &iter, "Font styles. ", -1,
188                                             "heading", NULL);
189   
190   gtk_text_buffer_insert (buffer, &iter, "For example, you can have ", -1);
191   gtk_text_buffer_insert_with_tags_by_name (buffer, &iter,
192                                             "italic", -1,
193                                             "italic", NULL);
194   gtk_text_buffer_insert (buffer, &iter, ", ", -1);  
195   gtk_text_buffer_insert_with_tags_by_name (buffer, &iter,
196                                             "bold", -1,
197                                             "bold", NULL);
198   gtk_text_buffer_insert (buffer, &iter, ", or ", -1);
199   gtk_text_buffer_insert_with_tags_by_name (buffer, &iter,
200                                             "monospace (typewriter)", -1,
201                                             "monospace", NULL);
202   gtk_text_buffer_insert (buffer, &iter, ", or ", -1);
203   gtk_text_buffer_insert_with_tags_by_name (buffer, &iter,
204                                             "big", -1,
205                                             "big", NULL);
206   gtk_text_buffer_insert (buffer, &iter, " text. ", -1);
207   gtk_text_buffer_insert (buffer, &iter, "It's best not to hardcode specific text sizes; you can use relative sizes as with CSS, such as ", -1);
208   gtk_text_buffer_insert_with_tags_by_name (buffer, &iter,
209                                             "xx-small", -1,
210                                             "xx-small", NULL);
211   gtk_text_buffer_insert (buffer, &iter, " or ", -1);
212   gtk_text_buffer_insert_with_tags_by_name (buffer, &iter,
213                                             "x-large", -1,
214                                             "x-large", NULL);
215   gtk_text_buffer_insert (buffer, &iter, " to ensure that your program properly adapts if the user changes the default font size.\n\n", -1);
216   
217   gtk_text_buffer_insert_with_tags_by_name (buffer, &iter, "Colors. ", -1,
218                                             "heading", NULL);
219   
220   gtk_text_buffer_insert (buffer, &iter, "Colors such as ", -1);  
221   gtk_text_buffer_insert_with_tags_by_name (buffer, &iter,
222                                             "a blue foreground", -1,
223                                             "blue_foreground", NULL);
224   gtk_text_buffer_insert (buffer, &iter, " or ", -1);  
225   gtk_text_buffer_insert_with_tags_by_name (buffer, &iter,
226                                             "a red background", -1,
227                                             "red_background", NULL);
228   gtk_text_buffer_insert (buffer, &iter, " or even ", -1);  
229   gtk_text_buffer_insert_with_tags_by_name (buffer, &iter,
230                                             "a stippled red background", -1,
231                                             "red_background",
232                                             "background_stipple",
233                                             NULL);
234
235   gtk_text_buffer_insert (buffer, &iter, " or ", -1);  
236   gtk_text_buffer_insert_with_tags_by_name (buffer, &iter,
237                                             "a stippled blue foreground on solid red background", -1,
238                                             "blue_foreground",
239                                             "red_background",
240                                             "foreground_stipple",
241                                             NULL);
242   gtk_text_buffer_insert (buffer, &iter, " (select that to read it) can be used.\n\n", -1);  
243
244   gtk_text_buffer_insert_with_tags_by_name (buffer, &iter, "Underline, strikethrough, and rise. ", -1,
245                                             "heading", NULL);
246   
247   gtk_text_buffer_insert_with_tags_by_name (buffer, &iter,
248                                             "Strikethrough", -1,
249                                             "strikethrough", NULL);
250   gtk_text_buffer_insert (buffer, &iter, ", ", -1);
251   gtk_text_buffer_insert_with_tags_by_name (buffer, &iter,
252                                             "underline", -1,
253                                             "underline", NULL);
254   gtk_text_buffer_insert (buffer, &iter, ", ", -1);
255   gtk_text_buffer_insert_with_tags_by_name (buffer, &iter,
256                                             "double underline", -1, 
257                                             "double_underline", NULL);
258   gtk_text_buffer_insert (buffer, &iter, ", ", -1);
259   gtk_text_buffer_insert_with_tags_by_name (buffer, &iter,
260                                             "superscript", -1,
261                                             "superscript", NULL);
262   gtk_text_buffer_insert (buffer, &iter, ", and ", -1);
263   gtk_text_buffer_insert_with_tags_by_name (buffer, &iter,
264                                             "subscript", -1,
265                                             "subscript", NULL);
266   gtk_text_buffer_insert (buffer, &iter, " are all supported.\n\n", -1);
267
268   gtk_text_buffer_insert_with_tags_by_name (buffer, &iter, "Images. ", -1,
269                                             "heading", NULL);
270   
271   gtk_text_buffer_insert (buffer, &iter, "The buffer can have images in it: ", -1);
272   gtk_text_buffer_insert_pixbuf (buffer, &iter, pixbuf);
273   gtk_text_buffer_insert_pixbuf (buffer, &iter, pixbuf);
274   gtk_text_buffer_insert_pixbuf (buffer, &iter, pixbuf);
275   gtk_text_buffer_insert (buffer, &iter, " for example.\n\n", -1);
276
277   gtk_text_buffer_insert_with_tags_by_name (buffer, &iter, "Spacing. ", -1,
278                                             "heading", NULL);
279
280   gtk_text_buffer_insert (buffer, &iter, "You can adjust the amount of space before each line.\n", -1);
281   
282   gtk_text_buffer_insert_with_tags_by_name (buffer, &iter,
283                                             "This line has a whole lot of space before it.\n", -1,
284                                             "big_gap_before_line", "wide_margins", NULL);
285   gtk_text_buffer_insert_with_tags_by_name (buffer, &iter,
286                                             "You can also adjust the amount of space after each line; this line has a whole lot of space after it.\n", -1,
287                                             "big_gap_after_line", "wide_margins", NULL);
288   
289   gtk_text_buffer_insert_with_tags_by_name (buffer, &iter,
290                                             "You can also adjust the amount of space between wrapped lines; this line has extra space between each wrapped line in the same paragraph. To show off wrapping, some filler text: the quick brown fox jumped over the lazy dog. Blah blah blah blah blah blah blah blah blah.\n", -1,
291                                             "double_spaced_line", "wide_margins", NULL);
292
293   gtk_text_buffer_insert (buffer, &iter, "Also note that those lines have extra-wide margins.\n\n", -1);
294
295   gtk_text_buffer_insert_with_tags_by_name (buffer, &iter, "Editability. ", -1,
296                                             "heading", NULL);
297   
298   gtk_text_buffer_insert_with_tags_by_name (buffer, &iter,
299                                             "This line is 'locked down' and can't be edited by the user - just try it! You can't delete this line.\n\n", -1,
300                                             "not_editable", NULL);
301
302   gtk_text_buffer_insert_with_tags_by_name (buffer, &iter, "Wrapping. ", -1,
303                                             "heading", NULL);
304
305   gtk_text_buffer_insert (buffer, &iter,
306                           "This line (and most of the others in this buffer) is word-wrapped, using the proper Unicode algorithm. Word wrap should work in all scripts and languages that GTK+ supports. Let's make this a long paragraph to demonstrate: blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah\n\n", -1);  
307   
308   gtk_text_buffer_insert_with_tags_by_name (buffer, &iter,
309                                             "This line has character-based wrapping, and can wrap between any two character glyphs. Let's make this a long paragraph to demonstrate: blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah\n\n", -1,
310                                             "char_wrap", NULL);
311   
312   gtk_text_buffer_insert_with_tags_by_name (buffer, &iter,
313                                             "This line has all wrapping turned off, so it makes the horizontal scrollbar appear.\n\n\n", -1,
314                                             "no_wrap", NULL);
315
316   gtk_text_buffer_insert_with_tags_by_name (buffer, &iter, "Justification. ", -1,
317                                             "heading", NULL);  
318   
319   gtk_text_buffer_insert_with_tags_by_name (buffer, &iter,
320                                             "\nThis line has center justification.\n", -1,
321                                             "center", NULL);
322
323   gtk_text_buffer_insert_with_tags_by_name (buffer, &iter,
324                                             "This line has right justification.\n", -1,
325                                             "right_justify", NULL);
326
327   gtk_text_buffer_insert_with_tags_by_name (buffer, &iter,
328                                             "\nThis line has big wide margins. Text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text.\n", -1,
329                                             "wide_margins", NULL);  
330
331   gtk_text_buffer_insert_with_tags_by_name (buffer, &iter, "Internationalization. ", -1,
332                                             "heading", NULL);
333           
334   gtk_text_buffer_insert (buffer, &iter,
335                           "You can put all sorts of Unicode text in the buffer.\n\nGerman (Deutsch S\303\274d) Gr\303\274\303\237 Gott\nGreek (\316\225\316\273\316\273\316\267\316\275\316\271\316\272\316\254) \316\223\316\265\316\271\316\254 \317\203\316\261\317\202\nHebrew      \327\251\327\234\327\225\327\235\nJapanese (\346\227\245\346\234\254\350\252\236)\n\nThe widget properly handles bidirectional text, word wrapping, DOS/UNIX/Unicode paragraph separators, grapheme boundaries, and so on using the Pango internationalization framework.\n", -1);  
336
337   gtk_text_buffer_insert (buffer, &iter, "Here's a word-wrapped quote in a right-to-left language:\n", -1);
338   gtk_text_buffer_insert_with_tags_by_name (buffer, &iter, "\331\210\331\202\330\257 \330\250\330\257\330\243 \330\253\331\204\330\247\330\253 \331\205\331\206 \330\243\331\203\330\253\330\261 \330\247\331\204\331\205\330\244\330\263\330\263\330\247\330\252 \330\252\331\202\330\257\331\205\330\247 \331\201\331\212 \330\264\330\250\331\203\330\251 \330\247\331\203\330\263\331\212\331\210\331\206 \330\250\330\261\330\247\331\205\330\254\331\207\330\247 \331\203\331\205\331\206\330\270\331\205\330\247\330\252 \331\204\330\247 \330\252\330\263\330\271\331\211 \331\204\331\204\330\261\330\250\330\255\330\214 \330\253\331\205 \330\252\330\255\331\210\331\204\330\252 \331\201\331\212 \330\247\331\204\330\263\331\206\331\210\330\247\330\252 \330\247\331\204\330\256\331\205\330\263 \330\247\331\204\331\205\330\247\330\266\331\212\330\251 \330\245\331\204\331\211 \331\205\330\244\330\263\330\263\330\247\330\252 \331\205\330\247\331\204\331\212\330\251 \331\205\331\206\330\270\331\205\330\251\330\214 \331\210\330\250\330\247\330\252\330\252 \330\254\330\262\330\241\330\247 \331\205\331\206 \330\247\331\204\331\206\330\270\330\247\331\205 \330\247\331\204\331\205\330\247\331\204\331\212 \331\201\331\212 \330\250\331\204\330\257\330\247\331\206\331\207\330\247\330\214 \331\210\331\204\331\203\331\206\331\207\330\247 \330\252\330\252\330\256\330\265\330\265 \331\201\331\212 \330\256\330\257\331\205\330\251 \331\202\330\267\330\247\330\271 \330\247\331\204\331\205\330\264\330\261\331\210\330\271\330\247\330\252 \330\247\331\204\330\265\330\272\331\212\330\261\330\251. \331\210\330\243\330\255\330\257 \330\243\331\203\330\253\330\261 \331\207\330\260\331\207 \330\247\331\204\331\205\330\244\330\263\330\263\330\247\330\252 \331\206\330\254\330\247\330\255\330\247 \331\207\331\210 \302\273\330\250\330\247\331\206\331\203\331\210\330\263\331\210\331\204\302\253 \331\201\331\212 \330\250\331\210\331\204\331\212\331\201\331\212\330\247.\n\n", -1,
339                                                 "rtl_quote", NULL);
340       
341   gtk_text_buffer_insert (buffer, &iter, "You can put widgets in the buffer: Here's a button: ", -1);
342   anchor = gtk_text_buffer_create_child_anchor (buffer, &iter);
343   gtk_text_buffer_insert (buffer, &iter, " and a menu: ", -1);
344   anchor = gtk_text_buffer_create_child_anchor (buffer, &iter);
345   gtk_text_buffer_insert (buffer, &iter, " and a scale: ", -1);
346   anchor = gtk_text_buffer_create_child_anchor (buffer, &iter);
347   gtk_text_buffer_insert (buffer, &iter, " and an animation: ", -1);
348   anchor = gtk_text_buffer_create_child_anchor (buffer, &iter);
349   gtk_text_buffer_insert (buffer, &iter, " finally a text entry: ", -1);
350   anchor = gtk_text_buffer_create_child_anchor (buffer, &iter);
351   gtk_text_buffer_insert (buffer, &iter, ".\n", -1);
352   
353   gtk_text_buffer_insert (buffer, &iter, "\n\nThis demo doesn't demonstrate all the GtkTextBuffer features; it leaves out, for example: invisible/hidden text, tab stops, application-drawn areas on the sides of the widget for displaying breakpoints and such...", -1);
354
355   /* Apply word_wrap tag to whole buffer */
356   gtk_text_buffer_get_bounds (buffer, &start, &end);
357   gtk_text_buffer_apply_tag_by_name (buffer, "word_wrap", &start, &end);
358
359   g_object_unref (pixbuf);
360 }
361
362 static gboolean
363 find_anchor (GtkTextIter *iter)
364 {
365   while (gtk_text_iter_forward_char (iter))
366     {
367       if (gtk_text_iter_get_child_anchor (iter))
368         return TRUE;
369     }
370   return FALSE;
371 }
372
373 static void
374 attach_widgets (GtkTextView *text_view)
375 {
376   GtkTextIter iter;
377   GtkTextBuffer *buffer;
378   int i;
379   
380   buffer = gtk_text_view_get_buffer (text_view);
381
382   gtk_text_buffer_get_start_iter (buffer, &iter);
383
384   i = 0;
385   while (find_anchor (&iter))
386     {
387       GtkTextChildAnchor *anchor;
388       GtkWidget *widget;
389       
390       anchor = gtk_text_iter_get_child_anchor (&iter);
391
392       if (i == 0)
393         {
394           widget = gtk_button_new_with_label ("Click Me");
395
396           g_signal_connect (widget, "clicked",
397                             G_CALLBACK (easter_egg_callback),
398                             NULL);
399         }
400       else if (i == 1)
401         {
402           widget = gtk_combo_box_new_text ();
403
404           gtk_combo_box_append_text (GTK_COMBO_BOX (widget), "Option 1");
405           gtk_combo_box_append_text (GTK_COMBO_BOX (widget), "Option 2");
406           gtk_combo_box_append_text (GTK_COMBO_BOX (widget), "Option 3");
407         }
408       else if (i == 2)
409         {
410           widget = gtk_hscale_new (NULL);
411           gtk_range_set_range (GTK_RANGE (widget), 0, 100);
412           gtk_widget_set_size_request (widget, 70, -1);
413         }
414       else if (i == 3)
415         {
416           gchar *filename = demo_find_file ("floppybuddy.gif", NULL);
417           widget = gtk_image_new_from_file (filename);
418           g_free (filename);
419         }
420       else if (i == 4)
421         {
422           widget = gtk_entry_new ();
423         }
424       else
425         {
426           widget = NULL; /* avoids a compiler warning */
427           g_assert_not_reached ();
428         }
429
430       gtk_text_view_add_child_at_anchor (text_view,
431                                          widget,
432                                          anchor);
433
434       gtk_widget_show_all (widget);
435
436       ++i;
437     }
438 }
439
440 GtkWidget *
441 do_textview (GtkWidget *do_widget)
442 {
443   static GtkWidget *window = NULL;
444
445   if (!window)
446     {
447       GtkWidget *vpaned;
448       GtkWidget *view1;
449       GtkWidget *view2;
450       GtkWidget *sw;
451       GtkTextBuffer *buffer;
452       
453       window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
454       gtk_window_set_screen (GTK_WINDOW (window),
455                              gtk_widget_get_screen (do_widget));
456       gtk_window_set_default_size (GTK_WINDOW (window),
457                                    450, 450);
458       
459       g_signal_connect (window, "destroy",
460                         G_CALLBACK (gtk_widget_destroyed), &window);
461
462       gtk_window_set_title (GTK_WINDOW (window), "TextView");
463       gtk_container_set_border_width (GTK_CONTAINER (window), 0);
464
465       vpaned = gtk_vpaned_new ();
466       gtk_container_set_border_width (GTK_CONTAINER(vpaned), 5);
467       gtk_container_add (GTK_CONTAINER (window), vpaned);
468
469       /* For convenience, we just use the autocreated buffer from
470        * the first text view; you could also create the buffer
471        * by itself with gtk_text_buffer_new(), then later create
472        * a view widget.
473        */
474       view1 = gtk_text_view_new ();
475       buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view1));
476       view2 = gtk_text_view_new_with_buffer (buffer);
477       
478       sw = gtk_scrolled_window_new (NULL, NULL);
479       gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
480                                       GTK_POLICY_AUTOMATIC,
481                                       GTK_POLICY_AUTOMATIC);
482       gtk_paned_add1 (GTK_PANED (vpaned), sw);
483
484       gtk_container_add (GTK_CONTAINER (sw), view1);
485
486       sw = gtk_scrolled_window_new (NULL, NULL);
487       gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
488                                       GTK_POLICY_AUTOMATIC,
489                                       GTK_POLICY_AUTOMATIC);
490       gtk_paned_add2 (GTK_PANED (vpaned), sw);
491
492       gtk_container_add (GTK_CONTAINER (sw), view2);
493
494       create_tags (buffer);
495       insert_text (buffer);
496
497       attach_widgets (GTK_TEXT_VIEW (view1));
498       attach_widgets (GTK_TEXT_VIEW (view2));
499       
500       gtk_widget_show_all (vpaned);
501     }
502
503   if (!gtk_widget_get_visible (window))
504     {
505       gtk_widget_show (window);
506     }
507   else
508     {
509       gtk_widget_destroy (window);
510       window = NULL;
511     }
512
513   return window;
514 }
515
516 static void
517 recursive_attach_view (int                 depth,
518                        GtkTextView        *view,
519                        GtkTextChildAnchor *anchor)
520 {
521   GtkWidget *child_view;
522   GtkWidget *event_box;
523   GdkColor color;
524   GtkWidget *align;
525   
526   if (depth > 4)
527     return;
528   
529   child_view = gtk_text_view_new_with_buffer (gtk_text_view_get_buffer (view));
530
531   /* Event box is to add a black border around each child view */
532   event_box = gtk_event_box_new ();
533   gdk_color_parse ("black", &color);
534   gtk_widget_modify_bg (event_box, GTK_STATE_NORMAL, &color);
535
536   align = gtk_alignment_new (0.5, 0.5, 1.0, 1.0);
537   gtk_container_set_border_width (GTK_CONTAINER (align), 1);
538   
539   gtk_container_add (GTK_CONTAINER (event_box), align);
540   gtk_container_add (GTK_CONTAINER (align), child_view);
541   
542   gtk_text_view_add_child_at_anchor (view, event_box, anchor);
543
544   recursive_attach_view (depth + 1, GTK_TEXT_VIEW (child_view), anchor);
545 }
546
547 static void
548 easter_egg_callback (GtkWidget *button,
549                      gpointer   data)
550 {
551   static GtkWidget *window = NULL;
552   gpointer window_ptr;
553   GtkTextBuffer *buffer;
554   GtkWidget     *view;
555   GtkTextIter    iter;
556   GtkTextChildAnchor *anchor;
557   GtkWidget *sw;
558
559   if (window)
560     {
561       gtk_window_present (GTK_WINDOW (window));
562       return;
563     }
564   
565   buffer = gtk_text_buffer_new (NULL);
566
567   gtk_text_buffer_get_start_iter (buffer, &iter);
568
569   gtk_text_buffer_insert (buffer, &iter,
570                           "This buffer is shared by a set of nested text views.\n Nested view:\n", -1);
571   anchor = gtk_text_buffer_create_child_anchor (buffer, &iter);
572   gtk_text_buffer_insert (buffer, &iter,
573                           "\nDon't do this in real applications, please.\n", -1);
574
575   view = gtk_text_view_new_with_buffer (buffer);
576   
577   recursive_attach_view (0, GTK_TEXT_VIEW (view), anchor);
578   
579   g_object_unref (buffer);
580
581   window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
582   sw = gtk_scrolled_window_new (NULL, NULL);
583   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
584                                   GTK_POLICY_AUTOMATIC,
585                                   GTK_POLICY_AUTOMATIC);
586
587   gtk_container_add (GTK_CONTAINER (window), sw);
588   gtk_container_add (GTK_CONTAINER (sw), view);
589
590   window_ptr = &window;
591   g_object_add_weak_pointer (G_OBJECT (window), window_ptr);
592
593   gtk_window_set_default_size (GTK_WINDOW (window), 300, 400);
594   
595   gtk_widget_show_all (window);
596 }
597