2005-09-06 09:21:05 -07:00
|
|
|
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4; coding: utf-8 -*-
|
2005-09-09 02:54:58 -07:00
|
|
|
*
|
2005-09-04 21:24:21 -07:00
|
|
|
* moopaneview.c
|
|
|
|
*
|
|
|
|
* Copyright (C) 2004-2005 by Yevgen Muntyan <muntyan@math.tamu.edu>
|
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*
|
|
|
|
* See COPYING file that comes with this distribution.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "mooedit/moopaneview.h"
|
2005-09-08 10:08:31 -07:00
|
|
|
#include "mooutils/moomarshals.h"
|
2005-09-04 21:24:21 -07:00
|
|
|
|
|
|
|
|
2005-09-08 10:08:31 -07:00
|
|
|
struct _MooPaneViewPrivate {
|
|
|
|
GHashTable *line_data;
|
|
|
|
gboolean busy;
|
|
|
|
gboolean scrolled;
|
|
|
|
GtkTextMark *end_mark;
|
|
|
|
};
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
gpointer data;
|
|
|
|
GDestroyNotify free_func;
|
|
|
|
} LineData;
|
|
|
|
|
|
|
|
static LineData *line_data_new (gpointer data,
|
|
|
|
GDestroyNotify free_func);
|
|
|
|
static void line_data_free (LineData *line_data);
|
2005-09-04 21:24:21 -07:00
|
|
|
|
2005-09-08 10:08:31 -07:00
|
|
|
static void moo_pane_view_finalize (GObject *object);
|
2005-09-04 21:24:21 -07:00
|
|
|
|
2005-09-08 10:08:31 -07:00
|
|
|
// static void moo_pane_view_set_property (GObject *object,
|
|
|
|
// guint prop_id,
|
|
|
|
// const GValue *value,
|
|
|
|
// GParamSpec *pspec);
|
|
|
|
// static void moo_pane_view_get_property (GObject *object,
|
|
|
|
// guint prop_id,
|
|
|
|
// GValue *value,
|
|
|
|
// GParamSpec *pspec);
|
|
|
|
|
|
|
|
static void moo_pane_view_realize (GtkWidget *widget);
|
|
|
|
static gboolean moo_pane_view_button_press (GtkWidget *widget,
|
2005-09-04 21:24:21 -07:00
|
|
|
GdkEventButton *event);
|
|
|
|
|
2005-09-08 10:08:31 -07:00
|
|
|
static GtkTextBuffer *get_buffer (MooPaneView *view);
|
|
|
|
static GHashTable *get_hash_table (MooPaneView *view);
|
|
|
|
|
2005-09-04 21:24:21 -07:00
|
|
|
|
|
|
|
enum {
|
2005-09-08 10:08:31 -07:00
|
|
|
CLICK,
|
2005-09-04 21:24:21 -07:00
|
|
|
LAST_SIGNAL
|
|
|
|
};
|
|
|
|
|
|
|
|
static guint signals[LAST_SIGNAL];
|
|
|
|
|
|
|
|
|
|
|
|
enum {
|
|
|
|
PROP_0,
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/* MOO_TYPE_PANE_VIEW */
|
2005-09-09 02:40:10 -07:00
|
|
|
G_DEFINE_TYPE (MooPaneView, moo_pane_view, MOO_TYPE_TEXT_VIEW)
|
2005-09-04 21:24:21 -07:00
|
|
|
|
|
|
|
|
|
|
|
static void moo_pane_view_class_init (MooPaneViewClass *klass)
|
|
|
|
{
|
|
|
|
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
|
|
|
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
|
|
|
|
|
2005-09-08 00:46:48 -07:00
|
|
|
// gobject_class->set_property = moo_pane_view_set_property;
|
|
|
|
// gobject_class->get_property = moo_pane_view_get_property;
|
2005-09-08 10:08:31 -07:00
|
|
|
gobject_class->finalize = moo_pane_view_finalize;
|
2005-09-04 21:24:21 -07:00
|
|
|
|
2005-09-08 10:08:31 -07:00
|
|
|
widget_class->realize = moo_pane_view_realize;
|
|
|
|
widget_class->button_press_event = moo_pane_view_button_press;
|
|
|
|
|
|
|
|
signals[CLICK] =
|
|
|
|
g_signal_new ("click",
|
|
|
|
G_OBJECT_CLASS_TYPE (klass),
|
|
|
|
G_SIGNAL_RUN_LAST,
|
|
|
|
G_STRUCT_OFFSET (MooPaneViewClass, click),
|
|
|
|
g_signal_accumulator_true_handled, NULL,
|
|
|
|
_moo_marshal_BOOL__POINTER_INT,
|
|
|
|
G_TYPE_BOOLEAN, 2,
|
|
|
|
G_TYPE_POINTER, G_TYPE_INT);
|
2005-09-04 21:24:21 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-09-08 00:46:48 -07:00
|
|
|
static void moo_pane_view_init (MooPaneView *view)
|
|
|
|
{
|
2005-09-08 10:08:31 -07:00
|
|
|
view->priv = g_new0 (MooPaneViewPrivate, 1);
|
|
|
|
|
|
|
|
g_object_set (view,
|
|
|
|
"editable", FALSE,
|
|
|
|
"cursor-visible", FALSE,
|
2005-09-09 02:40:10 -07:00
|
|
|
"current-line-color", "grey",
|
2005-09-08 10:08:31 -07:00
|
|
|
NULL);
|
2005-09-08 00:46:48 -07:00
|
|
|
}
|
2005-09-04 21:24:21 -07:00
|
|
|
|
|
|
|
|
2005-09-08 10:08:31 -07:00
|
|
|
static void moo_pane_view_finalize (GObject *object)
|
|
|
|
{
|
|
|
|
MooPaneView *view = MOO_PANE_VIEW (object);
|
|
|
|
|
|
|
|
if (view->priv->line_data)
|
|
|
|
g_hash_table_destroy (view->priv->line_data);
|
|
|
|
|
|
|
|
g_free (view->priv);
|
|
|
|
|
|
|
|
G_OBJECT_CLASS (moo_pane_view_parent_class)->finalize (object);
|
|
|
|
}
|
2005-09-04 21:24:21 -07:00
|
|
|
|
|
|
|
|
|
|
|
GtkWidget*
|
|
|
|
moo_pane_view_new (void)
|
|
|
|
{
|
|
|
|
return g_object_new (MOO_TYPE_PANE_VIEW, NULL);
|
|
|
|
}
|
2005-09-08 00:46:48 -07:00
|
|
|
|
|
|
|
|
|
|
|
void
|
2005-09-08 10:08:31 -07:00
|
|
|
moo_pane_view_clear (MooPaneView *view)
|
2005-09-08 00:46:48 -07:00
|
|
|
{
|
|
|
|
GtkTextBuffer *buffer;
|
2005-09-08 10:08:31 -07:00
|
|
|
GtkTextIter start, end;
|
|
|
|
|
|
|
|
g_return_if_fail (MOO_IS_PANE_VIEW (view));
|
|
|
|
|
|
|
|
buffer = get_buffer (view);
|
|
|
|
gtk_text_buffer_get_bounds (buffer, &start, &end);
|
|
|
|
gtk_text_buffer_delete (buffer, &start, &end);
|
|
|
|
|
|
|
|
if (view->priv->line_data)
|
|
|
|
{
|
|
|
|
g_hash_table_destroy (view->priv->line_data);
|
|
|
|
view->priv->line_data = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static GtkTextBuffer*
|
|
|
|
get_buffer (MooPaneView *view)
|
|
|
|
{
|
|
|
|
return gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static LineData*
|
|
|
|
line_data_new (gpointer data,
|
|
|
|
GDestroyNotify free_func)
|
|
|
|
{
|
|
|
|
LineData *line_data;
|
|
|
|
|
|
|
|
g_return_val_if_fail (data != NULL, NULL);
|
|
|
|
|
|
|
|
line_data = g_new (LineData, 1);
|
|
|
|
line_data->data = data;
|
|
|
|
line_data->free_func = free_func;
|
|
|
|
|
|
|
|
return line_data;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
line_data_free (LineData *line_data)
|
|
|
|
{
|
|
|
|
if (line_data)
|
|
|
|
{
|
|
|
|
if (line_data->data && line_data->free_func)
|
|
|
|
line_data->free_func (line_data->data);
|
|
|
|
g_free (line_data);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static GHashTable*
|
|
|
|
get_hash_table (MooPaneView *view)
|
|
|
|
{
|
|
|
|
if (!view->priv->line_data)
|
|
|
|
view->priv->line_data =
|
|
|
|
g_hash_table_new_full (g_direct_hash, g_direct_equal,
|
|
|
|
NULL, (GDestroyNotify) line_data_free);
|
|
|
|
|
|
|
|
return view->priv->line_data;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
moo_pane_view_button_press (GtkWidget *widget,
|
|
|
|
GdkEventButton *event)
|
|
|
|
{
|
|
|
|
GtkTextView *textview = GTK_TEXT_VIEW (widget);
|
|
|
|
MooPaneView *view = MOO_PANE_VIEW (widget);
|
|
|
|
int buffer_x, buffer_y, line;
|
|
|
|
GtkTextIter iter;
|
|
|
|
gpointer data;
|
|
|
|
gboolean handled = FALSE;
|
|
|
|
|
|
|
|
if (gtk_text_view_get_window_type (textview, event->window) == GTK_TEXT_WINDOW_TEXT)
|
|
|
|
{
|
|
|
|
gtk_text_view_window_to_buffer_coords (textview,
|
|
|
|
GTK_TEXT_WINDOW_TEXT,
|
|
|
|
event->x, event->y,
|
|
|
|
&buffer_x, &buffer_y);
|
|
|
|
/* XXX */
|
|
|
|
gtk_text_view_get_line_at_y (textview, &iter, buffer_y, NULL);
|
|
|
|
|
|
|
|
line = gtk_text_iter_get_line (&iter);
|
|
|
|
data = moo_pane_view_get_line_data (view, line);
|
|
|
|
|
|
|
|
g_signal_emit (view, signals[CLICK], 0, data, line, &handled);
|
|
|
|
|
|
|
|
if (handled)
|
|
|
|
gtk_text_buffer_place_cursor (get_buffer (view), &iter);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!handled)
|
|
|
|
return GTK_WIDGET_CLASS(moo_pane_view_parent_class)->button_press_event (widget, event);
|
|
|
|
else
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
moo_pane_view_realize (GtkWidget *widget)
|
|
|
|
{
|
|
|
|
GdkWindow *window;
|
|
|
|
|
|
|
|
GTK_WIDGET_CLASS(moo_pane_view_parent_class)->realize (widget);
|
|
|
|
|
|
|
|
window = gtk_text_view_get_window (GTK_TEXT_VIEW (widget),
|
|
|
|
GTK_TEXT_WINDOW_TEXT);
|
|
|
|
gdk_window_set_cursor (window, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* XXX check line */
|
|
|
|
void
|
|
|
|
moo_pane_view_set_line_data (MooPaneView *view,
|
|
|
|
int line,
|
|
|
|
gpointer data,
|
|
|
|
GDestroyNotify free_func)
|
|
|
|
{
|
|
|
|
g_return_if_fail (MOO_IS_PANE_VIEW (view));
|
|
|
|
g_return_if_fail (line >= 0);
|
|
|
|
|
|
|
|
if (data)
|
|
|
|
{
|
|
|
|
GHashTable *hash = get_hash_table (view);
|
|
|
|
g_hash_table_insert (hash, GINT_TO_POINTER (line),
|
|
|
|
line_data_new (data, free_func));
|
|
|
|
}
|
|
|
|
else if (view->priv->line_data)
|
|
|
|
{
|
|
|
|
g_hash_table_remove (view->priv->line_data, GINT_TO_POINTER (line));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
gpointer
|
|
|
|
moo_pane_view_get_line_data (MooPaneView *view,
|
|
|
|
int line)
|
|
|
|
{
|
|
|
|
g_return_val_if_fail (MOO_IS_PANE_VIEW (view), NULL);
|
|
|
|
g_return_val_if_fail (line >= 0, NULL);
|
|
|
|
|
|
|
|
if (!view->priv->line_data)
|
|
|
|
{
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
LineData *line_data =
|
|
|
|
g_hash_table_lookup (view->priv->line_data,
|
|
|
|
GINT_TO_POINTER (line));
|
|
|
|
return line_data ? line_data->data : NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
GtkTextTag*
|
|
|
|
moo_pane_view_create_tag (MooPaneView *view,
|
|
|
|
const char *tag_name,
|
|
|
|
const char *first_property_name,
|
|
|
|
...)
|
|
|
|
{
|
|
|
|
GtkTextTag *tag;
|
|
|
|
GtkTextBuffer *buffer;
|
|
|
|
va_list list;
|
|
|
|
|
|
|
|
g_return_val_if_fail (MOO_IS_PANE_VIEW (view), NULL);
|
|
|
|
|
|
|
|
buffer = get_buffer (view);
|
|
|
|
tag = gtk_text_buffer_create_tag (buffer, tag_name, NULL);
|
|
|
|
g_return_val_if_fail (tag != NULL, NULL);
|
|
|
|
|
|
|
|
if (first_property_name)
|
|
|
|
{
|
|
|
|
va_start (list, first_property_name);
|
|
|
|
g_object_set_valist (G_OBJECT (tag), first_property_name, list);
|
|
|
|
va_end (list);
|
|
|
|
}
|
|
|
|
|
|
|
|
return tag;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
GtkTextTag*
|
|
|
|
moo_pane_view_lookup_tag (MooPaneView *view,
|
|
|
|
const char *tag_name)
|
|
|
|
{
|
|
|
|
GtkTextTagTable *table;
|
|
|
|
GtkTextBuffer *buffer;
|
|
|
|
|
|
|
|
g_return_val_if_fail (MOO_IS_PANE_VIEW (view), NULL);
|
|
|
|
g_return_val_if_fail (tag_name != NULL, NULL);
|
|
|
|
|
|
|
|
buffer = get_buffer (view);
|
|
|
|
table = gtk_text_buffer_get_tag_table (buffer);
|
|
|
|
|
|
|
|
return gtk_text_tag_table_lookup (table, tag_name);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
moo_pane_view_write_line (MooPaneView *view,
|
|
|
|
const char *text,
|
|
|
|
gssize len,
|
|
|
|
GtkTextTag *tag)
|
|
|
|
{
|
|
|
|
int line;
|
|
|
|
|
|
|
|
g_return_val_if_fail (MOO_IS_PANE_VIEW (view), -1);
|
|
|
|
g_return_val_if_fail (text != NULL, -1);
|
|
|
|
|
|
|
|
line = moo_pane_view_start_line (view);
|
|
|
|
g_return_val_if_fail (line >= 0, -1);
|
|
|
|
|
|
|
|
moo_pane_view_write (view, text, len, tag);
|
|
|
|
moo_pane_view_end_line (view);
|
|
|
|
|
|
|
|
return line;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
check_if_scrolled (MooPaneView *view)
|
|
|
|
{
|
|
|
|
GdkWindow *text_window;
|
|
|
|
GdkRectangle rect;
|
|
|
|
GtkTextIter iter;
|
|
|
|
int line;
|
|
|
|
|
|
|
|
if (!GTK_WIDGET_REALIZED (view))
|
|
|
|
{
|
|
|
|
view->priv->scrolled = FALSE;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
text_window = gtk_text_view_get_window (GTK_TEXT_VIEW (view),
|
|
|
|
GTK_TEXT_WINDOW_TEXT);
|
|
|
|
|
|
|
|
gtk_text_view_get_visible_rect (GTK_TEXT_VIEW (view), &rect);
|
|
|
|
gtk_text_view_get_line_at_y (GTK_TEXT_VIEW (view), &iter,
|
|
|
|
rect.y + rect.height - 1, NULL);
|
|
|
|
line = gtk_text_iter_get_line (&iter);
|
|
|
|
|
|
|
|
if (line + 1 < gtk_text_buffer_get_line_count (get_buffer (view)))
|
|
|
|
view->priv->scrolled = TRUE;
|
|
|
|
else
|
|
|
|
view->priv->scrolled = FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
moo_pane_view_start_line (MooPaneView *view)
|
|
|
|
{
|
|
|
|
GtkTextBuffer *buffer;
|
|
|
|
GtkTextIter iter;
|
|
|
|
|
|
|
|
g_return_val_if_fail (MOO_IS_PANE_VIEW (view), -1);
|
|
|
|
g_return_val_if_fail (!view->priv->busy, -1);
|
|
|
|
|
|
|
|
view->priv->busy = TRUE;
|
|
|
|
|
|
|
|
buffer = get_buffer (view);
|
|
|
|
gtk_text_buffer_get_end_iter (buffer, &iter);
|
|
|
|
|
|
|
|
check_if_scrolled (view);
|
|
|
|
|
|
|
|
if (!gtk_text_iter_starts_line (&iter))
|
|
|
|
gtk_text_buffer_insert (buffer, &iter, "\n", 1);
|
|
|
|
|
|
|
|
return gtk_text_iter_get_line (&iter);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
moo_pane_view_write (MooPaneView *view,
|
|
|
|
const char *text,
|
|
|
|
gssize len,
|
|
|
|
GtkTextTag *tag)
|
|
|
|
{
|
|
|
|
GtkTextBuffer *buffer;
|
|
|
|
GtkTextIter iter;
|
2005-09-08 00:46:48 -07:00
|
|
|
|
|
|
|
g_return_if_fail (MOO_IS_PANE_VIEW (view));
|
|
|
|
g_return_if_fail (text != NULL);
|
2005-09-08 10:08:31 -07:00
|
|
|
g_return_if_fail (view->priv->busy);
|
|
|
|
|
|
|
|
buffer = get_buffer (view);
|
|
|
|
gtk_text_buffer_get_end_iter (buffer, &iter);
|
|
|
|
|
|
|
|
if (g_utf8_validate (text, len, NULL))
|
|
|
|
{
|
|
|
|
gtk_text_buffer_insert_with_tags (buffer, &iter, text, len, tag, NULL);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
char *text_utf8 = g_locale_to_utf8 (text, len, NULL, NULL, NULL);
|
|
|
|
|
|
|
|
if (text_utf8)
|
|
|
|
gtk_text_buffer_insert_with_tags (buffer, &iter, text_utf8, -1, tag, NULL);
|
|
|
|
else
|
|
|
|
g_warning ("%s: could not convert '%s' to utf8",
|
|
|
|
G_STRLOC, text);
|
|
|
|
|
|
|
|
g_free (text_utf8);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static GtkTextMark*
|
|
|
|
get_end_mark (MooPaneView *view)
|
|
|
|
{
|
|
|
|
if (!view->priv->end_mark)
|
|
|
|
{
|
|
|
|
GtkTextIter end;
|
|
|
|
GtkTextBuffer *buffer = get_buffer (view);
|
|
|
|
gtk_text_buffer_get_end_iter (buffer, &end);
|
|
|
|
view->priv->end_mark =
|
|
|
|
gtk_text_buffer_create_mark (buffer, NULL, &end, FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
return view->priv->end_mark;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
moo_pane_view_end_line (MooPaneView *view)
|
|
|
|
{
|
|
|
|
g_return_if_fail (MOO_IS_PANE_VIEW (view));
|
|
|
|
g_return_if_fail (view->priv->busy);
|
2005-09-08 00:46:48 -07:00
|
|
|
|
2005-09-08 10:08:31 -07:00
|
|
|
view->priv->busy = FALSE;
|
2005-09-08 00:46:48 -07:00
|
|
|
|
2005-09-08 10:08:31 -07:00
|
|
|
if (!view->priv->scrolled)
|
|
|
|
gtk_text_view_scroll_mark_onscreen (GTK_TEXT_VIEW (view),
|
|
|
|
get_end_mark (view));
|
2005-09-08 00:46:48 -07:00
|
|
|
}
|