The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
package CanvasBezierCurve;
#
# canvas-curve.c: bezier curve demo.
#
# Copyright (C) 2002 Mark McLoughlin
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
# 02111-1307, USA.
#
# Authors:
#     Mark McLoughlin <mark@skynet.ie>
#     muppet <scott at asofyet dot org> (gtk2-perl port)
#

# FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME 
#
# there's a nasty workaround in here.
#
# you can't set or get the bpath property of the GnomeCanvasBpath via 
# Glib::Object->get or Glib::Object->set (of course, that includes
# Gnome::Canvas::Item->new) -- you have to use $bpath->set_path_def and
# $bpath->get_path_def.
# 
# the problem is that the bpath property of of GnomeCanvasBpath is defined as a
# G_TYPE_POINTER, rather than a G_TYPE_BOXED as it should be.  we can't register
# copy/free/whatever functions for pointer values, so we can't use any of our
# way-cool Glib binding tools.  thus, until libgnomecanvas fixes this bug,
# we have to work around at this level.
#
# FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME 

use strict;
use Carp;
use Glib qw(TRUE FALSE);
use Gnome2::Canvas;

use constant STATE_INIT          => 0;
use constant STATE_FIRST_PRESS   => 1;
use constant STATE_FIRST_RELEASE => 2;
use constant STATE_SECOND_PRESS  => 3;

my $current_state = STATE_INIT;
my $current_item;
my @current_points = ();

sub draw_curve {
	my ($item, $x, $y) = @_;
	my $root = $item->parent;

	if ($current_state == STATE_INIT) {
		$current_points[0] = $x;
		$current_points[1] = $y;

	} elsif ($current_state == STATE_FIRST_PRESS) {
		$current_points[2] = $x;
		$current_points[3] = $y;

	        my $path_def = Gnome2::Canvas::PathDef->new;

		$path_def->moveto ($current_points[0], $current_points[1]);

		$path_def->lineto ($current_points[2], $current_points[3]);

		if ($current_item) {
			##$current_item->set (bpath => $path_def);
			$current_item->set_path_def ($path_def);
		} else {
			$current_item = Gnome2::Canvas::Item->new (
						$root,
						'Gnome2::Canvas::Bpath',
			###			can't do this here, see below
			###			bpath => $path_def,
						outline_color => 'blue',
						width_pixels => 5,
						cap_style => 'round');

			# hack, see above
			$current_item->set_path_def ($path_def);

			$current_item->signal_connect (event => \&item_event);
		}

	} elsif ($current_state == STATE_FIRST_RELEASE) {
		$current_points[4] = $x;
		$current_points[5] = $y;

	        my $path_def = Gnome2::Canvas::PathDef->new;

		$path_def->moveto ($current_points[0], $current_points[1]);

		$path_def->curveto ($current_points[4], $current_points[5],
				    $current_points[4], $current_points[5],
				    $current_points[2], $current_points[3]);

		###$current_item->set (bpath => $path_def);
		$current_item->set_path_def ($path_def);

	} elsif ($current_state == STATE_SECOND_PRESS) {
		$current_points[6] = $x;
		$current_points[7] = $y;

	        my $path_def = Gnome2::Canvas::PathDef->new;

		$path_def->moveto ($current_points[0], $current_points[1]);

		$path_def->curveto ($current_points[4], $current_points[5],
				    $current_points[6], $current_points[7],
				    $current_points[2], $current_points[3]);

		####$current_item->set (bpath => $path_def);
		$current_item->set_path_def ($path_def);

		$current_item = undef;

	} else {
		croak "not reached";
	}
}

sub item_event {
	my ($item, $event) = @_;
	if ($event->type eq 'button-press' &&
	    $event->button == 1 &&
	    $event->state >= 'shift-mask') {

		if ($item == $current_item) {
			$current_item = undef;
			$current_state = STATE_INIT;
		}

		$item->destroy;
		$item = undef;

		return TRUE;
	}

	return FALSE;
}

sub canvas_event {
	my ($item, $event) = @_;
	if ($event->type eq 'button-press') {
		return FALSE if $event->button != 1;

		if ($current_state == STATE_INIT) {
			draw_curve ($item, $event->x, $event->y);
			$current_state = STATE_FIRST_PRESS;
		} elsif ($current_state == STATE_FIRST_RELEASE) {
			draw_curve ($item, $event->x, $event->y);
			$current_state = STATE_SECOND_PRESS;
		} elsif ($current_state == STATE_SECOND_PRESS) {
			draw_curve ($item, $event->x, $event->y);
			$current_state = STATE_INIT;
		} else {
			croak "shouldn't have reached here $current_state";
		}

	} elsif ($event->type eq 'button-release') {
		return FALSE if $event->button != 1;

		if ($current_state == STATE_FIRST_PRESS) {
			draw_curve ($item, $event->x, $event->y);
			$current_state = STATE_FIRST_RELEASE;
		} else {
		}

	} elsif ($event->type eq 'motion-notify') {
		if ($current_state == STATE_FIRST_PRESS) {
			draw_curve ($item, $event->x, $event->y);
		}
	}

	return FALSE;
}

sub create_canvas {
	my $aa = shift;

#	gtk_widget_push_colormap (gdk_rgb_get_cmap ());

	my $canvas = $aa
	           ? Gnome2::Canvas->new_aa
	           : Gnome2::Canvas->new;

	$canvas->set_size_request (600, 250);
	$canvas->set_scroll_region (0, 0, 600, 250);
	$canvas->show;

	my $root = $canvas->root;

	my $item = Gnome2::Canvas::Item->new ($root,
				      'Gnome2::Canvas::Rect',
				      outline_color => 'black',
				      fill_color => 'white',
				      x1 => 0.0,
				      y1 => 0.0,
				      x2 => 600.0,
				      y2 => 250.0);

	Gnome2::Canvas::Item->new ($root,
			       'Gnome2::Canvas::Text',
			       text => ($aa ? "AntiAlias" : "Non-AntiAlias"),
			       x => 270.0,
			       y => 5.0,
			       font => 'Sans 12',
			       anchor => 'n',
			       fill_color => 'black');

#	gtk_widget_pop_colormap ();

	$item->signal_connect (event => \&canvas_event);

	my $frame = Gtk2::Frame->new;
	$frame->set_shadow_type ('in');

	$frame->add ($canvas);

	return $frame;
}

sub create {
	my $vbox = Gtk2::VBox->new (FALSE, 4);
	$vbox->set_border_width (4);
	$vbox->show;

	my $label = Gtk2::Label->new ("Drag a line with button 1. Then mark 2 control points wth\n"
			      ."button 1. Shift+click with button 1 to destroy the curve.\n");
	$vbox->pack_start ($label, FALSE, FALSE, 0);
	$label->show;

	my $canvas = create_canvas (FALSE);
	$vbox->pack_start ($canvas, TRUE, TRUE, 0);
	$canvas->show;

	my $aa_canvas = create_canvas (TRUE);
	$vbox->pack_start ($aa_canvas, TRUE, TRUE, 0);
	$aa_canvas->show;

	return $vbox;
}


1;