# HG changeset patch # User Nathan Walp # Date 1073714762 0 # Node ID ea073d234191e598f2c45ab6f679a9a051685edb # Parent b66733e6e6f29fc7699cf7ab01cb6cd155d63034 [gaim-migrate @ 8749] what you see is what you get committer: Tailor Script diff -r b66733e6e6f2 -r ea073d234191 ChangeLog --- a/ChangeLog Sat Jan 10 05:20:50 2004 +0000 +++ b/ChangeLog Sat Jan 10 06:06:02 2004 +0000 @@ -1,5 +1,8 @@ Gaim: The Pimpin' Penguin IM Clone that's good for the soul! +version 0.76cvs: + * WYSIWYG text input (with scrollbars, too!) + version 0.75 (01/09/2004): * New Yahoo! auth method * Yahoo! file transfer (Tim Ringenbach) diff -r b66733e6e6f2 -r ea073d234191 VERSION --- a/VERSION Sat Jan 10 05:20:50 2004 +0000 +++ b/VERSION Sat Jan 10 06:06:02 2004 +0000 @@ -1,1 +1,1 @@ -0.75 +0.76cvs diff -r b66733e6e6f2 -r ea073d234191 configure.ac --- a/configure.ac Sat Jan 10 05:20:50 2004 +0000 +++ b/configure.ac Sat Jan 10 06:06:02 2004 +0000 @@ -1,7 +1,7 @@ dnl Process this file with autoconf to produce a configure script. AC_INIT(src/main.c) AM_CONFIG_HEADER(config.h) -AM_INIT_AUTOMAKE([gaim], [0.75]) +AM_INIT_AUTOMAKE([gaim], [0.76cvs]) AC_PREREQ([2.50]) diff -r b66733e6e6f2 -r ea073d234191 src/dialogs.c --- a/src/dialogs.c Sat Jan 10 05:20:50 2004 +0000 +++ b/src/dialogs.c Sat Jan 10 06:06:02 2004 +0000 @@ -695,7 +695,6 @@ static void do_insert_link(GtkWidget *w, int resp, struct linkdlg *a) { GaimGtkConversation *gtkconv; - char *open_tag; const char *urltext, *showtext; gtkconv = GAIM_GTK_CONVERSATION(a->c); @@ -707,9 +706,8 @@ if (!strlen(showtext)) showtext = urltext; - open_tag = g_strdup_printf("%s", urltext, showtext); - gaim_gtk_surround(gtkconv, open_tag, ""); - g_free(open_tag); + gtk_imhtml_insert_link(GTK_IMHTML(gtkconv->entry), urltext, showtext); + gaim_gtk_advance_past(gtkconv, "", ""); } gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gtkconv->toolbar.link), FALSE); @@ -863,16 +861,12 @@ gtkconv = GAIM_GTK_CONVERSATION(c); gtkconv->fg_color = text_color; - g_snprintf(open_tag, 23, "", + g_snprintf(open_tag, 23, "#%02X%02X%02X", text_color.red / 256, text_color.green / 256, text_color.blue / 256); - gaim_gtk_surround(gtkconv, open_tag, ""); + gtk_imhtml_toggle_forecolor(GTK_IMHTML(gtkconv->entry), open_tag); - gaim_debug(GAIM_DEBUG_MISC, "fgcolor dialog", "#%02X%02X%02X\n", - text_color.red / 256, - text_color.green / 256, - text_color.blue / 256); g_free(open_tag); cancel_fgcolor(NULL, c); } @@ -894,16 +888,12 @@ gtkconv = GAIM_GTK_CONVERSATION(c); gtkconv->bg_color = text_color; - g_snprintf(open_tag, 25, "", + g_snprintf(open_tag, 25, "#%02X%02X%02X", text_color.red / 256, text_color.green / 256, text_color.blue / 256); - gaim_gtk_surround(gtkconv, open_tag, ""); - gaim_debug(GAIM_DEBUG_MISC, "bgcolor dialog", "#%02X%02X%02X\n", - text_color.red / 256, - text_color.green / 256, - text_color.blue / 256); - + gtk_imhtml_toggle_backcolor(GTK_IMHTML(gtkconv->entry), open_tag); + g_free(open_tag); cancel_bgcolor(NULL, c); } @@ -1351,21 +1341,12 @@ { GaimGtkConversation *gtkconv; char *smiley_text = g_object_get_data(G_OBJECT(widget), "smiley_text"); - GtkTextMark *select_mark, *insert_mark; - GtkTextIter select_iter, insert_iter; + GaimPlugin *proto = gaim_find_prpl(gaim_account_get_protocol_id(gaim_conversation_get_account(c))); gtkconv = GAIM_GTK_CONVERSATION(c); - select_mark = gtk_text_buffer_get_selection_bound(gtkconv->entry_buffer); - insert_mark = gtk_text_buffer_get_insert(gtkconv->entry_buffer); + gtk_imhtml_insert_smiley(GTK_IMHTML(gtkconv->entry), proto->info->name, smiley_text); - if(insert_mark != select_mark) { /* there is text selected */ - gtk_text_buffer_get_iter_at_mark(gtkconv->entry_buffer, &select_iter, select_mark); - gtk_text_buffer_get_iter_at_mark(gtkconv->entry_buffer, &insert_iter, insert_mark); - gtk_text_buffer_delete(gtkconv->entry_buffer, &select_iter, &insert_iter); - } - - gtk_text_buffer_insert_at_cursor(gtkconv->entry_buffer, smiley_text, -1); close_smiley_dialog(NULL, c); } diff -r b66733e6e6f2 -r ea073d234191 src/gtkconv.c --- a/src/gtkconv.c Sat Jan 10 05:20:50 2004 +0000 +++ b/src/gtkconv.c Sat Jan 10 06:06:02 2004 +0000 @@ -353,23 +353,73 @@ return FALSE; } +static void default_formatize(GaimConversation *conv) { + GaimGtkConversation *c = GAIM_GTK_CONVERSATION(conv); + GaimConnection *gc = gaim_conversation_get_gc(conv); + + if (gc && gc->flags & GAIM_CONNECTION_HTML) { + if (gaim_prefs_get_bool("/gaim/gtk/conversations/send_bold")) { + gtk_imhtml_toggle_bold(GTK_IMHTML(c->entry)); + } + + if (gaim_prefs_get_bool("/gaim/gtk/conversations/send_italic")) { + gtk_imhtml_toggle_italic(GTK_IMHTML(c->entry)); + } + + if (gaim_prefs_get_bool("/gaim/gtk/conversations/send_underline")) { + gtk_imhtml_toggle_underline(GTK_IMHTML(c->entry)); + } + + if (gaim_prefs_get_bool("/gaim/gtk/conversations/send_strikethrough")) { + /* Tell me noone uses by default ... maybe I won't do + _toggle_strikethrough and not let them */ + /* g_snprintf(buf2, limit, "%s", buf); + strcpy(buf, buf2); */ + } + + if (gaim_prefs_get_bool("/gaim/gtk/conversations/use_custom_font") || c->has_font) { + gtk_imhtml_toggle_fontface(GTK_IMHTML(c->entry), c->fontface); + } + + if (gaim_prefs_get_bool("/gaim/gtk/conversations/use_custom_size")) { + gtk_imhtml_font_set_size(GTK_IMHTML(c->entry), gaim_prefs_get_int("/gaim/gtk/conversations/font_size")); + } + + if (gaim_prefs_get_bool("/gaim/gtk/conversations/use_custom_fgcolor")) { + char *color = g_strdup_printf("#%02x%02x%02x", + c->fg_color.red / 256, + c->fg_color.green / 256, + c->fg_color.blue / 256); + gtk_imhtml_toggle_forecolor(GTK_IMHTML(c->entry), color); + g_free(color); + } + + if (!(gc->flags & GAIM_CONNECTION_NO_BGCOLOR) && gaim_prefs_get_bool("/gaim/gtk/conversations/use_custom_bgcolor")) { + char *color = g_strdup_printf("#%02x%02x%02x", + c->bg_color.red / 256, + c->bg_color.green / 256, + c->bg_color.blue / 256); + gtk_imhtml_toggle_backcolor(GTK_IMHTML(c->entry), color); + g_free(color); + } + } +} + static void send_cb(GtkWidget *widget, GaimConversation *conv) { GaimGtkConversation *gtkconv; - char *buf, *buf2; - GtkTextIter start_iter, end_iter; - int limit; + char *buf; GaimConnection *gc = gaim_conversation_get_gc(conv); gtkconv = GAIM_GTK_CONVERSATION(conv); - - gtk_text_buffer_get_start_iter(gtkconv->entry_buffer, &start_iter); - gtk_text_buffer_get_end_iter(gtkconv->entry_buffer, &end_iter); - buf2 = gtk_text_buffer_get_text(gtkconv->entry_buffer, - &start_iter, &end_iter, FALSE); - - set_toggle(gtkconv->toolbar.bold, FALSE); + + if (gc && gc->flags & GAIM_CONNECTION_HTML) + buf = gtk_imhtml_get_markup(GTK_IMHTML(gtkconv->entry)); + else + buf = gtk_imhtml_get_text(GTK_IMHTML(gtkconv->entry)); + + /* set_toggle(gtkconv->toolbar.bold, FALSE); set_toggle(gtkconv->toolbar.italic, FALSE); set_toggle(gtkconv->toolbar.underline, FALSE); set_toggle(gtkconv->toolbar.larger_size, FALSE); @@ -379,85 +429,18 @@ set_toggle(gtkconv->toolbar.fgcolor, FALSE); set_toggle(gtkconv->toolbar.bgcolor, FALSE); set_toggle(gtkconv->toolbar.link, FALSE); - + */ gtk_widget_grab_focus(gtkconv->entry); - limit = 32 * 1024; /* This will be done again in gaim_conv_im_send. *shrug* */ - - buf = g_malloc(limit); - strncpy(buf, buf2, limit); - - g_free(buf2); - if (strlen(buf) == 0) { g_free(buf); return; } - buf2 = g_malloc(limit); - - if (gc && gc->flags & GAIM_CONNECTION_HTML) { - if (gaim_prefs_get_bool("/gaim/gtk/conversations/send_bold")) { - g_snprintf(buf2, limit, "%s", buf); - strcpy(buf, buf2); - } - - if (gaim_prefs_get_bool("/gaim/gtk/conversations/send_italic")) { - g_snprintf(buf2, limit, "%s", buf); - strcpy(buf, buf2); - } - - if (gaim_prefs_get_bool("/gaim/gtk/conversations/send_underline")) { - g_snprintf(buf2, limit, "%s", buf); - strcpy(buf, buf2); - } - - if (gaim_prefs_get_bool("/gaim/gtk/conversations/send_strikethrough")) { - g_snprintf(buf2, limit, "%s", buf); - strcpy(buf, buf2); - } - - if (gaim_prefs_get_bool("/gaim/gtk/conversations/use_custom_font") || - gtkconv->has_font) { - - g_snprintf(buf2, limit, - "%s", gtkconv->fontface, buf); - strcpy(buf, buf2); - } - - if (gaim_prefs_get_bool("/gaim/gtk/conversations/use_custom_size")) { - g_snprintf(buf2, limit, - "%s", - gaim_prefs_get_int("/gaim/gtk/conversations/font_size"), - buf); - strcpy(buf, buf2); - } - - if (gaim_prefs_get_bool("/gaim/gtk/conversations/use_custom_fgcolor")) { - g_snprintf(buf2, limit, - "%s", - gtkconv->fg_color.red / 256, - gtkconv->fg_color.green / 256, - gtkconv->fg_color.blue / 256, buf); - strcpy(buf, buf2); - } - - if (!(gc->flags & GAIM_CONNECTION_NO_BGCOLOR) && gaim_prefs_get_bool("/gaim/gtk/conversations/use_custom_bgcolor")) { - g_snprintf(buf2, limit, - "%s", - gtkconv->bg_color.red / 256, - gtkconv->bg_color.green / 256, - gtkconv->bg_color.blue / 256, buf); - strcpy(buf, buf2); - } - } - - g_free(buf2); - if (gaim_conversation_get_type(conv) == GAIM_CONV_IM) gaim_conv_im_send(GAIM_CONV_IM(conv), buf); - else + else if (gaim_conversation_get_type(conv) == GAIM_CONV_CHAT) gaim_conv_chat_send(GAIM_CONV_CHAT(conv), buf); if (gaim_prefs_get_bool("/gaim/gtk/conversations/im/hide_on_send")) @@ -465,7 +448,8 @@ g_free(buf); - gtk_text_buffer_set_text(gtkconv->entry_buffer, "", -1); + gtk_imhtml_clear(GTK_IMHTML(gtkconv->entry)); + default_formatize(conv); } static void @@ -1353,8 +1337,8 @@ conv->send_history->next->data) { conv->send_history = conv->send_history->next; - gtk_text_buffer_set_text(gtkconv->entry_buffer, - conv->send_history->data, -1); + gtk_imhtml_clear(GTK_IMHTML(gtkconv->entry)); + gtk_imhtml_append_text_with_images(GTK_IMHTML(gtkconv->entry), conv->send_history->data, 0, NULL); } break; @@ -1366,9 +1350,10 @@ if (conv->send_history->prev) { conv->send_history = conv->send_history->prev; - if (conv->send_history->data) - gtk_text_buffer_set_text(gtkconv->entry_buffer, - conv->send_history->data, -1); + if (conv->send_history->data) { + gtk_imhtml_clear(GTK_IMHTML(gtkconv->entry)); + gtk_imhtml_append_text_with_images(GTK_IMHTML(gtkconv->entry), conv->send_history->data, 0, NULL); + } } break; @@ -2304,66 +2289,35 @@ static void do_bold(GtkWidget *bold, GaimGtkConversation *gtkconv) { - if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(bold))) - gaim_gtk_surround(gtkconv, "", ""); - else - gaim_gtk_advance_past(gtkconv, "", ""); - + gtk_imhtml_toggle_bold(GTK_IMHTML(gtkconv->entry)); gtk_widget_grab_focus(gtkconv->entry); } static void do_italic(GtkWidget *italic, GaimGtkConversation *gtkconv) { - if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(italic))) - gaim_gtk_surround(gtkconv, "", ""); - else - gaim_gtk_advance_past(gtkconv, "", ""); - + gtk_imhtml_toggle_italic(GTK_IMHTML(gtkconv->entry)); gtk_widget_grab_focus(gtkconv->entry); } static void do_underline(GtkWidget *underline, GaimGtkConversation *gtkconv) { - if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(underline))) - gaim_gtk_surround(gtkconv, "", ""); - else - gaim_gtk_advance_past(gtkconv, "", ""); - + gtk_imhtml_toggle_underline(GTK_IMHTML(gtkconv->entry)); gtk_widget_grab_focus(gtkconv->entry); } static void do_small(GtkWidget *smalltb, GaimGtkConversation *gtkconv) { - if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(smalltb))) - gaim_gtk_surround(gtkconv, "", ""); - else - gaim_gtk_advance_past(gtkconv, "", ""); - - gtk_widget_grab_focus(gtkconv->entry); -} - -static void -do_normal(GtkWidget *normal, GaimGtkConversation *gtkconv) -{ - if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(normal))) - gaim_gtk_surround(gtkconv, "", ""); - else - gaim_gtk_advance_past(gtkconv, "", ""); - + gtk_imhtml_font_shrink(GTK_IMHTML(gtkconv->entry)); gtk_widget_grab_focus(gtkconv->entry); } static void do_big(GtkWidget *large, GaimGtkConversation *gtkconv) { - if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(large))) - gaim_gtk_surround(gtkconv, "", ""); - else - gaim_gtk_advance_past(gtkconv, "", ""); - + gtk_imhtml_font_grow(GTK_IMHTML(gtkconv->entry)); gtk_widget_grab_focus(gtkconv->entry); } @@ -2374,12 +2328,7 @@ gtkconv = GAIM_GTK_CONVERSATION(conv); - if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(font))) - show_font_dialog(conv, font); - else if (gtkconv->dialogs.font != NULL) - cancel_font(font, conv); - else - gaim_gtk_advance_past(gtkconv, "", ""); + show_font_dialog(conv, font); } static void @@ -3440,7 +3389,7 @@ gtkconv->toolbar.larger_size = button; - /* Normal font size */ + /* Normal font size button = gaim_pixbuf_toolbar_button_from_stock(GAIM_STOCK_TEXT_NORMAL); gtk_size_group_add_widget(sg, button); gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0); @@ -3451,6 +3400,7 @@ G_CALLBACK(do_normal), gtkconv); gtkconv->toolbar.normal_size = button; + */ /* Decrease font size */ button = gaim_pixbuf_toolbar_button_from_stock(GAIM_STOCK_TEXT_SMALLER); @@ -3773,14 +3723,17 @@ /* Setup the entry widget. */ sw = gtk_scrolled_window_new(NULL, NULL); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), - GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); - gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw), GTK_SHADOW_IN); + GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw), + GTK_SHADOW_IN); gtk_box_pack_start(GTK_BOX(vbox), sw, TRUE, TRUE, 0); gtk_widget_show(sw); - gtkconv->entry_buffer = gtk_text_buffer_new(NULL); - gtkconv->entry = gtk_text_view_new_with_buffer(gtkconv->entry_buffer); - + gtkconv->entry = gtk_imhtml_new(NULL, NULL); + gtkconv->entry_buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(gtkconv->entry)); + gaim_setup_imhtml(gtkconv->entry); + gtk_imhtml_set_editable(GTK_IMHTML(gtkconv->entry), TRUE); + default_formatize(conv); gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(gtkconv->entry), GTK_WRAP_WORD_CHAR); gtk_widget_set_size_request(gtkconv->entry, -1, MAX(gaim_prefs_get_int("/gaim/gtk/conversations/chat/entry_height"), @@ -3874,14 +3827,17 @@ /* Setup the entry widget. */ sw = gtk_scrolled_window_new(NULL, NULL); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), - GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); - gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw), GTK_SHADOW_IN); + GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw), + GTK_SHADOW_IN); gtk_box_pack_start(GTK_BOX(vbox2), sw, TRUE, TRUE, 0); gtk_widget_show(sw); - gtkconv->entry_buffer = gtk_text_buffer_new(NULL); - gtkconv->entry = gtk_text_view_new_with_buffer(gtkconv->entry_buffer); - + gtkconv->entry = gtk_imhtml_new(NULL, NULL); + gtkconv->entry_buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(gtkconv->entry)); + gaim_setup_imhtml(gtkconv->entry); + gtk_imhtml_set_editable(GTK_IMHTML(gtkconv->entry), TRUE); + default_formatize(conv); gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(gtkconv->entry), GTK_WRAP_WORD_CHAR); gtk_widget_set_size_request(gtkconv->entry, -1, MAX(gaim_prefs_get_int("/gaim/gtk/conversations/im/entry_height"), diff -r b66733e6e6f2 -r ea073d234191 src/gtkimhtml.c --- a/src/gtkimhtml.c Sat Jan 10 05:20:50 2004 +0000 +++ b/src/gtkimhtml.c Sat Jan 10 06:06:02 2004 +0000 @@ -61,12 +61,30 @@ #define TOOLTIP_TIMEOUT 500 +static void insert_cb(GtkTextBuffer *buffer, GtkTextIter *iter, gchar *text, gint len, GtkIMHtml *imhtml); +void gtk_imhtml_close_tags(GtkIMHtml *imhtml); + /* POINT_SIZE converts from AIM font sizes to point sizes. It probably should be redone in such a * way that it base the sizes off the default font size rather than using arbitrary font sizes. */ #define MAX_FONT_SIZE 7 #define POINT_SIZE(x) (options & GTK_IMHTML_USE_POINTSIZE ? x : _point_sizes [MIN ((x), MAX_FONT_SIZE) - 1]) static gint _point_sizes [] = { 8, 10, 12, 14, 20, 30, 40 }; +enum { + TARGET_HTML, + TARGET_UTF8_STRING, + TARGET_COMPOUND_TEXT, + TARGET_STRING, + TARGET_TEXT +}; + +GtkTargetEntry selection_targets[] = { + { "text/html", 0, TARGET_HTML }, + { "UTF8_STRING", 0, TARGET_UTF8_STRING }, + { "COMPOUND_TEXT", 0, TARGET_COMPOUND_TEXT }, + { "STRING", 0, TARGET_STRING }, + { "TEXT", 0, TARGET_TEXT}}; + static GtkSmileyTree* gtk_smiley_tree_new () { @@ -98,12 +116,12 @@ t->children [index] = g_new0 (GtkSmileyTree, 1); } else index = GPOINTER_TO_INT(pos) - GPOINTER_TO_INT(t->values->str); - + t = t->children [index]; - + x++; } - + t->image = smiley; } @@ -155,7 +173,7 @@ layout = gtk_widget_create_pango_layout(imhtml->tip_window, imhtml->tip); - gtk_paint_flat_box (imhtml->tip_window->style, imhtml->tip_window->window, + gtk_paint_flat_box (imhtml->tip_window->style, imhtml->tip_window->window, GTK_STATE_NORMAL, GTK_SHADOW_OUT, NULL, imhtml->tip_window, "tooltip", 0, 0, -1, -1); @@ -181,7 +199,7 @@ imhtml->tip_timer = 0; return FALSE; } - + if (imhtml->tip_window){ gtk_widget_destroy (imhtml->tip_window); imhtml->tip_window = NULL; @@ -203,12 +221,12 @@ pango_layout_get_pixel_size(layout, &scr_w, NULL); - gap = PANGO_PIXELS((pango_font_metrics_get_ascent(font) + + gap = PANGO_PIXELS((pango_font_metrics_get_ascent(font) + pango_font_metrics_get_descent(font))/ 4); if (gap < 2) gap = 2; - baseline_skip = PANGO_PIXELS(pango_font_metrics_get_ascent(font) + + baseline_skip = PANGO_PIXELS(pango_font_metrics_get_ascent(font) + pango_font_metrics_get_descent(font)); w = 8 + scr_w; h = 8 + baseline_skip; @@ -226,7 +244,7 @@ else if (x < 0) x = 0; - y = y + PANGO_PIXELS(pango_font_metrics_get_ascent(font) + + y = y + PANGO_PIXELS(pango_font_metrics_get_ascent(font) + pango_font_metrics_get_descent(font)); gtk_widget_set_size_request (imhtml->tip_window, w, h); @@ -240,7 +258,7 @@ } gboolean gtk_motion_event_notify(GtkWidget *imhtml, GdkEventMotion *event, gpointer data) -{ +{ GtkTextIter iter; GdkWindow *win = event->window; int x, y; @@ -260,7 +278,7 @@ break; templist = templist->next; } - + if (GTK_IMHTML(imhtml)->tip) { if ((tip == GTK_IMHTML(imhtml)->tip)) { return FALSE; @@ -270,18 +288,22 @@ gtk_widget_destroy(GTK_IMHTML(imhtml)->tip_window); GTK_IMHTML(imhtml)->tip_window = NULL; } - gdk_window_set_cursor(win, GTK_IMHTML(imhtml)->arrow_cursor); + if (GTK_IMHTML(imhtml)->editable) + gdk_window_set_cursor(win, GTK_IMHTML(imhtml)->text_cursor); + else + gdk_window_set_cursor(win, GTK_IMHTML(imhtml)->arrow_cursor); if (GTK_IMHTML(imhtml)->tip_timer) g_source_remove(GTK_IMHTML(imhtml)->tip_timer); GTK_IMHTML(imhtml)->tip_timer = 0; } - + if(tip){ - gdk_window_set_cursor(win, GTK_IMHTML(imhtml)->hand_cursor); - GTK_IMHTML(imhtml)->tip_timer = g_timeout_add (TOOLTIP_TIMEOUT, + if (!GTK_IMHTML(imhtml)->editable) + gdk_window_set_cursor(win, GTK_IMHTML(imhtml)->hand_cursor); + GTK_IMHTML(imhtml)->tip_timer = g_timeout_add (TOOLTIP_TIMEOUT, gtk_imhtml_tip, imhtml); } - + GTK_IMHTML(imhtml)->tip = tip; g_slist_free(tags); return FALSE; @@ -298,7 +320,10 @@ g_source_remove(GTK_IMHTML(imhtml)->tip_timer); GTK_IMHTML(imhtml)->tip_timer = 0; } - gdk_window_set_cursor(event->window, GTK_IMHTML(imhtml)->arrow_cursor); + if (GTK_IMHTML(imhtml)->editable) + gdk_window_set_cursor(event->window, GTK_IMHTML(imhtml)->text_cursor); + else + gdk_window_set_cursor(event->window, GTK_IMHTML(imhtml)->arrow_cursor); /* propogate the event normally */ return FALSE; @@ -307,11 +332,11 @@ /* * XXX - This should be removed eventually. * - * This function exists to work around a gross bug in GtkTextView. - * Basically, we short circuit ctrl+a and ctrl+end because they make + * This function exists to work around a gross bug in GtkTextView. + * Basically, we short circuit ctrl+a and ctrl+end because they make * el program go boom. * - * It's supposed to be fixed in gtk2.2. You can view the bug report at + * It's supposed to be fixed in gtk2.2. You can view the bug report at * http://bugzilla.gnome.org/show_bug.cgi?id=107939 */ gboolean gtk_key_pressed_cb(GtkWidget *imhtml, GdkEventKey *event, gpointer data) @@ -335,62 +360,107 @@ } #if GTK_CHECK_VERSION(2,2,0) -static GtkIMHtmlCopyable *gtk_imhtml_copyable_new(GtkIMHtml *imhtml, GtkTextMark *mark, const gchar *text) +static void gtk_imhtml_clipboard_get(GtkClipboard *clipboard, GtkSelectionData *selection_data, guint info, GtkIMHtml *imhtml) { + GtkTextIter start, end; + GtkTextMark *sel = gtk_text_buffer_get_selection_bound(imhtml->text_buffer); + GtkTextMark *ins = gtk_text_buffer_get_insert(imhtml->text_buffer); + char *text; + gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &start, sel); + gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &end, ins); + + + if (info == TARGET_HTML) { + int len; + GString *str = g_string_new(NULL); + text = gtk_imhtml_get_markup_range(imhtml, &start, &end); + + /* Mozilla asks that we start our text/html with the Unicode byte order mark */ + str = g_string_append_unichar(str, 0xfeff); + str = g_string_append(str, text); + str = g_string_append_unichar(str, 0x0000); + char *selection = g_convert(str->str, str->len, "UCS-2", "UTF-8", NULL, &len, NULL); + gtk_selection_data_set (selection_data, gdk_atom_intern("text/html", FALSE), 16, selection, len); + g_string_free(str, TRUE); + g_free(selection); + } else { + text = gtk_text_buffer_get_text(imhtml->text_buffer, &start, &end, FALSE); + gtk_selection_data_set_text(selection_data, text, strlen(text)); + } + g_free(text); +} + +static void gtk_imhtml_primary_clipboard_clear(GtkClipboard *clipboard, GtkIMHtml *imhtml) { - GtkIMHtmlCopyable *copy = g_malloc(sizeof(GtkIMHtmlCopyable)); - copy->mark = mark; - copy->text = g_strdup(text); - imhtml->copyables = g_slist_append(imhtml->copyables, copy); - return copy; + GtkTextIter insert; + GtkTextIter selection_bound; + + gtk_text_buffer_get_iter_at_mark (imhtml->text_buffer, &insert, + gtk_text_buffer_get_mark (imhtml->text_buffer, "insert")); + gtk_text_buffer_get_iter_at_mark (imhtml->text_buffer, &selection_bound, + gtk_text_buffer_get_mark (imhtml->text_buffer, "selection_bound")); + + if (!gtk_text_iter_equal (&insert, &selection_bound)) + gtk_text_buffer_move_mark (imhtml->text_buffer, + gtk_text_buffer_get_mark (imhtml->text_buffer, "selection_bound"), + &insert); } static void copy_clipboard_cb(GtkIMHtml *imhtml, GtkClipboard *clipboard) { - GtkTextMark *sel = gtk_text_buffer_get_selection_bound(imhtml->text_buffer); - GtkTextMark *ins = gtk_text_buffer_get_insert(imhtml->text_buffer); - GtkTextIter start, end, smiley, last; - GString *str = g_string_new(NULL); - char *text; + gtk_clipboard_set_with_owner(gtk_widget_get_clipboard(GTK_WIDGET(imhtml), GDK_SELECTION_CLIPBOARD), + selection_targets, sizeof(selection_targets) / sizeof(GtkTargetEntry), + (GtkClipboardGetFunc)gtk_imhtml_clipboard_get, + (GtkClipboardClearFunc)NULL, G_OBJECT(imhtml)); - GSList *copyables; - - gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &start, sel); - gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &end, ins); - - if (gtk_text_iter_equal(&start, &end)) - return; - - gtk_text_iter_order(&start, &end); - last = start; + g_signal_stop_emission_by_name(imhtml, "copy-clipboard"); +} + +static void paste_received_cb (GtkClipboard *clipboard, GtkSelectionData *selection_data, gpointer data) +{ + char *text; + guint16 c; + GtkIMHtml *imhtml = data; - for (copyables = imhtml->copyables; copyables != NULL; copyables = copyables->next) { - GtkIMHtmlCopyable *copy = GTK_IMHTML_COPYABLE(copyables->data); - gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &smiley, copy->mark); - if (gtk_text_iter_compare(&end, &smiley) < 0) { - break; - } - if (gtk_text_iter_compare(&last, &smiley) <= 0) { - text = gtk_text_buffer_get_text(imhtml->text_buffer, &last, &smiley, FALSE); - str = g_string_append(str, text); - str = g_string_append(str, copy->text); - last = smiley; - g_free(text); - } + if (selection_data->length < 0) { + text = gtk_clipboard_wait_for_text(clipboard); + } else { + text = g_malloc((selection_data->format / 8) * selection_data->length); + memcpy(text, selection_data->data, selection_data->length * (selection_data->format / 8)); } - text = gtk_text_buffer_get_text(imhtml->text_buffer, &last, &end, FALSE); - str = g_string_append(str, text); - g_free(text); - - if (!gtk_text_iter_equal(&start, &last)) - gtk_clipboard_set_text(clipboard ? clipboard : - gtk_widget_get_clipboard(GTK_WIDGET(imhtml), GDK_SELECTION_CLIPBOARD), - str->str, str->len); - g_string_free(str, TRUE); + + memcpy (&c, text, 2); + if (c == 0xfeff) { + /* This is UCS2 */ + char *utf8 = g_convert(text+2, (selection_data->length * (selection_data->format / 8)) - 2, "UTF-8", "UCS-2", NULL, NULL, NULL); + g_free(text); + text = utf8; + } + gtk_imhtml_close_tags(imhtml); + gtk_imhtml_append_text_with_images(imhtml, text, GTK_IMHTML_NO_NEWLINE, NULL); +} + + +static void paste_clipboard_cb(GtkIMHtml *imhtml, gpointer blah) +{ + + GtkClipboard *clipboard = gtk_widget_get_clipboard(GTK_WIDGET(imhtml), GDK_SELECTION_CLIPBOARD); + gtk_clipboard_request_contents(clipboard, gdk_atom_intern("text/html", FALSE), + paste_received_cb, imhtml); + g_signal_stop_emission_by_name(imhtml, "paste-clipboard"); } static gboolean button_release_cb(GtkIMHtml *imhtml, GdkEventButton event, gpointer the_foibles_of_man) { - copy_clipboard_cb(imhtml, gtk_widget_get_clipboard(GTK_WIDGET(imhtml), GDK_SELECTION_PRIMARY)); + GtkClipboard *clipboard; + if (event.button == 1) { + if ((clipboard = gtk_widget_get_clipboard (GTK_WIDGET (imhtml), + GDK_SELECTION_PRIMARY))) + gtk_text_buffer_remove_selection_clipboard (imhtml->text_buffer, clipboard); + gtk_clipboard_set_with_owner(gtk_widget_get_clipboard(GTK_WIDGET(imhtml), GDK_SELECTION_PRIMARY), + selection_targets, sizeof(selection_targets) / sizeof(GtkTargetEntry), + (GtkClipboardGetFunc)gtk_imhtml_clipboard_get, + (GtkClipboardClearFunc)gtk_imhtml_primary_clipboard_clear, G_OBJECT(imhtml)); + } return FALSE; } #endif @@ -410,14 +480,12 @@ { GtkIMHtml *imhtml = GTK_IMHTML(object); GList *scalables; -#if GTK_CHECK_VERSION(2,2,0) - GSList *copyables; -#endif - + g_hash_table_destroy(imhtml->smiley_data); gtk_smiley_tree_destroy(imhtml->default_smilies); gdk_cursor_unref(imhtml->hand_cursor); gdk_cursor_unref(imhtml->arrow_cursor); + gdk_cursor_unref(imhtml->text_cursor); if(imhtml->tip_window){ gtk_widget_destroy(imhtml->tip_window); } @@ -429,13 +497,6 @@ scale->free(scale); } -#if GTK_CHECK_VERSION(2,2,0) - for (copyables = imhtml->copyables; copyables; copyables = copyables->next) { - GtkIMHtmlCopyable *copy = GTK_IMHTML_COPYABLE(copyables->data); - g_free(copy->text); - g_free(copy); - } -#endif g_list_free(imhtml->scalables); G_OBJECT_CLASS(parent_class)->finalize (object); } @@ -468,13 +529,12 @@ imhtml->end = gtk_text_buffer_create_mark(imhtml->text_buffer, NULL, &iter, FALSE); gtk_text_view_set_buffer(GTK_TEXT_VIEW(imhtml), imhtml->text_buffer); gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(imhtml), GTK_WRAP_WORD_CHAR); - gtk_text_view_set_editable(GTK_TEXT_VIEW(imhtml), FALSE); gtk_text_view_set_pixels_below_lines(GTK_TEXT_VIEW(imhtml), 5); - gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(imhtml), FALSE); + /*gtk_text_view_set_indent(GTK_TEXT_VIEW(imhtml), -15);*/ /*gtk_text_view_set_justification(GTK_TEXT_VIEW(imhtml), GTK_JUSTIFY_FILL);*/ - + /* These tags will be used often and can be reused--we create them on init and then apply them by name - * other tags (color, size, face, etc.) will have to be created and applied dynamically */ + * other tags (color, size, face, etc.) will have to be created and applied dynamically */ gtk_text_buffer_create_tag(imhtml->text_buffer, "BOLD", "weight", PANGO_WEIGHT_BOLD, NULL); gtk_text_buffer_create_tag(imhtml->text_buffer, "ITALICS", "style", PANGO_STYLE_ITALIC, NULL); gtk_text_buffer_create_tag(imhtml->text_buffer, "UNDERLINE", "underline", PANGO_UNDERLINE_SINGLE, NULL); @@ -483,10 +543,11 @@ gtk_text_buffer_create_tag(imhtml->text_buffer, "SUP", "rise", 5000, NULL); gtk_text_buffer_create_tag(imhtml->text_buffer, "PRE", "family", "Monospace", NULL); gtk_text_buffer_create_tag(imhtml->text_buffer, "search", "background", "#22ff00", "weight", "bold", NULL); - + gtk_text_buffer_create_tag(imhtml->text_buffer, "LINK", "foreground", "blue", "underline", PANGO_UNDERLINE_SINGLE, NULL); /* When hovering over a link, we show the hand cursor--elsewhere we show the plain ol' pointer cursor */ imhtml->hand_cursor = gdk_cursor_new (GDK_HAND2); imhtml->arrow_cursor = gdk_cursor_new (GDK_LEFT_PTR); + imhtml->text_cursor = gdk_cursor_new (GDK_XTERM); imhtml->show_smileys = TRUE; imhtml->show_comments = TRUE; @@ -499,8 +560,10 @@ g_signal_connect(G_OBJECT(imhtml), "motion-notify-event", G_CALLBACK(gtk_motion_event_notify), NULL); g_signal_connect(G_OBJECT(imhtml), "leave-notify-event", G_CALLBACK(gtk_leave_event_notify), NULL); g_signal_connect(G_OBJECT(imhtml), "key_press_event", G_CALLBACK(gtk_key_pressed_cb), NULL); + g_signal_connect_after(G_OBJECT(imhtml->text_buffer), "insert-text", G_CALLBACK(insert_cb), imhtml); #if GTK_CHECK_VERSION(2,2,0) g_signal_connect(G_OBJECT(imhtml), "copy-clipboard", G_CALLBACK(copy_clipboard_cb), NULL); + g_signal_connect(G_OBJECT(imhtml), "paste-clipboard", G_CALLBACK(paste_clipboard_cb), NULL); g_signal_connect(G_OBJECT(imhtml), "button-release-event", G_CALLBACK(button_release_cb), imhtml); #endif gtk_widget_add_events(GTK_WIDGET(imhtml), GDK_LEAVE_NOTIFY_MASK); @@ -509,10 +572,21 @@ imhtml->tip_timer = 0; imhtml->tip_window = NULL; + imhtml->edit.bold = NULL; + imhtml->edit.italic = NULL; + imhtml->edit.underline = NULL; + imhtml->edit.forecolor = NULL; + imhtml->edit.backcolor = NULL; + imhtml->edit.fontface = NULL; + imhtml->edit.sizespan = NULL; + imhtml->edit.fontsize = 3; + + imhtml->format_spans = NULL; + imhtml->scalables = NULL; -#if GTK_CHECK_VERSION(2,2,0) - imhtml->copyables = NULL; -#endif + + gtk_imhtml_set_editable(imhtml, FALSE); + } GtkWidget *gtk_imhtml_new(void *a, void *b) @@ -551,9 +625,8 @@ static void url_open(GtkWidget *w, struct url_data *data) { if(!data) return; + g_signal_emit(data->object, signals[URL_CLICKED], 0, data->url); - g_signal_emit(data->object, signals[URL_CLICKED], 0, data->url); - g_object_unref(data->object); g_free(data->url); g_free(data); @@ -572,9 +645,10 @@ /* The callback for an event on a link tag. */ gboolean tag_event(GtkTextTag *tag, GObject *imhtml, GdkEvent *event, GtkTextIter *arg2, char *url) { GdkEventButton *event_button = (GdkEventButton *) event; - + if (GTK_IMHTML(imhtml)->editable) + return FALSE; if (event->type == GDK_BUTTON_RELEASE) { - if (event_button->button == 1) { + if (event_button->button == 1) { GtkTextIter start, end; /* we shouldn't open a URL if the user has selected something: */ gtk_text_buffer_get_selection_bounds( @@ -602,7 +676,10 @@ g_source_remove(GTK_IMHTML(imhtml)->tip_timer); GTK_IMHTML(imhtml)->tip_timer = 0; } - gdk_window_set_cursor(event_button->window, GTK_IMHTML(imhtml)->arrow_cursor); + if (GTK_IMHTML(imhtml)->editable) + gdk_window_set_cursor(event_button->window, GTK_IMHTML(imhtml)->text_cursor); + else + gdk_window_set_cursor(event_button->window, GTK_IMHTML(imhtml)->arrow_cursor); menu = gtk_menu_new(); /* buttons and such */ @@ -895,9 +972,11 @@ gint *len, gint *type) { + char *close; *type = 1; - if (!strchr (string, '>')) + + if (!(close = strchr (string, '>'))) return FALSE; VALID_TAG ("B"); @@ -963,6 +1042,7 @@ VALID_TAG ("BR/"); /* hack until gtkimhtml handles things better */ VALID_TAG ("IMG"); VALID_TAG("SPAN"); + VALID_OPT_TAG("BR"); if (!g_ascii_strncasecmp(string, "!--", strlen ("!--"))) { gchar *e = strstr (string + strlen("!--"), "-->"); @@ -971,9 +1051,12 @@ *tag = g_strndup (string + strlen ("!--"), *len - strlen ("!---->")); return TRUE; } - } - - return FALSE; + } + + *type = -1; + *len = close - string + 1; + *tag = g_strndup(string, *len - 1); + return TRUE; } static gchar* @@ -1036,85 +1119,18 @@ } - -#define NEW_TEXT_BIT 0 -#define NEW_COMMENT_BIT 2 -#define NEW_SCALABLE_BIT 1 -#define NEW_BIT(x) ws [wpos] = '\0'; \ - mark2 = gtk_text_buffer_create_mark(imhtml->text_buffer, NULL, &iter, TRUE); \ - gtk_text_buffer_insert(imhtml->text_buffer, &iter, ws, -1); \ - gtk_text_buffer_get_end_iter(imhtml->text_buffer, &iter); \ - gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &siter, mark2); \ - gtk_text_buffer_delete_mark(imhtml->text_buffer, mark2); \ - if (bold) \ - gtk_text_buffer_apply_tag_by_name(imhtml->text_buffer, "BOLD", &siter, &iter); \ - if (italics) \ - gtk_text_buffer_apply_tag_by_name(imhtml->text_buffer, "ITALICS", &siter, &iter); \ - if (underline) \ - gtk_text_buffer_apply_tag_by_name(imhtml->text_buffer, "UNDERLINE", &siter, &iter); \ - if (strike) \ - gtk_text_buffer_apply_tag_by_name(imhtml->text_buffer, "STRIKE", &siter, &iter); \ - if (sub) \ - gtk_text_buffer_apply_tag_by_name(imhtml->text_buffer, "SUB", &siter, &iter); \ - if (sup) \ - gtk_text_buffer_apply_tag_by_name(imhtml->text_buffer, "SUP", &siter, &iter); \ - if (pre) \ - gtk_text_buffer_apply_tag_by_name(imhtml->text_buffer, "PRE", &siter, &iter); \ - if (bg) { \ - texttag = gtk_text_buffer_create_tag(imhtml->text_buffer, NULL, "background", bg, NULL); \ - gtk_text_buffer_apply_tag(imhtml->text_buffer, texttag, &siter, &iter); \ - } \ - if (fonts) { \ - GtkIMHtmlFontDetail *fd = fonts->data; \ - if (fd->fore) { \ - texttag = gtk_text_buffer_create_tag(imhtml->text_buffer, NULL, "foreground", fd->fore, NULL); \ - gtk_text_buffer_apply_tag(imhtml->text_buffer, texttag, &siter, &iter); \ - } \ - if (fd->back) { \ - texttag = gtk_text_buffer_create_tag(imhtml->text_buffer, NULL, "background", fd->back, NULL); \ - gtk_text_buffer_apply_tag(imhtml->text_buffer, texttag, &siter, &iter); \ - } \ - if (fd->face) { \ - texttag = gtk_text_buffer_create_tag(imhtml->text_buffer, NULL, "family", fd->face, NULL); \ - gtk_text_buffer_apply_tag(imhtml->text_buffer, texttag, &siter, &iter); \ - } \ - if (fd->size) { \ - texttag = gtk_text_buffer_create_tag(imhtml->text_buffer, NULL, "size-points", (double)POINT_SIZE(fd->size), NULL); \ - gtk_text_buffer_apply_tag(imhtml->text_buffer, texttag, &siter, &iter); \ - } \ - } \ - if (url) { \ - texttag = gtk_text_buffer_create_tag(imhtml->text_buffer, NULL, "foreground", "blue", "underline", PANGO_UNDERLINE_SINGLE, NULL); \ - g_signal_connect(G_OBJECT(texttag), "event", G_CALLBACK(tag_event), g_strdup(url)); \ - gtk_text_buffer_apply_tag(imhtml->text_buffer, texttag, &siter, &iter); \ - texttag = gtk_text_buffer_create_tag(imhtml->text_buffer, NULL, NULL); \ - g_object_set_data(G_OBJECT(texttag), "link_url", g_strdup(url)); \ - gtk_text_buffer_apply_tag(imhtml->text_buffer, texttag, &siter, &iter); \ - } \ - wpos = 0; \ - ws[0] = 0; \ - gtk_text_buffer_get_end_iter(imhtml->text_buffer, &iter); \ - if (x == NEW_SCALABLE_BIT) { \ - GdkRectangle rect; \ - gtk_text_view_get_visible_rect(GTK_TEXT_VIEW(imhtml), &rect); \ - scalable->add_to(scalable, imhtml, &iter); \ - scalable->scale(scalable, rect.width, rect.height); \ - imhtml->scalables = g_list_append(imhtml->scalables, scalable); \ - gtk_text_buffer_get_end_iter(imhtml->text_buffer, &iter); \ - } \ - - - GString* gtk_imhtml_append_text_with_images (GtkIMHtml *imhtml, const gchar *text, GtkIMHtmlOptions options, GSList *images) { + GtkTextMark *ins = gtk_text_buffer_get_insert(imhtml->text_buffer); + GtkTextIter insert; + GdkRectangle rect; gint pos = 0; GString *str = NULL; - GtkTextIter iter, siter; - GtkTextMark *mark, *mark2; - GtkTextTag *texttag; + GtkTextIter iter; + GtkTextMark *mark; gchar *ws; gchar *tag; gchar *url = NULL; @@ -1132,19 +1148,17 @@ sub = 0, sup = 0, title = 0, - pre = 0; + pre = 0; GSList *fonts = NULL; - - GdkRectangle rect; + GtkIMHtmlScalable *scalable = NULL; int y, height; - GtkIMHtmlScalable *scalable = NULL; g_return_val_if_fail (imhtml != NULL, NULL); g_return_val_if_fail (GTK_IS_IMHTML (imhtml), NULL); g_return_val_if_fail (text != NULL, NULL); - + printf("Appending: %s\n", text); c = text; len = strlen(text); ws = g_malloc(len + 1); @@ -1153,13 +1167,22 @@ if (options & GTK_IMHTML_RETURN_LOG) str = g_string_new(""); + gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &insert, ins); + gtk_text_buffer_get_end_iter(imhtml->text_buffer, &iter); mark = gtk_text_buffer_create_mark (imhtml->text_buffer, NULL, &iter, /* right grav */ FALSE); - gtk_text_view_get_visible_rect(GTK_TEXT_VIEW(imhtml), &rect); + gtk_text_view_get_visible_rect(GTK_TEXT_VIEW(imhtml), &rect); gtk_text_view_get_line_yrange(GTK_TEXT_VIEW(imhtml), &iter, &y, &height); - if(((y + height) - (rect.y + rect.height)) > height +#if GTK_CHECK_VERSION(2,2,0) + gtk_imhtml_primary_clipboard_clear(NULL, imhtml); +#endif + gtk_text_buffer_move_mark (imhtml->text_buffer, + gtk_text_buffer_get_mark (imhtml->text_buffer, "insert"), + &iter); + + if(((y + height) - (rect.y + rect.height)) > height && gtk_text_buffer_get_char_count(imhtml->text_buffer)){ options |= GTK_IMHTML_NO_SCROLL; } @@ -1168,85 +1191,123 @@ if (*c == '<' && gtk_imhtml_is_tag (c + 1, &tag, &tlen, &type)) { c++; pos++; - switch (type) + ws[wpos] = '\0'; + switch (type) { case 1: /* B */ case 2: /* BOLD */ case 54: /* STRONG */ - NEW_BIT (NEW_TEXT_BIT); + if (url) + gtk_imhtml_insert_link(imhtml, url, ws); + else + gtk_text_buffer_insert(imhtml->text_buffer, &iter, ws, wpos); + if (bold == 0) + gtk_imhtml_toggle_bold(imhtml); bold++; + ws[0] = '\0'; wpos = 0; break; case 3: /* /B */ case 4: /* /BOLD */ case 55: /* /STRONG */ - NEW_BIT (NEW_TEXT_BIT); + if (url) + gtk_imhtml_insert_link(imhtml, url, ws); + else + gtk_text_buffer_insert(imhtml->text_buffer, &iter, ws, wpos); + ws[0] = '\0'; wpos = 0; + if (bold) bold--; + if (bold == 0) + gtk_imhtml_toggle_bold(imhtml); break; case 5: /* I */ case 6: /* ITALIC */ case 52: /* EM */ - NEW_BIT (NEW_TEXT_BIT); + if (url) + gtk_imhtml_insert_link(imhtml, url, ws); + else + gtk_text_buffer_insert(imhtml->text_buffer, &iter, ws, wpos); + ws[0] = '\0'; wpos = 0; + if (italics == 0) + gtk_imhtml_toggle_italic(imhtml); italics++; break; case 7: /* /I */ case 8: /* /ITALIC */ case 53: /* /EM */ - NEW_BIT (NEW_TEXT_BIT); + if (url) + gtk_imhtml_insert_link(imhtml, url, ws); + else + gtk_text_buffer_insert(imhtml->text_buffer, &iter, ws, wpos); + ws[0] = '\0'; wpos = 0; if (italics) italics--; + if (italics == 0) + gtk_imhtml_toggle_italic(imhtml); break; case 9: /* U */ case 10: /* UNDERLINE */ - NEW_BIT (NEW_TEXT_BIT); + if (url) + gtk_imhtml_insert_link(imhtml, url, ws); + else + gtk_text_buffer_insert(imhtml->text_buffer, &iter, ws, wpos); + ws[0] = '\0'; wpos = 0; + if (underline == 0) + gtk_imhtml_toggle_underline(imhtml); underline++; break; case 11: /* /U */ case 12: /* /UNDERLINE */ - NEW_BIT (NEW_TEXT_BIT); + if (url) + gtk_imhtml_insert_link(imhtml, url, ws); + else + gtk_text_buffer_insert(imhtml->text_buffer, &iter, ws, wpos); + ws[0] = '\0'; wpos = 0; if (underline) underline--; + if (underline == 0) + gtk_imhtml_toggle_underline(imhtml); break; case 13: /* S */ case 14: /* STRIKE */ - NEW_BIT (NEW_TEXT_BIT); + //NEW_BIT (NEW_TEXT_BIT); strike++; break; case 15: /* /S */ case 16: /* /STRIKE */ - NEW_BIT (NEW_TEXT_BIT); + //NEW_BIT (NEW_TEXT_BIT); if (strike) strike--; break; case 17: /* SUB */ - NEW_BIT (NEW_TEXT_BIT); + //NEW_BIT (NEW_TEXT_BIT); sub++; break; case 18: /* /SUB */ - NEW_BIT (NEW_TEXT_BIT); + //NEW_BIT (NEW_TEXT_BIT); if (sub) sub--; break; case 19: /* SUP */ - NEW_BIT (NEW_TEXT_BIT); + //NEW_BIT (NEW_TEXT_BIT); sup++; break; case 20: /* /SUP */ - NEW_BIT (NEW_TEXT_BIT); + //NEW_BIT (NEW_TEXT_BIT); if (sup) sup--; break; case 21: /* PRE */ - NEW_BIT (NEW_TEXT_BIT); + //NEW_BIT (NEW_TEXT_BIT); pre++; break; case 22: /* /PRE */ - NEW_BIT (NEW_TEXT_BIT); + //NEW_BIT (NEW_TEXT_BIT); if (pre) pre--; break; case 23: /* TITLE */ - NEW_BIT (NEW_TEXT_BIT); + //NEW_BIT (NEW_TEXT_BIT); title++; break; case 24: /* /TITLE */ @@ -1260,28 +1321,49 @@ break; case 25: /* BR */ case 58: /* BR/ */ + case 61: /* BR (opt) */ ws[wpos] = '\n'; wpos++; - NEW_BIT (NEW_TEXT_BIT); + //NEW_BIT (NEW_TEXT_BIT); break; case 26: /* HR */ case 42: /* HR (opt) */ ws[wpos++] = '\n'; + if (url) + gtk_imhtml_insert_link(imhtml, url, ws); + else + gtk_text_buffer_insert(imhtml->text_buffer, &iter, ws, wpos); scalable = gtk_imhtml_hr_new(); - NEW_BIT(NEW_SCALABLE_BIT); + gtk_text_view_get_visible_rect(GTK_TEXT_VIEW(imhtml), &rect); + scalable->add_to(scalable, imhtml, &iter); + scalable->scale(scalable, rect.width, rect.height); + imhtml->scalables = g_list_append(imhtml->scalables, scalable); + ws[0] = '\0'; wpos = 0; ws[wpos++] = '\n'; + break; case 27: /* /FONT */ if (fonts) { GtkIMHtmlFontDetail *font = fonts->data; - NEW_BIT (NEW_TEXT_BIT); + if (url) + gtk_imhtml_insert_link(imhtml, url, ws); + else + gtk_text_buffer_insert(imhtml->text_buffer, &iter, ws, wpos); + ws[0] = '\0'; wpos = 0; + //NEW_BIT (NEW_TEXT_BIT); fonts = g_slist_remove (fonts, font); - if (font->face) + if (font->face) { + gtk_imhtml_toggle_fontface(imhtml, NULL); g_free (font->face); - if (font->fore) + } + if (font->fore) { + gtk_imhtml_toggle_forecolor(imhtml, NULL); g_free (font->fore); - if (font->back) + } + if (font->back) { + gtk_imhtml_toggle_backcolor(imhtml, NULL); g_free (font->back); + } if (font->sml) g_free (font->sml); g_free (font); @@ -1289,11 +1371,15 @@ break; case 28: /* /A */ if (url) { - NEW_BIT(NEW_TEXT_BIT); + gtk_imhtml_insert_link(imhtml, url, ws); g_free(url); + ws[0] = '\0'; wpos = 0; url = NULL; - break; + ins = gtk_text_buffer_get_insert(imhtml->text_buffer); + gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &iter, ins); } + break; + case 29: /* P */ case 30: /* /P */ case 31: /* H3 */ @@ -1319,9 +1405,13 @@ sml = gtk_imhtml_get_html_opt (tag, "SML="); if (!(color || back || face || size || sml)) break; - - NEW_BIT (NEW_TEXT_BIT); - + + if (url) + gtk_imhtml_insert_link(imhtml, url, ws); + else + gtk_text_buffer_insert(imhtml->text_buffer, &iter, ws, wpos); + ws[0] = '\0'; wpos = 0; + font = g_new0 (GtkIMHtmlFontDetail, 1); if (fonts) oldfont = fonts->data; @@ -1330,20 +1420,22 @@ font->fore = color; else if (oldfont && oldfont->fore) font->fore = g_strdup(oldfont->fore); + if (font->fore) + gtk_imhtml_toggle_forecolor(imhtml, font->fore); if (back && !(options & GTK_IMHTML_NO_COLOURS)) font->back = back; else if (oldfont && oldfont->back) font->back = g_strdup(oldfont->back); - + if (font->back) + gtk_imhtml_toggle_backcolor(imhtml, font->back); + if (face && !(options & GTK_IMHTML_NO_FONTS)) font->face = face; else if (oldfont && oldfont->face) font->face = g_strdup(oldfont->face); - if (font->face && (atoi(font->face) > 100)) { - g_free(font->face); - font->face = g_strdup("100"); - } + if (font->face) + gtk_imhtml_toggle_fontface(imhtml, font->face); if (sml) font->sml = sml; @@ -1359,11 +1451,12 @@ font->size = MAX (0, 3 - font->size); } else if (isdigit (*size)) { sscanf (size, "%hd", &font->size); - } + } if (font->size > 100) font->size = 100; } else if (oldfont) font->size = oldfont->size; + // gtk_imhtml_font_set_size(imhtml, font->size); g_free(size); fonts = g_slist_prepend (fonts, font); } @@ -1372,10 +1465,16 @@ if (!(options & GTK_IMHTML_NO_COLOURS)) { char *bgcolor = gtk_imhtml_get_html_opt (tag, "BGCOLOR="); if (bgcolor) { - NEW_BIT(NEW_TEXT_BIT); + if (url) + gtk_imhtml_insert_link(imhtml, url, ws); + else + gtk_text_buffer_insert(imhtml->text_buffer, &iter, ws, wpos); + ws[0] = '\0'; wpos = 0; + // NEW_BIT(NEW_TEXT_BIT); if (bg) g_free(bg); bg = bgcolor; + gtk_imhtml_toggle_backcolor(imhtml, bg); } } break; @@ -1383,9 +1482,12 @@ { gchar *href = gtk_imhtml_get_html_opt (tag, "HREF="); if (href) { - NEW_BIT (NEW_TEXT_BIT); - if (url) - g_free (url); + if (url) { + gtk_imhtml_insert_link(imhtml, url, ws); + g_free(url); + } else + gtk_text_buffer_insert(imhtml->text_buffer, &iter, ws, wpos); + ws[0] = '\0'; wpos = 0; url = href; } } @@ -1408,7 +1510,7 @@ } scalable = gtk_imhtml_image_new(img, filename); - NEW_BIT(NEW_SCALABLE_BIT); + //NEW_BIT(NEW_SCALABLE_BIT); g_object_unref(G_OBJECT(img)); } case 47: /* P (opt) */ @@ -1420,11 +1522,11 @@ case 57: /* /SPAN */ case 60: /* SPAN */ break; - case 61: /* comment */ - NEW_BIT (NEW_TEXT_BIT); + case 62: /* comment */ + //NEW_BIT (NEW_TEXT_BIT); if (imhtml->show_comments) wpos = g_snprintf (ws, len, "%s", tag); - NEW_BIT (NEW_COMMENT_BIT); + // NEW_BIT (NEW_COMMENT_BIT); break; default: break; @@ -1443,79 +1545,60 @@ if (!(options & GTK_IMHTML_NO_NEWLINE)) { ws[wpos] = '\n'; wpos++; - NEW_BIT (NEW_TEXT_BIT); + if (url) + gtk_imhtml_insert_link(imhtml, url, ws); + else + gtk_text_buffer_insert(imhtml->text_buffer, &iter, ws, wpos); + ws[0] = '\0'; + wpos = 0; + //NEW_BIT (NEW_TEXT_BIT); } c++; pos++; } else if (imhtml->show_smileys && (gtk_imhtml_is_smiley (imhtml, fonts, c, &smilelen) || gtk_imhtml_is_smiley(imhtml, NULL, c, &smilelen))) { - GtkTextChildAnchor *anchor; - GtkWidget *icon = NULL; - GtkTextIter copy; - GdkPixbufAnimation *annipixbuf = NULL; - GdkPixbuf *pixbuf = NULL; GtkIMHtmlFontDetail *fd; - + gchar *sml = NULL; if (fonts) { fd = fonts->data; sml = fd->sml; } - NEW_BIT (NEW_TEXT_BIT); + if (url) + gtk_imhtml_insert_link(imhtml, url, ws); + else { + printf("Inserting %s\n", ws); + gtk_text_buffer_insert(imhtml->text_buffer, &iter, ws, wpos); + } wpos = g_snprintf (ws, smilelen + 1, "%s", c); - anchor = gtk_text_buffer_create_child_anchor(imhtml->text_buffer, &iter); - annipixbuf = gtk_smiley_tree_image(imhtml, sml, ws); - if(annipixbuf) { - if(gdk_pixbuf_animation_is_static_image(annipixbuf)) { - pixbuf = gdk_pixbuf_animation_get_static_image(annipixbuf); - if(pixbuf) - icon = gtk_image_new_from_pixbuf(pixbuf); - } else { - icon = gtk_image_new_from_animation(annipixbuf); - } - } + gtk_imhtml_insert_smiley(imhtml, sml, ws); - if (icon) { - gtk_widget_show(icon); - gtk_text_view_add_child_at_anchor(GTK_TEXT_VIEW(imhtml), icon, anchor); -#if GTK_CHECK_VERSION(2,2,0) - gtk_imhtml_copyable_new(imhtml, - gtk_text_buffer_create_mark(imhtml->text_buffer, NULL, &iter, TRUE), - ws); -#endif - } - - copy = iter; - gtk_text_iter_backward_char(©); - if (bg) { - texttag = gtk_text_buffer_create_tag(imhtml->text_buffer, NULL, "background", bg, NULL); - gtk_text_buffer_apply_tag(imhtml->text_buffer, texttag, &iter, ©); - } - if (fonts) { - GtkIMHtmlFontDetail *fd = fonts->data; - if (fd->back) { - texttag = gtk_text_buffer_create_tag(imhtml->text_buffer, NULL, "background", fd->back, NULL); - gtk_text_buffer_apply_tag(imhtml->text_buffer, texttag, &iter, ©); - } - } + ins = gtk_text_buffer_get_insert(imhtml->text_buffer); + gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &iter, ins); + c += smilelen; pos += smilelen; wpos = 0; ws[0] = 0; - } else if (*c) { + } else if (*c) { ws [wpos++] = *c++; pos++; } else { break; } } - - NEW_BIT(NEW_TEXT_BIT); + if (url) + gtk_imhtml_insert_link(imhtml, url, ws); + else + gtk_text_buffer_insert(imhtml->text_buffer, &iter, ws, wpos); + ws[0] = '\0'; wpos = 0; + + //NEW_BIT(NEW_TEXT_BIT); if (url) { g_free (url); if (str) str = g_string_append (str, ""); } - + while (fonts) { GtkIMHtmlFontDetail *font = fonts->data; fonts = g_slist_remove (fonts, font); @@ -1569,10 +1652,14 @@ g_free (ws); if(bg) g_free(bg); + gtk_imhtml_close_tags(imhtml); if (!(options & GTK_IMHTML_NO_SCROLL)) gtk_text_view_scroll_to_mark (GTK_TEXT_VIEW (imhtml), mark, 0, TRUE, 0.0, 1.0); gtk_text_buffer_delete_mark (imhtml->text_buffer, mark); + gtk_text_buffer_move_mark (imhtml->text_buffer, + gtk_text_buffer_get_mark (imhtml->text_buffer, "insert"), + &iter); return str; } @@ -1606,12 +1693,32 @@ gtk_text_buffer_get_end_iter(imhtml->text_buffer, &end); gtk_text_buffer_delete(imhtml->text_buffer, &start, &end); + for(del = imhtml->format_spans; del; del = del->next) { + GtkIMHtmlFormatSpan *span = del->data; + if (span->start_tag) + g_free(span->start_tag); + if (span->end_tag) + g_free(span->end_tag); + g_free(span); + } + g_list_free(imhtml->format_spans); + imhtml->format_spans = NULL; + for(del = imhtml->scalables; del; del = del->next) { GtkIMHtmlScalable *scale = del->data; scale->free(scale); } g_list_free(imhtml->scalables); imhtml->scalables = NULL; + + imhtml->edit.bold = NULL; + imhtml->edit.italic = NULL; + imhtml->edit.underline = NULL; + imhtml->edit.fontface = NULL; + imhtml->edit.forecolor = NULL; + imhtml->edit.backcolor = NULL; + imhtml->edit.sizespan = NULL; + imhtml->edit.fontsize = 3; } void gtk_imhtml_page_up (GtkIMHtml *imhtml) @@ -1623,7 +1730,7 @@ gtk_text_view_get_iter_at_location(GTK_TEXT_VIEW(imhtml), &iter, rect.x, rect.y - rect.height); gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(imhtml), &iter, 0, TRUE, 0, 0); - + } void gtk_imhtml_page_down (GtkIMHtml *imhtml) { @@ -1781,7 +1888,7 @@ g_signal_connect_swapped(G_OBJECT(GTK_FILE_SELECTION(sel)->ok_button), "clicked", G_CALLBACK(gtk_widget_destroy), sel); g_signal_connect_swapped(G_OBJECT(GTK_FILE_SELECTION(sel)->cancel_button), "clicked", - G_CALLBACK(gtk_widget_destroy), sel); + G_CALLBACK(gtk_widget_destroy), sel); gtk_widget_show(sel); } @@ -1867,7 +1974,7 @@ { GtkIMHtmlHr *hr = (GtkIMHtmlHr *)scale; GtkTextChildAnchor *anchor = gtk_text_buffer_create_child_anchor(imhtml->text_buffer, iter); - + g_object_set_data(G_OBJECT(anchor), "text_tag", "
"); gtk_text_view_add_child_at_anchor(GTK_TEXT_VIEW(imhtml), hr->sep, anchor); } @@ -1883,17 +1990,16 @@ g_return_val_if_fail(imhtml != NULL, FALSE); g_return_val_if_fail(text != NULL, FALSE); - + if (imhtml->search_string && !strcmp(text, imhtml->search_string)) new_search = FALSE; - - + if (new_search) { gtk_imhtml_search_clear(imhtml); gtk_text_buffer_get_start_iter(imhtml->text_buffer, &iter); } else { gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &iter, - gtk_text_buffer_get_mark(imhtml->text_buffer, "search")); + gtk_text_buffer_get_mark(imhtml->text_buffer, "search")); } imhtml->search_string = g_strdup(text); @@ -1905,24 +2011,27 @@ gtk_text_buffer_create_mark(imhtml->text_buffer, "search", &end, FALSE); if (new_search) { gtk_text_buffer_remove_tag_by_name(imhtml->text_buffer, "search", &iter, &end); - do + do gtk_text_buffer_apply_tag_by_name(imhtml->text_buffer, "search", &start, &end); - while (gtk_source_iter_forward_search(&end, imhtml->search_string, - GTK_SOURCE_SEARCH_VISIBLE_ONLY | + while (gtk_source_iter_forward_search(&end, imhtml->search_string, + GTK_SOURCE_SEARCH_VISIBLE_ONLY | GTK_SOURCE_SEARCH_CASE_INSENSITIVE, &start, &end, NULL)); } return TRUE; } + + gtk_imhtml_search_clear(imhtml); + return FALSE; } void gtk_imhtml_search_clear(GtkIMHtml *imhtml) { GtkTextIter start, end; - + g_return_if_fail(imhtml != NULL); - + gtk_text_buffer_get_start_iter(imhtml->text_buffer, &start); gtk_text_buffer_get_end_iter(imhtml->text_buffer, &end); @@ -1931,3 +2040,546 @@ g_free(imhtml->search_string); imhtml->search_string = NULL; } + +/* Editable stuff */ +static void insert_cb(GtkTextBuffer *buffer, GtkTextIter *iter, gchar *text, gint len, GtkIMHtml *imhtml) +{ + GtkIMHtmlFormatSpan *span = NULL; + GtkTextIter end; + + gtk_text_iter_forward_chars(iter, len); + gtk_text_buffer_get_end_iter(imhtml->text_buffer, &end); + gtk_text_iter_forward_char(&end); + + if (!gtk_text_iter_equal(&end, iter)) + return; + + + if ((span = imhtml->edit.bold)) { + GtkTextIter bold; + gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &bold, span->start); + gtk_text_buffer_apply_tag_by_name(imhtml->text_buffer, "BOLD", &bold, iter); + } + + if ((span = imhtml->edit.italic)) { + GtkTextIter italic; + gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &italic, span->start); + gtk_text_buffer_apply_tag_by_name(imhtml->text_buffer, "ITALICS", &italic, + iter); + } + + if ((span = imhtml->edit.underline)) { + GtkTextIter underline; + gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &underline, span->start); + gtk_text_buffer_apply_tag_by_name(imhtml->text_buffer, "UNDERLINE", &underline, + iter); + } + + if ((span = imhtml->edit.forecolor)) { + GtkTextIter fore; + gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &fore, span->start); + gtk_text_buffer_apply_tag(imhtml->text_buffer, span->tag, &fore, iter); + } + + if ((span = imhtml->edit.backcolor)) { + GtkTextIter back; + gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &back, span->start); + gtk_text_buffer_apply_tag(imhtml->text_buffer, span->tag, &back, iter); + } + + if ((span = imhtml->edit.fontface)) { + GtkTextIter face; + gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &face, span->start); + gtk_text_buffer_apply_tag(imhtml->text_buffer, span->tag, &face, iter); + } + + if ((span = imhtml->edit.sizespan)) { + GtkTextIter size; + /* We create the tags here so that one can grow font or shrink font several times + * in a row without creating unnecessary tags */ + if (span->tag == NULL) { + span->tag = gtk_text_buffer_create_tag + (imhtml->text_buffer, NULL, "size-points", (double)_point_sizes [imhtml->edit.fontsize-1], NULL); + span->start_tag = g_strdup_printf("", imhtml->edit.fontsize); + span->end_tag = g_strdup(""); + } + gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &size, span->start); + gtk_text_buffer_apply_tag(imhtml->text_buffer, span->tag, &size, iter); + } +} + +void gtk_imhtml_set_editable(GtkIMHtml *imhtml, gboolean editable) +{ + gtk_text_view_set_editable(GTK_TEXT_VIEW(imhtml), editable); + gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(imhtml), editable); + imhtml->editable = editable; +} + +gboolean gtk_imhtml_get_editable(GtkIMHtml *imhtml) +{ + return imhtml->editable; +} + +gboolean gtk_imhtml_toggle_bold(GtkIMHtml *imhtml) +{ + GtkIMHtmlFormatSpan *span; + GtkTextMark *ins = gtk_text_buffer_get_insert(imhtml->text_buffer); + GtkTextIter iter; + gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &iter, ins); + if (!imhtml->edit.bold) { + span = g_malloc(sizeof(GtkIMHtmlFormatSpan)); + span->start = gtk_text_buffer_create_mark(imhtml->text_buffer, NULL, &iter, TRUE); + span->start_tag = g_strdup(""); + span->end = NULL; + span->end_tag = g_strdup(""); + span->buffer = imhtml->text_buffer; + span->tag = gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(imhtml->text_buffer), "BOLD"); + imhtml->edit.bold = span; + imhtml->format_spans = g_list_append(imhtml->format_spans, span); + } else { + span = imhtml->edit.bold; + span->end = gtk_text_buffer_create_mark(imhtml->text_buffer, NULL, &iter, TRUE); + imhtml->edit.bold = NULL; + } + return imhtml->edit.bold != NULL; +} + +gboolean gtk_imhtml_toggle_italic(GtkIMHtml *imhtml) +{ + GtkIMHtmlFormatSpan *span; + GtkTextMark *ins = gtk_text_buffer_get_insert(imhtml->text_buffer); + GtkTextIter iter; + gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &iter, ins); + if (!imhtml->edit.italic) { + span = g_malloc(sizeof(GtkIMHtmlFormatSpan)); + span->start = gtk_text_buffer_create_mark(imhtml->text_buffer, NULL, &iter, TRUE); + span->start_tag = g_strdup(""); + span->end = NULL; + span->end_tag = g_strdup(""); + span->buffer = imhtml->text_buffer; + span->tag = gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(imhtml->text_buffer), "ITALIC"); + imhtml->edit.italic = span; + imhtml->format_spans = g_list_append(imhtml->format_spans, span); + } else { + span = imhtml->edit.italic; + span->end = gtk_text_buffer_create_mark(imhtml->text_buffer, NULL, &iter, TRUE); + imhtml->edit.italic = NULL; + } + return imhtml->edit.italic != NULL; +} + +gboolean gtk_imhtml_toggle_underline(GtkIMHtml *imhtml) +{ + GtkIMHtmlFormatSpan *span; + GtkTextMark *ins = gtk_text_buffer_get_insert(imhtml->text_buffer); + GtkTextIter iter; + gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &iter, ins); + if (!imhtml->edit.underline) { + span = g_malloc(sizeof(GtkIMHtmlFormatSpan)); + span->start = gtk_text_buffer_create_mark(imhtml->text_buffer, NULL, &iter, TRUE); + span->start_tag = g_strdup(""); + span->end = NULL; + span->end_tag = g_strdup(""); + span->buffer = imhtml->text_buffer; + span->tag = gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(imhtml->text_buffer), "UNDERLINE"); + imhtml->edit.underline = span; + imhtml->format_spans = g_list_append(imhtml->format_spans, span); + } else { + span = imhtml->edit.underline; + span->end = gtk_text_buffer_create_mark(imhtml->text_buffer, NULL, &iter, TRUE); + imhtml->edit.underline = NULL; + } + return imhtml->edit.underline != NULL; +} + +void gtk_imhtml_font_set_size(GtkIMHtml *imhtml, gint size) +{ + GtkIMHtmlFormatSpan *span; + GtkTextMark *ins = gtk_text_buffer_get_insert(imhtml->text_buffer); + GtkTextIter iter; + gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &iter, ins); + + imhtml->edit.fontsize = size; + + if (imhtml->edit.sizespan) { + GtkTextIter iter2; + gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &iter2, imhtml->edit.sizespan->start); + if (gtk_text_iter_equal(&iter2, &iter)) + return; + span = imhtml->edit.sizespan; + span->end = gtk_text_buffer_create_mark(imhtml->text_buffer, NULL, &iter, TRUE); + } + if (size != -1) { + span = g_malloc(sizeof(GtkIMHtmlFormatSpan)); + span->start = gtk_text_buffer_create_mark(imhtml->text_buffer, NULL, &iter, TRUE); + span->end = NULL; + span->buffer = imhtml->text_buffer; + span->tag = NULL; + imhtml->edit.sizespan = span; + imhtml->format_spans = g_list_append(imhtml->format_spans, span); + } +} + +void gtk_imhtml_font_shrink(GtkIMHtml *imhtml) +{ + GtkIMHtmlFormatSpan *span; + GtkTextMark *ins = gtk_text_buffer_get_insert(imhtml->text_buffer); + GtkTextIter iter; + gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &iter, ins); + if (imhtml->edit.fontsize == 1) + return; + + imhtml->edit.fontsize--; + + if (imhtml->edit.sizespan) { + GtkTextIter iter2; + gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &iter2, imhtml->edit.sizespan->start); + if (gtk_text_iter_equal(&iter2, &iter)) + return; + span = imhtml->edit.sizespan; + span->end = gtk_text_buffer_create_mark(imhtml->text_buffer, NULL, &iter, TRUE); + } + + span = g_malloc(sizeof(GtkIMHtmlFormatSpan)); + span->start = gtk_text_buffer_create_mark(imhtml->text_buffer, NULL, &iter, TRUE); + span->end = NULL; + span->buffer = imhtml->text_buffer; + span->tag = NULL; + imhtml->edit.sizespan = span; + imhtml->format_spans = g_list_append(imhtml->format_spans, span); +} + +void gtk_imhtml_font_grow(GtkIMHtml *imhtml) +{ + GtkIMHtmlFormatSpan *span; + GtkTextMark *ins = gtk_text_buffer_get_insert(imhtml->text_buffer); + GtkTextIter iter; + gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &iter, ins); + if (imhtml->edit.fontsize == MAX_FONT_SIZE) + return; + + imhtml->edit.fontsize++; + + if (imhtml->edit.sizespan) { + GtkTextIter iter2; + gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &iter2, imhtml->edit.sizespan->start); + if (gtk_text_iter_equal(&iter2, &iter)) + return; + span = imhtml->edit.sizespan; + span->end = gtk_text_buffer_create_mark(imhtml->text_buffer, NULL, &iter, TRUE); + } + + span = g_malloc(sizeof(GtkIMHtmlFormatSpan)); + span->start = gtk_text_buffer_create_mark(imhtml->text_buffer, NULL, &iter, TRUE); + span->end = NULL; + span->tag = NULL; + span->buffer = imhtml->text_buffer; + imhtml->edit.sizespan = span; + imhtml->format_spans = g_list_append(imhtml->format_spans, span); +} + +gboolean gtk_imhtml_toggle_forecolor(GtkIMHtml *imhtml, const char *color) +{ + GtkIMHtmlFormatSpan *span; + GtkTextMark *ins = gtk_text_buffer_get_insert(imhtml->text_buffer); + GtkTextIter iter; + gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &iter, ins); + if (!imhtml->edit.forecolor) { + span = g_malloc(sizeof(GtkIMHtmlFormatSpan)); + span->start = gtk_text_buffer_create_mark(imhtml->text_buffer, NULL, &iter, TRUE); + span->start_tag = g_strdup_printf("", color); + span->end = NULL; + span->end_tag = g_strdup(""); + span->buffer = imhtml->text_buffer; + span->tag = gtk_text_buffer_create_tag(imhtml->text_buffer, NULL, "foreground", color, NULL); + imhtml->edit.forecolor = span; + imhtml->format_spans = g_list_append(imhtml->format_spans, span); + } else { + span = imhtml->edit.forecolor; + span->end = gtk_text_buffer_create_mark(imhtml->text_buffer, NULL, &iter, TRUE); + imhtml->edit.forecolor = NULL; + } + + + return imhtml->edit.forecolor != NULL; +} + +gboolean gtk_imhtml_toggle_backcolor(GtkIMHtml *imhtml, const char *color) +{ + GtkIMHtmlFormatSpan *span; + GtkTextMark *ins = gtk_text_buffer_get_insert(imhtml->text_buffer); + GtkTextIter iter; + gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &iter, ins); + if (!imhtml->edit.backcolor) { + span = g_malloc(sizeof(GtkIMHtmlFormatSpan)); + span->start = gtk_text_buffer_create_mark(imhtml->text_buffer, NULL, &iter, TRUE); + span->start_tag = g_strdup_printf("", color); + span->end = NULL; + span->end_tag = g_strdup(""); + span->buffer = imhtml->text_buffer; + span->tag = gtk_text_buffer_create_tag(imhtml->text_buffer, NULL, "background", color, NULL); + imhtml->edit.backcolor = span; + imhtml->format_spans = g_list_append(imhtml->format_spans, span); + } else { + span = imhtml->edit.backcolor; + span->end = gtk_text_buffer_create_mark(imhtml->text_buffer, NULL, &iter, TRUE); + imhtml->edit.backcolor = NULL; + } + return imhtml->edit.backcolor != NULL; +} + +gboolean gtk_imhtml_toggle_fontface(GtkIMHtml *imhtml, const char *face) +{ + GtkIMHtmlFormatSpan *span; + GtkTextMark *ins = gtk_text_buffer_get_insert(imhtml->text_buffer); + GtkTextIter iter; + gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &iter, ins); + if (!imhtml->edit.fontface) { + span = g_malloc(sizeof(GtkIMHtmlFormatSpan)); + span->start = gtk_text_buffer_create_mark(imhtml->text_buffer, NULL, &iter, TRUE); + span->start_tag = g_strdup_printf("", face); + span->end = NULL; + span->end_tag = g_strdup(""); + span->buffer = imhtml->text_buffer; + span->tag = gtk_text_buffer_create_tag(imhtml->text_buffer, NULL, "family", face, NULL); + imhtml->edit.fontface = span; + imhtml->format_spans = g_list_append(imhtml->format_spans, span); + } else { + span = imhtml->edit.fontface; + span->end = gtk_text_buffer_create_mark(imhtml->text_buffer, NULL, &iter, TRUE); + imhtml->edit.fontface = NULL; + } + return imhtml->edit.fontface != NULL; +} + +void gtk_imhtml_insert_link(GtkIMHtml *imhtml, const char *url, const char *text) +{ + GtkIMHtmlFormatSpan *span = g_malloc(sizeof(GtkIMHtmlFormatSpan)); + GtkTextMark *mark = gtk_text_buffer_get_insert(imhtml->text_buffer); + GtkTextIter iter; + GtkTextTag *tag, *linktag; + + tag = gtk_text_buffer_create_tag(imhtml->text_buffer, NULL, NULL); + g_object_set_data(G_OBJECT(tag), "link_url", g_strdup(url)); + + linktag = gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(imhtml->text_buffer), "LINK"); + + gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &iter, mark); + span->start = gtk_text_buffer_create_mark(imhtml->text_buffer, NULL, &iter, TRUE); + span->buffer = imhtml->text_buffer; + span->start_tag = g_strdup_printf("", url); + span->end_tag = g_strdup(""); + g_signal_connect(G_OBJECT(tag), "event", G_CALLBACK(tag_event), g_strdup(url)); + + gtk_text_buffer_insert_with_tags(imhtml->text_buffer, &iter, text, strlen(text), linktag, tag, NULL); + span->end = gtk_text_buffer_create_mark(imhtml->text_buffer, NULL, &iter, TRUE); + imhtml->format_spans = g_list_append(imhtml->format_spans, span); +} + +void gtk_imhtml_insert_smiley(GtkIMHtml *imhtml, const char *sml, char *smiley) +{ + GtkTextMark *ins = gtk_text_buffer_get_insert(imhtml->text_buffer); + GtkTextIter iter; + GdkPixbuf *pixbuf = NULL; + GdkPixbufAnimation *annipixbuf = NULL; + GtkWidget *icon = NULL; + GtkTextChildAnchor *anchor; + + gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &iter, ins); + anchor = gtk_text_buffer_create_child_anchor(imhtml->text_buffer, &iter); + g_object_set_data(G_OBJECT(anchor), "text_tag", g_strdup(smiley)); + + annipixbuf = gtk_smiley_tree_image(imhtml, sml, smiley); + if(annipixbuf) { + if(gdk_pixbuf_animation_is_static_image(annipixbuf)) { + pixbuf = gdk_pixbuf_animation_get_static_image(annipixbuf); + if(pixbuf) + icon = gtk_image_new_from_pixbuf(pixbuf); + } else { + icon = gtk_image_new_from_animation(annipixbuf); + } + } + + if (icon) { + gtk_widget_show(icon); + gtk_text_view_add_child_at_anchor(GTK_TEXT_VIEW(imhtml), icon, anchor); + } +} + +int span_compare_begin(const GtkIMHtmlFormatSpan *a, const GtkIMHtmlFormatSpan *b, GtkTextBuffer *buffer) +{ + GtkTextIter ia, ib; + gtk_text_buffer_get_iter_at_mark(buffer, &ia, a->start); + gtk_text_buffer_get_iter_at_mark(buffer, &ib, b->start); + return gtk_text_iter_compare(&ia, &ib); +} + +int span_compare_end(GtkIMHtmlFormatSpan *a, GtkIMHtmlFormatSpan *b) +{ + GtkTextIter ia, ib; + gtk_text_buffer_get_iter_at_mark(a->buffer, &ia, a->start); + gtk_text_buffer_get_iter_at_mark(b->buffer, &ib, b->start); + /* The -1 here makes it so that if I have two spans that close at the same point, the + * span added second will be closed first, as in Hello. Without this, + * it would be Hello */ + return gtk_text_iter_compare(&ia, &ib) - 1; +} + +/* Basic notion here: traverse through the text buffer one-by-one, non-character elements, such + * as smileys and IM images are represented by the Unicode "unknown" character. Handle them. Else + * check the list of formatted strings, sorted by the position of the starting tags and apply them as + * needed. After applying the start tags, add the end tags to the "closers" list, which is sorted by + * location of ending tags. These get applied in a similar fashion. Finally, replace <, >, &, and " + * with their HTML equivilent. */ +char *gtk_imhtml_get_markup_range(GtkIMHtml *imhtml, GtkTextIter *start, GtkTextIter *end) +{ + gunichar c; + GtkIMHtmlFormatSpan *sspan = NULL, *espan = NULL; + GtkTextIter iter, siter, eiter; + GList *starters = imhtml->format_spans; + GList *closers = NULL; + GString *str = g_string_new(""); + g_list_sort_with_data(starters, (GCompareDataFunc)span_compare_begin, imhtml->text_buffer); + + gtk_text_iter_order(start, end); + iter = *start; + + + /* Initialize these to the end iter */ + gtk_text_buffer_get_end_iter(imhtml->text_buffer, &siter); + eiter = siter; + + if (starters) { + while (starters) { + GtkTextIter tagend; + sspan = (GtkIMHtmlFormatSpan*)starters->data; + gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &siter, sspan->start); + if (gtk_text_iter_compare(&siter, start) > 0) + break; + if (sspan->end) + gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &tagend, sspan->end); + if (sspan->end == NULL || gtk_text_iter_compare(&tagend, start) > 0) { + str = g_string_append(str, sspan->start_tag); + closers = g_list_insert_sorted(closers, sspan, (GCompareFunc)span_compare_end); + espan = (GtkIMHtmlFormatSpan*)closers->data; + gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &eiter, espan->end); + } + sspan = (GtkIMHtmlFormatSpan*)starters->data; + gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &siter, sspan->start); + starters = starters->next; + } + if (!starters) { + gtk_text_buffer_get_end_iter(imhtml->text_buffer, &siter); + sspan = NULL; + } + } + + while ((c = gtk_text_iter_get_char(&iter)) != 0 && !gtk_text_iter_equal(&iter, end)) { + if (c == 0xFFFC) { + GtkTextChildAnchor* anchor = gtk_text_iter_get_child_anchor(&iter); + char *text = g_object_get_data(G_OBJECT(anchor), "text_tag"); + str = g_string_append(str, text); + } else { + while (gtk_text_iter_equal(&eiter, &iter)) { + /* This is where we shall insert the ending tag of + * this format span */ + str = g_string_append(str, espan->end_tag); + closers = g_list_remove(closers, espan); + if (!closers) { + espan = NULL; + gtk_text_buffer_get_end_iter(imhtml->text_buffer, &eiter); + } else { + espan = (GtkIMHtmlFormatSpan*)closers->data; + gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &eiter, espan->end); + } + } + while (gtk_text_iter_equal(&siter, &iter)) { + /* This is where we shall insert the starting tag of + * this format span */ + str = g_string_append(str, sspan->start_tag); + if (sspan->end) { + closers = g_list_insert_sorted(closers, sspan, (GCompareFunc)span_compare_end); + espan = (GtkIMHtmlFormatSpan*)closers->data; + gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &eiter, espan->end); + + } + starters = starters->next; + if (starters) { + sspan = (GtkIMHtmlFormatSpan*)starters->data; + gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &siter, sspan->start); + } else { + sspan = NULL; + gtk_text_buffer_get_end_iter(imhtml->text_buffer, &siter); + } + + } + + if (c == '<') + str = g_string_append(str, "<"); + else if (c == '>') + str = g_string_append(str, ">"); + else if (c == '&') + str = g_string_append(str, "&"); + else if (c == '"') + str = g_string_append(str, """); + else if (c == '\n') + str = g_string_append(str, "
"); + else + str = g_string_append_unichar(str, c); + } + gtk_text_iter_forward_char(&iter); + } + while (closers) { + GtkIMHtmlFormatSpan *span = (GtkIMHtmlFormatSpan*)closers->data; + str = g_string_append(str, span->end_tag); + closers = g_list_remove(closers, span); + + } + printf("Gotten: %s\n", str->str); + return g_string_free(str, FALSE); +} + +void gtk_imhtml_close_tags(GtkIMHtml *imhtml) +{ + + if (imhtml->edit.bold) + gtk_imhtml_toggle_bold(imhtml); + + if (imhtml->edit.italic) + gtk_imhtml_toggle_italic(imhtml); + + if (imhtml->edit.underline) + gtk_imhtml_toggle_underline(imhtml); + + if (imhtml->edit.forecolor) + gtk_imhtml_toggle_forecolor(imhtml, NULL); + + if (imhtml->edit.backcolor) + gtk_imhtml_toggle_backcolor(imhtml, NULL); + + if (imhtml->edit.fontface) + gtk_imhtml_toggle_fontface(imhtml, NULL); + + if (imhtml->edit.sizespan) + gtk_imhtml_font_set_size(imhtml, -1); + +} + +char *gtk_imhtml_get_markup(GtkIMHtml *imhtml) +{ + GtkTextIter start, end; + + gtk_text_buffer_get_start_iter(imhtml->text_buffer, &start); + gtk_text_buffer_get_end_iter(imhtml->text_buffer, &end); + return gtk_imhtml_get_markup_range(imhtml, &start, &end); +} + +char *gtk_imhtml_get_text(GtkIMHtml *imhtml) +{ + GtkTextIter start_iter, end_iter; + gtk_text_buffer_get_start_iter(imhtml->text_buffer, &start_iter); + gtk_text_buffer_get_end_iter(imhtml->text_buffer, &end_iter); + return gtk_text_buffer_get_text(imhtml->text_buffer, &start_iter, &end_iter, FALSE); + +} + diff -r b66733e6e6f2 -r ea073d234191 src/gtkimhtml.h --- a/src/gtkimhtml.h Sat Jan 10 05:20:50 2004 +0000 +++ b/src/gtkimhtml.h Sat Jan 10 06:06:02 2004 +0000 @@ -39,7 +39,6 @@ #define GTK_IS_IMHTML(obj) (GTK_CHECK_TYPE ((obj), GTK_TYPE_IMHTML)) #define GTK_IS_IMHTML_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GTK_TYPE_IMHTML)) #define GTK_IMHTML_SCALABLE(obj) ((GtkIMHtmlScalable *)obj) -#define GTK_IMHTML_COPYABLE(obj) ((GtkIMHtmlCopyable *)obj) typedef struct _GtkIMHtml GtkIMHtml; typedef struct _GtkIMHtmlClass GtkIMHtmlClass; @@ -49,7 +48,16 @@ typedef struct _GtkIMHtmlScalable GtkIMHtmlScalable; typedef struct _GtkIMHtmlImage GtkIMHtmlImage; typedef struct _GtkIMHtmlHr GtkIMHtmlHr; -typedef struct _GtkIMHtmlCopyable GtkIMHtmlCopyable; + + +typedef struct { + GtkTextMark *start; + GtkTextMark *end; + char *start_tag; + char *end_tag; + GtkTextBuffer *buffer; + GtkTextTag *tag; +} GtkIMHtmlFormatSpan; struct _GtkIMHtml { GtkTextView text_view; @@ -58,6 +66,7 @@ gboolean comments, smileys; GdkCursor *hand_cursor; GdkCursor *arrow_cursor; + GdkCursor *text_cursor; GHashTable *smiley_data; GtkSmileyTree *default_smilies; @@ -71,9 +80,21 @@ GList *scalables; GdkRectangle old_rect; - GSList *copyables; + gchar *search_string; - gchar *search_string; + gboolean editable; + struct { + GtkIMHtmlFormatSpan *bold; + GtkIMHtmlFormatSpan *italic; + GtkIMHtmlFormatSpan *underline; + GtkIMHtmlFormatSpan *forecolor; + GtkIMHtmlFormatSpan *backcolor; + GtkIMHtmlFormatSpan *fontface; + GtkIMHtmlFormatSpan *sizespan; + int fontsize; + } edit; + char *clipboard_string; + GList *format_spans; }; struct _GtkIMHtmlClass { @@ -109,11 +130,6 @@ void (*free)(struct _GtkIMHtmlScalable *); }; -struct _GtkIMHtmlCopyable { - GtkTextMark *mark; - char *text; -}; - struct _GtkIMHtmlImage { GtkIMHtmlScalable scalable; GtkImage *image; @@ -184,6 +200,24 @@ gboolean gtk_imhtml_search_find(GtkIMHtml *imhtml, const gchar *text); void gtk_imhtml_search_clear(GtkIMHtml *imhtml); +/* Editable stuff */ +void gtk_imhtml_set_editable(GtkIMHtml *imhtml, gboolean editable); +gboolean gtk_imhtml_get_editable(GtkIMHtml *imhtml); +gboolean gtk_imhtml_toggle_bold(GtkIMHtml *imhtml); +gboolean gtk_imhtml_toggle_italic(GtkIMHtml *imhtml); +gboolean gtk_imhtml_toggle_underline(GtkIMHtml *imhtml); +gboolean gtk_imhtml_toggle_forecolor(GtkIMHtml *imhtml, const char *color); +gboolean gtk_imhtml_toggle_backcolor(GtkIMHtml *imhtml, const char *color); +gboolean gtk_imhtml_toggle_fontface(GtkIMHtml *imhtml, const char *face); +void gtk_imhtml_insert_link(GtkIMHtml *imhtml, const char *url, const char *text); +void gtk_imhtml_insert_smiley(GtkIMHtml *imhtml, const char *sml, char *smiley); +void gtk_imhtml_font_set_size(GtkIMHtml *imhtml, gint size); +void gtk_imhtml_font_shrink(GtkIMHtml *imhtml); +void gtk_imhtml_font_grow(GtkIMHtml *imhtml); +char *gtk_imhtml_get_markup_range(GtkIMHtml *imhtml, GtkTextIter *start, GtkTextIter *end); +char *gtk_imhtml_get_markup(GtkIMHtml *imhtml); +char *gtk_imhtml_get_text(GtkIMHtml *imhtml); + #ifdef __cplusplus } #endif diff -r b66733e6e6f2 -r ea073d234191 src/gtkutils.c --- a/src/gtkutils.c Sat Jan 10 05:20:50 2004 +0000 +++ b/src/gtkutils.c Sat Jan 10 06:06:02 2004 +0000 @@ -242,8 +242,6 @@ gaim_gtk_set_font_face(GaimGtkConversation *gtkconv, const char *font) { - char *pre_fontface; - if (gtkconv == NULL || font == NULL) return; @@ -253,12 +251,10 @@ gtkconv->has_font = TRUE; - pre_fontface = g_strconcat("fontface, "\">", NULL); - gaim_gtk_surround(gtkconv, pre_fontface, ""); + gtk_imhtml_toggle_fontface(GTK_IMHTML(gtkconv->entry), gtkconv->fontface); gtk_widget_grab_focus(gtkconv->entry); - g_free(pre_fontface); } static int