]> Pileus Git - ~andy/gtk/blob - demos/gtk-demo/textview.c
7693862ade66c487994ebb44fa8a79180b309c48
[~andy/gtk] / demos / gtk-demo / textview.c
1 /* Text Widget
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 #define gray50_width 2
14 #define gray50_height 2
15 static char gray50_bits[] = {
16   0x02, 0x01
17 };
18
19 static void
20 create_tags (GtkTextBuffer *buffer)
21 {
22   GdkBitmap *stipple;
23
24   /* Create a bunch of tags. Note that it's also possible to
25    * create tags with gtk_text_tag_new() then add them to the
26    * tag table for the buffer, gtk_text_buffer_create_tag() is
27    * just a convenience function. Also note that you don't have
28    * to give tags a name; pass NULL for the name to create an
29    * anonymous tag.
30    *
31    * In any real app, another useful optimization would be to create
32    * a GtkTextTagTable in advance, and reuse the same tag table for
33    * all the buffers with the same tag set, instead of creating
34    * new copies of the same tags for every buffer.
35    *
36    * Tags are assigned default priorities in order of addition to the
37    * tag table.  That is, tags created later that affect the same text
38    * property affected by an earlier tag will override the earlier
39    * tag.  You can modify tag priorities with
40    * gtk_text_tag_set_priority().
41    */
42
43   gtk_text_buffer_create_tag (buffer, "heading",
44                               "weight", PANGO_WEIGHT_BOLD,
45                               "size", 15 * PANGO_SCALE,
46                               NULL);
47   
48   gtk_text_buffer_create_tag (buffer, "italic",
49                               "style", PANGO_STYLE_ITALIC, NULL);
50
51   gtk_text_buffer_create_tag (buffer, "bold",
52                               "weight", PANGO_WEIGHT_BOLD, NULL);  
53   
54   gtk_text_buffer_create_tag (buffer, "big",
55                               /* points times the PANGO_SCALE factor */
56                               "size", 20 * PANGO_SCALE, NULL);
57
58   gtk_text_buffer_create_tag (buffer, "xx-small",
59                               "scale", PANGO_SCALE_XX_SMALL, NULL);
60
61   gtk_text_buffer_create_tag (buffer, "x-large",
62                               "scale", PANGO_SCALE_X_LARGE, NULL);
63   
64   gtk_text_buffer_create_tag (buffer, "monospace",
65                               "family", "monospace", NULL);
66   
67   gtk_text_buffer_create_tag (buffer, "blue_foreground",
68                               "foreground", "blue", NULL);  
69
70   gtk_text_buffer_create_tag (buffer, "red_background",
71                               "background", "red", NULL);
72
73   stipple = gdk_bitmap_create_from_data (NULL,
74                                          gray50_bits, gray50_width,
75                                          gray50_height);
76   
77   gtk_text_buffer_create_tag (buffer, "background_stipple",
78                               "background_stipple", stipple, NULL);
79
80   gtk_text_buffer_create_tag (buffer, "foreground_stipple",
81                               "foreground_stipple", stipple, NULL);
82
83   g_object_unref (G_OBJECT (stipple));
84
85   gtk_text_buffer_create_tag (buffer, "big_gap_before_line",
86                               "pixels_above_lines", 30, NULL);
87
88   gtk_text_buffer_create_tag (buffer, "big_gap_after_line",
89                               "pixels_below_lines", 30, NULL);
90
91   gtk_text_buffer_create_tag (buffer, "double_spaced_line",
92                               "pixels_inside_wrap", 10, NULL);
93
94   gtk_text_buffer_create_tag (buffer, "not_editable",
95                               "editable", FALSE, NULL);
96   
97   gtk_text_buffer_create_tag (buffer, "word_wrap",
98                               "wrap_mode", GTK_WRAP_WORD, NULL);
99
100   gtk_text_buffer_create_tag (buffer, "char_wrap",
101                               "wrap_mode", GTK_WRAP_CHAR, NULL);
102
103   gtk_text_buffer_create_tag (buffer, "no_wrap",
104                               "wrap_mode", GTK_WRAP_NONE, NULL);
105   
106   gtk_text_buffer_create_tag (buffer, "center",
107                               "justification", GTK_JUSTIFY_CENTER, NULL);
108
109   gtk_text_buffer_create_tag (buffer, "right_justify",
110                               "justification", GTK_JUSTIFY_RIGHT, NULL);
111
112   gtk_text_buffer_create_tag (buffer, "wide_margins",
113                               "left_margin", 50, "right_margin", 50,
114                               NULL);
115   
116   gtk_text_buffer_create_tag (buffer, "strikethrough",
117                               "strikethrough", TRUE, NULL);
118   
119   gtk_text_buffer_create_tag (buffer, "underline",
120                               "underline", PANGO_UNDERLINE_SINGLE, NULL);
121
122   gtk_text_buffer_create_tag (buffer, "double_underline",
123                               "underline", PANGO_UNDERLINE_DOUBLE, NULL);
124
125   gtk_text_buffer_create_tag (buffer, "superscript",
126                               "rise", 10 * PANGO_SCALE,   /* 10 pixels */
127                               "size", 8 * PANGO_SCALE,    /* 8 points */
128                               NULL);
129   
130   gtk_text_buffer_create_tag (buffer, "subscript",
131                               "rise", -10 * PANGO_SCALE,   /* 10 pixels */
132                               "size", 8 * PANGO_SCALE,     /* 8 points */
133                               NULL);
134
135   gtk_text_buffer_create_tag (buffer, "rtl_quote",
136                               "wrap_mode", GTK_WRAP_WORD,
137                               "direction", GTK_TEXT_DIR_RTL,
138                               "indent", 30,
139                               "left_margin", 20,
140                               "right_margin", 20,
141                               NULL);
142 }
143
144 static void
145 insert_text (GtkTextBuffer *buffer)
146 {
147   GtkTextIter iter;
148   GtkTextIter start, end;
149   GdkPixbuf *pixbuf;
150   GdkPixbuf *scaled;
151   GtkTextChildAnchor *anchor;
152
153   pixbuf = gdk_pixbuf_new_from_file ("./gtk-logo-rgb.gif", NULL);
154   if (pixbuf == NULL)
155     gdk_pixbuf_new_from_file (DEMOCODEDIR"/gtk-logo-rgb.gif", NULL);
156
157   if (pixbuf == NULL)
158     {
159       g_printerr ("Failed to load image file gtk-logo-rgb.gif\n");
160       exit (1);
161     }
162
163   scaled = gdk_pixbuf_scale_simple (pixbuf, 32, 32, GDK_INTERP_BILINEAR);
164   g_object_unref (G_OBJECT (pixbuf));
165   pixbuf = scaled;
166   
167   /* get start of buffer; each insertion will revalidate the
168    * iterator to point to just after the inserted text.
169    */
170   gtk_text_buffer_get_iter_at_offset (buffer, &iter, 0);
171
172   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);
173
174   gtk_text_buffer_insert_with_tags_by_name (buffer, &iter, "Font styles. ", -1,
175                                             "heading", NULL);
176   
177   gtk_text_buffer_insert (buffer, &iter, "For example, you can have ", -1);
178   gtk_text_buffer_insert_with_tags_by_name (buffer, &iter,
179                                             "italic", -1,
180                                             "italic", NULL);
181   gtk_text_buffer_insert (buffer, &iter, ", ", -1);  
182   gtk_text_buffer_insert_with_tags_by_name (buffer, &iter,
183                                             "bold", -1,
184                                             "bold", NULL);
185   gtk_text_buffer_insert (buffer, &iter, ", or ", -1);
186   gtk_text_buffer_insert_with_tags_by_name (buffer, &iter,
187                                             "monospace (typewriter)", -1,
188                                             "monospace", NULL);
189   gtk_text_buffer_insert (buffer, &iter, ", or ", -1);
190   gtk_text_buffer_insert_with_tags_by_name (buffer, &iter,
191                                             "big", -1,
192                                             "big", NULL);
193   gtk_text_buffer_insert (buffer, &iter, " text. ", -1);
194   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);
195   gtk_text_buffer_insert_with_tags_by_name (buffer, &iter,
196                                             "xx-small", -1,
197                                             "xx-small", NULL);
198   gtk_text_buffer_insert (buffer, &iter, " or ", -1);
199   gtk_text_buffer_insert_with_tags_by_name (buffer, &iter,
200                                             "x-large", -1,
201                                             "x-large", NULL);
202   gtk_text_buffer_insert (buffer, &iter, " to ensure that your program properly adapts if the user changes the default font size.\n\n", -1);
203   
204   gtk_text_buffer_insert_with_tags_by_name (buffer, &iter, "Colors. ", -1,
205                                             "heading", NULL);
206   
207   gtk_text_buffer_insert (buffer, &iter, "Colors such as ", -1);  
208   gtk_text_buffer_insert_with_tags_by_name (buffer, &iter,
209                                             "a blue foreground", -1,
210                                             "blue_foreground", NULL);
211   gtk_text_buffer_insert (buffer, &iter, " or ", -1);  
212   gtk_text_buffer_insert_with_tags_by_name (buffer, &iter,
213                                             "a red background", -1,
214                                             "red_background", NULL);
215   gtk_text_buffer_insert (buffer, &iter, " or even ", -1);  
216   gtk_text_buffer_insert_with_tags_by_name (buffer, &iter,
217                                             "a stippled red background", -1,
218                                             "red_background",
219                                             "background_stipple",
220                                             NULL);
221
222   gtk_text_buffer_insert (buffer, &iter, " or ", -1);  
223   gtk_text_buffer_insert_with_tags_by_name (buffer, &iter,
224                                             "a stippled blue foreground on solid red background", -1,
225                                             "blue_foreground",
226                                             "red_background",
227                                             "foreground_stipple",
228                                             NULL);
229   gtk_text_buffer_insert (buffer, &iter, " (select that to read it) can be used.\n\n", -1);  
230
231   gtk_text_buffer_insert_with_tags_by_name (buffer, &iter, "Underline, strikethrough, and rise. ", -1,
232                                             "heading", NULL);
233   
234   gtk_text_buffer_insert_with_tags_by_name (buffer, &iter,
235                                             "Strikethrough", -1,
236                                             "strikethrough", NULL);
237   gtk_text_buffer_insert (buffer, &iter, ", ", -1);
238   gtk_text_buffer_insert_with_tags_by_name (buffer, &iter,
239                                             "underline", -1,
240                                             "underline", NULL);
241   gtk_text_buffer_insert (buffer, &iter, ", ", -1);
242   gtk_text_buffer_insert_with_tags_by_name (buffer, &iter,
243                                             "double underline", -1, 
244                                             "double_underline", NULL);
245   gtk_text_buffer_insert (buffer, &iter, ", ", -1);
246   gtk_text_buffer_insert_with_tags_by_name (buffer, &iter,
247                                             "superscript", -1,
248                                             "superscript", NULL);
249   gtk_text_buffer_insert (buffer, &iter, ", and ", -1);
250   gtk_text_buffer_insert_with_tags_by_name (buffer, &iter,
251                                             "subscript", -1,
252                                             "subscript", NULL);
253   gtk_text_buffer_insert (buffer, &iter, " are all supported.\n\n", -1);
254
255   gtk_text_buffer_insert_with_tags_by_name (buffer, &iter, "Images. ", -1,
256                                             "heading", NULL);
257   
258   gtk_text_buffer_insert (buffer, &iter, "The buffer can have images in it: ", -1);
259   gtk_text_buffer_insert_pixbuf (buffer, &iter, pixbuf);
260   gtk_text_buffer_insert_pixbuf (buffer, &iter, pixbuf);
261   gtk_text_buffer_insert_pixbuf (buffer, &iter, pixbuf);
262   gtk_text_buffer_insert (buffer, &iter, " for example.\n\n", -1);
263
264   gtk_text_buffer_insert_with_tags_by_name (buffer, &iter, "Spacing. ", -1,
265                                             "heading", NULL);
266
267   gtk_text_buffer_insert (buffer, &iter, "You can adjust the amount of space before each line.\n", -1);
268   
269   gtk_text_buffer_insert_with_tags_by_name (buffer, &iter,
270                                             "This line has a whole lot of space before it.\n", -1,
271                                             "big_gap_before_line", "wide_margins", NULL);
272   gtk_text_buffer_insert_with_tags_by_name (buffer, &iter,
273                                             "You can also adjust the amount of space after each line; this line has a whole lot of space after it.\n", -1,
274                                             "big_gap_after_line", "wide_margins", NULL);
275   
276   gtk_text_buffer_insert_with_tags_by_name (buffer, &iter,
277                                             "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,
278                                             "double_spaced_line", "wide_margins", NULL);
279
280   gtk_text_buffer_insert (buffer, &iter, "Also note that those lines have extra-wide margins.\n\n", -1);
281
282   gtk_text_buffer_insert_with_tags_by_name (buffer, &iter, "Editability. ", -1,
283                                             "heading", NULL);
284   
285   gtk_text_buffer_insert_with_tags_by_name (buffer, &iter,
286                                             "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,
287                                             "not_editable", NULL);
288
289   gtk_text_buffer_insert_with_tags_by_name (buffer, &iter, "Wrapping. ", -1,
290                                             "heading", NULL);
291
292   gtk_text_buffer_insert (buffer, &iter,
293                           "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);  
294   
295   gtk_text_buffer_insert_with_tags_by_name (buffer, &iter,
296                                             "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,
297                                             "char_wrap", NULL);
298   
299   gtk_text_buffer_insert_with_tags_by_name (buffer, &iter,
300                                             "This line has all wrapping turned off, so it makes the horizontal scrollbar appear.\n\n\n", -1,
301                                             "no_wrap", NULL);
302
303   gtk_text_buffer_insert_with_tags_by_name (buffer, &iter, "Justification. ", -1,
304                                             "heading", NULL);  
305   
306   gtk_text_buffer_insert_with_tags_by_name (buffer, &iter,
307                                             "\nThis line has center justification.\n", -1,
308                                             "center", NULL);
309
310   gtk_text_buffer_insert_with_tags_by_name (buffer, &iter,
311                                             "This line has right justification.\n", -1,
312                                             "right_justify", NULL);
313
314   gtk_text_buffer_insert_with_tags_by_name (buffer, &iter,
315                                             "\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,
316                                             "wide_margins", NULL);  
317
318   gtk_text_buffer_insert_with_tags_by_name (buffer, &iter, "Internationalization. ", -1,
319                                             "heading", NULL);
320           
321   gtk_text_buffer_insert (buffer, &iter,
322                           "You can put all sorts of Unicode text in the buffer.\n\nGerman (Deutsch Süd) Grüß Gott\nGreek (Ελληνικά) Γειά σας\nHebrew  שלום\nJapanese (日本語)\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);  
323
324   gtk_text_buffer_insert (buffer, &iter, "Here's a word-wrapped quote in a right-to-left language:\n", -1);
325   gtk_text_buffer_insert_with_tags_by_name (buffer, &iter, "وقد بدأ ثلاث من أكثر المؤسسات تقدما في شبكة اكسيون برامجها كمنظمات لا تسعى للربح، ثم تحولت في السنوات الخمس الماضية إلى مؤسسات مالية منظمة، وباتت جزءا من النظام المالي في بلدانها، ولكنها تتخصص في خدمة قطاع المشروعات الصغيرة. وأحد أكثر هذه المؤسسات نجاحا هو »بانكوسول« في بوليفيا.\n\n", -1,
326                                                 "rtl_quote", NULL);
327       
328   gtk_text_buffer_insert (buffer, &iter, "You can put widgets in the buffer: Here's a button: ", -1);
329   anchor = gtk_text_buffer_create_child_anchor (buffer, &iter);
330   gtk_text_buffer_insert (buffer, &iter, " and a menu: ", -1);
331   anchor = gtk_text_buffer_create_child_anchor (buffer, &iter);
332   gtk_text_buffer_insert (buffer, &iter, " and a scale: ", -1);
333   anchor = gtk_text_buffer_create_child_anchor (buffer, &iter);
334   gtk_text_buffer_insert (buffer, &iter, " and an animation: ", -1);
335   anchor = gtk_text_buffer_create_child_anchor (buffer, &iter);
336   gtk_text_buffer_insert (buffer, &iter, " finally a text entry: ", -1);
337   anchor = gtk_text_buffer_create_child_anchor (buffer, &iter);
338   gtk_text_buffer_insert (buffer, &iter, ".\n", -1);
339   
340   gtk_text_buffer_insert (buffer, &iter, "\n\nThis demo doesn't demonstrate all the GtkTextBuffer features; it leaves out, for example: invisible/hidden text (doesn't work in GTK 2, but planned), tab stops, application-drawn areas on the sides of the widget for displaying breakpoints and such...", -1);
341
342   /* Apply word_wrap tag to whole buffer */
343   gtk_text_buffer_get_bounds (buffer, &start, &end);
344   gtk_text_buffer_apply_tag_by_name (buffer, "word_wrap", &start, &end);
345
346   g_object_unref (G_OBJECT (pixbuf));
347 }
348
349 static gboolean
350 find_anchor (GtkTextIter *iter)
351 {
352   while (gtk_text_iter_forward_char (iter))
353     {
354       if (gtk_text_iter_get_child_anchor (iter))
355         return TRUE;
356     }
357   return FALSE;
358 }
359
360 static void
361 attach_widgets (GtkTextView *text_view)
362 {
363   GtkTextIter iter;
364   GtkTextBuffer *buffer;
365   int i;
366   
367   buffer = gtk_text_view_get_buffer (text_view);
368
369   gtk_text_buffer_get_start_iter (buffer, &iter);
370
371   i = 0;
372   while (find_anchor (&iter))
373     {
374       GtkTextChildAnchor *anchor;
375       GtkWidget *widget;
376       
377       anchor = gtk_text_iter_get_child_anchor (&iter);
378
379       if (i == 0)
380         {
381           widget = gtk_button_new_with_label ("Click Me");
382         }
383       else if (i == 1)
384         {
385           GtkWidget *menu_item;
386           GtkWidget *menu;
387
388           menu = gtk_menu_new ();
389           
390           widget = gtk_option_menu_new ();
391
392           menu_item = gtk_menu_item_new_with_label ("Option 1");
393           gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
394           menu_item = gtk_menu_item_new_with_label ("Option 2");
395           gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
396           menu_item = gtk_menu_item_new_with_label ("Option 3");
397           gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
398
399           gtk_option_menu_set_menu (GTK_OPTION_MENU (widget), menu);
400         }
401       else if (i == 2)
402         {
403           widget = gtk_hscale_new (NULL);
404           gtk_range_set_range (GTK_RANGE (widget), 0, 100);
405           gtk_widget_set_size_request (widget, 70, -1);
406         }
407       else if (i == 3)
408         {
409           if (g_file_test ("./floppybuddy.gif", G_FILE_TEST_EXISTS))
410             widget = gtk_image_new_from_file ("./floppybuddy.gif");
411           else
412             widget = gtk_image_new_from_file (DEMOCODEDIR"/floppybuddy.gif");
413         }
414       else if (i == 4)
415         {
416           widget = gtk_entry_new ();
417         }
418       else
419         {
420           widget = NULL; /* avoids a compiler warning */
421           g_assert_not_reached ();
422         }
423
424       gtk_text_view_add_child_at_anchor (text_view,
425                                          widget,
426                                          anchor);
427
428       gtk_widget_show_all (widget);
429
430       ++i;
431     }
432 }
433
434 GtkWidget *
435 do_textview (void)
436 {
437   static GtkWidget *window = NULL;
438
439   if (!window)
440     {
441       GtkWidget *vpaned;
442       GtkWidget *view1;
443       GtkWidget *view2;
444       GtkWidget *sw;
445       GtkTextBuffer *buffer;
446       
447       window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
448       gtk_window_set_default_size (GTK_WINDOW (window),
449                                    450, 450);
450       
451       g_signal_connect (window, "destroy",
452                         G_CALLBACK (gtk_widget_destroyed), &window);
453
454       gtk_window_set_title (GTK_WINDOW (window), "TextView");
455       gtk_container_set_border_width (GTK_CONTAINER (window), 0);
456
457       vpaned = gtk_vpaned_new ();
458       gtk_container_set_border_width (GTK_CONTAINER(vpaned), 5);
459       gtk_container_add (GTK_CONTAINER (window), vpaned);
460
461       /* For convenience, we just use the autocreated buffer from
462        * the first text view; you could also create the buffer
463        * by itself with gtk_text_buffer_new(), then later create
464        * a view widget.
465        */
466       view1 = gtk_text_view_new ();
467       buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view1));
468       view2 = gtk_text_view_new_with_buffer (buffer);
469       
470       sw = gtk_scrolled_window_new (NULL, NULL);
471       gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
472                                       GTK_POLICY_AUTOMATIC,
473                                       GTK_POLICY_AUTOMATIC);
474       gtk_paned_add1 (GTK_PANED (vpaned), sw);
475
476       gtk_container_add (GTK_CONTAINER (sw), view1);
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_add2 (GTK_PANED (vpaned), sw);
483
484       gtk_container_add (GTK_CONTAINER (sw), view2);
485
486       create_tags (buffer);
487       insert_text (buffer);
488
489       attach_widgets (GTK_TEXT_VIEW (view1));
490       attach_widgets (GTK_TEXT_VIEW (view2));
491       
492       gtk_widget_show_all (vpaned);
493     }
494
495   if (!GTK_WIDGET_VISIBLE (window))
496     {
497       gtk_widget_show (window);
498     }
499   else
500     {
501       gtk_widget_destroy (window);
502       window = NULL;
503     }
504
505   return window;
506 }
507