2005-10-11 08:37:40 +02:00
|
|
|
#include <glib-object.h>
|
|
|
|
#include <gtk/gtk.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include "gm-text-scroller.h"
|
2005-11-06 17:27:26 +01:00
|
|
|
#include "../gm-debug.h"
|
2005-10-11 08:37:40 +02:00
|
|
|
#include "../gm-support.h"
|
|
|
|
|
|
|
|
#define GM_TEXT_SCROLLER_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE((object), GM_TYPE_TEXT_SCROLLER, GmTextScrollerPrivate))
|
|
|
|
|
|
|
|
struct _GmTextScrollerPrivate {
|
|
|
|
GtkTextView *text_view;
|
|
|
|
GtkTextBuffer *text_buffer;
|
|
|
|
GtkScrolledWindow *scrolled_window;
|
|
|
|
|
|
|
|
gint character_height;
|
|
|
|
gboolean end_scrolled;
|
|
|
|
gint idle_handler;
|
|
|
|
};
|
|
|
|
|
|
|
|
void on_gm_text_scroller_text_view_style_set(GtkTextView *view,
|
|
|
|
GtkStyle *previous_style, GmTextScroller *scroller);
|
|
|
|
void on_gm_text_scroller_text_buffer_changed(GtkTextBuffer *text_buffer,
|
|
|
|
GmTextScroller *scroller);
|
|
|
|
void on_gm_text_scroller_text_view_notify(GtkTextView *text_view, GParamSpec *arg1,
|
|
|
|
GmTextScroller *scroller);
|
|
|
|
void on_gm_text_scroller_text_view_destroy(GtkTextView *text_view,
|
|
|
|
GmTextScroller *scroller);
|
|
|
|
|
|
|
|
/* Signals
|
|
|
|
|
|
|
|
enum {
|
|
|
|
PROTO
|
|
|
|
NUM_SIGNALS
|
|
|
|
};
|
|
|
|
|
|
|
|
static guint gm_text_scroller_signals[NUM_SIGNALS] = {0};*/
|
|
|
|
|
|
|
|
G_DEFINE_TYPE(GmTextScroller, gm_text_scroller, G_TYPE_OBJECT)
|
|
|
|
|
|
|
|
static void
|
|
|
|
gm_text_scroller_finalize(GObject *object) {
|
|
|
|
GmTextScroller *obj = GM_TEXT_SCROLLER(object);
|
|
|
|
|
|
|
|
if (obj->priv->text_buffer != NULL) {
|
|
|
|
g_signal_handlers_disconnect_by_func(obj->priv->text_buffer,
|
|
|
|
G_CALLBACK(on_gm_text_scroller_text_buffer_changed),
|
|
|
|
obj);
|
|
|
|
|
|
|
|
g_object_unref(obj->priv->text_buffer);
|
|
|
|
}
|
|
|
|
|
|
|
|
g_signal_handlers_disconnect_by_func(obj->priv->text_view,
|
|
|
|
G_CALLBACK(on_gm_text_scroller_text_view_notify), obj);
|
|
|
|
g_signal_handlers_disconnect_by_func(obj->priv->text_view,
|
|
|
|
G_CALLBACK(on_gm_text_scroller_text_view_style_set), obj);
|
|
|
|
g_signal_handlers_disconnect_by_func(obj->priv->text_view,
|
|
|
|
G_CALLBACK(on_gm_text_scroller_text_view_destroy), obj);
|
|
|
|
|
|
|
|
G_OBJECT_CLASS(gm_text_scroller_parent_class)->finalize(object);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gm_text_scroller_class_init(GmTextScrollerClass *klass) {
|
|
|
|
GObjectClass *object_class = G_OBJECT_CLASS(klass);
|
|
|
|
|
|
|
|
object_class->finalize = gm_text_scroller_finalize;
|
|
|
|
|
|
|
|
/*gm_text_scroller_signals[PROTO] =
|
|
|
|
g_signal_new("proto",
|
|
|
|
G_OBJECT_CLASS_TYPE(object_class),
|
|
|
|
G_SIGNAL_RUN_LAST,
|
|
|
|
G_STRUCT_OFFSET(GmTextScrollerClass, proto),
|
|
|
|
NULL, NULL,
|
|
|
|
g_cclosure_marshal_VOID__VOID,
|
|
|
|
G_TYPE_NONE,
|
|
|
|
0);*/
|
|
|
|
|
|
|
|
g_type_class_add_private(object_class, sizeof(GmTextScrollerPrivate));
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gm_text_scroller_init(GmTextScroller *obj) {
|
|
|
|
obj->priv = GM_TEXT_SCROLLER_GET_PRIVATE(obj);
|
|
|
|
|
|
|
|
obj->priv->character_height = 10;
|
|
|
|
obj->priv->end_scrolled = FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
gm_text_scroller_update_text_buffer(GmTextScroller *scroller) {
|
|
|
|
if (scroller->priv->text_buffer != NULL) {
|
|
|
|
g_signal_handlers_disconnect_by_func(scroller->priv->text_buffer,
|
|
|
|
G_CALLBACK(on_gm_text_scroller_text_buffer_changed),
|
|
|
|
scroller);
|
|
|
|
|
|
|
|
g_object_unref(scroller->priv->text_buffer);
|
|
|
|
}
|
|
|
|
|
|
|
|
scroller->priv->text_buffer =
|
|
|
|
gtk_text_view_get_buffer(scroller->priv->text_view);
|
|
|
|
|
|
|
|
if (scroller->priv->text_buffer != NULL) {
|
|
|
|
g_object_ref(scroller->priv->text_buffer);
|
|
|
|
|
|
|
|
g_signal_connect(scroller->priv->text_buffer, "changed",
|
|
|
|
G_CALLBACK(on_gm_text_scroller_text_buffer_changed), scroller);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
gm_text_scroller_scroll_end(GmTextScroller *scroller) {
|
|
|
|
GtkTextMark *mark;
|
|
|
|
GtkTextIter iter;
|
|
|
|
|
|
|
|
mark = gtk_text_buffer_get_mark(scroller->priv->text_buffer,
|
|
|
|
"end-of-buffer");
|
|
|
|
|
|
|
|
if (mark == NULL) {
|
|
|
|
gtk_text_buffer_get_end_iter(scroller->priv->text_buffer, &iter);
|
|
|
|
mark = gtk_text_buffer_create_mark(scroller->priv->text_buffer,
|
|
|
|
"end-of-buffer", &iter, FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
gtk_text_view_scroll_to_mark(scroller->priv->text_view, mark, 0.0,
|
|
|
|
TRUE, 1.0, 1.0);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
gm_text_scroller_scroll_begin(GmTextScroller *scroller) {
|
|
|
|
GtkTextMark *mark;
|
|
|
|
GtkTextIter iter;
|
|
|
|
|
|
|
|
mark = gtk_text_buffer_get_mark(scroller->priv->text_buffer,
|
|
|
|
"begin-of-buffer");
|
|
|
|
|
|
|
|
if (mark == NULL) {
|
|
|
|
gtk_text_buffer_get_start_iter(scroller->priv->text_buffer, &iter);
|
|
|
|
mark = gtk_text_buffer_create_mark(scroller->priv->text_buffer,
|
|
|
|
"begin-of-buffer", &iter, TRUE);
|
|
|
|
}
|
|
|
|
|
|
|
|
gtk_text_view_scroll_to_mark(scroller->priv->text_view, mark, 0.0,
|
|
|
|
TRUE, 0.0, 0.0);
|
|
|
|
}
|
|
|
|
|
|
|
|
gboolean
|
|
|
|
gm_text_scroller_scroll_end_idle(GmTextScroller *scroller) {
|
|
|
|
scroller->priv->idle_handler = 0;
|
|
|
|
gm_text_scroller_scroll_end(scroller);
|
|
|
|
scroller->priv->end_scrolled = FALSE;
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
gm_text_scroller_prepare(GmTextScroller *scroller) {
|
|
|
|
GtkAdjustment *ad = gtk_scrolled_window_get_vadjustment(
|
|
|
|
scroller->priv->scrolled_window);
|
|
|
|
|
|
|
|
scroller->priv->end_scrolled = scroller->priv->end_scrolled ||
|
|
|
|
((ad->page_size + ad->value) >= ad->upper -
|
|
|
|
(double)(scroller->priv->character_height));
|
|
|
|
|
|
|
|
if (scroller->priv->idle_handler == 0 && scroller->priv->end_scrolled) {
|
|
|
|
scroller->priv->idle_handler = g_idle_add((GSourceFunc)
|
|
|
|
gm_text_scroller_scroll_end_idle, scroller);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
gm_text_scroller_update_character_height(GmTextScroller *scroller) {
|
|
|
|
GtkRcStyle *style = gtk_widget_get_modifier_style(GTK_WIDGET(
|
|
|
|
scroller->priv->text_view));
|
|
|
|
PangoContext *pc = gtk_widget_create_pango_context(GTK_WIDGET(
|
|
|
|
scroller->priv->text_view));
|
|
|
|
PangoLayout *pl;
|
|
|
|
gint cwidth, cheight;
|
|
|
|
|
|
|
|
if (style->font_desc != NULL) {
|
|
|
|
pango_context_set_font_description(pc, style->font_desc);
|
|
|
|
pl = pango_layout_new(pc);
|
|
|
|
|
|
|
|
pango_layout_set_text(pl, "G", 1);
|
|
|
|
pango_layout_get_pixel_size(pl, &(cwidth),
|
|
|
|
&(cheight));
|
|
|
|
|
|
|
|
if (scroller->priv->character_height != cheight) {
|
|
|
|
scroller->priv->character_height = cheight;
|
|
|
|
}
|
|
|
|
|
|
|
|
g_object_unref(pl);
|
|
|
|
}
|
|
|
|
|
|
|
|
g_object_unref(pc);
|
|
|
|
}
|
|
|
|
|
|
|
|
GmTextScroller *
|
|
|
|
gm_text_scroller_new(GtkTextView *text_view) {
|
|
|
|
GmTextScroller *obj;
|
|
|
|
GtkWidget *parent = gtk_widget_get_parent(GTK_WIDGET(text_view));
|
|
|
|
|
|
|
|
if (parent == NULL || !GTK_IS_SCROLLED_WINDOW(parent)) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
obj = GM_TEXT_SCROLLER(g_object_new(GM_TYPE_TEXT_SCROLLER,
|
|
|
|
NULL));
|
|
|
|
|
|
|
|
obj->priv->text_view = text_view;
|
|
|
|
obj->priv->scrolled_window = GTK_SCROLLED_WINDOW(parent);
|
|
|
|
|
|
|
|
gm_text_scroller_update_text_buffer(obj);
|
|
|
|
gm_text_scroller_update_character_height(obj);
|
|
|
|
|
|
|
|
g_signal_connect(text_view, "notify",
|
|
|
|
G_CALLBACK(on_gm_text_scroller_text_view_notify), obj);
|
|
|
|
g_signal_connect(text_view, "style-set",
|
|
|
|
G_CALLBACK(on_gm_text_scroller_text_view_style_set), obj);
|
|
|
|
g_signal_connect(text_view, "destroy",
|
|
|
|
G_CALLBACK(on_gm_text_scroller_text_view_destroy), obj);
|
|
|
|
|
|
|
|
obj->priv->idle_handler = g_idle_add((GSourceFunc)
|
|
|
|
gm_text_scroller_scroll_end_idle, obj);
|
|
|
|
|
|
|
|
return obj;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Callbacks
|
|
|
|
|
|
|
|
void
|
|
|
|
on_gm_text_scroller_text_view_notify(GtkTextView *text_view, GParamSpec *arg1,
|
|
|
|
GmTextScroller *scroller) {
|
|
|
|
if (strcmp(arg1->name, "buffer") == 0) {
|
|
|
|
// Buffer changed
|
|
|
|
gm_text_scroller_update_text_buffer(scroller);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
on_gm_text_scroller_text_view_style_set(GtkTextView *view,
|
|
|
|
GtkStyle *previous_style, GmTextScroller *scroller) {
|
|
|
|
gm_text_scroller_update_character_height(scroller);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
on_gm_text_scroller_text_buffer_changed(GtkTextBuffer *text_buffer,
|
|
|
|
GmTextScroller *scroller) {
|
|
|
|
// Changed...
|
|
|
|
gm_text_scroller_prepare(scroller);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
on_gm_text_scroller_text_view_destroy(GtkTextView *text_view,
|
|
|
|
GmTextScroller *scroller) {
|
|
|
|
// Remove ourselfs when the text view dies
|
|
|
|
g_object_unref(scroller);
|
|
|
|
}
|