1#!/usr/bin/perl -w 2# 3# Multiple Views 4# 5# The GtkTextView widget displays a GtkTextBuffer. One GtkTextBuffer 6# can be displayed by multiple GtkTextViews. This demo has two views 7# displaying a single buffer, and shows off the widget's text 8# formatting features. 9# 10 11package textview; 12 13use Glib qw(TRUE FALSE); 14use Gtk2; 15use Carp; 16 17# get the PANGO_WEIGHT_* constants 18use Gtk2::Pango; 19 20##static void easter_egg_callback (GtkWidget *button, gpointer data); 21 22use constant gray50_width => 2; 23use constant gray50_height => 2; 24my $gray50_bits = pack 'CC', 0x02, 0x01; 25 26sub create_tags { 27 my $buffer = shift; 28 29 # Create a bunch of tags. Note that it's also possible to 30 # create tags with gtk_text_tag_new() then add them to the 31 # tag table for the buffer, gtk_text_buffer_create_tag() is 32 # just a convenience function. Also note that you don't have 33 # to give tags a name; pass NULL for the name to create an 34 # anonymous tag. 35 # 36 # In any real app, another useful optimization would be to create 37 # a GtkTextTagTable in advance, and reuse the same tag table for 38 # all the buffers with the same tag set, instead of creating 39 # new copies of the same tags for every buffer. 40 # 41 # Tags are assigned default priorities in order of addition to the 42 # tag table. That is, tags created later that affect the same text 43 # property affected by an earlier tag will override the earlier 44 # tag. You can modify tag priorities with 45 # gtk_text_tag_set_priority(). 46 47 $buffer->create_tag ("heading", 48 weight => PANGO_WEIGHT_BOLD, 49 size => 15 * PANGO_SCALE, 50 ); 51 52 $buffer->create_tag ("italic", style => 'italic'); 53 $buffer->create_tag ("bold", weight => PANGO_WEIGHT_BOLD); 54 $buffer->create_tag ("big", size => 20 * PANGO_SCALE); 55 # points times the PANGO_SCALE factor 56 57 $buffer->create_tag ("xx-small", scale => PANGO_SCALE_XX_SMALL); 58 $buffer->create_tag ("x-large", scale => PANGO_SCALE_X_LARGE); 59 $buffer->create_tag ("monospace", family => "monospace"); 60 $buffer->create_tag ("blue_foreground", foreground => "blue"); 61 $buffer->create_tag ("red_background", background => "red"); 62 63 my $stipple = Gtk2::Gdk::Bitmap->create_from_data (undef, 64 $gray50_bits, gray50_width, 65 gray50_height); 66 67 $buffer->create_tag ("background_stipple", background_stipple => $stipple); 68 $buffer->create_tag ("foreground_stipple", foreground_stipple => $stipple); 69 70 $buffer->create_tag ("big_gap_before_line", pixels_above_lines => 30); 71 $buffer->create_tag ("big_gap_after_line", pixels_below_lines => 30); 72 $buffer->create_tag ("double_spaced_line", pixels_inside_wrap => 10); 73 $buffer->create_tag ("not_editable", editable => FALSE); 74 $buffer->create_tag ("word_wrap", wrap_mode => 'word'); 75 $buffer->create_tag ("char_wrap", wrap_mode => 'char'); 76 $buffer->create_tag ("no_wrap", wrap_mode => 'none'); 77 $buffer->create_tag ("center", justification => 'center'); 78 $buffer->create_tag ("right_justify", justification => 'right'); 79 $buffer->create_tag ("wide_margins", left_margin => 50, right_margin => 50); 80 $buffer->create_tag ("strikethrough", strikethrough => TRUE); 81 $buffer->create_tag ("underline", underline => 'single'); 82 $buffer->create_tag ("double_underline", underline => 'double'); 83 84 $buffer->create_tag ("superscript", 85 rise => 10 * PANGO_SCALE, # 10 pixels 86 size => 8 * PANGO_SCALE, # 8 points 87 ); 88 89 $buffer->create_tag ("subscript", 90 rise => -10 * PANGO_SCALE, # 10 pixels 91 size => 8 * PANGO_SCALE, # 8 points 92 ); 93 94 $buffer->create_tag ("rtl_quote", 95 wrap_mode => 'word', 96 direction => 'rtl', 97 indent => 30, 98 left_margin => 20, 99 right_margin => 20, 100 ); 101} 102 103sub insert_text { 104 my $buffer = shift; 105 106 # demo_find_file() looks in the the current directory first, 107 # so you can run gtk-demo without installing GTK, then looks 108 # in the location where the file is installed. 109 110 # croaks if it can't find the file 111 my $filename = "gtk-logo-rgb.gif"; 112 my $pixbuf; 113 eval { 114 $pixbuf = Gtk2::Gdk::Pixbuf->new_from_file ( 115 main::demo_find_file ($filename)); 116 }; 117 if ($@) { 118 die "caught exception from Gtk2::Gdk::Pixbuf->new_from_file --- $@"; 119 } 120 121 my $scaled = $pixbuf->scale_simple (32, 32, 'bilinear'); 122 $pixbuf = $scaled; 123 124 # get start of buffer; each insertion will revalidate the 125 # iterator to point to just after the inserted text. 126 127 my $iter = $buffer->get_iter_at_offset (0); 128 129 $buffer->insert ($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"); 130 131 $buffer->insert_with_tags_by_name ($iter, "Font styles. ", "heading"); 132 133 $buffer->insert ($iter, "For example, you can have "); 134 $buffer->insert_with_tags_by_name ($iter, "italic", "italic"); 135 $buffer->insert ($iter, ", "); 136 $buffer->insert_with_tags_by_name ($iter, "bold", "bold"); 137 $buffer->insert ($iter, ", or "); 138 $buffer->insert_with_tags_by_name ($iter, "monospace (typewriter)", "monospace"); 139 $buffer->insert ($iter, ", or "); 140 $buffer->insert_with_tags_by_name ($iter, "big", "big"); 141 $buffer->insert ($iter, " text. "); 142 $buffer->insert ($iter, "It's best not to hardcode specific text sizes; you can use relative sizes as with CSS, such as "); 143 $buffer->insert_with_tags_by_name ($iter, "xx-small", "xx-small"); 144 $buffer->insert ($iter, " or "); 145 $buffer->insert_with_tags_by_name ($iter, "x-large", "x-large"); 146 $buffer->insert ($iter, " to ensure that your program properly adapts if the user changes the default font size.\n\n"); 147 148 $buffer->insert_with_tags_by_name ($iter, "Colors. ", "heading"); 149 150 $buffer->insert ($iter, "Colors such as "); 151 $buffer->insert_with_tags_by_name ($iter, "a blue foreground", "blue_foreground"); 152 $buffer->insert ($iter, " or "); 153 $buffer->insert_with_tags_by_name ($iter, "a red background", "red_background"); 154 $buffer->insert ($iter, " or even "); 155 $buffer->insert_with_tags_by_name ($iter, "a stippled red background", 156 "red_background", 157 "background_stipple"); 158 159 $buffer->insert ($iter, " or "); 160 $buffer->insert_with_tags_by_name ($iter, "a stippled blue foreground on solid red background", 161 "blue_foreground", 162 "red_background", 163 "foreground_stipple"); 164 $buffer->insert ($iter, " (select that to read it) can be used.\n\n"); 165 166 $buffer->insert_with_tags_by_name ($iter, "Underline, strikethrough, and rise. ", 167 "heading"); 168 169 $buffer->insert_with_tags_by_name ($iter, "Strikethrough", "strikethrough"); 170 $buffer->insert ($iter, ", "); 171 $buffer->insert_with_tags_by_name ($iter, "underline", "underline"); 172 $buffer->insert ($iter, ", "); 173 $buffer->insert_with_tags_by_name ($iter, "double underline", "double_underline"); 174 $buffer->insert ($iter, ", "); 175 $buffer->insert_with_tags_by_name ($iter, "superscript", "superscript"); 176 $buffer->insert ($iter, ", and "); 177 $buffer->insert_with_tags_by_name ($iter, "subscript", "subscript"); 178 $buffer->insert ($iter, " are all supported.\n\n"); 179 180 $buffer->insert_with_tags_by_name ($iter, "Images. ", "heading"); 181 182 $buffer->insert ($iter, "The buffer can have images in it: "); 183 $buffer->insert_pixbuf ($iter, $pixbuf); 184 $buffer->insert_pixbuf ($iter, $pixbuf); 185 $buffer->insert_pixbuf ($iter, $pixbuf); 186 $buffer->insert ($iter, " for example.\n\n"); 187 188 $buffer->insert_with_tags_by_name ($iter, "Spacing. ", "heading"); 189 190 $buffer->insert ($iter, "You can adjust the amount of space before each line.\n"); 191 192 $buffer->insert_with_tags_by_name ($iter, "This line has a whole lot of space before it.\n", 193 "big_gap_before_line", "wide_margins"); 194 $buffer->insert_with_tags_by_name ($iter, "You can also adjust the amount of space after each line; this line has a whole lot of space after it.\n", 195 "big_gap_after_line", "wide_margins"); 196 197 $buffer->insert_with_tags_by_name ($iter, 198 "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", 199 "double_spaced_line", "wide_margins"); 200 201 $buffer->insert ($iter, "Also note that those lines have extra-wide margins.\n\n"); 202 203 $buffer->insert_with_tags_by_name ($iter, "Editability. ", "heading"); 204 205 $buffer->insert_with_tags_by_name ($iter, "This line is 'locked down' and can't be edited by the user - just try it! You can't delete this line.\n\n", 206 "not_editable"); 207 208 $buffer->insert_with_tags_by_name ($iter, "Wrapping. ", "heading"); 209 210 $buffer->insert ($iter, "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"); 211 212 $buffer->insert_with_tags_by_name ($iter, "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", 213 "char_wrap"); 214 215 $buffer->insert_with_tags_by_name ($iter, "This line has all wrapping turned off, so it makes the horizontal scrollbar appear.\n\n\n", 216 "no_wrap"); 217 218 $buffer->insert_with_tags_by_name ($iter, "Justification. ", "heading"); 219 220 $buffer->insert_with_tags_by_name ($iter, "\nThis line has center justification.\n", "center"); 221 222 $buffer->insert_with_tags_by_name ($iter, "This line has right justification.\n", 223 "right_justify"); 224 225 $buffer->insert_with_tags_by_name ($iter, 226 "\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", 227 "wide_margins"); 228 229 $buffer->insert_with_tags_by_name ($iter, "Internationalization. ", 230 "heading"); 231 232 $buffer->insert ($iter, "You can put all sorts of Unicode text in the buffer.\n\nGerman (S\x{fc}ddeutschland) Gr\x{fc}\x{df} Gott\nGreek (\x{395}\x{3bb}\x{3bb}\x{3b7}\x{3bd}\x{3b9}\x{3ba}\x{3ac}) \x{393}\x{3b5}\x{3b9}\x{3ac} \x{3c3}\x{3b1}\x{3c2}\nHebrew \x{5e9}\x{5dc}\x{5d5}\x{5dd}\nJapanese (\x{65e5}\x{672c}\x{8a9e})\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"); 233 234 $buffer->insert ($iter, "Here's a word-wrapped quote in a right-to-left language:\n"); 235 $buffer->insert_with_tags_by_name ($iter, "\x{648}\x{642}\x{62f} \x{628}\x{62f}\x{623} \x{62b}\x{644}\x{627}\x{62b} \x{645}\x{646} \x{623}\x{643}\x{62b}\x{631} \x{627}\x{644}\x{645}\x{624}\x{633}\x{633}\x{627}\x{62a} \x{62a}\x{642}\x{62f}\x{645}\x{627} \x{641}\x{64a} \x{634}\x{628}\x{643}\x{629} \x{627}\x{643}\x{633}\x{64a}\x{648}\x{646} \x{628}\x{631}\x{627}\x{645}\x{62c}\x{647}\x{627} \x{643}\x{645}\x{646}\x{638}\x{645}\x{627}\x{62a} \x{644}\x{627} \x{62a}\x{633}\x{639}\x{649} \x{644}\x{644}\x{631}\x{628}\x{62d}\x{60c} \x{62b}\x{645} \x{62a}\x{62d}\x{648}\x{644}\x{62a} \x{641}\x{64a} \x{627}\x{644}\x{633}\x{646}\x{648}\x{627}\x{62a} \x{627}\x{644}\x{62e}\x{645}\x{633} \x{627}\x{644}\x{645}\x{627}\x{636}\x{64a}\x{629} \x{625}\x{644}\x{649} \x{645}\x{624}\x{633}\x{633}\x{627}\x{62a} \x{645}\x{627}\x{644}\x{64a}\x{629} \x{645}\x{646}\x{638}\x{645}\x{629}\x{60c} \x{648}\x{628}\x{627}\x{62a}\x{62a} \x{62c}\x{632}\x{621}\x{627} \x{645}\x{646} \x{627}\x{644}\x{646}\x{638}\x{627}\x{645} \x{627}\x{644}\x{645}\x{627}\x{644}\x{64a} \x{641}\x{64a} \x{628}\x{644}\x{62f}\x{627}\x{646}\x{647}\x{627}\x{60c} \x{648}\x{644}\x{643}\x{646}\x{647}\x{627} \x{62a}\x{62a}\x{62e}\x{635}\x{635} \x{641}\x{64a} \x{62e}\x{62f}\x{645}\x{629} \x{642}\x{637}\x{627}\x{639} \x{627}\x{644}\x{645}\x{634}\x{631}\x{648}\x{639}\x{627}\x{62a} \x{627}\x{644}\x{635}\x{63a}\x{64a}\x{631}\x{629}. \x{648}\x{623}\x{62d}\x{62f} \x{623}\x{643}\x{62b}\x{631} \x{647}\x{630}\x{647} \x{627}\x{644}\x{645}\x{624}\x{633}\x{633}\x{627}\x{62a} \x{646}\x{62c}\x{627}\x{62d}\x{627} \x{647}\x{648} \x{bb}\x{628}\x{627}\x{646}\x{643}\x{648}\x{633}\x{648}\x{644}\x{ab} \x{641}\x{64a} \x{628}\x{648}\x{644}\x{64a}\x{641}\x{64a}\x{627}.\n\n", 236 "rtl_quote"); 237 238 $buffer->insert ($iter, "You can put widgets in the buffer: Here's a button: "); 239 $anchor = $buffer->create_child_anchor ($iter); 240 $buffer->insert ($iter, " and a menu: "); 241 $anchor = $buffer->create_child_anchor ($iter); 242 $buffer->insert ($iter, " and a scale: "); 243 $anchor = $buffer->create_child_anchor ($iter); 244 $buffer->insert ($iter, " and an animation: "); 245 $anchor = $buffer->create_child_anchor ($iter); 246 $buffer->insert ($iter, " finally a text entry: "); 247 $anchor = $buffer->create_child_anchor ($iter); 248 $buffer->insert ($iter, ".\n"); 249 250 $buffer->insert ($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..."); 251 252 # Apply word_wrap tag to whole buffer 253 $buffer->apply_tag_by_name ("word_wrap", $buffer->get_bounds); 254} 255 256sub find_anchor { 257 my $iter = shift; 258 while ($iter->forward_char) { 259 return TRUE if $iter->get_child_anchor; 260 } 261 return FALSE; 262} 263 264sub attach_widgets { 265 my $text_view = shift; 266 267 my $buffer = $text_view->get_buffer; 268 269 my $iter = $buffer->get_start_iter; 270 271 my $i = 0; 272 while (find_anchor ($iter)) { 273 my $widget; 274 275 my $anchor = $iter->get_child_anchor; 276 277 if ($i == 0) { 278 $widget = Gtk2::Button->new ("Click Me"); 279 280 $widget->signal_connect (clicked => \&easter_egg_callback); 281 282 } elsif ($i == 1) { 283 if (Gtk2->CHECK_VERSION (2, 4, 0)) { 284 $widget = Gtk2::ComboBox->new_text; 285 $widget->append_text ("Option 1"); 286 $widget->append_text ("Option 2"); 287 $widget->append_text ("Option 3"); 288 289 } else { 290 # ComboBox is not available, use OptionMenu instead 291 my $menu = Gtk2::Menu->new; 292 $menu->append (Gtk2::MenuItem->new ("Option 1")); 293 $menu->append (Gtk2::MenuItem->new ("Option 2")); 294 $menu->append (Gtk2::MenuItem->new ("Option 3")); 295 296 $widget = Gtk2::OptionMenu->new; 297 $widget->set_menu ($menu); 298 } 299 300 } elsif ($i == 2) { 301 $widget = Gtk2::HScale->new (undef); 302 $widget->set_range (0, 100); 303 $widget->set_size_request (70, -1); 304 305 } elsif ($i == 3) { 306 my $filename = main::demo_find_file ("floppybuddy.gif"); 307 $widget = Gtk2::Image->new_from_file ($filename); 308 309 } elsif ($i == 4) { 310 $widget = Gtk2::Entry->new; 311 312 } else { 313 croak "shouldn't get here"; 314 } 315 316 $text_view->add_child_at_anchor ($widget, $anchor); 317 318 $widget->show_all; 319 320 ++$i; 321 } 322} 323 324my $window; 325 326sub do { 327 if (!$window) { 328 329 $window = Gtk2::Window->new ('toplevel'); 330 $window->set_default_size (450, 450); 331 332 $window->signal_connect (destroy => sub { $window = undef; }); 333 334 $window->set_title ("TextView"); 335 $window->set_border_width (0); 336 337 my $vpaned = Gtk2::VPaned->new; 338 $vpaned->set_border_width (5); 339 $window->add ($vpaned); 340 341 # For convenience, we just use the autocreated buffer from 342 # the first text view; you could also create the buffer 343 # by itself with gtk_text_buffer_new(), then later create 344 # a view widget. 345 346 my $view1 = Gtk2::TextView->new; 347 my $buffer = $view1->get_buffer; 348 my $view2 = Gtk2::TextView->new_with_buffer ($buffer); 349 350 my $sw = Gtk2::ScrolledWindow->new; 351 $sw->set_policy ('automatic', 'automatic'); 352 $vpaned->add1 ($sw); 353 354 $sw->add ($view1); 355 356 $sw = Gtk2::ScrolledWindow->new; 357 $sw->set_policy ('automatic', 'automatic'); 358 $vpaned->add2 ($sw); 359 360 $sw->add ($view2); 361 362 create_tags ($buffer); 363 insert_text ($buffer); 364 365 attach_widgets ($view1); 366 attach_widgets ($view2); 367 368 $vpaned->show_all; 369 } 370 371 if (!$window->visible) { 372 $window->show; 373 374 } else { 375 $window->destroy; 376 $window = undef; 377 } 378 379 return $window; 380} 381 382sub recursive_attach_view { 383 my ($depth, $view, $anchor) = @_; 384 385 return if $depth > 4; 386 387 my $child_view = Gtk2::TextView->new_with_buffer ($view->get_buffer); 388 389 # Event box is to add a black border around each child view 390 my $event_box = Gtk2::EventBox->new; 391 my $color = Gtk2::Gdk::Color->parse ("black"); 392 $event_box->modify_bg ('normal', $color); 393 394 my $align = Gtk2::Alignment->new (0.5, 0.5, 1.0, 1.0); 395 $align->set_border_width (1); 396 397 $event_box->add ($align); 398 $align->add ($child_view); 399 400 $view->add_child_at_anchor ($event_box, $anchor); 401 402 recursive_attach_view ($depth + 1, $child_view, $anchor); 403} 404 405sub easter_egg_callback { 406 my $button = shift; 407 408 if ($tvee_window) { 409 $tvee_window->present; 410 return; 411 } 412 413 my $buffer = Gtk2::TextBuffer->new (undef); 414 415 my $iter = $buffer->get_start_iter; 416 417 $buffer->insert ($iter, "This buffer is shared by a set of nested text views.\n Nested view:\n"); 418 my $anchor = $buffer->create_child_anchor ($iter); 419 $buffer->insert ($iter, "\nDon't do this in real applications, please.\n"); 420 421 my $view = Gtk2::TextView->new_with_buffer ($buffer); 422 423 recursive_attach_view (0, $view, $anchor); 424 425 $tvee_window = Gtk2::Window->new ('toplevel'); 426 my $sw = Gtk2::ScrolledWindow->new (undef, undef); 427 $sw->set_policy ('automatic', 'automatic'); 428 429 $tvee_window->add ($sw); 430 $sw->add ($view); 431 432 $tvee_window->signal_connect (destroy => sub {$tvee_window = undef; 1}); 433 434 $tvee_window->set_default_size (300, 400); 435 436 $tvee_window->show_all; 437} 438 4391; 440__END__ 441Copyright (C) 2003 by the gtk2-perl team (see the file AUTHORS for the 442full list) 443 444This library is free software; you can redistribute it and/or modify it under 445the terms of the GNU Library General Public License as published by the Free 446Software Foundation; either version 2.1 of the License, or (at your option) any 447later version. 448 449This library is distributed in the hope that it will be useful, but WITHOUT ANY 450WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 451PARTICULAR PURPOSE. See the GNU Library General Public License for more 452details. 453 454You should have received a copy of the GNU Library General Public License along 455with this library; if not, write to the Free Software Foundation, Inc., 45651 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 457