From e4d5b5598ce3aad9ac9039b1963048b94d3638a3 Mon Sep 17 00:00:00 2001 From: Jesse van den Kieboom Date: Mon, 2 Jan 2006 17:45:00 +0000 Subject: [PATCH] Fixed bold, inverse, link dragging --- gnoemoe/widgets/gm-world-text-view.c | 1124 +++++++++++++++++--------- 1 file changed, 744 insertions(+), 380 deletions(-) diff --git a/gnoemoe/widgets/gm-world-text-view.c b/gnoemoe/widgets/gm-world-text-view.c index 9723c90..4aff2c5 100644 --- a/gnoemoe/widgets/gm-world-text-view.c +++ b/gnoemoe/widgets/gm-world-text-view.c @@ -8,45 +8,84 @@ #include "../gm-debug.h" #include "../gm-support.h" -/* Callback definitions */ -void on_gm_world_text_view_style_set(GmWorldTextView *view, - GtkStyle *previous_style, gpointer user_data); -gboolean on_gm_world_text_view_url_event(GtkTextTag *tag, GObject *object, - GdkEvent *event, GtkTextIter *iter, GmWorldTextView *view); -gboolean on_gm_world_text_view_event(GmWorldTextView *view, - GdkEventMotion *event, GtkTextTag *tag); +static gboolean gm_world_text_view_button_press_event(GtkWidget *widget, + GdkEventButton *event); +static gboolean gm_world_text_view_button_release_event(GtkWidget *widget, + GdkEventButton *event); +static gboolean gm_world_text_view_motion_event(GtkWidget *widget, + GdkEventMotion *event); +static gboolean gm_world_text_view_leave_event(GtkWidget *widget, + GdkEventCrossing *event); +static void gm_world_text_view_drag_data_get(GtkWidget *widget, + GdkDragContext *context, GtkSelectionData *data, guint info, + guint time); +static void gm_world_text_view_drag_end(GtkWidget *widget, + GdkDragContext *context); +static void gm_world_text_view_style_set(GtkWidget *widget, + GtkStyle *previous_style); + +static void gm_world_text_view_populate_popup(GtkTextView *text_view, + GtkMenu *menu); + +/* Callback definitions */ +// Popup menu actions callbacks +static void on_gm_world_text_view_copy_address(GtkMenuItem *item, + GmWorldTextView *view); +static void on_gm_world_text_view_open_address(GtkMenuItem *item, + GmWorldTextView *view); + +static gboolean on_gm_world_text_view_url_event(GtkTextTag *tag, + GObject *object, GdkEvent *event, GtkTextIter *iter, + GmWorldTextView *view); +static gboolean on_gm_world_text_view_event(GmWorldTextView *view, + GdkEventMotion *event, GtkTextTag *tag); + /*void on_gm_world_text_view_color_table_bold_toggled(GmColorTable *table, gboolean bold, GmWorldTextView *view);*/ -void on_gm_world_text_view_color_table_color_changed(GmColorTable *table, +static void on_gm_world_text_view_color_table_color_changed(GmColorTable *table, gchar *name, GmWorldTextView *view); -void on_gm_world_text_view_color_table_font_changed(GmColorTable *table, +static void on_gm_world_text_view_color_table_font_changed(GmColorTable *table, gchar *font_description, GmWorldTextView *view); -void on_gm_world_text_view_populate_popup(GmWorldTextView *view, GtkMenu *menu, - gpointer user_data); -void on_gm_world_text_view_drag_data_get(GmWorldTextView *view, - GdkDragContext *context, GtkSelectionData *data, guint info, - guint time, gpointer user_data); -void gm_world_text_view_create_tags(GmWorldTextView *view); -void gm_world_text_view_init_tags(GmWorldTextView *view); -void gm_world_text_view_update_font(GmWorldTextView *view); +static void gm_world_text_view_create_tags(GmWorldTextView *view); +static void gm_world_text_view_init_tags(GmWorldTextView *view); +static void gm_world_text_view_update_font(GmWorldTextView *view); - -#define GM_WORLD_TEXT_VIEW_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE((object), GM_TYPE_WORLD_TEXT_VIEW, GmWorldTextViewPrivate)) -#define GM_WORLD_TEXT_VIEW_BUFFER(view) gtk_text_view_get_buffer(GTK_TEXT_VIEW(view)) +#define GM_WORLD_TEXT_VIEW_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE( \ + (object), GM_TYPE_WORLD_TEXT_VIEW, GmWorldTextViewPrivate)) +#define GM_WORLD_TEXT_VIEW_BUFFER(view) gtk_text_view_get_buffer( \ + GTK_TEXT_VIEW(view)) typedef struct _GmWorldTextViewInsertInfo GmWorldTextViewInsertInfo; +typedef struct _BlinkInfo { + guint blink; + GtkTextBuffer *text; + gchar *fill; + + guint timeout; + GmWorldTextView *view; + gint offset; +} BlinkInfo; + +static void blink_info_free(BlinkInfo *info); + struct _GmWorldTextViewInsertInfo { GList *tags; gboolean bold; gboolean inverse; + guint blink; + gchar *text; }; struct _GmWorldTextViewPrivate { - gboolean is_hand; + GSList *blinkers; + guint blink_state; + GtkTextTag *tag_blink; + GtkTextTag *tag_blink_fast; + guint blink_timeout_id; guint character_width; guint character_height; @@ -58,6 +97,7 @@ struct _GmWorldTextViewPrivate { guint drag_x; guint drag_y; GtkTargetList *source_target_list; + gboolean is_hand; }; /* Signals */ @@ -69,6 +109,7 @@ enum { }; static guint world_text_view_signals[NUM_SIGNALS] = {0}; +static GtkTextViewClass *parent_class = NULL; G_DEFINE_TYPE(GmWorldTextView, gm_world_text_view, GTK_TYPE_TEXT_VIEW) @@ -76,8 +117,23 @@ G_DEFINE_TYPE(GmWorldTextView, gm_world_text_view, GTK_TYPE_TEXT_VIEW) static void gm_world_text_view_finalize(GObject *object) { GmWorldTextView *view = GM_WORLD_TEXT_VIEW(object); + GSList *item; + BlinkInfo *info; - gm_world_text_view_set_color_table(view, NULL); + gm_world_text_view_set_color_table(view, NULL); + + if (view->priv->blink_timeout_id) { + g_source_remove(view->priv->blink_timeout_id); + } + + for (item = view->priv->blinkers; item; item = item->next) { + info = (BlinkInfo *)(item->data); + + g_source_remove(info->timeout); + blink_info_free(info); + } + + g_slist_free(view->priv->blinkers); g_list_free(view->priv->last_info.tags); g_free(view->priv->last_info.text); @@ -88,9 +144,25 @@ gm_world_text_view_finalize(GObject *object) { static void gm_world_text_view_class_init(GmWorldTextViewClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS(klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass); + GtkTextViewClass *text_view_class = GTK_TEXT_VIEW_CLASS(klass); + parent_class = GTK_TEXT_VIEW_CLASS(g_type_class_peek_parent(klass)); + + text_view_class->populate_popup = gm_world_text_view_populate_popup; + object_class->finalize = gm_world_text_view_finalize; + widget_class->button_press_event = + gm_world_text_view_button_press_event; + widget_class->button_release_event = + gm_world_text_view_button_release_event; + widget_class->motion_notify_event = gm_world_text_view_motion_event; + widget_class->leave_notify_event = gm_world_text_view_leave_event; + widget_class->drag_data_get = gm_world_text_view_drag_data_get; + widget_class->drag_end = gm_world_text_view_drag_end; + widget_class->style_set = gm_world_text_view_style_set; + world_text_view_signals[URL_ACTIVATE] = g_signal_new("url_activate", G_OBJECT_CLASS_TYPE(object_class), @@ -119,11 +191,15 @@ gm_world_text_view_class_init(GmWorldTextViewClass *klass) { enum { DRAG_URL, + DRAG_URI_LIST, + DRAG_TEXT, NUM_TARGETS }; static const GtkTargetEntry drag_targets[] = { - {"_NETSCAPE_URL", 0, DRAG_URL} + {"_NETSCAPE_URL", 0, DRAG_URL}, + {"text/uri-list", 0, DRAG_URI_LIST}, + {"text/plain", 0, DRAG_TEXT}, }; static void @@ -147,10 +223,8 @@ gm_world_text_view_init(GmWorldTextView *view) { // Set default wrapping mode gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(view), GTK_WRAP_WORD_CHAR); - - view->priv->is_hand = FALSE; + view->priv->color_table = NULL; - view->priv->max_lines = 2000; view->priv->last_info.bold = FALSE; view->priv->last_info.inverse = FALSE; @@ -159,25 +233,281 @@ gm_world_text_view_init(GmWorldTextView *view) { view->priv->source_target_list = gtk_target_list_new(drag_targets, NUM_TARGETS); - - // Connect style set signal - g_signal_connect(view, "style-set", - G_CALLBACK(on_gm_world_text_view_style_set), NULL); - g_signal_connect(view, "populate_popup", - G_CALLBACK(on_gm_world_text_view_populate_popup), NULL); - g_signal_connect(view, "drag_data_get", - G_CALLBACK(on_gm_world_text_view_drag_data_get), NULL); } /* Private functions */ -void +static void +blink_info_free(BlinkInfo *info) { + g_object_unref(info->text); + g_free(info->fill); + g_free(info); +} + +static gboolean +gm_world_text_view_motion_event(GtkWidget *widget, GdkEventMotion *event) { + GtkTextView *text_view = GTK_TEXT_VIEW(widget); + GmWorldTextView *view = GM_WORLD_TEXT_VIEW(widget); + + if (event->window == gtk_text_view_get_window(text_view, + GTK_TEXT_WINDOW_TEXT) && view->priv->drag_url && + gtk_drag_check_threshold(widget, view->priv->drag_x, + view->priv->drag_y, event->x, event->y)) { + + view->priv->drag_url = FALSE; + + gtk_drag_begin(widget, view->priv->source_target_list, + GDK_ACTION_COPY | GDK_ACTION_LINK | GDK_ACTION_ASK, + 1, (GdkEvent *)event); + return FALSE; + } + + if (GTK_WIDGET_CLASS(parent_class)->motion_notify_event) { + return GTK_WIDGET_CLASS(parent_class)->motion_notify_event( + widget, event); + } + + return FALSE; +} + +static gboolean +gm_world_text_view_button_press_event(GtkWidget *widget, + GdkEventButton *event) { + GtkTextView *text_view = GTK_TEXT_VIEW(widget); + GmWorldTextView *view = GM_WORLD_TEXT_VIEW(widget); + GtkTextIter start, end; + GtkTextBuffer *buffer = gtk_text_view_get_buffer(text_view); + GtkTextTag *tag; + gint x, y; + + if (event->button == 1 && event->state == 0 && event->window == + gtk_text_view_get_window(text_view, GTK_TEXT_WINDOW_TEXT)) { + // Are we at a link + gtk_text_view_window_to_buffer_coords(text_view, GTK_TEXT_WINDOW_TEXT, + event->x, event->y, &x, &y); + gtk_text_view_get_iter_at_location(text_view, &start, x, y); + tag = gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(buffer), + "url"); + + if (gtk_text_iter_has_tag(&start, tag)) { + end = start; + + if (!gtk_text_iter_begins_tag(&start, tag)) { + gtk_text_iter_backward_to_tag_toggle(&start, tag); + } + if (!gtk_text_iter_ends_tag(&end, tag)) { + gtk_text_iter_forward_to_tag_toggle(&end, tag); + } + + view->priv->drag_url_text = gtk_text_buffer_get_text(buffer, + &start, &end, FALSE); + + view->priv->drag_x = event->x; + view->priv->drag_y = event->y; + view->priv->drag_url = TRUE; + return TRUE; + } + } + + if (GTK_WIDGET_CLASS(parent_class)->button_press_event) { + return GTK_WIDGET_CLASS(parent_class)->button_press_event(widget, + event); + } + + return FALSE; +} + +static void +gm_world_text_view_no_selection(GmWorldTextView *view) { + GtkTextBuffer *buffer = GM_WORLD_TEXT_VIEW_BUFFER(view); + GtkTextIter iter; + + gtk_text_buffer_get_iter_at_mark(buffer, &iter, + gtk_text_buffer_get_insert(buffer)); + gtk_text_buffer_move_mark(buffer, + gtk_text_buffer_get_selection_bound(buffer), &iter); +} + +static gboolean +gm_world_text_view_button_release_event(GtkWidget *widget, + GdkEventButton *event) { + GmWorldTextView *view = GM_WORLD_TEXT_VIEW(widget); + + if (event->button == 1 && view->priv->drag_url) { + gm_world_text_view_no_selection(view); + view->priv->drag_url = FALSE; + g_free(view->priv->drag_url_text); + view->priv->drag_url_text = NULL; + } + + if (GTK_WIDGET_CLASS(parent_class)->button_release_event) { + return GTK_WIDGET_CLASS(parent_class)->button_release_event(widget, + event); + } + + return FALSE; +} + +static +gboolean gm_world_text_view_leave_event(GtkWidget *widget, + GdkEventCrossing *event) { + GM_WORLD_TEXT_VIEW(widget)->priv->drag_url = FALSE; + + if (GTK_WIDGET_CLASS(parent_class)->leave_notify_event) { + return GTK_WIDGET_CLASS(parent_class)->leave_notify_event(widget, + event); + } + + return FALSE; +} + +static void +gm_world_text_view_drag_data_get(GtkWidget *widget, GdkDragContext *context, + GtkSelectionData *data, guint info, guint time) { + GmWorldTextView *view = GM_WORLD_TEXT_VIEW(widget); + GdkAtom target = data->target; + gchar *str = NULL; + + if (view->priv->drag_url_text) { + if (target == gdk_atom_intern("_NETSCAPE_URL", FALSE) || + target == gdk_atom_intern("text/plain", FALSE)) { + str = g_strdup(view->priv->drag_url_text); + } else if (target == gdk_atom_intern("text/uri-list", FALSE)) { + str = g_strconcat(view->priv->drag_url_text, "\r\n", NULL); + } else { + g_assert_not_reached(); + } + } else if (GTK_WIDGET_CLASS(parent_class)->drag_data_get) { + GTK_WIDGET_CLASS(parent_class)->drag_data_get(widget, context, data, + info, time); + return; + } else { + g_assert_not_reached(); + } + + gtk_selection_data_set(data, target, 8, (const guchar *)str, + strlen(str)); + g_free(str); +} + +static void +gm_world_text_view_drag_end(GtkWidget *widget, GdkDragContext *context) { + GmWorldTextView *view = GM_WORLD_TEXT_VIEW(widget); + + if (view->priv->drag_url_text) { + g_free(view->priv->drag_url_text); + view->priv->drag_url_text = NULL; + } else if (GTK_WIDGET_CLASS(parent_class)->motion_notify_event) { + GTK_WIDGET_CLASS(parent_class)->drag_end(widget, context); + } +} + +static void +gm_world_text_view_style_set(GtkWidget *widget, + GtkStyle *previous_style) { + GmWorldTextView *view = GM_WORLD_TEXT_VIEW(widget); + GtkRcStyle *style; + PangoContext *pc; + PangoLayout *pl; + gint cwidth, cheight; + + if (GTK_WIDGET_CLASS(parent_class)->style_set) { + GTK_WIDGET_CLASS(parent_class)->style_set(widget, previous_style); + } + + style = gtk_widget_get_modifier_style(widget); + pc = gtk_widget_create_pango_context(widget); + + 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 (view->priv->character_width != (guint)cwidth || + view->priv->character_height != (guint)cheight) { + view->priv->character_width = (guint)cwidth; + view->priv->character_height = (guint)cheight; + + g_signal_emit(view, + world_text_view_signals[CHARACTER_SIZE_CHANGED], 0, + view->priv->character_width, view->priv->character_height); + } + + g_object_unref(pl); + } + + g_object_unref(pc); +} + +static void +gm_world_text_view_populate_popup(GtkTextView *text_view, + GtkMenu *menu) { + GmWorldTextView *view = GM_WORLD_TEXT_VIEW(text_view); + GtkTextTagTable *table; + GtkTextTag *tag; + gint x, y; + GtkTextIter iter, start, end; + GtkWidget *item; + gchar *str = NULL; + + GM_DEBUG("Populate menu"); + + if (parent_class->populate_popup) { + parent_class->populate_popup(text_view, menu); + } + + table = gtk_text_buffer_get_tag_table(GM_WORLD_TEXT_VIEW_BUFFER(view)); + tag = gtk_text_tag_table_lookup(table, "url"); + + gtk_widget_get_pointer(GTK_WIDGET(view), &x, &y); + gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(view), + GTK_TEXT_WINDOW_WIDGET, x, y, &x, &y); + + gtk_text_view_get_iter_at_location(GTK_TEXT_VIEW(view), &iter, x, y); + start = end = iter; + + if (gtk_text_iter_backward_to_tag_toggle(&start, tag) && + gtk_text_iter_forward_to_tag_toggle(&end, tag)) { + str = gtk_text_buffer_get_text(GM_WORLD_TEXT_VIEW_BUFFER(view), + &start, &end, FALSE); + } + + GM_DEBUG("%s", str); + if (str == NULL || *str == '\0') { + return; + } + + item = gtk_menu_item_new(); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); + gtk_widget_show(item); + + /* Set data just to get the string freed when not needed. */ + g_object_set_data_full(G_OBJECT(menu), "url", str, (GDestroyNotify)g_free); + + item = gtk_menu_item_new_with_mnemonic(_("_Copy Link Address")); + g_signal_connect(item, "activate", + G_CALLBACK(on_gm_world_text_view_copy_address), view); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); + gtk_widget_show(item); + + item = gtk_menu_item_new_with_mnemonic(_("_Open Link")); + g_signal_connect(item, "activate", + G_CALLBACK(on_gm_world_text_view_open_address), view); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); + gtk_widget_show(item); +} + +static void gm_world_text_view_update_color_tag(GmWorldTextView *view, const gchar *name, GtkTextTag *tag) { GtkTextBuffer *buf = GM_WORLD_TEXT_VIEW_BUFFER(view); + GtkTextTagTable *table = gtk_text_buffer_get_tag_table(buf); GdkColor col; if (tag == NULL) { - tag = gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(buf), name); + tag = gtk_text_tag_table_lookup(table, name); } if (view->priv->color_table != NULL) { @@ -185,22 +515,34 @@ gm_world_text_view_update_color_tag(GmWorldTextView *view, const gchar *name, } if (name[0] == 'f') { - g_object_set(G_OBJECT(tag), "foreground-gdk", &col, NULL); - - /*if (name[strlen(name) - 1] == 'h') { - // Color tag for bold colors - //g_object_set(G_OBJECT(tag), "weight", PANGO_WEIGHT_NORMAL, NULL); - if (view->priv->color_table == NULL || - !gm_color_table_bold(view->priv->color_table)) { - g_object_set(G_OBJECT(tag), "foreground-set", FALSE, NULL); - } - }*/ + if (view->priv->color_table == NULL) { + gdk_color_parse("#ffffff", &col); + } + + g_object_set(tag, "foreground-gdk", &col, NULL); + + if (strcmp(name, "fg_default") == 0) { + // Update inverse_bg + tag = gtk_text_tag_table_lookup(table, "inverse_bg"); + g_object_set(tag, "background-gdk", &col, NULL); + } } else { - g_object_set(G_OBJECT(tag), "background-gdk", &col, NULL); + if (view->priv->color_table == NULL) { + gdk_color_parse("#000000", &col); + } + + g_object_set(tag, "background-gdk", &col, NULL); + + if (strcmp(name, "bg_default")) { + // Update inverse_fg + tag = gtk_text_tag_table_lookup(table, "inverse_fg"); + g_object_set(tag, "foreground-gdk", &col, NULL); + } + } } -void +static void gm_world_text_view_check_buffer_size(GmWorldTextView *view) { GtkTextBuffer *buf = GM_WORLD_TEXT_VIEW_BUFFER(view); GtkTextIter start, end; @@ -213,7 +555,7 @@ gm_world_text_view_check_buffer_size(GmWorldTextView *view) { } } -void +static void gm_world_text_view_create_tags(GmWorldTextView *view) { /* Create all the tags */ int i; @@ -226,26 +568,32 @@ gm_world_text_view_create_tags(GmWorldTextView *view) { "underline", PANGO_UNDERLINE_SINGLE, NULL); g_signal_connect(tag, "event", G_CALLBACK(on_gm_world_text_view_url_event), view); - g_signal_connect(view, "event", G_CALLBACK(on_gm_world_text_view_event), + g_signal_connect(view, "event", G_CALLBACK(on_gm_world_text_view_event), tag); + + view->priv->tag_blink = gtk_text_buffer_create_tag(buf, "blink", NULL); + view->priv->tag_blink_fast = gtk_text_buffer_create_tag(buf, "blink-fast", + NULL); + gtk_text_buffer_create_tag(buf, "blink-after", "weight", + PANGO_WEIGHT_ULTRABOLD, NULL); + + tag = gtk_text_buffer_create_tag(buf, "inverse_bg", NULL); + if (view->priv->color_table != NULL) { + gm_color_table_get(view->priv->color_table, "fg_default", &col); + g_object_set(tag, "background-gdk", &col, NULL); + } + + tag = gtk_text_buffer_create_tag(buf, "inverse_fg", NULL); + if (view->priv->color_table != NULL) { + gm_color_table_get(view->priv->color_table, "bg_default", &col); + g_object_set(tag, "background-gdk", &col, NULL); + } for (i = 0; i < (int)(sizeof(ansi_colors) / sizeof(ansinamepair)); i++) { tag = gtk_text_buffer_create_tag(buf, ansi_colors[i].name, NULL); gm_world_text_view_update_color_tag(view, ansi_colors[i].name, tag); } - if (view->priv->color_table != NULL) { - gm_color_table_get(view->priv->color_table, "fg_default", &col); - } - - gtk_text_buffer_create_tag(buf, "inverse_bg", "background-gdk", &col, NULL); - - if (view->priv->color_table != NULL) { - gm_color_table_get(view->priv->color_table, "bg_default", &col); - } - - gtk_text_buffer_create_tag(buf, "inverse_fg", "background-gdk", &col, NULL); - for (i = 0; i < (int)(sizeof(ansi_styles) / sizeof(ansinamepair)); i++) { tag = gtk_text_buffer_create_tag(buf, ansi_styles[i].name, NULL); @@ -303,26 +651,26 @@ gm_world_text_view_create_tags(GmWorldTextView *view) { } } -void +static void gm_world_text_view_update_tags(GmWorldTextView *view) { - int i; - GtkTextTag *tag; - + gint i; + for (i = 0; i < (int)(sizeof(ansi_colors) / sizeof(ansinamepair)); i++) { gm_world_text_view_update_color_tag(view, ansi_colors[i].name, NULL); } + /* tag = gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table( GM_WORLD_TEXT_VIEW_BUFFER(view)), "bold"); - /*if (gm_color_table_bold(view->priv->color_table)) { + if (gm_color_table_bold(view->priv->color_table)) { g_object_set(G_OBJECT(tag), "weight", PANGO_WEIGHT_NORMAL, NULL); - } else {*/ + } else { g_object_set(G_OBJECT(tag), "weight", PANGO_WEIGHT_ULTRABOLD, NULL); - //} + }*/ } -void +static void gm_world_text_view_init_tags(GmWorldTextView *view) { GdkColor col; @@ -335,7 +683,7 @@ gm_world_text_view_init_tags(GmWorldTextView *view) { gm_world_text_view_update_tags(view); } -void +static void gm_world_text_view_update_font(GmWorldTextView *view) { PangoFontDescription *f = pango_font_description_from_string( gm_color_table_font_description(view->priv->color_table)); @@ -346,7 +694,7 @@ gm_world_text_view_update_font(GmWorldTextView *view) { } } -gboolean +static gboolean g_utf8_toint(gchar *str, guint *result) { gunichar c; *result = 0; @@ -363,33 +711,106 @@ g_utf8_toint(gchar *str, guint *result) { return TRUE; } -void -gm_world_text_view_insert_text(GmWorldTextView *view, const gchar *text, - GList *tags) { - GtkTextIter end_iter, start_iter; - GtkTextBuffer *tb = GM_WORLD_TEXT_VIEW_BUFFER(view); - gint start_offset; - gchar *name; +static void +gm_world_text_view_blinker_iters(BlinkInfo *info, GtkTextIter *start, + GtkTextIter *end, GtkTextTag **tag_blink) { + GtkTextBuffer *buffer = GM_WORLD_TEXT_VIEW_BUFFER(info->view); + GtkTextTag *tag; + + if (info->blink == 1) { + tag = info->view->priv->tag_blink; + } else { + tag = info->view->priv->tag_blink_fast; + } - gtk_text_buffer_get_end_iter(tb, &end_iter); - start_offset = gtk_text_iter_get_offset(&end_iter); - - gtk_text_buffer_insert(tb, &end_iter, text, -1); - - if (tags) { - gtk_text_buffer_get_iter_at_offset(tb, &start_iter, start_offset); - gtk_text_buffer_get_end_iter(tb, &end_iter); - - for (; tags; tags = tags->next) { - g_object_get(GTK_TEXT_TAG(tags->data), "name", &name, NULL); - g_free(name); - gtk_text_buffer_apply_tag(tb, GTK_TEXT_TAG(tags->data), &start_iter, - &end_iter); + gtk_text_buffer_get_iter_at_offset(buffer, start, info->offset); + + if (end != NULL) { + *end = *start; + + if (!gtk_text_iter_forward_to_tag_toggle(end, tag)) { + *end = *start; } } + + if (tag_blink != NULL) { + *tag_blink = tag; + } } -const gchar * +static void +gm_world_text_view_blinker_set_visible(BlinkInfo *info, gboolean visible) { + GtkTextBuffer *buffer = GM_WORLD_TEXT_VIEW_BUFFER(info->view); + GtkTextIter start, end, s, e; + GtkTextTag *tag; + + gm_world_text_view_blinker_iters(info, &start, &end, &tag); + gtk_text_buffer_delete(buffer, &start, &end); + + gm_world_text_view_blinker_iters(info, &start, NULL, NULL); + + if (visible) { + gtk_text_buffer_get_bounds(info->text, &s, &e); + gtk_text_buffer_insert_range(buffer, &start, &s, &e); + } else { + gtk_text_buffer_insert_with_tags(buffer, &start, info->fill, -1, tag, + NULL); + } +} + +static gboolean +gm_world_text_view_blinker_timeout(BlinkInfo *info) { + GtkTextBuffer *buffer = GM_WORLD_TEXT_VIEW_BUFFER(info->view); + GtkTextTag *tag; + GtkTextIter start, end; + + gm_world_text_view_blinker_set_visible(info, TRUE); + gm_world_text_view_blinker_iters(info, &start, &end, &tag); + + gtk_text_buffer_remove_tag(buffer, tag, &start, &end); + gtk_text_buffer_apply_tag_by_name(buffer, "blink-after", &start, &end); + + info->view->priv->blinkers = g_slist_remove(info->view->priv->blinkers, + info); + + blink_info_free(info); + + return FALSE; +} + +static gboolean +gm_world_text_view_blink_timeout(GmWorldTextView *view) { + guint blink_state = view->priv->blink_state; + GSList *blinker; + BlinkInfo *info; + + if (!view->priv->blinkers) { + view->priv->blink_state = 0; + view->priv->blink_timeout_id = 0; + return FALSE; + } + + for (blinker = view->priv->blinkers; blinker; blinker = blinker->next) { + info = (BlinkInfo *)(blinker->data); + + if (info->blink == 1 && (blink_state == 0 || blink_state == 2)) { + gm_world_text_view_blinker_set_visible(info, blink_state == 2); + } else if (info->blink == 2) { + gm_world_text_view_blinker_set_visible(info, blink_state == 1 || + blink_state == 3); + } + } + + if (blink_state == 3) { + view->priv->blink_state = 0; + } else { + view->priv->blink_state = blink_state + 1; + } + + return TRUE; +} + +static const gchar * gm_world_text_view_tagname_from_code(guint code) { gint i, len; @@ -411,8 +832,8 @@ gm_world_text_view_tagname_from_code(guint code) { return NULL; } -GList * -gm_world_text_view_tags_fix_for_bold(GmWorldTextView *view, GList *tags) { +static GList * +gm_world_text_view_tags_apply_bold(GmWorldTextView *view, GList *tags) { GList *t, *item; GtkTextTag *tag, *htag; gboolean fg_set; @@ -442,7 +863,7 @@ gm_world_text_view_tags_fix_for_bold(GmWorldTextView *view, GList *tags) { g_free(hname); } - + g_free(name); } @@ -456,7 +877,7 @@ gm_world_text_view_tags_fix_for_bold(GmWorldTextView *view, GList *tags) { return tags; } -GList * +static GList * gm_world_text_view_tags_fix_for_inverse(GmWorldTextView *view, GList *tags) { gboolean bold = view->priv->last_info.bold; gboolean fg_changed = FALSE, bg_changed = FALSE; @@ -502,11 +923,11 @@ gm_world_text_view_tags_fix_for_inverse(GmWorldTextView *view, GList *tags) { } else if (strcmp(name, "inverse_bg") == 0) { fg_changed = TRUE; new_tags = g_list_append(new_tags, gtk_text_tag_table_lookup(tab, - "fg_default")); + "bg_default")); } else if (strcmp(name, "inverse_fg") == 0) { bg_changed = TRUE; new_tags = g_list_append(new_tags, gtk_text_tag_table_lookup(tab, - "bg_default")); + "fg_default")); } else { new_tags = g_list_append(new_tags, tag); } @@ -538,7 +959,7 @@ static const gchar * tag_checks[] = { NULL }; -gboolean +static gboolean gm_world_text_view_tags_overlap(GtkTextTag *t1, GtkTextTag *t2) { int i = 0; gboolean val1, val2; @@ -547,7 +968,7 @@ gm_world_text_view_tags_overlap(GtkTextTag *t1, GtkTextTag *t2) { g_object_get(G_OBJECT(t1), tag_checks[i], &val1, NULL); g_object_get(G_OBJECT(t2), tag_checks[i], &val2, NULL); - if (val1 && val2) { + if (val1 && val2) { return TRUE; } @@ -557,7 +978,7 @@ gm_world_text_view_tags_overlap(GtkTextTag *t1, GtkTextTag *t2) { return FALSE; } -GList * +static GList * gm_world_text_view_tags_remove_obsolete(GmWorldTextView *view, GList *tags, GtkTextTag *tag) { GList *t = g_list_copy(tags), *item; @@ -575,8 +996,8 @@ gm_world_text_view_tags_remove_obsolete(GmWorldTextView *view, GList *tags, return tags; } -GList * -gm_world_text_view_tags_remove_high(GmWorldTextView *view, GList *tags) { +static GList * +gm_world_text_view_tags_remove_bold(GmWorldTextView *view, GList *tags) { GList *t = g_list_copy(tags), *item; gchar *name; @@ -593,40 +1014,111 @@ gm_world_text_view_tags_remove_high(GmWorldTextView *view, GList *tags) { return tags; } -GList * -gm_world_text_view_tags_add(GmWorldTextView *view, GList *tags, GtkTextTag *tag, - gboolean inverse) { - gboolean weight; - gboolean fg_set; - gchar *name, *nameh; - GtkTextTag *tagh; +static void +gm_world_text_view_insert_text(GmWorldTextView *view, const gchar *text, + GmWorldTextViewInsertInfo *insert_info) { + GtkTextIter end_iter, start_iter, ins; + GtkTextBuffer *tb = GM_WORLD_TEXT_VIEW_BUFFER(view); + gint start_offset; + BlinkInfo *info; + GList *tags; - tags = gm_world_text_view_tags_remove_obsolete(view, tags, tag); + if (insert_info->bold) { + // If it needs to be bold then apply high colors for every color + insert_info->tags = gm_world_text_view_tags_apply_bold(view, + insert_info->tags); + } else { + // Remove all possible high colors + insert_info->tags = gm_world_text_view_tags_remove_bold(view, + insert_info->tags); + } - g_object_get(G_OBJECT(tag), "foreground-set", &fg_set, NULL); + gtk_text_buffer_get_end_iter(tb, &end_iter); + start_offset = gtk_text_iter_get_offset(&end_iter); + + gtk_text_buffer_insert(tb, &end_iter, text, -1); + + gtk_text_buffer_get_iter_at_offset(tb, &start_iter, start_offset); + gtk_text_buffer_get_end_iter(tb, &end_iter); + + for (tags = insert_info->tags; tags; tags = tags->next) { + gtk_text_buffer_apply_tag(tb, GTK_TEXT_TAG(tags->data), &start_iter, + &end_iter); + } - if (fg_set) { - tags = gm_world_text_view_tags_remove_high(view, tags); - g_object_get(G_OBJECT(tag), "name", &name, NULL); - nameh = g_strconcat(name, "_h", NULL); + if (insert_info->blink) { + info = g_new0(BlinkInfo, 1); + info->blink = insert_info->blink; + info->offset = gtk_text_iter_get_offset(&start_iter); + info->view = view; - tagh = gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table( - GM_WORLD_TEXT_VIEW_BUFFER(view)), nameh); - if (tagh) { - tags = g_list_append(tags, tagh); + if (insert_info->blink == 1) { + gtk_text_buffer_apply_tag_by_name(tb, "blink", &start_iter, + &end_iter); + } else { + gtk_text_buffer_apply_tag_by_name(tb, "blink-fast", &start_iter, + &end_iter); + } + + info->text = gtk_text_buffer_new(gtk_text_buffer_get_tag_table(tb)); + gtk_text_buffer_get_iter_at_offset(info->text, &ins, 0); + gtk_text_buffer_insert_range(info->text, &ins, &start_iter, &end_iter); + + info->fill = g_strnfill(g_utf8_strlen(text, -1), ' '); + + if (!view->priv->blink_timeout_id) { + view->priv->blink_timeout_id = g_timeout_add(250, + (GSourceFunc)gm_world_text_view_blink_timeout, view); } - g_free(name); - g_free(nameh); + /* Let them blink for about... 10 seconds */ + info->timeout = g_timeout_add(10000, + (GSourceFunc)gm_world_text_view_blinker_timeout, info); + + view->priv->blinkers = g_slist_append(view->priv->blinkers, info); } +} - g_object_get(G_OBJECT(tag), "weight-set", &weight, NULL); +static GList * +gm_world_text_view_tags_add(GmWorldTextView *view, GList *tags, + GtkTextTag *tag) { + tags = gm_world_text_view_tags_remove_obsolete(view, tags, tag); + return g_list_append(tags, tag); +} + +static void +gm_world_text_view_tag_urls(GmWorldTextView *view, gchar *text, + GtkTextIter *started, GtkTextIter *ended) { + gint num_matches, i; + GArray *start, *end; + gint s = 0, e = 0; + GtkTextBuffer *buffer = GM_WORLD_TEXT_VIEW_BUFFER(view); + GtkTextTag *url_tag = gtk_text_tag_table_lookup( + gtk_text_buffer_get_tag_table(buffer), "url"); + GtkTextIter urlstart, urlend; - if (weight) { - tags = gm_world_text_view_tags_fix_for_bold(view, tags); + start = g_array_new(FALSE, FALSE, sizeof(gint)); + end = g_array_new(FALSE, FALSE, sizeof(gint)); + + num_matches = gm_url_regex_match(text, strlen(text), start, end); + + for (i = 0; i < num_matches; i++) { + urlstart = *started; + urlend = *started; + + s = g_array_index(start, gint, i); + e = g_array_index(end, gint, i); + + gtk_text_iter_forward_cursor_positions(&urlstart, + g_utf8_pointer_to_offset(text, text + s)); + gtk_text_iter_forward_cursor_positions(&urlend, + g_utf8_pointer_to_offset(text, text + e)); + + gtk_text_buffer_apply_tag(buffer, url_tag, &urlstart, &urlend); } - return g_list_append(tags, tag); + g_array_free(start, TRUE); + g_array_free(end, TRUE); } /* Public/exported functions */ @@ -683,41 +1175,6 @@ gm_world_text_view_color_table(GmWorldTextView *view) { return view->priv->color_table; } -void -gm_world_text_view_tag_urls(GmWorldTextView *view, gchar *text, - GtkTextIter *started, GtkTextIter *ended) { - gint num_matches, i; - GArray *start, *end; - gint s = 0, e = 0; - GtkTextBuffer *buffer = GM_WORLD_TEXT_VIEW_BUFFER(view); - GtkTextTag *url_tag = gtk_text_tag_table_lookup( - gtk_text_buffer_get_tag_table(buffer), "url"); - GtkTextIter urlstart, urlend; - - start = g_array_new(FALSE, FALSE, sizeof(gint)); - end = g_array_new(FALSE, FALSE, sizeof(gint)); - - num_matches = gm_url_regex_match(text, strlen(text), start, end); - - for (i = 0; i < num_matches; i++) { - urlstart = *started; - urlend = *started; - - s = g_array_index(start, gint, i); - e = g_array_index(end, gint, i); - - gtk_text_iter_forward_cursor_positions(&urlstart, - g_utf8_pointer_to_offset(text, text + s)); - gtk_text_iter_forward_cursor_positions(&urlend, - g_utf8_pointer_to_offset(text, text + e)); - - gtk_text_buffer_apply_tag(buffer, url_tag, &urlstart, &urlend); - } - - g_array_free(start, TRUE); - g_array_free(end, TRUE); -} - gchar * gm_world_text_view_insert(GmWorldTextView *view, const gchar *text) { gchar *ptr, *ansi_start, *ansi_stop, **ansis; @@ -749,7 +1206,8 @@ gm_world_text_view_insert(GmWorldTextView *view, const gchar *text) { gdk_beep(); while (ptr) { - g_string_erase(new_line, g_utf8_pointer_to_offset(new_line->str, ptr), 1); + g_string_erase(new_line, g_utf8_pointer_to_offset(new_line->str, + ptr), 1); ptr = g_utf8_strchr(new_line->str, -1, '\x07'); } } @@ -766,7 +1224,7 @@ gm_world_text_view_insert(GmWorldTextView *view, const gchar *text) { if (i != 0) { ptr = g_strndup(new_line->str, ansi_start - new_line->str); - gm_world_text_view_insert_text(view, ptr, info->tags); + gm_world_text_view_insert_text(view, ptr, info); g_free(ptr); } @@ -781,48 +1239,73 @@ gm_world_text_view_insert(GmWorldTextView *view, const gchar *text) { if (ansis[i][0] != '\0' && g_utf8_toint(ansis[i], &seq)) { skip = FALSE; switch (seq) { - case A_FG_DEFAULT: case A_FG_BLACK: case A_FG_RED: case A_FG_GREEN: - case A_FG_YELLOW: case A_FG_BLUE: case A_FG_PURPLE: case A_FG_CYAN: - case A_FG_WHITE: + case A_FG_DEFAULT: case A_FG_BLACK: case A_FG_RED: + case A_FG_GREEN: case A_FG_YELLOW: + case A_FG_BLUE: case A_FG_PURPLE: + case A_FG_CYAN: case A_FG_WHITE: if (info->inverse) { seq += 10; } - break; - case A_BG_DEFAULT: case A_BG_BLACK: case A_BG_RED: case A_BG_GREEN: - case A_BG_YELLOW: case A_BG_BLUE: case A_BG_PURPLE: case A_BG_CYAN: - case A_BG_WHITE: + break; + case A_BG_DEFAULT: case A_BG_BLACK: case A_BG_RED: + case A_BG_GREEN: case A_BG_YELLOW: + case A_BG_BLUE: case A_BG_PURPLE: + case A_BG_CYAN: case A_BG_WHITE: if (info->inverse) { seq -= 10; } - break; + break; case A_BOLD: info->bold = TRUE; if (info->inverse) skip = TRUE; - break; + break; + case A_FAINT: + info->bold = TRUE; + + if (info->inverse) + skip = TRUE; + break; case A_BOLD_OFF: info->bold = FALSE; if (info->inverse) skip = TRUE; - break; + break; case A_INVERSE: info->inverse = !(info->inverse); - info->tags = gm_world_text_view_tags_fix_for_inverse(view, info->tags); - break; + info->tags = + gm_world_text_view_tags_fix_for_inverse( + view, info->tags); + break; case A_INVERSE_OFF: if (info->inverse) { - info->tags = gm_world_text_view_tags_fix_for_inverse(view, info->tags); + info->tags = + gm_world_text_view_tags_fix_for_inverse( + view, info->tags); info->inverse = FALSE; } - break; + break; + case A_BLINK: + info->blink = 1; + break; + case A_BLINK_OFF: + info->blink = 0; + break; + case A_BLINK_FAST: + info->blink = 2; + break; + case A_BLINK_FAST_OFF: + info->blink = 0; + break; case A_DEFAULT: g_list_free(info->tags); info->tags = NULL; info->bold = FALSE; info->inverse = FALSE; - break; + info->blink = 0; + break; } name = (gchar *)gm_world_text_view_tagname_from_code(seq); @@ -831,8 +1314,8 @@ gm_world_text_view_insert(GmWorldTextView *view, const gchar *text) { tag = gtk_text_tag_table_lookup(tab, name); if (tag != NULL) { - info->tags = gm_world_text_view_tags_add(view, info->tags, tag, - info->inverse); + info->tags = gm_world_text_view_tags_add(view, + info->tags, tag); } } } @@ -848,7 +1331,7 @@ gm_world_text_view_insert(GmWorldTextView *view, const gchar *text) { } if (new_line && new_line->len != 0) { - gm_world_text_view_insert_text(view, new_line->str, info->tags); + gm_world_text_view_insert_text(view, new_line->str, info); } if (new_line) { @@ -875,104 +1358,92 @@ gm_world_text_view_get_metrics(GmWorldTextView *view, guint *width, } /* Callbacks */ -void -on_gm_world_text_view_style_set(GmWorldTextView *view, GtkStyle *previous_style, - gpointer user_data) { - GtkRcStyle *style = gtk_widget_get_modifier_style(GTK_WIDGET(view)); - PangoContext *pc = gtk_widget_create_pango_context(GTK_WIDGET(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 (view->priv->character_width != (guint)cwidth || - view->priv->character_height != (guint)cheight) { - view->priv->character_width = (guint)cwidth; - view->priv->character_height = (guint)cheight; - - g_signal_emit(view, world_text_view_signals[CHARACTER_SIZE_CHANGED], 0, - view->priv->character_width, view->priv->character_height); - } - - g_object_unref(pl); - } - - g_object_unref(pc); -} - -gboolean +static gboolean on_gm_world_text_view_url_event(GtkTextTag *tag, GObject *object, GdkEvent *event, GtkTextIter *iter, GmWorldTextView *view) { GtkTextIter start, end; gchar *str = NULL; GtkTextBuffer *buffer = GM_WORLD_TEXT_VIEW_BUFFER(view); - start = end = *iter; - - if (gtk_text_iter_backward_to_tag_toggle(&start, tag) && - gtk_text_iter_forward_to_tag_toggle(&end, tag)) { - str = gtk_text_buffer_get_text(buffer, &start, &end, FALSE); - } - - if (str == NULL) { - return FALSE; - } - - if (event->type == GDK_MOTION_NOTIFY && view->priv->drag_url && - !(event->motion.state & GDK_CONTROL_MASK) && - gtk_drag_check_threshold(GTK_WIDGET(view), view->priv->drag_x, - view->priv->drag_y, event->motion.x, event->motion.y)) { - view->priv->drag_url = FALSE; - view->priv->drag_url_text = g_strdup(str); - gtk_drag_begin(GTK_WIDGET(view), view->priv->source_target_list, - GDK_ACTION_COPY | GDK_ACTION_LINK | GDK_ACTION_ASK, - 1, event); - - GM_DEBUG("Drag started!"); - } - - if (event->type == GDK_BUTTON_PRESS && - !(event->button.state & GDK_CONTROL_MASK)) { - GM_DEBUG("Possible drag init!"); - view->priv->drag_x = event->button.x; - view->priv->drag_y = event->button.y; - view->priv->drag_url = TRUE; - } - - if (event->type == GDK_BUTTON_RELEASE && view->priv->drag_url && - event->button.button == 1) { - GM_DEBUG("Drag stopped!"); - view->priv->drag_url = FALSE; - } - - /* If the link is being selected, don't do anything. */ - gtk_text_buffer_get_selection_bounds(buffer, &start, &end); - - if (gtk_text_iter_get_offset(&start) != gtk_text_iter_get_offset(&end)) { - g_free(str); - return FALSE; - } - - if (event->type == GDK_BUTTON_RELEASE && event->button.button == 1) { + if (event->type == GDK_BUTTON_RELEASE && event->button.button == 1 && + !(event->button.state & GDK_CONTROL_MASK) && + !(event->button.state & GDK_SHIFT_MASK) && + !(event->button.state & GDK_MOD1_MASK)) { start = end = *iter; if (gtk_text_iter_backward_to_tag_toggle(&start, tag) && gtk_text_iter_forward_to_tag_toggle(&end, tag)) { - g_signal_emit(view, world_text_view_signals[URL_ACTIVATE], 0, str); + str = gtk_text_buffer_get_text(buffer, &start, &end, FALSE); } - } - g_free(str); + if (str == NULL) { + return FALSE; + } + + /* If the link is being selected, don't do anything. */ + gtk_text_buffer_get_selection_bounds(buffer, &start, &end); + + if (!gtk_text_iter_equal(&start, &end)) { + g_free(str); + return FALSE; + } + + g_signal_emit(view, world_text_view_signals[URL_ACTIVATE], 0, str); + g_free(str); + } + return FALSE; } -void +gboolean +on_gm_world_text_view_event(GmWorldTextView *view, GdkEventMotion *event, + GtkTextTag *tag) { + static GdkCursor *hand = NULL; + GtkTextWindowType type; + GtkTextIter iter; + GdkWindow *win; + gint x, y, buf_x, buf_y; + gboolean has_tag; + + type = gtk_text_view_get_window_type(GTK_TEXT_VIEW(view), event->window); + + if (type != GTK_TEXT_WINDOW_TEXT) { + return FALSE; + } + + if (event->type != GDK_MOTION_NOTIFY) { + return FALSE; + } + + /* Get where the pointer really is. */ + win = gtk_text_view_get_window(GTK_TEXT_VIEW(view), type); + gdk_window_get_pointer(win, &x, &y, NULL); + + /* Get the iter where the cursor is at */ + gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(view), type, + x, y, &buf_x, &buf_y); + + gtk_text_view_get_iter_at_location(GTK_TEXT_VIEW(view), &iter, buf_x, + buf_y); + + if (!hand) { + hand = gdk_cursor_new(GDK_FLEUR); + } + + has_tag = gtk_text_iter_has_tag(&iter, tag); + + if (has_tag && !view->priv->is_hand) { + view->priv->is_hand = TRUE; + gdk_window_set_cursor(win, hand); + } else if (!has_tag && view->priv->is_hand) { + view->priv->is_hand = FALSE; + gdk_window_set_cursor(win, NULL); + } + + return FALSE; +} + +static void on_gm_world_text_view_copy_address(GtkMenuItem *item, GmWorldTextView *view) { GtkClipboard *clipboard; gchar *url = (gchar *)(g_object_get_data(G_OBJECT(gtk_widget_get_parent( @@ -982,7 +1453,7 @@ on_gm_world_text_view_copy_address(GtkMenuItem *item, GmWorldTextView *view) { gtk_clipboard_set_text(clipboard, url, -1); } -void +static void on_gm_world_text_view_open_address(GtkMenuItem *item, GmWorldTextView *view) { gchar *url = (gchar *)(g_object_get_data(G_OBJECT(gtk_widget_get_parent( GTK_WIDGET(item))), "url")); @@ -990,105 +1461,7 @@ on_gm_world_text_view_open_address(GtkMenuItem *item, GmWorldTextView *view) { g_signal_emit(view, world_text_view_signals[URL_ACTIVATE], 0, url); } -void -on_gm_world_text_view_populate_popup(GmWorldTextView *view, GtkMenu *menu, - gpointer user_data) { - GtkTextTagTable *table; - GtkTextTag *tag; - gint x, y; - GtkTextIter iter, start, end; - GtkWidget *item; - gchar *str = NULL; - - table = gtk_text_buffer_get_tag_table(GM_WORLD_TEXT_VIEW_BUFFER(view)); - tag = gtk_text_tag_table_lookup(table, "url"); - - gtk_widget_get_pointer(GTK_WIDGET(view), &x, &y); - gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(view), - GTK_TEXT_WINDOW_WIDGET, x, y, &x, &y); - - gtk_text_view_get_iter_at_location(GTK_TEXT_VIEW(view), &iter, x, y); - start = end = iter; - - if (gtk_text_iter_backward_to_tag_toggle(&start, tag) && - gtk_text_iter_forward_to_tag_toggle(&end, tag)) { - str = gtk_text_buffer_get_text(GM_WORLD_TEXT_VIEW_BUFFER(view), - &start, &end, FALSE); - } - - if (str == NULL || *str == '\0') { - return; - } - - item = gtk_menu_item_new(); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); - gtk_widget_show(item); - - /* Set data just to get the string freed when not needed. */ - g_object_set_data_full(G_OBJECT(menu), "url", str, (GDestroyNotify)g_free); - - item = gtk_menu_item_new_with_mnemonic(_("_Copy Link Address")); - g_signal_connect(item, "activate", - G_CALLBACK(on_gm_world_text_view_copy_address), view); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); - gtk_widget_show(item); - - item = gtk_menu_item_new_with_mnemonic(_("_Open Link")); - g_signal_connect(item, "activate", - G_CALLBACK(on_gm_world_text_view_open_address), view); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); - gtk_widget_show(item); -} - -gboolean -on_gm_world_text_view_event(GmWorldTextView *view, GdkEventMotion *event, - GtkTextTag *tag) { - static GdkCursor *hand = NULL; - static GdkCursor *beam = NULL; - GtkTextWindowType type; - GtkTextIter iter; - GdkWindow *win; - gint x, y, buf_x, buf_y; - gboolean has_tag; - - type = gtk_text_view_get_window_type(GTK_TEXT_VIEW(view), event->window); - - if (type != GTK_TEXT_WINDOW_TEXT) { - return FALSE; - } - - if (event->type != GDK_MOTION_NOTIFY) { - return FALSE; - } - - /* Get where the pointer really is. */ - win = gtk_text_view_get_window(GTK_TEXT_VIEW(view), type); - gdk_window_get_pointer(win, &x, &y, NULL); - - /* Get the iter where the cursor is at */ - gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(view), type, - x, y, &buf_x, &buf_y); - - gtk_text_view_get_iter_at_location(GTK_TEXT_VIEW(view), &iter, buf_x, buf_y); - - if (!hand) { - hand = gdk_cursor_new(GDK_FLEUR); - beam = gdk_cursor_new(GDK_XTERM); - } - - has_tag = gtk_text_iter_has_tag(&iter, tag); - - if (has_tag && !view->priv->is_hand) { - view->priv->is_hand = TRUE; - gdk_window_set_cursor(win, hand); - } else if (!has_tag && view->priv->is_hand) { - view->priv->is_hand = FALSE; - gdk_window_set_cursor(win, beam); - } - - return FALSE; -} - +/* void gm_world_text_view_color_table_fix_high_color(GtkTextTag *tag, gpointer data) { gboolean bold = (gboolean)(GPOINTER_TO_INT(data)); @@ -1110,7 +1483,7 @@ gm_world_text_view_color_table_fix_high_color(GtkTextTag *tag, gpointer data) { g_free(name); } -/*void +void on_gm_world_text_view_color_table_bold_toggled(GmColorTable *table, gboolean bold, GmWorldTextView *view) { GtkTextTag *tag; @@ -1128,12 +1501,13 @@ on_gm_world_text_view_color_table_bold_toggled(GmColorTable *table, } // Fix high color tags - gtk_text_tag_table_foreach(tb, gm_world_text_view_color_table_fix_high_color, + gtk_text_tag_table_foreach(tb, + gm_world_text_view_color_table_fix_high_color, GINT_TO_POINTER(bold)); } }*/ -void +static void on_gm_world_text_view_color_table_color_changed(GmColorTable *table, gchar *name, GmWorldTextView *view) { GdkColor col; @@ -1149,18 +1523,8 @@ on_gm_world_text_view_color_table_color_changed(GmColorTable *table, } } -void +static void on_gm_world_text_view_color_table_font_changed(GmColorTable *table, gchar *font_description, GmWorldTextView *view) { gm_world_text_view_update_font(view); } - -void on_gm_world_text_view_drag_data_get(GmWorldTextView *view, - GdkDragContext *context, GtkSelectionData *data, guint info, - guint time, gpointer user_data) { - switch (info) { - case DRAG_URL: - gtk_selection_data_set_text(data, view->priv->drag_url_text, -1); - break; - } -}