1#!/usr/bin/perl -w 2# mv-demo.pl --- 3# Last modify Time-stamp: <Ye Wenbin 2007-10-07 21:48:03> 4# Version: v 0.0 2007/09/26 13:31:45 5# Author: Ye Wenbin <wenbinye@gmail.com> 6 7use strict; 8use warnings; 9 10use FindBin qw/$Bin/; 11use lib "$Bin/../blib/arch"; 12use lib "$Bin/../blib/lib"; 13 14use Gtk2 '-init'; 15use Glib qw(TRUE FALSE); 16use Goo::Canvas; 17 18my $window = create_window(); 19Gtk2->main; 20 21#{{{ Main window 22sub create_window { 23 my $window = Gtk2::Window->new('toplevel'); 24 $window->signal_connect('delete_event' => sub { Gtk2->main_quit; }); 25 $window->set_default_size(640, 600); 26 $window->show; 27 28 my $notebook = Gtk2::Notebook->new; 29 $window->add($notebook); 30 $notebook->show; 31 32 foreach my $pkg ( 33 "Primitives", 34 "Arrowhead", 35 "Fifteen", 36 "Reparent", 37 "Scalability", 38 "Grabs", 39 "Events", 40 "Paths", 41 "Focus", 42 "Animation", 43 "Clipping", 44 ) { 45 $notebook->append_page( 46 $pkg->create_canvas(), 47 Gtk2::Label->new($pkg) 48 ); 49 } 50 return $window; 51} 52#}}} 53 54#{{{ Primitives 55package Primitives; 56use Gtk2; 57use Glib qw(TRUE FALSE); 58use constant { 59 VERTICES => 10, 60 RADIUS => 60, 61 SCALE => 7, 62}; 63use Math::Trig qw/pi/; 64 65sub create_canvas { 66 my $pkg = shift; 67 my $vbox = Gtk2::VBox->new; 68 my $group; 69 my ($hbox, $w, $swin, $canvas, $adj); 70 my $bg_color = Gtk2::Gdk::Color->new(50000, 50000, 65535); 71 72 $vbox->set_border_width(4); 73 $vbox->show; 74 $w = Gtk2::Label->new("Drag an item with button 1. Click button 2 on an item to lower it, or button 3 to raise it."); 75 $vbox->pack_start($w, FALSE, FALSE, 0); 76 $w->show; 77 78 $hbox = Gtk2::HBox->new(FALSE, 4); 79 $vbox->pack_start($hbox, FALSE, FALSE, 0); 80 $hbox->show; 81 # Create the canvas 82 $canvas = Goo::Canvas->new; 83 $canvas->modify_base('normal', $bg_color); 84 $canvas->set_bounds(0, 0, 604, 454); 85 86 ###### Frist Row 87 # Zoom 88 $w = Gtk2::Label->new("Zoom:"); 89 $hbox->pack_start($w, FALSE, FALSE, 0); 90 $w->show; 91 92 $adj = Gtk2::Adjustment->new(1, 0.05, 100, 0.05, 0.5, 0.5); 93 $w = Gtk2::SpinButton->new($adj, 0, 2); 94 $adj->signal_connect("value-changed", \&zoom_changed, $canvas); 95 $w->set_size_request(50, -1); 96 $hbox->pack_start($w, FALSE, FALSE, 0); 97 $w->show; 98 # Center 99 $w = Gtk2::CheckButton->new_with_label("Center scroll region"); 100 $hbox->pack_start($w, FALSE, FALSE, 0); 101 # $w->show; 102 $w->signal_connect("toggled", \¢er_toggled, $canvas); 103 # Move Ellipse 104 $w = Gtk2::Button->new_with_label('Move Ellipse'); 105 $hbox->pack_start($w, FALSE, FALSE, 0); 106 $w->show; 107 $w->signal_connect("clicked", \&move_ellipse_clicked, $canvas); 108 # Animate Ellipse 109 $w = Gtk2::Button->new_with_label('Animate Ellipse'); 110 $hbox->pack_start($w, FALSE, FALSE, 0); 111 $w->show; 112 $w->signal_connect("clicked", \&animate_ellipse_clicked, $canvas); 113 # Stop Animation 114 $w = Gtk2::Button->new_with_label('Stop Animation'); 115 $hbox->pack_start($w, FALSE, FALSE, 0); 116 $w->show; 117 $w->signal_connect("clicked", \&stop_animation_clicked, $canvas); 118 # Create PDF 119 $w = Gtk2::Button->new_with_label('Write PDF'); 120 $hbox->pack_start($w, FALSE, FALSE, 0); 121 $w->show; 122 $w->signal_connect("clicked", \&write_pdf_clicked, $canvas); 123 ##### Start anothor Row 124 $hbox = Gtk2::HBox->new(FALSE, 4); 125 $vbox->pack_start($hbox, FALSE, FALSE, 0); 126 $hbox->show; 127 # Scroll to 128 $w = Gtk2::Label->new('Scroll to:'); 129 $hbox->pack_start($w, FALSE, FALSE, 0); 130 $w->show; 131 132 $w = Gtk2::Button->new_with_label('50,50'); 133 $hbox->pack_start($w, FALSE, FALSE, 0); 134 $w->show; 135 $w->signal_connect("clicked", \&scroll_to_50_50_clicked, $canvas); 136 $w = Gtk2::Button->new_with_label('250,250'); 137 $hbox->pack_start($w, FALSE, FALSE, 0); 138 $w->show; 139 $w->signal_connect("clicked", \&scroll_to_250_250_clicked, $canvas); 140 $w = Gtk2::Button->new_with_label('500,500'); 141 $hbox->pack_start($w, FALSE, FALSE, 0); 142 $w->show; 143 $w->signal_connect("clicked", \&scroll_to_500_500_clicked, $canvas); 144 # Scroll anchor 145 $w = Gtk2::Label->new('Anchor:'); 146 $hbox->pack_start($w, FALSE, FALSE, 0); 147 $w->show; 148 149 foreach my $anchor( 'NW', 'N', 'NE', 'W', 'SW', 'S', 'SE' ) { 150 $w = Gtk2::RadioButton->new_with_label($group, $anchor); 151 $group = $w; 152 $hbox->pack_start($w, FALSE, FALSE, 0); 153 $w->show; 154 $w->signal_connect('toggled', \&anchor_toggled, $canvas); 155 $w->{anchor} = lc($anchor); 156 } 157 # Layout the stuff 158 $swin = Gtk2::ScrolledWindow->new(); 159 $swin->show; 160 $vbox->pack_start($swin, TRUE, TRUE, 0); 161 $canvas->show; 162 $swin->add($canvas); 163 $canvas->signal_connect('item-created', \&on_item_created); 164 my $model = Goo::Canvas::GroupModel->new; 165 create_model($model); 166 $canvas->set_root_item_model($model); 167 if ( 0 ) { 168 $canvas->signal_connect_after('key_press_event', \&key_press); 169 $canvas->can_focus(TRUE); 170 $canvas->grab_focus; 171 } 172 return $vbox; 173} 174 175sub create_model { 176 my $root = shift; 177 setup_divisions($root); 178 setup_rectangles($root); 179 setup_ellipses($root); 180 setup_lines($root); 181 setup_polygons($root); 182 setup_texts($root); 183 setup_images($root); 184 setup_invisible_texts($root); 185} 186 187sub setup_divisions { 188 my $root = shift; 189 my ($group, $item); 190 $group = Goo::Canvas::GroupModel->new($root); 191 $group->{'skip-signal-connection'} = TRUE; 192 $group->translate(2, 2); 193 $item = Goo::Canvas::RectModel->new( 194 $group, 0, 0, 600, 450, 195 'line-width' => 4 196 ); 197 $item->{'skip-signal-connection'} = TRUE; 198 $item = Goo::Canvas::PolylineModel->new_line( 199 $group, 0, 150, 600, 150, 200 'line-width' => 4, 201 ); 202 $item->{'skip-signal-connection'} = TRUE; 203 $item = Goo::Canvas::PolylineModel->new_line( 204 $group, 0, 300, 600, 300, 205 'line-width' => 4, 206 ); 207 $item->{'skip-signal-connection'} = TRUE; 208 $item = Goo::Canvas::PolylineModel->new_line( 209 $group, 200, 0, 200, 450, 210 'line-width' => 4, 211 ); 212 $item->{'skip-signal-connection'} = TRUE; 213 $item = Goo::Canvas::PolylineModel->new_line( 214 $group, 400, 0, 400, 450, 215 'line-width' => 4, 216 ); 217 $item->{'skip-signal-connection'} = TRUE; 218 setup_heading ($group, "Rectangles", 0); 219 setup_heading ($group, "Ellipses", 1); 220 setup_heading ($group, "Texts", 2); 221 setup_heading ($group, "Images", 3); 222 setup_heading ($group, "Lines", 4); 223 setup_heading ($group, "Polygons", 7); 224} 225 226sub setup_heading { 227 my ($root, $text, $pos) = @_; 228 my $x = ($pos%3)*200 + 100; 229 my $y = (int($pos/3))*150 + 5; 230 my $item = Goo::Canvas::TextModel->new( 231 $root, $text, $x, $y, -1, 'n', 232 'font' => 'Sans 12' 233 ); 234 $item->skew_y(30, $x, $y); 235} 236 237sub setup_rectangles { 238 my $root = shift; 239 my ($item, $pattern); 240 my @stipple_data = ( 241 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255 242 ); 243 $item = Goo::Canvas::RectModel->new( 244 $root, 20, 30, 50, 30, 245 'stroke-color' => 'red', 246 'line-width' => 8, 247 ); 248 $pattern = create_stipple('mediumseagreen', \@stipple_data); 249 $item = Goo::Canvas::RectModel->new( 250 $root, 90, 40, 90, 60, 251 'fill-pattern' => $pattern, 252 'stroke-color' => 'black', 253 'line-width' => 4, 254 ); 255 $item = Goo::Canvas::RectModel->new( 256 $root, 10, 80, 70, 60, 257 'fill-color' => 'steelblue', 258 ); 259 $item = Goo::Canvas::RectModel->new( 260 $root, 20, 90, 70, 60, 261 'fill-color-rgba' => 0x3cb37180, 262 'stroke-color' => 'blue', 263 'line-width' => 2, 264 ); 265 $item = Goo::Canvas::RectModel->new( 266 $root, 110, 80, 50, 30, 267 'radius-x' => 20, 268 'radius-y' => 10, 269 'stroke-color' => 'yellow', 270 'fill-color-rgba' => 0x3cb3f180, 271 ); 272 $item = Goo::Canvas::RectModel->new( 273 $root, 30, 20, 50, 30, 274 'fill-color' => 'yellow', 275 ); 276} 277 278sub create_stipple { 279 our @stipples; 280 my($color_name, $stipple_data) = @_; 281 my $color = Gtk2::Gdk::Color->parse($color_name); 282 $stipple_data->[2] = $stipple_data->[14] = $color->red >> 8; 283 $stipple_data->[1] = $stipple_data->[13] = $color->green >> 8; 284 $stipple_data->[0] = $stipple_data->[12] = $color->blue >> 8; 285 my $stipple_str = join('', map {chr} @$stipple_data); 286 push @stipples, \$stipple_str; # make $stipple_str refcnt increase 287 my $surface = Cairo::ImageSurface->create_for_data( 288 $stipple_str, 'argb32', 289 2, 2, 8 290 ); 291 my $pattern = Cairo::SurfacePattern->create($surface); 292 $pattern->set_extend('repeat'); 293 return Goo::Cairo::Pattern->new($pattern); 294} 295 296sub setup_ellipses { 297 my $root = shift; 298 my @stipple_data = ( 299 0, 0, 0, 255, 0, 0, 0, 0, 300 0, 0, 0, 0, 0, 0, 0, 255 301 ); 302 my $ellipse1 = Goo::Canvas::EllipseModel->new( 303 $root, 245, 45, 25, 15, 304 'stroke-color' => 'goldenrod', 305 'line-width' => 8 306 ); 307 my $ellipse2 = Goo::Canvas::EllipseModel->new( 308 $root, 335, 70, 45, 30, 309 'fill-color' => 'wheat', 310 'stroke-color' => 'midnightblue', 311 'line-width' => 4, 312 'title' => 'An ellipse' 313 ); 314 $root->{ellipse} = $ellipse2; 315 my $pattern = create_stipple('cadetblue', \@stipple_data); 316 my $ellipse3 = Goo::Canvas::EllipseModel->new( 317 $root, 245, 110, 35, 30, 318 'fill-pattern' => $pattern, 319 'stroke-color' => 'black', 320 'line-width' => 1, 321 ); 322} 323 324sub setup_lines { 325 my $root = shift; 326 my $line; 327 328 polish_diamond($root); 329 make_hilbert($root); 330 $line = Goo::Canvas::PolylineModel->new( 331 $root, FALSE, 332 [ 340, 170, 333 340, 230, 334 390, 230, 335 390, 170 ], 336 'stroke-color' => 'midnightblue', 337 'line-width' => 3, 338 'start-arrow' => TRUE, 339 'end-arrow' => TRUE, 340 'arrow-tip-length' => 3, 341 'arrow-length' => 4, 342 'arrow-width' => 3.5 343 ); 344 $line = Goo::Canvas::PolylineModel->new( 345 $root, FALSE, 346 [ 356, 180, 347 374, 220, ], 348 'stroke-color' => 'blue', 349 'line-width' => 1, 350 'start-arrow' => TRUE, 351 'end-arrow' => TRUE, 352 'arrow-tip-length' => 5, 353 'arrow-length' => 6, 354 'arrow-width' => 6, 355 ); 356 $line = Goo::Canvas::PolylineModel->new( 357 $root, FALSE, 358 [356, 220, 359 374, 180,], 360 'stroke-color' => 'blue', 361 'line-width' => 1, 362 'start-arrow' => TRUE, 363 'end-arrow' => TRUE, 364 'arrow-tip-length' => 5, 365 'arrow-length' => 6, 366 'arrow-width' => 6, 367 ); 368 $line = Goo::Canvas::PolylineModel->new($root, FALSE, []); 369 $line = Goo::Canvas::PolylineModel->new( 370 $root, FALSE, 371 [356, 220], 372 'start-arrow' => TRUE, 373 'end-arrow' => TRUE, 374 ); 375} 376 377sub polish_diamond { 378 my $root = shift; 379 my $item; 380 my ($a, $x1, $y1, $x2, $y2); 381 my $group = Goo::Canvas::GroupModel->new( 382 $root, 383 'line-width' => 1, 384 'line-cap' => 'round' 385 ); 386 $group->translate(270, 230); 387 for my $i ( 0..VERTICES ) { 388 $a = 2*pi*$i/VERTICES; 389 $x1 = RADIUS * cos($a); 390 $y1 = RADIUS * sin($a); 391 for my $j ( $i+1..VERTICES ) { 392 $a = 2*pi*$j/VERTICES; 393 $x2 = RADIUS * cos($a); 394 $y2 = RADIUS * sin($a); 395 $item = Goo::Canvas::PolylineModel->new_line( 396 $group, $x1, $y1, $x2, $y2 397 ); 398 $item->{'skip-signal-connection'} = TRUE; 399 } 400 } 401} 402 403sub make_hilbert { 404 my $root = shift; 405 my $hilbert = "urdrrulurulldluuruluurdrurddldrrruluurdrurddldrddlulldrdldrrurd"; 406 my @stipple_data = ( 407 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255 408 ); 409 my $pattern = create_stipple('red', \@stipple_data); 410 my @points = ( [340, 290] ); 411 my $pp = $points[0]; 412 foreach ( 0..length($hilbert)-1 ) { 413 my @p; 414 my $c = substr($hilbert, $_, 1); 415 if ( $c eq 'u' ) { 416 $p[0] = $pp->[0]; 417 $p[1] = $pp->[1] - SCALE; 418 } 419 elsif ( $c eq 'd' ) { 420 $p[0] = $pp->[0]; 421 $p[1] = $pp->[1] + SCALE; 422 } 423 elsif ( $c eq 'l' ) { 424 $p[0] = $pp->[0] - SCALE; 425 $p[1] = $pp->[1]; 426 } 427 elsif ( $c eq 'r' ) { 428 $p[0] = $pp->[0] + SCALE; 429 $p[1] = $pp->[1]; 430 } 431 push @points, \@p; 432 $pp = \@p; 433 } 434 my $item = Goo::Canvas::PolylineModel->new( 435 $root, FALSE, [map {@{$_}} @points], 436 'line-width' => 4, 437 'stroke-pattern' => $pattern, 438 'line-cap' => 'square', 439 'line-join' => 'miter' 440 ); 441} 442 443sub setup_polygons { 444 my $root = shift; 445 my $line; 446 my @stipple_data = ( 447 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255 448 ); 449 my @points = ( 450 210, 320, 451 210, 380, 452 260, 350 453 ); 454 my $pattern = create_stipple('blue', \@stipple_data); 455 $line = Goo::Canvas::PolylineModel->new( 456 $root, TRUE, \@points, 457 'line-width' => 1, 458 'fill-pattern' => $pattern, 459 'stroke-color' => 'black' 460 ); 461 @points = ( 462 270.0, 330.0, 463 270.0, 430.0, 464 390.0, 430.0, 465 390.0, 330.0, 466 310.0, 330.0, 467 310.0, 390.0, 468 350.0, 390.0, 469 350.0, 370.0, 470 330.0, 370.0, 471 330.0, 350.0, 472 370.0, 350.0, 473 370.0, 410.0, 474 290.0, 410.0, 475 290.0, 330.0, 476 ); 477 $line = Goo::Canvas::PolylineModel->new( 478 $root, TRUE, \@points, 479 'fill-color' => 'tan', 480 'stroke-color' => 'black', 481 'line-width' => 3, 482 ); 483} 484 485sub setup_texts { 486 my $root = shift; 487 my @stipple_data = ( 488 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255 489 ); 490 my $pattern = create_stipple('blue', \@stipple_data); 491 my $item; 492 $item = Goo::Canvas::TextModel->new( 493 make_anchor($root, 420, 20), 494 'Anchor NW', 495 0, 0, -1, 'nw', 496 'font' => 'Sans Bold 24', 497 'fill-pattern' => $pattern, 498 ); 499 500 $item = Goo::Canvas::TextModel->new( 501 make_anchor($root, 470, 75), 502 "Anchor center\nJustify center\nMultiline text\nb8bit text ÅÄÖåäö", 503 0, 0, -1, 'center', 504 "font" => "monospace bold 14", 505 "alignment" => 'center', 506 "fill-color" => "firebrick", 507 ); 508 509 $item = Goo::Canvas::TextModel->new( 510 make_anchor($root, 590, 140), 511"Clipped text\nClipped text\nClipped text\nClipped text\nClipped text\nClipped text", 512 0, 0, -1, 'se', 513 'font' =>'Sans 12', 514 'fill-color' => 'darkgreen' 515 ); 516 517 $item = Goo::Canvas::TextModel->new( 518 make_anchor($root, 420, 240), 519 "This is a very long paragraph that will need to be wrapped over several lines so we can see what happens to line-breaking as the view is zoomed in and out.", 520 0, 0, 180, 'w', 521 'font' => 'Sans 12', 522 'fill-color' => 'goldenrod' 523 ); 524} 525 526sub make_anchor { 527 my($root, $x, $y) = @_; 528 my $group = Goo::Canvas::GroupModel->new($root); 529 my $transform = Goo::Cairo::Matrix->new( 530 Cairo::Matrix->init(0.8, 0.2, -0.3, 0.5, $x, $y ), 531 ); 532 my $item; 533 534 $group->translate($x, $y); 535 $group->set( 'transform' => $transform ); 536 $item = Goo::Canvas::RectModel->new( 537 $group, -2.5, -2.5, 4, 4, 538 'line-width' => 1, 539 ); 540 return $group; 541} 542 543sub setup_images { 544 my $root = shift; 545 my ($im, $image); 546 use Data::Dumper qw(Dumper); 547 $im = Gtk2::Gdk::Pixbuf->new_from_file("$FindBin::Bin/toroid.png"); 548 if ( $im ) { 549 my $w = $im->get_width; 550 my $h = $im->get_height; 551 $image = Goo::Canvas::ImageModel->new( 552 $root, $im, 100-$w/2, 225-$h/2, 553 'width' => $w, 554 'height' => $h 555 ); 556 } else { 557 warn "Could not foundhe toroid.png sample file\n"; 558 } 559 plant_flower ($root, 20.0, 170.0, 'nw'); 560 plant_flower ($root, 180.0, 170.0, 'ne'); 561 plant_flower ($root, 20.0, 280.0, 'sw'); 562 plant_flower ($root, 180.0, 280.0, 'se'); 563} 564 565sub plant_flower { 566 my ($root, $x, $y, $anchor) = @_; 567 my $surface = Cairo::ImageSurface->create_from_png("$FindBin::Bin/flower.png"); 568 my $w = $surface->get_width; 569 my $h = $surface->get_height; 570 my $pattern = Cairo::SurfacePattern->create($surface); 571 my $image = Goo::Canvas::ImageModel->new( 572 $root, undef, $x, $y, 573 'pattern' => Goo::Cairo::Pattern->new($pattern), 574 'width' => $w, 575 'height' => $h, 576 ); 577} 578 579sub setup_invisible_texts { 580 my $root = shift; 581 Goo::Canvas::TextModel->new( 582 $root, "Visible above 0.8x", 500, 330, -1, 'center', 583 "visibility" => 'visible_above_threshold', 584 "visibility-threshold" => 0.8, 585 ); 586 Goo::Canvas::RectModel->new( 587 $root, 410.5, 322.5, 180, 15, 588 "line-width" => 1.0, 589 "visibility" => 'visible-above-threshold', 590 "visibility-threshold" => 0.8, 591 ); 592 593 Goo::Canvas::TextModel->new( 594 $root, "Visible above 1.5x", 500, 350, -1, 'center', 595 "visibility" => 'visible-above-threshold', 596 "visibility-threshold" => 1.5, 597 ); 598 Goo::Canvas::RectModel->new( 599 $root, 410.5, 342.5, 180, 15, 600 "line-width" => 1.0, 601 "visibility" => 'visible-above-threshold', 602 "visibility-threshold" => 1.5, 603 ); 604 605 Goo::Canvas::TextModel->new( 606 $root, "Visible above 3.0x", 500, 370, -1, 'center', 607 "visibility" => 'visible-above-threshold', 608 "visibility-threshold" => 3.0, 609 ); 610 Goo::Canvas::RectModel->new( 611 $root, 410.5, 362.5, 180, 15, 612 "line-width" => 1.0, 613 "visibility" => 'visible-above-threshold', 614 "visibility-threshold" => 3.0, 615 ); 616 617 # This should never be seen. 618 Goo::Canvas::TextModel->new( 619 $root, "Always Invisible", 500, 390, -1, 'center', 620 "visibility" => 'invisible', 621 ); 622 Goo::Canvas::RectModel->new( 623 $root, 410.5, 350.5, 180, 15, 624 "line-width" => 1.0, 625 "visibility" => 'invisible', 626 ); 627} 628 629#{{{ Signals 630sub on_item_created { 631 my ($canvas, $item, $model, $data) = @_; 632 if ( ! $model->get_parent ) { 633 $item->signal_connect('button_press_event', \&on_background_button_press); 634 } elsif ( not $model->{'skip-signal-connection'} ) { 635 $item->signal_connect('motion_notify_event', \&on_motion_notify); 636 $item->signal_connect('button_press_event', \&on_button_press); 637 $item->signal_connect('button_release_event', \&on_button_release); 638 } 639} 640 641sub on_motion_notify { 642 my ($item, $target, $ev) = @_; 643 my $model = $item->get_model; 644 if ( $model->{dragging} && $ev->state >= 'button1-mask' ) { 645 $model->translate($ev->x - $model->{drag_x}, 646 $ev->y - $model->{drag_y}); 647 } 648 return TRUE; 649} 650 651sub on_button_press { 652 my ($item, $target, $ev) = @_; 653 my $model = $item->get_model; 654 if ( $ev->button == 1 ) { 655 if ( $ev->state >= 'shift-mask' ) { 656 my $parent = $model->get_parent; 657 $parent->remove_child($parent->find_child($model)); 658 } else { 659 $model->{drag_x} = $ev->x; 660 $model->{drag_y} = $ev->y; 661 my $fleur = Gtk2::Gdk::Cursor->new('fleur'); 662 my $canvas = $item->get_canvas; 663 $canvas->pointer_grab($item, ['pointer-motion-mask', 'button-release-mask'], 664 $fleur, $ev->time); 665 $model->{dragging} = TRUE; 666 } 667 } 668 elsif ( $ev->button == 2 ) { 669 $model->lower; 670 } 671 elsif ( $ev->button == 3 ) { 672 $model->raise; 673 } 674 return TRUE; 675} 676 677sub on_button_release { 678 my ($item, $target, $ev) = @_; 679 my $canvas = $item->get_canvas; 680 $canvas->pointer_ungrab($item, $ev->time); 681 $item->get_model->{dragging} = FALSE; 682 return TRUE; 683} 684 685sub on_background_button_press { 686 return TRUE; 687} 688 689sub zoom_changed { 690 my ($adj, $canvas) = @_; 691 $canvas->set_scale($adj->get_value); 692} 693 694sub center_toggled { 695} 696 697sub anchor_toggled { 698 my ($but, $canvas) = @_; 699 if ( $but->get_active ) { 700 $canvas->set("anchor" => $but->{anchor}); 701 } 702} 703 704sub scroll_to_50_50_clicked { 705 my ($but, $canvas) = @_; 706 $canvas->scroll_to(50, 50); 707} 708 709sub scroll_to_250_250_clicked { 710 my ($but, $canvas) = @_; 711 $canvas->scroll_to(250, 250); 712} 713 714sub scroll_to_500_500_clicked { 715 my ($but, $canvas) = @_; 716 $canvas->scroll_to(500, 500); 717} 718 719sub animate_ellipse_clicked { 720 my ($but, $canvas) = @_; 721 $canvas->get_root_item_model->{ellipse}->animate(100, 100, 1, 90, TRUE, 1000, 40, 'bounce'); 722} 723 724sub stop_animation_clicked { 725 my ($but, $canvas) = @_; 726 $canvas->get_root_item_model->{ellipse}->stop_animation(); 727} 728 729sub move_ellipse_clicked { 730 my ($but, $canvas) = @_; 731 my $ellipse = $canvas->get_root_item_model->{ellipse}; 732 if ( !exists $ellipse->{last_state} ) { 733 $ellipse->{last_state} = 0; 734 } 735 my $last_state = $ellipse->{last_state}; 736 if ( $last_state == 0 ) { 737 $ellipse->set( 738 'center-x' => 300, 739 'center-y' => 70, 740 'radius-x' => 45, 741 'radius-y' => 30, 742 'fill-color' => 'red', 743 'stroke-color' => 'midnightblue', 744 'line-width' => 4, 745 'title' => 'A red ellipse' 746 ); 747 $last_state = 1; 748 } 749 elsif ( $last_state == 1 ) { 750 $ellipse->set( 751 'center-x' => 390, 752 'center-y' => 150, 753 'radius-x' => 45, 754 'radius-y' => 40, 755 'fill-color' => 'brown', 756 'stroke-color' => 'midnightblue', 757 'line-width' => 4, 758 'title' => 'A brown ellipse' 759 ); 760 $last_state = 2; 761 } 762 elsif ( $last_state == 2 ) { 763 $ellipse->set( 764 'center-x' => 0, 765 'center-y' => 0, 766 'radius-y' => 30, 767 ); 768 $ellipse->set_simple_transform(100, 100, 1, 0); 769 $last_state = 3; 770 } 771 elsif ( $last_state == 3 ) { 772 $ellipse->set_simple_transform(200, 200, 2, 0); 773 $last_state = 4; 774 } 775 elsif ( $last_state == 4 ) { 776 $ellipse->set_simple_transform(200, 200, 1, 45); 777 $last_state = 5; 778 } 779 elsif ( $last_state == 5 ) { 780 $ellipse->set_simple_transform(-50, -50, 0.2, 225); 781 $last_state = 6; 782 } 783 else { 784 $ellipse->set( 785 'center-x' => 335, 786 'center-y' => 70, 787 'radius-x' => 45, 788 'radius-y' => 30, 789 'fill-color' => 'purple', 790 'stroke-color' => 'midnightblue', 791 'line-width' => 4, 792 'title' => 'A purple ellipse' 793 ); 794 $last_state = 0; 795 } 796 $ellipse->{last_state} = $last_state; 797 return TRUE; 798} 799sub write_pdf_clicked { 800 my ($but, $canvas) = @_; 801 print "Write PDF...\n"; 802 my $surface = Cairo::PdfSurface->create("demo.pdf", 9*72, 10*72); 803 my $cr = Cairo::Context->create($surface); 804 $cr->translate(20, 130); 805 $canvas->render($cr, undef, 1); 806 $cr->show_page; 807 return TRUE; 808} 809 810#}}} 811#}}} 812 813#{{{ Arrowhead 814package Arrowhead; 815use Gtk2; 816use Glib qw(TRUE FALSE); 817use constant { 818 LEFT => 50.0, 819 RIGHT => 350.0, 820 MIDDLE => 150.0, 821 DEFAULT_WIDTH => 2, 822 DEFAULT_SHAPE_A => 4, 823 DEFAULT_SHAPE_B => 5, 824 DEFAULT_SHAPE_C => 4, 825}; 826 827sub create_canvas { 828 my $pkg = shift; 829 my ($w, $frame, $canvas, $root, $item); 830 my $vbox = Gtk2::VBox->new; 831 $vbox->show; 832 $vbox->set_border_width(4); 833 $w = Gtk2::Label->new( <<EOF ); 834This demo allows you to edit arrowhead shapes. Drag the little boxes 835to change the shape of the line and its arrowhead. You can see the 836arrows at their normal scale on the right hand side of the window. 837EOF 838 $vbox->pack_start($w, FALSE, FALSE, 0); 839 $w->show; 840 $w = Gtk2::Alignment->new(0.5, 0.5, 0, 0); 841 $vbox->pack_start($w, TRUE, TRUE, 0); 842 $w->show; 843 $frame = Gtk2::Frame->new; 844 $frame->set_shadow_type('in'); 845 $w->add($frame); 846 $frame->show; 847 848 $canvas = Goo::Canvas->new; 849 $canvas->set_size_request(500, 350); 850 $canvas->set_bounds(0, 0, 500, 350); 851 $frame->add($canvas); 852 $canvas->show; 853 $canvas->{width} = DEFAULT_WIDTH; 854 $canvas->{shape_a} = DEFAULT_SHAPE_A; 855 $canvas->{shape_b} = DEFAULT_SHAPE_B; 856 $canvas->{shape_c} = DEFAULT_SHAPE_C; 857 $canvas->signal_connect('item-created', \&on_item_created); 858 859 $root = Goo::Canvas::GroupModel->new; 860 $canvas->set_root_item_model($root); 861 # Big arrow 862 $item = Goo::Canvas::PolylineModel->new_line( 863 $root, LEFT, MIDDLE, RIGHT, MIDDLE, 864 'stroke-color' => 'mediumseagreen', 865 'end_arrow' => TRUE, 866 ); 867 $canvas->{big_arrow} = $item; 868 # Arrow outline 869 $item = Goo::Canvas::PolylineModel->new( 870 $root, TRUE, undef, 871 "stroke-color" => 'black', 872 'line-width' => 2, 873 'line-cap' => 'round', 874 'line-join' => 'round' 875 ); 876 $canvas->{outline} = $item; 877 # Drag boxes 878 create_drag_box($canvas, $root, 'width_drag_box'); 879 create_drag_box($canvas, $root, 'shape_a_drag_box'); 880 create_drag_box($canvas, $root, 'shape_b_c_drag_box'); 881 # Dimensions 882 create_dimension ($canvas, $root, "width_arrow", "width_text", 'e'); 883 create_dimension ($canvas, $root, "shape_a_arrow", "shape_a_text", 'n'); 884 create_dimension ($canvas, $root, "shape_b_arrow", "shape_b_text", 'n'); 885 create_dimension ($canvas, $root, "shape_c_arrow", "shape_c_text", 'w'); 886 # Info 887 create_info ($canvas, $root, "width_info", LEFT, 260); 888 create_info ($canvas, $root, "shape_a_info", LEFT, 280); 889 create_info ($canvas, $root, "shape_b_info", LEFT, 300); 890 create_info ($canvas, $root, "shape_c_info", LEFT, 320); 891 # Division line 892 Goo::Canvas::PolylineModel->new_line( 893 $root, RIGHT + 50, 0, RIGHT+ 50, 1000, 894 'fill-color' => 'black', 895 'line-width' => 2 896 ); 897 # Sample arrows 898 create_sample_arrow ($canvas, $root, "sample_1", 899 RIGHT + 100, 30, RIGHT + 100, MIDDLE - 30); 900 create_sample_arrow ($canvas, $root, "sample_2", 901 RIGHT + 70, MIDDLE, RIGHT + 130, MIDDLE); 902 create_sample_arrow ($canvas, $root, "sample_3", 903 RIGHT + 70, MIDDLE + 30, RIGHT + 130, MIDDLE + 120); 904 # Done 905 set_arrow_shape($canvas); 906 return $vbox; 907} 908 909sub set_dimension { 910 my ($canvas, $arrow_name, $text_name, $x1, $y1, $x2, $y2, $tx, $ty, $dim) = @_; 911 my $points = Goo::Canvas::Points->new([$x1, $y1, $x2, $y2]); 912 $canvas->{$arrow_name}->set(points => $points); 913 $canvas->{$text_name}->set(text => sprintf("%.2f", $dim), 914 x => $tx, 915 y => $ty); 916} 917 918sub move_drag_box { 919 my ($item, $x, $y) = @_; 920 $item->set(x => $x-5, 921 y => $y-5); 922} 923 924sub set_arrow_shape { 925 my $canvas = shift; 926 my $width = $canvas->{width}; 927 my $shape_a = $canvas->{shape_a}; 928 my $shape_b = $canvas->{shape_b}; 929 my $shape_c = $canvas->{shape_c}; 930 # Big arrow 931 $canvas->{big_arrow}->set( 932 'line-width' => 10*$width, 933 'arrow-tip-length' => $shape_a, 934 'arrow-length' => $shape_b, 935 'arrow-width' => $shape_c 936 ); 937 # Outline 938 my @points; 939 $points[0] = RIGHT -int(10 *$shape_a*$width); 940 $points[1] = MIDDLE-int(10*$width/2); 941 $points[2] = RIGHT - 10 * $shape_b * $width; 942 $points[3] = MIDDLE - 10 * ($shape_c * $width / 2.0); 943 $points[4] = RIGHT; 944 $points[5] = MIDDLE; 945 $points[6] = RIGHT - 10 * $shape_b * $width; 946 $points[7] = MIDDLE + 10 * ($shape_c * $width / 2.0); 947 $points[8] = RIGHT -int(10 *$shape_a*$width); 948 $points[9] = MIDDLE + 10 * $width / 2; 949 $canvas->{outline}->set( 950 points => Goo::Canvas::Points->new(\@points) 951 ); 952 move_drag_box($canvas->{width_drag_box}, LEFT, MIDDLE-10*$width/2); 953 move_drag_box($canvas->{shape_a_drag_box}, 954 RIGHT-10*$shape_a*$width, MIDDLE); 955 move_drag_box($canvas->{shape_b_c_drag_box}, 956 RIGHT-10*$shape_b*$width, MIDDLE-10*($shape_c*$width/2)); 957 # Dimensions 958 set_dimension($canvas, 'width_arrow', 'width_text', 959 LEFT - 10, 960 MIDDLE - 10 * $width / 2.0, 961 LEFT - 10, 962 MIDDLE + 10 * $width / 2.0, 963 LEFT - 15, 964 MIDDLE, 965 $width); 966 set_dimension ($canvas, "shape_a_arrow", "shape_a_text", 967 RIGHT - 10 * $shape_a * $width, 968 MIDDLE + 10 * ($shape_c * $width / 2.0) + 10, 969 RIGHT, 970 MIDDLE + 10 * ($shape_c * $width / 2.0) + 10, 971 RIGHT - 10 * $shape_a * $width / 2.0, 972 MIDDLE + 10 * ($shape_c * $width / 2.0) + 15, 973 $shape_a); 974 set_dimension ($canvas, "shape_b_arrow", "shape_b_text", 975 RIGHT - 10 * $shape_b * $width, 976 MIDDLE + 10 * ($shape_c * $width / 2.0) + 35, 977 RIGHT, 978 MIDDLE + 10 * ($shape_c * $width / 2.0) + 35, 979 RIGHT - 10 * $shape_b * $width / 2.0, 980 MIDDLE + 10 * ($shape_c * $width / 2.0) + 40, 981 $shape_b); 982 983 set_dimension ($canvas, "shape_c_arrow", "shape_c_text", 984 RIGHT + 10, 985 MIDDLE - 10 * $shape_c * $width / 2.0, 986 RIGHT + 10, 987 MIDDLE + 10 * $shape_c * $width / 2.0, 988 RIGHT + 15, 989 MIDDLE, 990 $shape_c); 991 # Info 992 $canvas->{width_info}->set( 993 text => sprintf("line-width: %.2f", $width) 994 ); 995 $canvas->{shape_a_info}->set( 996 text => sprintf("arrow-tip-length: %.2f (* line-width)", 997 $shape_a) 998 ); 999 $canvas->{shape_b_info}->set( 1000 text => sprintf("arrow-length: %.2f (* line-width)", 1001 $shape_b) 1002 ); 1003 $canvas->{shape_c_info}->set( 1004 text => sprintf("arrow-width: %.2f (* line-width)", 1005 $shape_c) 1006 ); 1007 # Sample arrows 1008 for ( qw/ sample_1 sample_2 sample_3 / ) { 1009 $canvas->{$_}->set( 1010 "line-width" => $width, 1011 "arrow-tip-length" => $shape_a, 1012 "arrow-length" => $shape_b, 1013 "arrow-width" => $shape_c, 1014 ); 1015 } 1016} 1017 1018sub create_dimension { 1019 my ($canvas, $root, $arrow_name, $text_name, $anchor) = @_; 1020 my $item; 1021 $item = Goo::Canvas::PolylineModel->new( 1022 $root, FALSE, undef, 1023 'fill-color' => 'black', 1024 'start-arrow' => TRUE, 1025 'end-arrow' => TRUE, 1026 ); 1027 $canvas->{$arrow_name} = $item; 1028 $item = Goo::Canvas::TextModel->new( 1029 $root, "", 0, 0, -1, $anchor, 1030 'fill-color' => 'black', 1031 'font' => 'Sans 12', 1032 ); 1033 $canvas->{$text_name} = $item; 1034} 1035 1036sub create_info { 1037 my ($canvas, $root, $info_name, $x, $y) = @_; 1038 my $item = Goo::Canvas::TextModel->new( 1039 $root, "", $x, $y, -1, 'nw', 1040 'fill-color' => 'black', 1041 'font' => 'Sans 12', 1042 ); 1043 $canvas->{$info_name} = $item; 1044} 1045 1046sub create_sample_arrow { 1047 my ($canvas, $root, $sample_name, $x1, $y1, $x2, $y2) = @_; 1048 my $item = Goo::Canvas::PolylineModel->new_line( 1049 $root, $x1, $y1, $x2, $y2, 1050 'start-arrow' => TRUE, 1051 'end-arrow' => TRUE, 1052 ); 1053 $canvas->{$sample_name} = $item; 1054} 1055 1056sub on_enter_notify { 1057 my ($item, $target, $ev) = @_; 1058 my $model = $target->get_model; 1059 $model->set('fill-color' => 'red'); 1060 return TRUE; 1061} 1062 1063sub on_leave_notify { 1064 my ($item, $target, $ev) = @_; 1065 my $model= $target->get_model; 1066 $model->set('fill-color' => 'black'); 1067 return TRUE; 1068} 1069 1070sub on_button_press { 1071 my ($item, $target, $ev) = @_; 1072 my $fleur = Gtk2::Gdk::Cursor->new('fleur'); 1073 $item->get_canvas->pointer_grab( 1074 $item, ['pointer-motion-mask', 'button-release-mask'], 1075 $fleur, $ev->time); 1076 return TRUE; 1077} 1078 1079sub on_button_release { 1080 my ($item, $target, $ev) = @_; 1081 $item->get_canvas->pointer_ungrab( 1082 $item, $ev->time 1083 ); 1084 return TRUE; 1085} 1086 1087sub on_motion { 1088 my ($item, $target, $ev)= @_; 1089 my $canvas = $item->get_canvas; 1090 my $model = $target->get_model; 1091 my ($x, $y, $width, $shape_a, $shape_b, $shape_c); 1092 my $change = FALSE; 1093 unless ( $ev->state >= 'button1-mask' ) { 1094 return FALSE; 1095 } 1096 if ( $model == $canvas->{width_drag_box} ) { 1097 $y = $ev->y; 1098 $width = (MIDDLE-$y)/5; 1099 if ( $width < 0) { 1100 return FALSE; 1101 } 1102 $canvas->{width} = $width; 1103 set_arrow_shape($canvas); 1104 } 1105 elsif ( $model == $canvas->{shape_a_drag_box} ) { 1106 $x = $ev->x; 1107 $width = $canvas->{width}; 1108 $shape_a = (RIGHT-$x)/10/$width; 1109 if ( ($shape_a < 0) || ($shape_a>30) ) { 1110 return FALSE; 1111 } 1112 $canvas->{shape_a} =$shape_a; 1113 set_arrow_shape($canvas); 1114 } 1115 elsif ( $model == $canvas->{shape_b_c_drag_box} ) { 1116 $x = $ev->x; 1117 $width = $canvas->{width}; 1118 $shape_b = (RIGHT-$x)/10/$width; 1119 if ( ($shape_b >= 0) && ($shape_b <=30) ) { 1120 $canvas->{shape_b} = $shape_b; 1121 $change = TRUE; 1122 } 1123 $y = $ev->y; 1124 $shape_c = (MIDDLE-$y) * 2/10/$width; 1125 if ( $shape_c >= 0 ) { 1126 $canvas->{shape_c} = $shape_c; 1127 $change = TRUE; 1128 } 1129 if ( $change ) { 1130 set_arrow_shape($canvas); 1131 } 1132 } 1133 return TRUE; 1134} 1135 1136sub create_drag_box { 1137 my ($canvas, $root, $box_name) = @_; 1138 my $item = Goo::Canvas::RectModel->new( 1139 $root, 0, 0, 10, 10, 1140 'fill-color' => 'black', 1141 'stroke-color' => 'black', 1142 'line-width' => 1, 1143 ); 1144 $canvas->{$box_name} = $item; 1145} 1146 1147sub on_item_created { 1148 my ($canvas, $item, $model) = @_; 1149 if ( $model->isa("Goo::Canvas::RectModel")) { 1150 $item->signal_connect( 1151 'enter_notify_event' => \&on_enter_notify 1152 ); 1153 $item->signal_connect( 1154 'leave_notify_event' => \&on_leave_notify, 1155 ); 1156 $item->signal_connect( 1157 'button_press_event' => \&on_button_press 1158 ); 1159 $item->signal_connect( 1160 'button_release_event' => \&on_button_release 1161 ); 1162 $item->signal_connect( 1163 'motion_notify_event' => \&on_motion 1164 ); 1165 } 1166} 1167 1168#}}} 1169 1170#{{{ Fifteen 1171package Fifteen; 1172use Gtk2; 1173use Glib qw(TRUE FALSE); 1174 1175use constant { 1176 PIECE_SIZE => 50, 1177 SCRAMBLE_MOVES => 256, 1178}; 1179 1180sub create_canvas { 1181 my $pkg = shift; 1182 my $vbox = Gtk2::VBox->new; 1183 my ($alignment, $frame, $canvas, $root, $button); 1184 my ($x, $y, @board); 1185 1186 $vbox->set_border_width(4); 1187 $vbox->show; 1188 1189 $alignment = Gtk2::Alignment->new(0.5, 0.5, 0, 0); 1190 $vbox->pack_start($alignment, TRUE, TRUE, 0); 1191 $alignment->show; 1192 1193 $frame = Gtk2::Frame->new(); 1194 $frame->set_shadow_type('in'); 1195 $alignment->add($frame); 1196 $frame->show; 1197 1198 # Create the canvas and board 1199 $canvas = Goo::Canvas->new; 1200 $root = Goo::Canvas::GroupModel->new; 1201 $canvas->set_root_item_model($root); 1202 $canvas->signal_connect('item-created', \&on_item_created); 1203 $canvas->set_size_request( PIECE_SIZE * 4 + 1, 1204 PIECE_SIZE * 4 + 1); 1205 $canvas->set_bounds(0, 0, PIECE_SIZE * 4+1, PIECE_SIZE * 4 + 1); 1206 $frame->add($canvas); 1207 $canvas->show; 1208 1209 foreach my $i( 0..14 ) { 1210 $x = $i % 4; 1211 $y = int($i / 4); 1212 my $item = Goo::Canvas::GroupModel->new($root); 1213 $item->translate($x * PIECE_SIZE, $y * PIECE_SIZE); 1214 my $rect = Goo::Canvas::RectModel->new( 1215 $item, 0, 0, PIECE_SIZE, PIECE_SIZE, 1216 'fill-color' => get_piece_color($i), 1217 'stroke-color' => 'black', 1218 'line-width' => 1 1219 ); 1220 my $text = Goo::Canvas::TextModel->new( 1221 $item, $i+1, PIECE_SIZE/2, PIECE_SIZE/2, -1, 'center', 1222 'font' => 'Sans bold 24', 1223 'fill-color' => 'black' 1224 ); 1225 $item->{text} = $text; 1226 $item->{piece_num} = $i; 1227 $item->{piece_pos} = $i; 1228 push @board, $item; 1229 } 1230 push @board, undef; 1231 $canvas->{board} = \@board; 1232 $button = Gtk2::Button->new("Scramble"); 1233 $vbox->pack_start($button, FALSE, FALSE, 0); 1234 $button->signal_connect('clicked', \&scramble, $canvas); 1235 $button->show; 1236 return $vbox; 1237} 1238 1239sub get_piece_color { 1240 use integer; 1241 my $i = shift; 1242 my $x = $i % 4; 1243 my $y = $i / 4; 1244 my $r = (( 4- $x) * 255) /4; 1245 my $g = (( 4- $y) * 255) /4; 1246 my $b = 128; 1247 return sprintf("#%02x%02x%02x", $r, $g, $b); 1248} 1249 1250sub piece_enter_notify { 1251 my ($item, $target, $ev)= @_; 1252 my $model = $item->get_model; 1253 $model->{text}->set( 1254 'fill-color' => 'white' 1255 ); 1256 return FALSE; 1257} 1258 1259sub piece_leave_notify { 1260 my ($item, $target, $ev)= @_; 1261 my $model = $item->get_model; 1262 $model->{text}->set( 1263 'fill-color' => 'black' 1264 ); 1265 return FALSE; 1266} 1267 1268sub piece_button_press { 1269 my ($item, $target, $event, $data) = @_; 1270 my ($num, $pos, $text, $x, $y, $move, $dx, $dy, $newpos); 1271 1272 my $canvas = $item->get_canvas; 1273 my $model = $item->get_model; 1274 my $board = $canvas->{board}; 1275 $num = $model->{piece_num}; 1276 $pos = $model->{piece_pos}; 1277 $text = $model->{text}; 1278 $x = $pos % 4; 1279 $y = int($pos / 4); 1280 $move = TRUE; 1281 if ( $y>0 && !$board->[($y-1)*4+$x] ) { 1282 $dx = 0; 1283 $dy = -1; 1284 $y--; 1285 } 1286 elsif ( $y<3 && !$board->[($y+1)*4+$x] ) { 1287 $dx = 0; 1288 $dy = 1; 1289 $y++; 1290 } 1291 elsif ( $x>0 && !$board->[$y*4+$x-1] ) { 1292 $dx = -1; 1293 $dy = 0; 1294 $x--; 1295 } 1296 elsif ( $x<3 && !$board->[$y*4+$x+1] ) { 1297 $dx = 1; 1298 $dy = 0; 1299 $x++; 1300 } 1301 else { 1302 $move = FALSE; 1303 } 1304 if ( $move ) { 1305 $newpos = $y*4+$x; 1306 $board->[$pos] = undef; 1307 $board->[$newpos] = $model; 1308 $model->{piece_pos} = $newpos; 1309 $model->translate($dx*PIECE_SIZE, $dy*PIECE_SIZE); 1310 test_win($board, $canvas); 1311 } 1312 return FALSE; 1313} 1314 1315sub test_win { 1316 my ($board, $canvas) = @_; 1317 foreach ( 0..14 ) { 1318 if ( !$board->[$_] || $board->[$_]{piece_num} != $_ ) { 1319 return; 1320 } 1321 } 1322 if ( 1 ) { 1323 my $item = ($board->[0] || $board->[1]); 1324 my $dia = Gtk2::MessageDialog->new( 1325 $canvas->get_toplevel, 'destroy-with-parent', 1326 'info', 'ok', 1327 'You stud, you win!', 1328 ); 1329 $dia->show; 1330 $dia->signal_connect( 'response' => sub { $dia->destroy; } ); 1331 } 1332 return TRUE; 1333} 1334 1335sub on_item_created { 1336 my ($canvas, $item, $model) = @_; 1337 if ( $model->get_parent && $model->isa("Goo::Canvas::GroupModel") ) { 1338 $item->signal_connect( 1339 'enter_notify_event' => \&piece_enter_notify 1340 ); 1341 $item->signal_connect( 1342 'leave_notify_event' => \&piece_leave_notify, 1343 ); 1344 $item->signal_connect( 1345 'button-press-event' => \&piece_button_press, 1346 ); 1347 } 1348} 1349 1350sub scramble { 1351 my ($but, $canvas) = @_; 1352 my $board = $canvas->{board}; 1353 my ($x, $y, $dir, $oldpos); 1354 my $pos = 0; 1355 foreach ( @$board ) { 1356 last unless $_; 1357 $pos++; 1358 } 1359 for ( 0..SCRAMBLE_MOVES ) { 1360 my $done = 0; 1361 $x = $y = 0; 1362 while ( !$done ) { 1363 $dir = int(rand(4)); 1364 $done = 1; 1365 if ( $dir == 0 && $pos > 3 ) { 1366 $y = -1; 1367 } elsif ( $dir==1 && $pos < 12 ) { 1368 $y = 1; 1369 } elsif ( $dir == 2 && ($pos%4) != 0 ) { 1370 $x = -1; 1371 } 1372 elsif ( $dir == 3 && ($pos %4) != 3 ) { 1373 $x = 1; 1374 } 1375 else { 1376 $done = 0; 1377 } 1378 } 1379 $oldpos = $pos + $y*4 + $x; 1380 $board->[$pos] = $board->[$oldpos]; 1381 $board->[$oldpos] = undef; 1382 $board->[$pos]->{piece_pos} = $pos; 1383 $board->[$pos]->translate(-$x*PIECE_SIZE, -$y*PIECE_SIZE); 1384 $pos = $oldpos; 1385 } 1386} 1387#}}} 1388 1389#{{{ Reparent 1390package Reparent; 1391use Gtk2; 1392use Glib qw(TRUE FALSE); 1393 1394sub create_canvas { 1395 my $pkg = shift; 1396 my ($w, $alignment, $frame, $canvas, $root, $parent1, $parent2, $item, $group); 1397 my $vbox = Gtk2::VBox->new; 1398 $vbox->show; 1399 $vbox->set_border_width(4); 1400 # Instructions 1401 $w = Gtk2::Label->new("Reparent test: click on the items to switch them between parents"); 1402 $vbox->pack_start($w, FALSE, FALSE, 0); 1403 $w->show; 1404 # Frame and canvas 1405 $alignment = Gtk2::Alignment->new(0.5, 0.5, 0, 0); 1406 $vbox->pack_start($alignment, FALSE, FALSE, 0); 1407 $alignment->show; 1408 $frame = Gtk2::Frame->new(); 1409 $frame->set_shadow_type('in'); 1410 $alignment->add($frame); 1411 $frame->show; 1412 $canvas = Goo::Canvas->new; 1413 $canvas->show; 1414 $canvas->signal_connect('item-created', \&on_item_created); 1415 1416 $root = Goo::Canvas::GroupModel->new; 1417 $canvas->set_size_request( 400, 200); 1418 $canvas->set_bounds( 0, 0, 300, 200); 1419 $frame->add($canvas); 1420 # First parent and box 1421 $parent1 = Goo::Canvas::GroupModel->new($root); 1422 Goo::Canvas::RectModel->new( 1423 $parent1, 0, 0, 200, 200, 1424 'fill-color' => 'tan' 1425 ); 1426 # Second parent and box 1427 $parent2 = Goo::Canvas::GroupModel->new($root); 1428 $parent2->translate(200, 0); 1429 Goo::Canvas::RectModel->new( 1430 $parent2, 0, 0, 200, 200, 1431 'fill-color' => '#204060' 1432 ); 1433 # Big circle to be reparented 1434 $item = Goo::Canvas::EllipseModel->new( 1435 $parent1, 100, 100, 90, 90, 1436 'stroke-color' => 'black', 1437 'fill-color' => 'mediumseagreen', 1438 'line-width' => 3, 1439 ); 1440 $item->{parent1} = $parent1; 1441 $item->{parent2} = $parent2; 1442 # A group to be reparented 1443 $group = Goo::Canvas::GroupModel->new($parent2); 1444 $group->{parent1} = $parent1; 1445 $group->{parent2} = $parent2; 1446 $group->translate(100, 100); 1447 Goo::Canvas::EllipseModel->new( 1448 $group, 0, 0, 50, 50, 1449 'stroke-color' => 'black', 1450 'fill-color' => 'wheat', 1451 'line-width' => 3, 1452 ); 1453 Goo::Canvas::EllipseModel->new( 1454 $group, 0, 0, 25, 25, 1455 'fill-color' => 'steelblue', 1456 ); 1457 $canvas->set_root_item_model($root); 1458 return $vbox; 1459} 1460 1461sub on_item_created { 1462 my ($canvas, $item, $model) = @_; 1463 if ( $model->{parent1} ) { 1464 $item->signal_connect( 1465 'button-press-event' => \&on_button_press 1466 ); 1467 } 1468} 1469 1470sub on_button_press { 1471 my ($item, $target, $ev) = @_; 1472 if ( $ev->button != 1 || $ev->type ne 'button-press' ) { 1473 return FALSE; 1474 } 1475 my $model = $item->get_model; 1476 my $parent1 = $model->{parent1}; 1477 my $parent2 = $model->{parent2}; 1478 my $parent = $model->get_parent; 1479 my $child_num = $parent->find_child($model); 1480 $parent->remove_child($child_num); 1481 if ( $parent == $parent1 ) { 1482 $parent2->add_child($model, -1); 1483 } 1484 else { 1485 $parent1->add_child($model, -1); 1486 } 1487 return TRUE; 1488} 1489 1490#}}} 1491 1492#{{{ Scalability 1493package Scalability; 1494use Gtk2; 1495use Glib qw(TRUE FALSE); 1496use constant { 1497 N_COLS => 5, 1498 N_ROWS => 20, 1499 PADDING => 10, 1500}; 1501 1502sub create_canvas { 1503 my $pkg = shift; 1504 my $vbox = Gtk2::VBox->new; 1505 my ($table, $frame, $canvas, $root, $width, $height, $pixbuf, 1506 $swin, $item); 1507 my $use_image = 1; 1508 $vbox->show; 1509 $vbox->set_border_width(4); 1510 $table = Gtk2::Table->new(2, 2, FALSE); 1511 $table->set_row_spacings(4); 1512 $table->set_col_spacings(4); 1513 $vbox->pack_start($table, TRUE, TRUE, 0); 1514 $table->show; 1515 $frame = Gtk2::Frame->new(); 1516 $frame->set_shadow_type('in'); 1517 $table->attach($frame, 0,1, 0,1, 1518 ['expand', 'fill', 'shrink'], 1519 ['expand', 'fill', 'shrink'], 1520 0, 0); 1521 $frame->show; 1522 # Create the canvas and board 1523 $pixbuf = Gtk2::Gdk::Pixbuf->new_from_file("$FindBin::Bin/toroid.png"); 1524 if ( $use_image ) { 1525 $width = $pixbuf->get_width + 3; 1526 $height = $pixbuf->get_height + 1; 1527 } 1528 else { 1529 $width = 37; 1530 $height = 19; 1531 } 1532 $canvas = Goo::Canvas->new; 1533 $root = Goo::Canvas::GroupModel->new; 1534 $canvas->set_root_item_model($root); 1535 $canvas->set_size_request( 600, 450); 1536 $canvas->set_bounds( 0, 0, N_COLS*($width+PADDING), N_ROWS*($height+PADDING)); 1537 $canvas->show; 1538 $swin = Gtk2::ScrolledWindow->new(); 1539 $swin->show; 1540 $frame->add($swin); 1541 $swin->add($canvas); 1542 for my $i( 0..N_COLS-1 ) { 1543 for my $j ( 0..N_ROWS-1 ) { 1544 if ( $use_image ) { 1545 $item = Goo::Canvas::ImageModel->new( 1546 $root, $pixbuf, $i*($width+PADDING), $j*($height+PADDING), 1547 ); 1548 } 1549 else { 1550 $item = Goo::Canvas::RectModel->new( 1551 $root, $i*($width+PADDING), $j*($height+PADDING), 1552 $width, $height, 1553 'fill-color' => (($i+$j)%2 ? 'mediumseagreen' : 'steelblue'), 1554 ); 1555 } 1556 } 1557 } 1558 1559 return $vbox; 1560} 1561#}}} 1562 1563#{{{ Grabs 1564package Grabs; 1565use Gtk2; 1566use Glib qw(TRUE FALSE); 1567 1568sub create_canvas { 1569 my $pkg = shift; 1570 my ($w); 1571 my $table = Gtk2::Table->new(5, 2, FALSE); 1572 $table->set_border_width(12); 1573 $table->set_row_spacings(12); 1574 $table->set_col_spacings(12); 1575 $table->show; 1576 $w = Gtk2::Label->new(<<INS); 1577Move the mouse over the widgets and canvas items on the right to see what events they receive. 1578Click buttons to start explicit or implicit pointer grabs and see what events they receive now. 1579(They should all receive the same events.) 1580INS 1581 $table->attach($w, 0,2, 0,1, [],[],0,0); 1582 $w->show; 1583 # Drawing area with explicit grabs. 1584 create_fixed ($table, 1, 1585 "Widget with Explicit Grabs:", 1586 "widget-explicit"); 1587 1588 # Drawing area with implicit grabs. 1589 create_fixed ($table, 2, 1590 "Widget with Implicit Grabs:", 1591 "widget-implicit"); 1592 1593 # Canvas with explicit grabs. 1594 _create_canvas ($table, 3, 1595 "Canvas with Explicit Grabs:", 1596 "canvas-explicit"); 1597 1598 # Canvas with implicit grabs. 1599 _create_canvas ($table, 4, 1600 "Canvas with Implicit Grabs:", 1601 "canvas-implicit"); 1602 1603 return $table; 1604} 1605 1606sub create_fixed { 1607 my ($table, $row, $text, $id) = @_; 1608 my ($label, $fixed, $drawing_area, $view_id); 1609 $label = Gtk2::Label->new($text); 1610 $table->attach($label, 0, 1, $row, $row+1, [], [], 0, 0); 1611 $label->show; 1612 $fixed = Gtk2::Fixed->new; 1613 $fixed->set_has_window(TRUE); 1614 $fixed->set_events( 1615 ['exposure_mask', 'button_press_mask', 1616 'button_release_mask', 'pointer_motion_mask', 1617 'pointer_motion_hint_mask', 'key_press_mask', 1618 'key_release_mask', 'enter_notify_mask', 1619 'leave_notify_mask', 'focus_change_mask'] 1620 ); 1621 $fixed->set_size_request(200, 100); 1622 $table->attach($fixed, 1, 2, $row, $row+1, [], [], 0, 0); 1623 $fixed->show; 1624 $view_id = "$id-background"; 1625 $fixed->signal_connect( 1626 'expose_event', \&on_widget_expose, $view_id 1627 ); 1628 $fixed->signal_connect( "enter_notify_event", 1629 \&on_widget_enter_notify, $view_id); 1630 $fixed->signal_connect( "leave_notify_event", 1631 \&on_widget_leave_notify, $view_id); 1632 $fixed->signal_connect( "motion_notify_event", 1633 \&on_widget_motion_notify, $view_id); 1634 $fixed->signal_connect( "button_press_event", 1635 \&on_widget_button_press, $view_id); 1636 $fixed->signal_connect( "button_release_event", 1637 \&on_widget_button_release, $view_id); 1638 # Left 1639 my $pos = 20; 1640 for ( 'left', 'right' ) { 1641 $drawing_area = Gtk2::DrawingArea->new; 1642 $drawing_area->set_events( 1643 ['exposure_mask', 'button_press_mask', 1644 'button_release_mask', 'pointer_motion_mask', 1645 'pointer_motion_hint_mask', 'key_press_mask', 1646 'key_release_mask', 'enter_notify_mask', 1647 'leave_notify_mask', 'focus_change_mask'] 1648 ); 1649 $drawing_area->set_size_request(60, 60); 1650 $fixed->put($drawing_area, $pos, 20); 1651 $pos += 100; 1652 $drawing_area->show; 1653 $view_id = "$id-$_"; 1654 $drawing_area->signal_connect( "enter_notify_event", 1655 \&on_widget_enter_notify, $view_id); 1656 $drawing_area->signal_connect( "leave_notify_event", 1657 \&on_widget_leave_notify, $view_id); 1658 $drawing_area->signal_connect( "motion_notify_event", 1659 \&on_widget_motion_notify, $view_id); 1660 $drawing_area->signal_connect( "button_press_event", 1661 \&on_widget_button_press, $view_id); 1662 $drawing_area->signal_connect( "button_release_event", 1663 \&on_widget_button_release, $view_id); 1664 } 1665} 1666 1667sub _create_canvas { 1668 my ($table, $row, $text, $id) = @_; 1669 my ($label, $canvas, $root, $rect); 1670 $label = Gtk2::Label->new($text); 1671 $table->attach($label, 0, 1, $row, $row+1, [], [], 0, 0); 1672 $label->show; 1673 $canvas = Goo::Canvas->new; 1674 $root = Goo::Canvas::GroupModel->new; 1675 $canvas->set_root_item_model($root); 1676 $canvas->signal_connect('item-created', \&on_item_created); 1677 $canvas->set_size_request(200, 100); 1678 $canvas->set_bounds(0, 0, 200, 100); 1679 $table->attach($canvas, 1, 2, $row, $row+1, [], [], 0, 0); 1680 $canvas->show; 1681 $rect = Goo::Canvas::RectModel->new( 1682 $root, 0, 0, 200, 100, 1683 'stroke-pattern' => undef, 1684 'fill-color' => 'yellow', 1685 ); 1686 $rect->{id} = "$id-yellow"; 1687 $rect = Goo::Canvas::RectModel->new( 1688 $root, 20, 20, 60, 60, 1689 'stroke-pattern' => undef, 1690 'fill-color' => 'blue', 1691 ); 1692 $rect->{id} = $id.'-blue'; 1693 $rect = Goo::Canvas::RectModel->new( 1694 $root, 120, 20, 60, 60, 1695 'stroke-pattern' => undef, 1696 'fill-color' => 'red', 1697 ); 1698 $rect->{id} = $id.'-red'; 1699} 1700 1701sub on_item_created { 1702 my ($canvas, $item, $model) = @_; 1703 if ( $model->isa("Goo::Canvas::RectModel")) { 1704 $item->signal_connect( "enter_notify_event", 1705 \&on_enter_notify); 1706 $item->signal_connect( "leave_notify_event", 1707 \&on_leave_notify); 1708 $item->signal_connect( "motion_notify_event", 1709 \&on_motion_notify); 1710 $item->signal_connect( "button_press_event", 1711 \&on_button_press); 1712 $item->signal_connect( "button_release_event", 1713 \&on_button_release); 1714 } 1715} 1716# FIXME: the box is not showed 1717sub on_widget_expose { 1718 my ($widget, $ev, $id) = @_; 1719 print "$id received 'expose' signal\n"; 1720 $widget->style->paint_box( 1721 $widget->window, 'normal','in',$ev->area, $widget, undef, 1722 0, 0, $widget->allocation->width, $widget->allocation->height 1723 ); 1724 return FALSE; 1725} 1726 1727sub on_widget_enter_notify { 1728 my ($widget, $ev, $id) = @_; 1729 print "$id received 'enter-notify' signal\n"; 1730 return TRUE; 1731} 1732 1733sub on_widget_leave_notify { 1734 my ($widget, $ev, $id) = @_; 1735 print "$id received 'leave-notify' signal\n"; 1736 return TRUE; 1737} 1738 1739sub on_widget_motion_notify { 1740 my ($widget, $ev, $id) = @_; 1741 print "$id received 'motion-notify' signal(window: ", 1742 sprintf("0x%x", $ev->window->get_pointer), ")\n"; 1743 if ( $ev->is_hint ) { 1744 $ev->window->get_pointer(); 1745 } 1746 return TRUE; 1747} 1748 1749sub on_widget_button_press { 1750 my ($widget, $ev, $id) = @_; 1751 print "$id received 'button-press' signal\n"; 1752 if ( $id =~ /explicit/ ) { 1753 my $mask = [ 1754 'button_press_mask', 'button_release_mask', 1755 'pointer_motion_mask', 'pointer_motion_hint_mask', 1756 'enter_notify_mask', 'leave_notify_mask', 1757 ]; 1758 my $staus = $widget->window->pointer_grab(FALSE, $mask, FALSE, undef, $ev->time); 1759 if ( $staus eq 'success' ) { 1760 print "grabbed pointer\n"; 1761 } else { 1762 print "pointer grab failed\n"; 1763 } 1764 } 1765 return TRUE; 1766} 1767 1768sub on_widget_button_release { 1769 my ($widget, $ev, $id) = @_; 1770 print "$id received 'button-release' signal\n"; 1771 if ( $id =~ /explicit/ ) { 1772 my $display = $widget->get_display; 1773 $display->pointer_ungrab($ev->time); 1774 print "released pointer grab\n"; 1775 } 1776 return TRUE; 1777} 1778 1779sub on_enter_notify { 1780 my ($item, $target, $ev) = @_; 1781 my $model = $target->get_model; 1782 print "$model->{id} received 'enter-notify' signal\n"; 1783 return FALSE; 1784} 1785sub on_leave_notify { 1786 my ($item, $target, $ev) = @_; 1787 my $model = $target->get_model; 1788 print "$model->{id} received 'leave-notify' signal\n"; 1789 return FALSE; 1790} 1791 1792sub on_motion_notify { 1793 my ($item, $target, $ev) = @_; 1794 my $model = $target->get_model; 1795 print "$model->{id} received 'motion-notify' signal\n"; 1796 return FALSE; 1797} 1798 1799sub on_button_press { 1800 my ($item, $target, $ev) = @_; 1801 my $model = $item->get_model; 1802 print "$model->{id} received 'button-press' signal\n"; 1803 if ( $model->{id} =~ /explicit/ ) { 1804 my $mask = [ 1805 'button_press_mask', 'button_release_mask', 1806 'pointer_motion_mask', 'pointer_motion_hint_mask', 1807 'enter_notify_mask', 'leave_notify_mask', 1808 ]; 1809 my $canvas = $item->get_canvas; 1810 my $staus = $canvas->pointer_grab( $item, $mask, undef, $ev->time); 1811 if ( $staus eq 'success' ) { 1812 print "grabbed pointer\n"; 1813 } else { 1814 print "pointer grab failed\n"; 1815 } 1816 } 1817 return FALSE; 1818} 1819 1820sub on_button_release { 1821 my ($item, $target, $ev) = @_; 1822 my $model = $item->get_model; 1823 print "$model->{id} received 'button-released' signal\n"; 1824 if ( $model->{id} =~ /explicit/ ) { 1825 my $canvas = $item->get_canvas; 1826 $canvas->pointer_ungrab($item, $ev->time); 1827 print "released pointer grab\n"; 1828 } 1829 return FALSE; 1830} 1831 1832#}}} 1833 1834#{{{ Events 1835package Events; 1836use Gtk2; 1837use Glib qw(TRUE FALSE); 1838 1839sub create_canvas { 1840 my $pkg = shift; 1841 my $vbox = Gtk2::VBox->new; 1842 my ($alignment, $frame, $label, $canvas); 1843 1844 $vbox->show; 1845 $vbox->set_border_width(4); 1846 # Instructions 1847 $label = Gtk2::Label->new(<<INS); 1848Move the mouse over the items to check they receive the right motion events. 1849The first 2 items in each group are 1) invisible and 2) visible but unpainted. 1850INS 1851 $label->show; 1852 $vbox->pack_start($label, FALSE, FALSE, 0); 1853 # Frame and canvas 1854 $alignment = Gtk2::Alignment->new(0.5, 0.5, 0, 0); 1855 $vbox->pack_start($alignment, FALSE, FALSE, 0); 1856 $alignment->show; 1857 $frame = Gtk2::Frame->new(); 1858 $frame->set_shadow_type('in'); 1859 $alignment->add($frame); 1860 $frame->show; 1861 $canvas = Goo::Canvas->new; 1862 my $root = Goo::Canvas::GroupModel->new; 1863 $canvas->signal_connect('item-created', \&on_item_created); 1864 $canvas->set_root_item_model($root); 1865 $canvas->set_size_request(600, 450); 1866 $canvas->set_bounds(0, 0, 600, 450); 1867 $frame->add($canvas); 1868 $canvas->show; 1869 create_events_area($canvas, 0, 'none', 'none'); 1870 create_events_area($canvas, 1, 'visible-painted', 'visible-painted'); 1871 create_events_area($canvas, 2, 'visible-fill', 'visible-fill'); 1872 create_events_area($canvas, 3, 'visible-stroke', 'visible-stroke'); 1873 create_events_area($canvas, 4, 'visible', 'visible'); 1874 create_events_area($canvas, 5, 'painted', 'painted'); 1875 create_events_area($canvas, 6, 'fill', 'fill'); 1876 create_events_area($canvas, 7, 'stroke', 'stroke'); 1877 create_events_area($canvas, 8, 'all', 'all'); 1878 return $vbox; 1879} 1880 1881sub create_events_area { 1882 my ($canvas, $area_num, $pointer_events, $label) = @_; 1883 my $row = int($area_num/3); 1884 my $col = $area_num%3; 1885 my $x = $col * 200; 1886 my $y = $row * 150; 1887 my $root = $canvas->get_root_item_model; 1888 my $dash = Goo::Canvas::LineDash->new([5, 5]); 1889 my $rect; 1890 1891 # Create invisible item 1892 $rect = Goo::Canvas::RectModel->new( 1893 $root, $x+45, $y+35, 30, 30, 1894 'fill-color' => 'red', 1895 'visibility' => 'invisible', 1896 'line-width' => 5, 1897 'pointer_events' => $pointer_events 1898 ); 1899 $rect->{id} = $label . ' invisible'; 1900 # Display a thin rect around it to indicate it is there 1901 $rect = Goo::Canvas::RectModel->new( 1902 $root, $x+42.5, $y+32.5, 36, 36, 1903 'line-dash' => $dash, 1904 'line-width' => 1, 1905 'stroke-color' => 'gray', 1906 ); 1907 # Create unpainted item. 1908 $rect = Goo::Canvas::RectModel->new( 1909 $root, $x+85, $y+35, 30, 30, 1910 'stroke-pattern' => undef, 1911 'line-width' => 5, 1912 'pointer_events' => $pointer_events 1913 ); 1914 $rect->{id} = $label . ' unpainted'; 1915 # Display a thin rect around it to indicate it is there 1916 $rect = Goo::Canvas::RectModel->new( 1917 $root, $x+82.5, $y+32.5, 36, 36, 1918 'line-dash' => $dash, 1919 'line-width' => 1, 1920 'stroke-color' => 'gray', 1921 ); 1922 # Create stroked item 1923 $rect = Goo::Canvas::RectModel->new( 1924 $root, $x+125, $y+35, 30, 30, 1925 'line-width' => 5, 1926 'pointer_events' => $pointer_events 1927 ); 1928 $rect->{id} = $label . ' stroked'; 1929 # Create filled item 1930 $rect = Goo::Canvas::RectModel->new( 1931 $root, $x+60, $y+75, 30, 30, 1932 'fill-color' => 'red', 1933 'stroke-pattern' => undef, 1934 'line-width' => 5, 1935 'pointer_events' => $pointer_events 1936 ); 1937 $rect->{id} = $label . ' filled'; 1938 # Create filled & filled item 1939 $rect = Goo::Canvas::RectModel->new( 1940 $root, $x+100, $y+75, 30, 30, 1941 'fill-color' => 'red', 1942 'line-width' => 5, 1943 'pointer_events' => $pointer_events 1944 ); 1945 $rect->{id} = $label . ' filled & filled'; 1946 Goo::Canvas::TextModel->new( 1947 $root, $label, $x+100, $y+130, -1, 'center', 1948 'font' => 'Sans 12', 1949 'fill-color' => 'blue', 1950 ); 1951} 1952 1953sub on_item_created { 1954 my ($canvas, $item, $model) = @_; 1955 $item->signal_connect( 1956 'motion_notify_event' => \&on_motion_notify 1957 ); 1958} 1959 1960sub on_motion_notify { 1961 my ($item, $target, $ev) = @_; 1962 return unless $target; 1963 my $model = $target->get_model; 1964 return unless $model && $model->{id}; 1965 print "$model->{id} received 'motion-notify' signal\n"; 1966} 1967 1968#}}} 1969 1970#{{{ Paths 1971package Paths; 1972use Gtk2; 1973use Glib qw(TRUE FALSE); 1974 1975sub create_canvas { 1976 my $pkg = shift; 1977 my ($swin, $canvas); 1978 my $vbox = Gtk2::VBox->new; 1979 $vbox->show; 1980 $vbox->set_border_width(4); 1981 $swin = Gtk2::ScrolledWindow->new(); 1982 $swin->set_shadow_type('in'); 1983 $swin->show; 1984 $vbox->add($swin); 1985 $canvas = Goo::Canvas->new; 1986 $canvas->set_size_request(600, 450); 1987 $canvas->set_bounds(0, 0, 1000, 1000); 1988 $canvas->show; 1989 $swin->add($canvas); 1990 my $root = Goo::Canvas::GroupModel->new; 1991 $canvas->set_root_item_model($root); 1992 setup_canvas($canvas); 1993 return $vbox; 1994} 1995 1996sub setup_canvas { 1997 my $canvas = shift; 1998 my $root = $canvas->get_root_item_model; 1999 my $path; 2000 $path = Goo::Canvas::PathModel->new( $root, "M 20 20 L 40 40", ); 2001 $path = Goo::Canvas::PathModel->new( $root, "M30 20 l20, 20", ); 2002 $path = Goo::Canvas::PathModel->new( $root, "M 60 20 H 80", ); 2003 $path = Goo::Canvas::PathModel->new( $root, "M60 40 h20", ); 2004 $path = Goo::Canvas::PathModel->new( $root, "M 100,20 V 40", ); 2005 $path = Goo::Canvas::PathModel->new( $root, "M 120 20 v 20", ); 2006 $path = Goo::Canvas::PathModel->new( $root, "M 140 20 h20 v20 h-20 z", ); 2007 $path = 2008 Goo::Canvas::PathModel->new( $root, 2009 "M 180 20 h20 v20 h-20 z m 5,5 h10 v10 h-10 z", 2010 "fill-color", "red", "fill-rule", 'even_odd', ); 2011 2012 $path = Goo::Canvas::PathModel->new( $root, "M 220 20 L 260 20 L 240 40 z", 2013 "fill-color", "red", "stroke-color", "blue", "line-width", 3.0, ); 2014 2015 # Test the bezier curve commands: CcSsQqTt. 2016 $path = 2017 Goo::Canvas::PathModel->new( $root, 2018 "M20,100 C20,50 100,50 100,100 S180,150 180,100", 2019 ); 2020 2021 $path = 2022 Goo::Canvas::PathModel->new( $root, "M220,100 c0,-50 80,-50 80,0 s80,50 80,0", 2023 ); 2024 2025 $path = 2026 Goo::Canvas::PathModel->new( $root, "M20,200 Q60,130 100,200 T180,200", ); 2027 2028 $path = Goo::Canvas::PathModel->new( $root, "M220,200 q40,-70 80,0 t80,0", ); 2029 2030 # Test the elliptical arc commands: Aa. 2031 $path = 2032 Goo::Canvas::PathModel->new( $root, "M200,500 h-150 a150,150 0 1,0 150,-150 z", 2033 "fill-color", "red", "stroke-color", "blue", "line-width", 5.0, ); 2034 2035 $path = 2036 Goo::Canvas::PathModel->new( $root, "M175,475 v-150 a150,150 0 0,0 -150,150 z", 2037 "fill-color", "yellow", "stroke-color", "blue", "line-width", 5.0, ); 2038 2039 $path = Goo::Canvas::PathModel->new( 2040 $root, 2041 "M400,600 l 50,-25 " 2042 . "a25,25 -30 0,1 50,-25 l 50,-25 " 2043 . "a25,50 -30 0,1 50,-25 l 50,-25 " 2044 . "a25,75 -30 0,1 50,-25 l 50,-25 " 2045 . "a25,100 -30 0,1 50,-25 l 50,-25", 2046 "stroke-color", 2047 "red", 2048 "line-width", 2049 5.0, 2050 ); 2051 2052 $path = Goo::Canvas::PathModel->new( $root, "M 525,75 a100,50 0 0,0 100,50", 2053 "stroke-color", "red", "line-width", 5.0, ); 2054 $path = Goo::Canvas::PathModel->new( $root, "M 725,75 a100,50 0 0,1 100,50", 2055 "stroke-color", "red", "line-width", 5.0, ); 2056 $path = Goo::Canvas::PathModel->new( $root, "M 525,200 a100,50 0 1,0 100,50", 2057 "stroke-color", "red", "line-width", 5.0, ); 2058 $path = Goo::Canvas::PathModel->new( $root, "M 725,200 a100,50 0 1,1 100,50", 2059 "stroke-color", "red", "line-width", 5.0, ); 2060} 2061 2062 2063#}}} 2064 2065#{{{ Focus 2066package Focus; 2067use Gtk2; 2068use Glib qw(TRUE FALSE); 2069 2070sub create_canvas { 2071 my $pkg = shift; 2072 my ($label, $swin, $canvas); 2073 my $vbox = Gtk2::VBox->new; 2074 $vbox->show; 2075 $vbox->set_border_width(4); 2076 $label = Gtk2::Label->new("Use Tab, Shift+Tab or the arrow keys to move the keyboard focus between the canvas items."); 2077 $swin = Gtk2::ScrolledWindow->new(); 2078 $swin->set_shadow_type('in'); 2079 $swin->show; 2080 $vbox->add($swin); 2081 $canvas = Goo::Canvas->new; 2082 $canvas->can_focus(TRUE); 2083 $canvas->set_size_request(600, 450); 2084 $canvas->set_bounds(0, 0, 1000, 1000); 2085 $canvas->show; 2086 $swin->add($canvas); 2087 my $root = Goo::Canvas::GroupModel->new; 2088 $canvas->set_root_item_model($root); 2089 $canvas->signal_connect('item-created', \&on_item_created); 2090 setup_canvas($canvas); 2091 return $vbox; 2092} 2093 2094sub setup_canvas { 2095 my $canvas = shift; 2096 create_focus_box ($canvas, 110, 80, 50, 30, "red"); 2097 create_focus_box ($canvas, 300, 160, 50, 30, "orange"); 2098 create_focus_box ($canvas, 500, 50, 50, 30, "yellow"); 2099 create_focus_box ($canvas, 70, 400, 50, 30, "blue"); 2100 create_focus_box ($canvas, 130, 200, 50, 30, "magenta"); 2101 create_focus_box ($canvas, 200, 160, 50, 30, "green"); 2102 create_focus_box ($canvas, 450, 450, 50, 30, "cyan"); 2103 create_focus_box ($canvas, 300, 350, 50, 30, "grey"); 2104 create_focus_box ($canvas, 900, 900, 50, 30, "gold"); 2105 create_focus_box ($canvas, 800, 150, 50, 30, "thistle"); 2106 create_focus_box ($canvas, 600, 800, 50, 30, "azure"); 2107 create_focus_box ($canvas, 700, 250, 50, 30, "moccasin"); 2108 create_focus_box ($canvas, 500, 100, 50, 30, "cornsilk"); 2109 create_focus_box ($canvas, 200, 750, 50, 30, "plum"); 2110 create_focus_box ($canvas, 400, 800, 50, 30, "orchid"); 2111} 2112 2113sub create_focus_box { 2114 my ($canvas, $x, $y, $width, $height, $color) = @_; 2115 my $root = $canvas->get_root_item_model; 2116 my $item = Goo::Canvas::RectModel->new( 2117 $root, $x, $y, $width, $height, 2118 'stroke-pattern' => undef, 2119 'fill-color' => $color, 2120 'line-width' => 5, 2121 'can-focus' => TRUE, 2122 ); 2123 $item->{id} = $color; 2124} 2125 2126sub on_item_created { 2127 my ($canvas, $item, $model) = @_; 2128 if ( $model->isa('Goo::Canvas::RectModel')) { 2129 $item->signal_connect('focus_in_event' => \&on_focus_in); 2130 $item->signal_connect('focus_out_event' => \&on_focus_out); 2131 $item->signal_connect('button_press_event' => \&on_button_press); 2132 $item->signal_connect('key_press_event' => \&on_key_press); 2133 } 2134} 2135 2136sub on_key_press { 2137 my($item, $target, $ev) = @_; 2138 my $model = $item->get_model; 2139 print $model->{id} || "Unknown", " received key_press event\n"; 2140 return FALSE; 2141} 2142sub on_button_press { 2143 my($item, $target, $ev) = @_; 2144 my $model = $item->get_model; 2145 print $model->{id} || "Unknown", " received button_press event\n"; 2146 my $canvas = $item->get_canvas; 2147 $canvas->grab_focus($item); 2148 return TRUE; 2149} 2150sub on_focus_out { 2151 my ($item, $target, $ev) = @_; 2152 my $model = $item->get_model; 2153 print $model->{id} || "Unknown", " received focus_out event\n"; 2154 $model->set("stroke-pattern" => undef); 2155 return FALSE; 2156} 2157sub on_focus_in { 2158 my ($item, $target, $ev) = @_; 2159 my $model = $item->get_model; 2160 print $model->{id} || "Unknown", " received focus_in event\n"; 2161 $model->set("stroke-color" => "black"); 2162 return FALSE; 2163} 2164 2165#}}} 2166 2167#{{{ Animation 2168package Animation; 2169use Gtk2; 2170use Glib qw(TRUE FALSE); 2171 2172sub create_canvas { 2173 my $pkg = shift; 2174 my ($hbox, $w, $swin, $canvas); 2175 my $vbox = Gtk2::VBox->new; 2176 $vbox->show; 2177 $vbox->set_border_width(4); 2178 $hbox = Gtk2::HBox->new(FALSE, 4); 2179 $vbox->pack_start($hbox, FALSE, FALSE, 0); 2180 $hbox->show; 2181 $w = Gtk2::ToggleButton->new('Start Animation'); 2182 $hbox->pack_start($w, FALSE, FALSE, 0); 2183 $w->show; 2184 $w->signal_connect('toggled', \&toggle_animation_clicked); 2185 $swin = Gtk2::ScrolledWindow->new(); 2186 $swin->set_shadow_type('in'); 2187 $swin->show; 2188 $vbox->add($swin); 2189 $canvas = Goo::Canvas->new; 2190 $canvas->set_size_request(600, 450); 2191 $canvas->set_bounds(0, 0, 1000, 1000); 2192 $canvas->show; 2193 $w->{canvas} = $canvas; 2194 $swin->add($canvas); 2195 my $root = Goo::Canvas::GroupModel->new; 2196 $canvas->set_root_item_model($root); 2197 setup_canvas($canvas); 2198 return $vbox; 2199} 2200 2201sub setup_canvas { 2202 my $canvas = shift; 2203 my $root = $canvas->get_root_item_model; 2204 my ($rect1, $rect2, $rect3, $rect4, $ellipse1, $ellipse2); 2205 # Absolute 2206 $ellipse1 = Goo::Canvas::EllipseModel->new( 2207 $root, 0, 0, 25, 15, 2208 'fill-color' => 'blue', 2209 ); 2210 $ellipse1->translate(100, 100); 2211 $rect1 = Goo::Canvas::RectModel->new( 2212 $root, -10, -10, 20, 20, 2213 'fill-color' => 'blue', 2214 ); 2215 $rect1->translate(100, 200); 2216 $rect3 = Goo::Canvas::RectModel->new( 2217 $root, -10, -10, 20, 20, 2218 'fill-color' => 'blue', 2219 ); 2220 $rect3->translate(200, 200); 2221 # Relative 2222 $ellipse2 = Goo::Canvas::EllipseModel->new( 2223 $root, 0, 0, 25, 15, 2224 'fill-color' => 'red', 2225 ); 2226 $ellipse2->translate(100, 400); 2227 $rect2 = Goo::Canvas::RectModel->new( 2228 $root, -10, -10, 20, 20, 2229 'fill-color' => 'red', 2230 ); 2231 $rect2->translate(100, 500); 2232 $rect4 = Goo::Canvas::RectModel->new( 2233 $root, -10, -10, 20, 20, 2234 'fill-color' => 'red', 2235 ); 2236 $rect4->translate(200, 500); 2237 $canvas->{items} = [$rect1, $rect2, $rect3, $rect4, $ellipse1, $ellipse2]; 2238} 2239 2240sub toggle_animation_clicked { 2241 my $but = shift; 2242 if ( $but->get_active ) { 2243 $but->set_label('Stop Animation'); 2244 start_animation($but); 2245 } 2246 else { 2247 $but->set_label('Start Animation'); 2248 stop_animation($but); 2249 } 2250} 2251sub start_animation { 2252 my $but = shift; 2253 my ($rect1, $rect2, $rect3, $rect4, $ellipse1, $ellipse2) = @{$but->{canvas}{items}}; 2254 2255 # Absolute 2256 $ellipse1->set_simple_transform (100, 100, 1, 0); 2257 $ellipse1->animate (500, 100, 2, 720, TRUE, 2000, 40, 2258 'bounce'); 2259 2260 $rect1->set_simple_transform (100, 200, 1, 0); 2261 $rect1->animate (100, 200, 1, 350, TRUE, 40 * 36, 40, 2262 'restart'); 2263 2264 $rect3->set_simple_transform (200, 200, 1, 0); 2265 $rect3->animate (200, 200, 3, 0, TRUE, 400, 40, 2266 'bounce'); 2267 2268 # Relative 2269 $ellipse2->set_simple_transform (100, 400, 1, 0); 2270 $ellipse2->animate (400, 0, 2, 720, FALSE, 2000, 40, 2271 'bounce'); 2272 2273 $rect2->set_simple_transform (100, 500, 1, 0); 2274 $rect2->animate (0, 0, 1, 350, FALSE, 40 * 36, 40, 2275 'restart'); 2276 2277 $rect4->set_simple_transform (200, 500, 1, 0); 2278 $rect4->animate (0, 0, 3, 0, FALSE, 400, 40, 2279 'bounce'); 2280} 2281 2282sub stop_animation { 2283 my $but = shift; 2284 my ($rect1, $rect2, $rect3, $rect4, $ellipse1, $ellipse2) = @{$but->{canvas}{items}}; 2285 $ellipse1->stop_animation (); 2286 $ellipse2->stop_animation (); 2287 $rect1->stop_animation (); 2288 $rect2->stop_animation (); 2289 $rect3->stop_animation (); 2290 $rect4->stop_animation (); 2291} 2292 2293#}}} 2294 2295#{{{ Clipping 2296package Clipping; 2297use Gtk2; 2298use Glib qw(TRUE FALSE); 2299 2300sub create_canvas { 2301 my $pkg = shift; 2302 my ($hbox, $swin, $canvas); 2303 my $vbox = Gtk2::VBox->new; 2304 $vbox->show; 2305 $vbox->set_border_width(4); 2306 $swin = Gtk2::ScrolledWindow->new(); 2307 $swin->set_shadow_type('in'); 2308 $swin->show; 2309 $vbox->add($swin); 2310 $canvas = Goo::Canvas->new; 2311 $canvas->set_size_request(600, 450); 2312 $canvas->set_bounds(0, 0, 1000, 1000); 2313 $canvas->show; 2314 $swin->add($canvas); 2315 setup_canvas($canvas); 2316 return $vbox; 2317} 2318 2319sub setup_canvas { 2320 my $canvas = shift; 2321 my $root = Goo::Canvas::GroupModel->new; 2322 $canvas->set_root_item_model($root); 2323 my $model; 2324 $model = Goo::Canvas::EllipseModel->new( 2325 $root, 0, 0, 50, 30, 2326 'fill-color' => 'blue', 2327 ); 2328 $model->translate(100, 100); 2329 $model->rotate(30, 0, 0); 2330 $canvas->get_item($model)->signal_connect( 2331 'button-press-event' => \&on_button_press, 2332 "Blue ellipse (unclipped)" 2333 ); 2334 $model = Goo::Canvas::RectModel->new( 2335 $root, 200, 50, 100, 100, 2336 'fill-color' => 'red', 2337 'clip-fill-rule' => 'even-odd' 2338 ); 2339 $canvas->get_item($model)->signal_connect('button-press-event' => \&on_button_press, 2340 "Red rectangle (unclipped)"); 2341 $model = Goo::Canvas::RectModel->new( 2342 $root, 380, 50, 100, 100, 2343 'fill-color' => 'yellow' 2344 ); 2345 $canvas->get_item($model)->signal_connect('button-press-event' => \&on_button_press, 2346 "Yellow rectangle(unclipped)"); 2347 # clipped items 2348 $model = Goo::Canvas::EllipseModel->new( 2349 $root, 0, 0, 50, 30, 2350 'fill-color' => 'blue', 2351 'clip-path' => "M 0 0 h 100 v 100 h -100 Z" 2352 ); 2353 $model->translate (100, 300); 2354 $model->rotate (30, 0, 0); 2355 $canvas->get_item($model)->signal_connect('button-press-event' => \&on_button_press, 2356 "Blue ellipse"); 2357 $model = Goo::Canvas::RectModel->new( 2358 $root, 200, 250, 100, 100, 2359 'fill-color' => 'red', 2360 'clip-path' => "M 250 300 h 100 v 100 h -100 Z", 2361 'clip-fill-rule' => 'even-odd' 2362 ); 2363 $canvas->get_item($model)->signal_connect('button-press-event' => \&on_button_press, 2364 "Red rectangle"); 2365 $model = Goo::Canvas::RectModel->new( 2366 $root, 380, 250, 100, 100, 2367 'fill-color' => 'yellow', 2368 'clip-path' => "M480,230 l40,100 l-80 0 z", 2369 ); 2370 $canvas->get_item($model)->signal_connect('button-press-event' => \&on_button_press, 2371 'Yellow rectangle'); 2372 # Table with clipped items 2373 my $table = Goo::Canvas::TableModel->new($root); 2374 $table->translate (200, 400); 2375 $table->rotate (30, 0, 0); 2376 $model = Goo::Canvas::EllipseModel->new( 2377 $table, 0, 0, 50, 30, 2378 'fill-color' => 'blue', 2379 'clip-path' => "M 0 0 h 100 v 100 h -100 Z", 2380 ); 2381 $model->translate (100, 300); 2382 $model->rotate (30, 0, 0); 2383 $canvas->get_item($model)->signal_connect('button-press-event' => \&on_button_press, 2384 'Blue ellipse'); 2385 $model = Goo::Canvas::RectModel->new( 2386 $table, 200, 250, 100, 100, 2387 'fill-color' => 'red', 2388 "clip-path" => "M 250 300 h 100 v 100 h -100 Z", 2389 "clip-fill-rule" => 'even-odd', 2390 ); 2391 $table->set_child_properties( 2392 $model, 2393 'column' => 1, 2394 ); 2395 $canvas->get_item($model)->signal_connect('button-press-event' => \&on_button_press, 2396 'Red rectangle'); 2397 $model = Goo::Canvas::RectModel->new( 2398 $table, 380, 250, 100, 100, 2399 'fill-color' => 'yellow', 2400 'clip-path' => "M480,230 l40,100 l-80 0 z" 2401 ); 2402 $table->set_child_properties( 2403 $model, 2404 'column' => 2, 2405 ); 2406 $canvas->get_item($model)->signal_connect('button-press-event' => \&on_button_press, 2407 'Yellow rectangle'); 2408} 2409 2410sub on_button_press { 2411 my ($item, $target, $ev, $id) = @_; 2412 printf "%s received 'button-press' at %g, %g, (root: %g, %g)\n", 2413 $id, $ev->x, $ev->y, $ev->x_root, $ev->y_root; 2414 return TRUE; 2415} 2416 2417#}}} 2418