Beefy Boxes and Bandwidth Generously Provided by pair Networks
Think about Loose Coupling
 
PerlMonks  

Gtk2 TextView-w-linenumbers

by zentara (Archbishop)
on Jan 23, 2006 at 14:56 UTC ( [id://524941]=CUFP: print w/replies, xml ) Need Help??

Hi, I recently posted Gtk2 Visual Grep, in which I have a hack to turn linenumbers on/off for copying-and-pasting. Well there is a better way to do line numbers with Gtk2, it's called Gtk2::SourceView, which has automatic line numbers and syntax highlighting.

However :-), being the amateur that I am, I wanted to make a way to do it, without needing to install additional modules. My first attempt was to scroll-link 2 textviews side-by-side, one showing line numbers, and the other showing the lines. This method was a bit bloated and had some scrolling problems.

But luckily for me, muppet(on the gtk2-perl maillist) did a quick conversion from the c Gtk2 code and made a line-numbered-textview class. Since this is such a choice gem of a snippet, and will almost certainly be asked for as monks learn Perl/Gtk2; I present it here. (muppet is so busy, he seldom posts here anymore.)

#!/usr/bin/perl -w # by muppet of the gtk2-perl maillist and perlmonks =doc This is a quick port of the line-number drawing code from gtksourcevie +w. I've removed the stuff about markers and the ability to turn off line numbers. =cut package LineNumberedTextView; use strict; use Gtk2; use Glib ':constants'; use Glib::Object::Subclass Gtk2::TextView::, signals => { expose_event => \&_expose_event, }, ; sub INIT_INSTANCE { my $self = shift; # just make up a size, it will be set properly later. $self->set_border_window_size (left => 10); # start with a monospace font; the user can set whatever else he l +ikes. $self->modify_font (Gtk2::Pango::FontDescription->from_string ('mo +nospace')); } # This function is taken from gtk+/tests/testtext.c sub _get_lines { my ($text_view, $first_y, $last_y, $buffer_coords, $numbers) = @_; my $last_line_num = -1; @$buffer_coords = (); @$numbers = (); # Get iter at first y (my $iter, undef) = $text_view->get_line_at_y ($first_y); # For each iter, get its location and add it to the arrays. # Stop when we pass last_y my $count = 0; my $size = 0; while (! $iter->is_end) { my ($y, $height) = $text_view->get_line_yrange ($iter); push @$buffer_coords, $y; push @$numbers, $iter->get_line; ++$count; last if (($y + $height) >= $last_y); $iter->forward_line; } if ($iter->is_end) { my ($y, $height) = $text_view->get_line_yrange ($iter); my $line_num = $iter->get_line; if ($line_num != $last_line_num) { push @$buffer_coords, $y; push @$numbers, $line_num; ++$count; } } return $count; } sub _paint_margin { my ($self, $event) = @_; my $win = $self->get_window ('left'); my $y1 = $event->area->y; my $y2 = $y1 + $event->area->height; # get the extents of the line printing (undef, $y1) = $self->window_to_buffer_coords ('left', 0, $y1); (undef, $y2) = $self->window_to_buffer_coords ('left', 0, $y2); my @numbers; my @pixels; # get the line numbers and y coordinates. my $count = $self->_get_lines ($y1, $y2, \@pixels, \@numbers); # A zero-lined document should display a "1"; we don't need to wor +ry about # scrolling effects of the text widget in this special case if ($count == 0) { $count = 1; push @pixels, 0; push @numbers, 0; } #warn ("Painting line numbers $numbers[0] - $numbers[$#numbers]\n" +); # set size. my $str = sprintf "%d", $self->get_buffer->get_line_count; my $layout = $self->create_pango_layout ($str); my ($text_width, undef) = $layout->get_pixel_size; $layout->set_width ($text_width); $layout->set_alignment ('right'); # determine the width of the left margin. my $margin_width = $text_width + 4; $self->set_border_window_size (left => $margin_width); my $i = 0; my $cur = $self->get_buffer->get_iter_at_mark ($self->get_buffer-> +get_insert); # Remember to account for zero-indexing my $cur_line = $cur->get_line + 1; while ($i < $count) { my $pos; (undef, $pos) = $self->buffer_to_window_coords ('left', 0, $pixels[$i] +); my $line_to_paint = $numbers[$i] + 1; if ($line_to_paint == $cur_line) { $layout->set_markup ("<b>$line_to_paint</b>"); } else { $layout->set_markup ("$line_to_paint"); } $self->style->paint_layout ($win, $self->state, FALSE, undef, $self, undef, $text_width + 2, $pos, $layout); ++$i; } } sub _expose_event { my ($self, $event) = @_; # if the event is actually on the left window, we need to repaint # our line numbers. if ($event->window == $self->get_window ('left')) { return $self->_paint_margin ($event); } else { # otherwise, we just let TextView do all the work. return $self->signal_chain_from_overridden ($event); } } ############## package main; use strict; use Gtk2 -init; my $window = Gtk2::Window->new; my $scroller = Gtk2::ScrolledWindow->new; my $textview = LineNumberedTextView->new; my $buffer = $textview->get_buffer; my @lines = (1..500); foreach my $line (@lines) { $buffer->insert ($buffer->get_end_iter, "$line\n"); } my $end_mark = $buffer->create_mark( 'end', $buffer->get_end_iter, 0 ) +; $textview->scroll_to_mark( $end_mark, 0.0,0, 0.0, 1.0 ); #while (<>) { # $buffer->insert ($buffer->get_end_iter, $_); #} $window->add ($scroller); $scroller->add ($textview); $window->set_default_size (500, 300); $window->show_all; $window->signal_connect (destroy => sub {Gtk2->main_quit}); Gtk2->main; # vim: set et sw=4 sts=4 :

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: CUFP [id://524941]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others pondering the Monastery: (8)
As of 2024-03-28 10:10 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found