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