/*
* linenum.c
* This file is part of Leafpad
*
* Copyright (C) 2004 Tarot Osuji
*
* 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 of the License, 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
*/
#include <gtk/gtk.h>
#define DV(x)
static gint min_number_window_width;
static const gint margin = 5;
static gint calculate_min_number_window_width(GtkWidget *widget)
{
PangoLayout *layout;
gchar *str;
gint width, col = 4;
str = g_strnfill(col, 0x20);
layout = gtk_widget_create_pango_layout(widget, str);
g_free (str);
pango_layout_get_pixel_size(layout, &width, NULL);
g_object_unref(G_OBJECT(layout));
return width;
}
/* taken from gedit and gtksourceview */
/* originated from gtk+/tests/testtext.c */
static void
get_lines (GtkTextView *text_view,
gint y1,
gint y2,
GArray *buffer_coords,
GArray *numbers,
gint *countp)
{
GtkTextIter iter;
gint count;
gint size;
gint last_line_num;
g_array_set_size (buffer_coords, 0);
g_array_set_size (numbers, 0);
/* Get iter at first y */
gtk_text_view_get_line_at_y (text_view, &iter, y1, NULL);
/* For each iter, get its location and add it to the arrays.
* Stop when we pass y2
*/
count = 0;
size = 0;
while (!gtk_text_iter_is_end (&iter))
{
gint y, height;
gtk_text_view_get_line_yrange (text_view, &iter, &y, &height);
g_array_append_val (buffer_coords, y);
last_line_num = gtk_text_iter_get_line (&iter);
g_array_append_val (numbers, last_line_num);
++count;
if ((y + height) >= y2)
break;
gtk_text_iter_forward_line (&iter);
}
if (gtk_text_iter_is_end (&iter))
{
gint y, height;
gint line_num;
gtk_text_view_get_line_yrange (text_view, &iter, &y, &height);
line_num = gtk_text_iter_get_line (&iter);
if (line_num != last_line_num) {
g_array_append_val (buffer_coords, y);
g_array_append_val (numbers, line_num);
++count;
}
}
*countp = count;
}
static gint
line_numbers_expose (GtkWidget *widget,
GdkEventExpose *event)
{
GtkTextView *text_view;
GdkWindow *win;
/* GtkStyle *style; */
PangoLayout *layout;
PangoAttrList *alist;
PangoAttribute *attr;
GArray *numbers;
GArray *pixels;
gint y1, y2;
gint count;
gint layout_width;
gint justify_width = 0;
gint i;
/* gchar *str; */
gchar str [8]; /* we don't expect more than ten million lines */
text_view = GTK_TEXT_VIEW (widget);
/* See if this expose is on the line numbers window */
/* left_win = gtk_text_view_get_window (text_view,
GTK_TEXT_WINDOW_LEFT);
right_win = gtk_text_view_get_window (text_view,
GTK_TEXT_WINDOW_RIGHT);
if (event->window == left_win)
{
type = GTK_TEXT_WINDOW_LEFT;
target = event->window;
}
else if (event->window == right_win)
{
type = GTK_TEXT_WINDOW_RIGHT;
target = right_win;
}
else
return FALSE;
*/
win = gtk_text_view_get_window (text_view,
GTK_TEXT_WINDOW_LEFT);
if (event->window != win)
return FALSE;
/* style = gtk_style_copy (widget->style);
* style = gtk_style_copy (gtk_widget_get_default_style());
*/
y1 = event->area.y;
y2 = y1 + event->area.height;
gtk_text_view_window_to_buffer_coords (text_view,
GTK_TEXT_WINDOW_LEFT,
0,
y1,
NULL,
&y1);
gtk_text_view_window_to_buffer_coords (text_view,
GTK_TEXT_WINDOW_LEFT,
0,
y2,
NULL,
&y2);
numbers = g_array_new (FALSE, FALSE, sizeof (gint));
pixels = g_array_new (FALSE, FALSE, sizeof (gint));
get_lines (text_view,
y1,
y2,
pixels,
numbers,
&count);
/* a zero-lined document should display a "1"; we don't need to worry about
scrolling effects of the text widget in this special case */
if (count == 0)
{
gint y = 0;
gint n = 0;
count = 1;
g_array_append_val (pixels, y);
g_array_append_val (numbers, n);
}
DV({g_print("Painting line numbers %d - %d\n",
g_array_index(numbers, gint, 0),
g_array_index(numbers, gint, count - 1)); });
layout = gtk_widget_create_pango_layout (widget, "");
/* str = g_strdup_printf ("%d", gtk_text_buffer_get_line_count(text_view->buffer)); */
g_snprintf (str, sizeof (str),
"%d", MAX (99, gtk_text_buffer_get_line_count(text_view->buffer)));
pango_layout_set_text (layout, str, -1);
/* g_free (str); */
pango_layout_get_pixel_size (layout, &layout_width, NULL);
min_number_window_width = calculate_min_number_window_width(widget);
if (layout_width > min_number_window_width)
gtk_text_view_set_border_window_size (text_view,
GTK_TEXT_WINDOW_LEFT,
layout_width + margin);
else {
/* if ((gtk_text_view_get_border_window_size (text_view, GTK_TEXT_WINDOW_LEFT) - 5) > layout_width) { */
gtk_text_view_set_border_window_size (text_view,
GTK_TEXT_WINDOW_LEFT,
min_number_window_width + margin);
/* } */
justify_width = min_number_window_width - layout_width;
}
pango_layout_set_width (layout, layout_width);
pango_layout_set_alignment (layout, PANGO_ALIGN_RIGHT);
alist = pango_attr_list_new();
attr = pango_attr_foreground_new(
widget->style->text_aa->red,
widget->style->text_aa->green,
widget->style->text_aa->blue);
attr->start_index = 0;
attr->end_index = G_MAXUINT;
pango_attr_list_insert(alist, attr);
pango_layout_set_attributes(layout, alist);
/* Draw fully internationalized numbers! */
i = 0;
while (i < count)
{
gint pos;
gtk_text_view_buffer_to_window_coords (text_view,
GTK_TEXT_WINDOW_LEFT,
0,
g_array_index (pixels, gint, i),
NULL,
&pos);
/* str = g_strdup_printf ("%d", g_array_index (numbers, gint, i) + 1); */
g_snprintf (str, sizeof (str),
"%d", g_array_index (numbers, gint, i) + 1);
pango_layout_set_text (layout, str, -1);
gtk_paint_layout (widget->style,
win,
GTK_WIDGET_STATE (widget),
FALSE,
NULL,
widget,
NULL,
layout_width + justify_width + 1, pos,
layout);
/* g_free (str); */
++i;
}
g_array_free (pixels, TRUE);
g_array_free (numbers, TRUE);
g_object_unref (G_OBJECT (layout));
/* g_object_ref (G_OBJECT (style)); */
/* don't stop emission, need to draw children */
return FALSE;
}
void show_line_numbers(GtkWidget *text_view, gboolean visible)
{
if (visible) {
min_number_window_width = calculate_min_number_window_width(text_view);
gtk_text_view_set_border_window_size(
GTK_TEXT_VIEW(text_view),
GTK_TEXT_WINDOW_LEFT,
min_number_window_width + margin);
g_signal_connect(
G_OBJECT(text_view),
"expose_event",
G_CALLBACK(line_numbers_expose),
NULL);
} else {
gtk_text_view_set_border_window_size(
GTK_TEXT_VIEW(text_view),
GTK_TEXT_WINDOW_LEFT,
0);
g_signal_handlers_disconnect_by_func(
G_OBJECT(text_view),
G_CALLBACK(line_numbers_expose),
NULL);
}
}