# HG changeset patch # User Sean Egan # Date 1070350422 0 # Node ID 6a9acef3b867d3dc3cf55f65af23dd3f53175696 # Parent 1d0314b997471814147a376fd2f26a66b74b4d24 [gaim-migrate @ 8339] Committing this now so that I don't accidentally destroy it again. We're going WYSIWYG, folks. This is the beginning of it. Don't bother trying to tell me what doesn't work yet. This is just a sneak-peek. Bold, Italics, and Underline work fairly well. The toggle buttons in the tooltips won't necessarily be accurate yet, and things will get screwed up if you say, start typing, make something bold, then go back to before where you started typing and type there. It'll all be fixed eventually. NOTE: I am not liable for any sexual arousal caused by using this code committer: Tailor Script diff -r 1d0314b99747 -r 6a9acef3b867 src/gtkconv.c --- a/src/gtkconv.c Tue Dec 02 07:08:02 2003 +0000 +++ b/src/gtkconv.c Tue Dec 02 07:33:42 2003 +0000 @@ -117,6 +117,15 @@ static GtkWidget *invite_dialog = NULL; +enum { + TARGET_XURL=0, + TARGET_URI_LIST, + TARGET_BLIST_NODE, + TARGET_STRING, + TARGET_NETSCAPE_URL, + TARGET_PLAIN_TEXT +}; + /* Prototypes. <-- because Paco-Paco hates this comment. */ static void check_everything(GtkTextBuffer *buffer); static void set_toggle(GtkWidget *tb, gboolean active); @@ -3792,6 +3801,7 @@ { GaimConvWindow *win = conv->window; GaimConversation *c; + gchar* uri; if (sd->target == gdk_atom_intern("GAIM_BLIST_NODE", FALSE)) { GaimBlistNode *n = NULL; @@ -3809,6 +3819,64 @@ gaim_conv_window_add_conversation(win, c); } + if (info == TARGET_NETSCAPE_URL) + { + GaimGtkConversation *gtkconv = GAIM_GTK_CONVERSATION(conv); + gchar* p=(gchar*) sd->data; + gchar* q; + gchar* link; + + if (p==NULL) + return; + g_strchomp(p); + q = strchr(p,'\n'); + if (q==NULL) + { + link=g_strconcat("",p,"",NULL); + } + else + { + uri = g_strndup(p,q-p); + q++; + link=g_strconcat("",q,"",NULL); + g_free(uri); + } + gtk_text_buffer_insert_at_cursor(GTK_TEXT_BUFFER(gtkconv->entry_buffer), link, -1); + g_free(link); + } + if (info == TARGET_XURL) + { + GaimGtkConversation *gtkconv = GAIM_GTK_CONVERSATION(conv); + gchar* link; + uri=g_strdup((gchar*) sd->data); + g_strstrip(uri); + link=g_strconcat("",uri,"",NULL); + g_free(uri); + gtk_text_buffer_insert_at_cursor(GTK_TEXT_BUFFER(gtkconv->entry_buffer), link, -1); + g_free(link); + } + if (info == TARGET_URI_LIST && (gchar*)sd->data != NULL + && g_ascii_strncasecmp((gchar*)sd->data,"file:",5)!=0) + { + GaimGtkConversation *gtkconv = GAIM_GTK_CONVERSATION(conv); + gchar *p, *q, *link; + q=g_strdup((gchar*) sd->data); + g_strstrip(q); + p=strchr(q,'\n'); + if (p==NULL) + { + uri=g_strdup(q); /* strdup'ing this to make it match the else */ + } + else + { + uri=g_strndup(q,q-p); + } + g_free(q); + link=g_strconcat("",uri,"",NULL); + g_free(uri); + gtk_text_buffer_insert_at_cursor(GTK_TEXT_BUFFER(gtkconv->entry_buffer), link, -1); + g_free(link); + } } /************************************************************************** @@ -3957,10 +4025,13 @@ static const GtkTargetEntry te[] = { - {"text/plain", 0, 0}, - {"text/uri-list", 0, 1}, - {"GAIM_BLIST_NODE", 0, 2}, - {"STRING", 0, 3} + {"x-url/ftp", 0, TARGET_XURL}, + {"x-url/http", 0, TARGET_XURL}, + {"text/uri-list", 0, TARGET_URI_LIST}, + {"GAIM_BLIST_NODE", 0, TARGET_BLIST_NODE}, + {"STRING", 0, TARGET_STRING}, + {"_NETSCAPE_URL", 0, TARGET_NETSCAPE_URL}, + {"text/plain", 0, TARGET_PLAIN_TEXT} }; static void @@ -4034,20 +4105,15 @@ GTK_DEST_DEFAULT_DROP, te, sizeof(te) / sizeof(GtkTargetEntry), GDK_ACTION_DEFAULT | GDK_ACTION_COPY | GDK_ACTION_MOVE); - gtk_drag_dest_set(gtkconv->entry, - GTK_DEST_DEFAULT_MOTION | - GTK_DEST_DEFAULT_DROP, - te, sizeof(te) / sizeof(GtkTargetEntry), - GDK_ACTION_COPY); - + gtk_drag_dest_set(gtkconv->entry, 0, + te, sizeof(te) / sizeof(GtkTargetEntry), + GDK_ACTION_COPY); g_signal_connect(G_OBJECT(pane), "drag_data_received", G_CALLBACK(conv_dnd_recv), conv); g_signal_connect(G_OBJECT(gtkconv->imhtml), "drag_data_received", G_CALLBACK(conv_dnd_recv), conv); -#if 0 g_signal_connect(G_OBJECT(gtkconv->entry), "drag_data_received", G_CALLBACK(conv_dnd_recv), conv); -#endif /* Setup the container for the tab. */ gtkconv->tab_cont = tab_cont = gtk_vbox_new(FALSE, 5); diff -r 1d0314b99747 -r 6a9acef3b867 src/gtkimhtml.c --- a/src/gtkimhtml.c Tue Dec 02 07:08:02 2003 +0000 +++ b/src/gtkimhtml.c Tue Dec 02 07:33:42 2003 +0000 @@ -59,6 +59,8 @@ #define TOOLTIP_TIMEOUT 500 +static void insert_cb(GtkTextBuffer *buffer, GtkTextIter *iter, gchar *text, gint len, 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 @@ -268,7 +270,10 @@ 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; @@ -296,7 +301,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; @@ -416,6 +424,7 @@ 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); } @@ -466,9 +475,7 @@ 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_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 @@ -485,6 +492,7 @@ /* 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; @@ -497,6 +505,7 @@ 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), "button-release-event", G_CALLBACK(button_release_cb), imhtml); @@ -507,10 +516,16 @@ imhtml->tip_timer = 0; imhtml->tip_window = NULL; + imhtml->edit.bold = NULL; + imhtml->edit.italic = NULL; + imhtml->edit.underline = NULL; + 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) @@ -600,7 +615,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 */ @@ -1600,6 +1618,19 @@ gtk_text_buffer_get_start_iter(imhtml->text_buffer, &start); gtk_text_buffer_get_end_iter(imhtml->text_buffer, &end); gtk_text_buffer_delete(imhtml->text_buffer, &start, &end); + + while (imhtml->format_spans) { + GtkIMHtmlFormatSpan *span = imhtml->format_spans->data; + if (span->start_tag) + g_free(span->start_tag); + if (span->end_tag) + g_free(span->end_tag); + g_free(span); + imhtml->format_spans = imhtml->format_spans->next; + } + imhtml->edit.bold = NULL; + imhtml->edit.italic = NULL; + imhtml->edit.underline = NULL; } void gtk_imhtml_page_up (GtkIMHtml *imhtml) @@ -1919,3 +1950,207 @@ 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; + + if (!imhtml->editable) + return; + + if ((span = imhtml->edit.bold)) { + GtkTextIter bold; + gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &bold, span->start); + gtk_text_iter_forward_chars(iter, len); + 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_iter_forward_chars(iter, len); + 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_iter_forward_chars(iter, len); + gtk_text_buffer_apply_tag_by_name(imhtml->text_buffer, "UNDERLINE", &underline, 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 = g_malloc(sizeof(GtkIMHtmlFormatSpan)); + 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->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; + 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 = g_malloc(sizeof(GtkIMHtmlFormatSpan)); + 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->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; + 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 = g_malloc(sizeof(GtkIMHtmlFormatSpan)); + 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->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; + imhtml->edit.underline = 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.underline = NULL; + } + return imhtml->edit.underline != NULL; +} + +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); + return gtk_text_iter_compare(&ia, &ib); +} + +/* 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(GtkIMHtml *imhtml) +{ + 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_buffer_get_start_iter(imhtml->text_buffer, &iter); + + /* Initialize these to the end iter */ + gtk_text_buffer_get_end_iter(imhtml->text_buffer, &siter); + eiter = siter; + + if (starters) { + sspan = (GtkIMHtmlFormatSpan*)starters->data; + gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &siter, sspan->start); + } + + while ((c = gtk_text_iter_get_char(&iter)) != 0) { + if (c == 0xFFFC) { + /* This is an image or a smiley */ + } 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 = (GtkIMHtmlFormatSpan*)closers->data; + gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &eiter, espan->end); + } else { + espan = NULL; + gtk_text_buffer_get_end_iter(imhtml->text_buffer, &eiter); + } + } + 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) { + espan = sspan; + gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &eiter, espan->end); + closers = g_list_insert_sorted(closers, sspan, (GCompareFunc)span_compare_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); + } + + } + + str = g_string_append_unichar(str, c); + } + gtk_text_iter_forward_char(&iter); + } + return g_string_free(str, FALSE); +} + +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 1d0314b99747 -r 6a9acef3b867 src/gtkimhtml.h --- a/src/gtkimhtml.h Tue Dec 02 07:08:02 2003 +0000 +++ b/src/gtkimhtml.h Tue Dec 02 07:33:42 2003 +0000 @@ -49,6 +49,15 @@ typedef struct _GtkIMHtmlHr GtkIMHtmlHr; typedef struct _GtkIMHtmlCopyable GtkIMHtmlCopyable; + +typedef struct { + GtkTextMark *start; + GtkTextMark *end; + char *start_tag; + char *end_tag; + GtkTextBuffer *buffer; +} GtkIMHtmlFormatSpan; + struct _GtkIMHtml { GtkTextView text_view; GtkTextBuffer *text_buffer; @@ -56,6 +65,7 @@ gboolean comments, smileys; GdkCursor *hand_cursor; GdkCursor *arrow_cursor; + GdkCursor *text_cursor; GHashTable *smiley_data; GtkSmileyTree *default_smilies; @@ -72,6 +82,14 @@ GSList *copyables; gchar *search_string; + + gboolean editable; + struct { + GtkIMHtmlFormatSpan *bold; + GtkIMHtmlFormatSpan *italic; + GtkIMHtmlFormatSpan *underline; + } edit; + GList *format_spans; }; struct _GtkIMHtmlClass { @@ -182,6 +200,15 @@ 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_imthml_toggle_italic(GtkIMHtml *imhtml); +gboolean gtk_imhtml_toggle_underline(GtkIMHtml *imhtml); +char *gtk_imhtml_get_markup(GtkIMHtml *imhtml); +char *gtk_imhtml_get_text(GtkIMHtml *imhtml); + #ifdef __cplusplus } #endif