Beefy Boxes and Bandwidth Generously Provided by pair Networks
more useful options
 
PerlMonks  

Gtk2 TextView-w-linenumbers

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

Description: 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 :

Comment on Gtk2 TextView-w-linenumbers
Download Code

Back to Snippets Section

Log In?
Username:
Password:

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

How do I use this? | Other CB clients
Other Users?
Others avoiding work at the Monastery: (5)
As of 2015-07-06 04:52 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    The top three priorities of my open tasks are (in descending order of likelihood to be worked on) ...









    Results (70 votes), past polls