The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#!/usr/bin/perl -w

=doc

Implement a custom CellRenderer with a multi-line editor, by deriving a
TextView subclass which implements the CellEditable GInterface.

=cut

#
# Copyright (C) 2003 by muppet.
# 
# This library is free software; you can redistribute it and/or modify it under
# the terms of the GNU Library General Public License as published by the Free
# Software Foundation; either version 2.1 of the License, or (at your option)
# any later version.
# 
# This library 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 Library General Public License for
# more details.
# 
# You should have received a copy of the GNU Library General Public License
# along with this library; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place - Suite 330, Boston, MA  02111-1307  USA.
#
# $Id$
#

use strict;

use Gtk2 -init;
use Gtk2::Gdk::Keysyms;

#############################################################################
# First, we need a TextView which implements CellEditable.

package Mup::MultilineEntry;

use Data::Dumper;
use constant TRUE => 1;
use constant FALSE => 0;

use Glib::Object::Subclass
	Gtk2::TextView::,
	# tell Glib that this new type will implement the CellEditable
	# GInterface.
	interfaces => [ Gtk2::CellEditable:: ],
	;

sub set_text { shift->get_buffer->set_text (shift); }
sub get_text {
	my $buffer = shift->get_buffer;
	$buffer->get_text ($buffer->get_start_iter,
	                   $buffer->get_end_iter, TRUE);
}

sub START_EDITING { warn "start editing\n"; }
sub EDITING_DONE { warn "editing done\n"; }
sub REMOVE_WIDGET { warn "remove widget\n"; }

###############################################################################
# and now, a CellRenderer that uses Mup::MultilineEntry for editing.

package Mup::CellRendererMultiline;

use Glib qw(TRUE FALSE);

use Glib::Object::Subclass
	"Gtk2::CellRendererText",
;

sub START_EDITING {
	my ($cell, $event, $view, $path, $background_area,
	    $cell_area, $flags) = @_;

	my $entry = Mup::MultilineEntry->new;
	$entry->set (border_width => $cell->get ('ypad'));
	$entry->set_text ($cell->get ('text'));
	$entry->grab_focus;

	$entry->signal_connect (key_press_event => sub {
		my ($widget, $event) = @_;

		if (($event->keyval == $Gtk2::Gdk::Keysyms{Return} ||
		     $event->keyval == $Gtk2::Gdk::Keysyms{KP_Enter})
		    and not $event->state & 'control-mask') {
			$cell->signal_emit (edited => $path, $entry->get_text);
			$entry->destroy;
			return TRUE;
		}

		return FALSE;
	});

	# Make sure the entry has the correct height.  On some versions of
	# gtk+, the entry would otherwise be just a few pixels tall.
	$entry->set (height_request => $cell_area->height);

	$entry->show;

	return $entry;
}


###############################################################################
# driver code.

package main;

use Glib qw(TRUE FALSE);

my $window = Gtk2::Window->new;
$window->set_title ("Multiline CellRenderer");
$window->signal_connect (delete_event => sub { Gtk2->main_quit; FALSE });

my $model = Gtk2::ListStore->new (qw(Glib::String));
my $view = Gtk2::TreeView->new ($model);

foreach ('this is a test',
         "here's some text\nwith newlines in\nain't it cool\nno rubbish bin",
	 "try editing with both\nrenderers.",
	 "in the custom one\nuse Ctrl+Enter to\nadd a new line") {
	$model->set ($model->append, 0 => $_);
}

sub cell_edited {
	my ($cell, $path, $new_value) = @_;
	my $iter = Gtk2::TreePath->new_from_string ($path);
	$model->set ($model->get_iter ($iter), 0 => $new_value);
}

my $renderer = Mup::CellRendererMultiline->new;
$renderer->set (editable => TRUE);
$renderer->signal_connect (edited => \&cell_edited);
my $column = Gtk2::TreeViewColumn->new_with_attributes ("custom renderer",
                                                        $renderer,
                                                        text => 0);

$view->append_column ($column);

$renderer = Gtk2::CellRendererText->new;
$renderer->set (editable => TRUE);
$renderer->signal_connect (edited => \&cell_edited);
$column = Gtk2::TreeViewColumn->new_with_attributes ("standard renderer",
                                                     $renderer,
                                                     text => 0);

$view->append_column ($column);

my $scroller = Gtk2::ScrolledWindow->new;
$scroller->set_policy (qw(automatic automatic));
$scroller->add ($view);

my $check = Gtk2::CheckButton->new ('resizable columns');
$check->set_active (FALSE);
$check->signal_connect (toggled => sub {
	map { $_->set_resizable ($check->get_active); } $view->get_columns;
});

my $box = Gtk2::VBox->new;
$box->add ($scroller);
$box->pack_start ($check, FALSE, FALSE, 0);
$window->add ($box);
$window->set_default_size (300, 200);
$window->show_all;

Gtk2->main;