Mercurial > pidgin.yaz
changeset 1428:00b3d02a2168
[gaim-migrate @ 1438]
gtkhtml has gotten replaced by gtkimhtml.
committer: Tailor Script <tailor@pidgin.im>
author | Eric Warmenhoven <eric@warmenhoven.org> |
---|---|
date | Fri, 26 Jan 2001 02:02:36 +0000 |
parents | 28278bd61403 |
children | 865a6ead3258 |
files | ChangeLog po/POTFILES.in src/Makefile.am src/buddy_chat.c src/conversation.c src/dialogs.c src/gaim.h src/gtkhtml.c src/gtkhtml.h src/gtkimhtml.c src/gtkimhtml.h src/prefs.c |
diffstat | 12 files changed, 3210 insertions(+), 4797 deletions(-) [+] |
line wrap: on
line diff
--- a/ChangeLog Thu Jan 25 20:31:12 2001 +0000 +++ b/ChangeLog Fri Jan 26 02:02:36 2001 +0000 @@ -10,6 +10,7 @@ * Can use all away states for ICQ, Yahoo (N/A, Be Right Back, etc.) * Fixed hanging trees * Can close windows by hitting 'Esc' (optional) + * Better HTML Widget version 0.11.0-pre4: * ICQ upgraded to use icqlib 1.1.0
--- a/po/POTFILES.in Thu Jan 25 20:31:12 2001 +0000 +++ b/po/POTFILES.in Fri Jan 26 02:02:36 2001 +0000 @@ -6,7 +6,7 @@ src/buddy_chat.c src/conversation.c src/dialogs.c -src/gtkhtml.c +src/gtkimhtml.c src/html.c src/multi.c src/oscar.c
--- a/src/Makefile.am Thu Jan 25 20:31:12 2001 +0000 +++ b/src/Makefile.am Fri Jan 26 02:02:36 2001 +0000 @@ -10,7 +10,7 @@ conversation.c \ dialogs.c \ gaimrc.c \ - gtkhtml.c \ + gtkimhtml.c \ gtkspell.c \ gtkticker.c \ html.c \ @@ -42,7 +42,7 @@ conversation.c \ dialogs.c \ gaimrc.c \ - gtkhtml.c \ + gtkimhtml.c \ gtkspell.c \ gtkticker.c \ html.c \ @@ -86,7 +86,7 @@ getopt.c \ getopt.h \ getopt1.c \ - gtkhtml.h \ + gtkimhtml.h \ gtkspell.h \ gtkticker.h \ multi.h \
--- a/src/buddy_chat.c Thu Jan 25 20:31:12 2001 +0000 +++ b/src/buddy_chat.c Fri Jan 26 02:02:36 2001 +0000 @@ -28,7 +28,7 @@ #include <stdio.h> #include <stdlib.h> #include <gtk/gtk.h> -#include "gtkhtml.h" +#include "gtkimhtml.h" #include "gtkspell.h" #include <gdk/gdkkeysyms.h> @@ -39,6 +39,9 @@ #include "pixmaps/join.xpm" #include "pixmaps/close.xpm" +#include "pixmaps/luke03.xpm" +#include "pixmaps/oneeye.xpm" + static GtkWidget *joinchat; static struct gaim_connection *joinchatgc; static GtkWidget *entry; @@ -696,12 +699,20 @@ gtk_widget_set_usize(sw, 320, 160); gtk_widget_show(sw); - text = gtk_html_new(NULL, NULL); + text = gtk_imhtml_new(NULL, NULL); b->text = text; gtk_container_add(GTK_CONTAINER(sw), text); + GTK_LAYOUT(text)->hadjustment->step_increment = 10.0; + GTK_LAYOUT(text)->vadjustment->step_increment = 10.0; + if (!(display_options & OPT_DISP_SHOW_SMILEY)) + gtk_imhtml_show_smileys(GTK_IMHTML(text), FALSE); + if (display_options & OPT_DISP_SHOW_TIME) + gtk_imhtml_show_comments(GTK_IMHTML(text), TRUE); + gtk_signal_connect(GTK_OBJECT(text), "url_clicked", GTK_SIGNAL_FUNC(open_url_nw), NULL); + gtk_imhtml_associate_smiley(GTK_IMHTML(text), "C:)", luke03_xpm); + gtk_imhtml_associate_smiley(GTK_IMHTML(text), "C:-)", luke03_xpm); + gtk_imhtml_associate_smiley(GTK_IMHTML(text), "O-)", oneeye_xpm); gtk_widget_show(text); - GTK_HTML(text)->hadj->step_increment = 10.0; - GTK_HTML(text)->vadj->step_increment = 10.0; lbox = gtk_vbox_new(FALSE, 5); gtk_paned_pack2(GTK_PANED(hpaned), lbox, TRUE, TRUE);
--- a/src/conversation.c Thu Jan 25 20:31:12 2001 +0000 +++ b/src/conversation.c Fri Jan 26 02:02:36 2001 +0000 @@ -31,7 +31,7 @@ #include <stdlib.h> #include <ctype.h> #include <gtk/gtk.h> -#include "gtkhtml.h" +#include "gtkimhtml.h" #include <gdk/gdkkeysyms.h> #include "convo.h" #include "gtkspell.h" @@ -52,22 +52,6 @@ #include "pixmaps/fgcolor.xpm" #include "pixmaps/bgcolor.xpm" -#include "pixmaps/angel.xpm" -#include "pixmaps/bigsmile.xpm" -#include "pixmaps/burp.xpm" -#include "pixmaps/crossedlips.xpm" -#include "pixmaps/cry.xpm" -#include "pixmaps/embarrassed.xpm" -#include "pixmaps/kiss.xpm" -#include "pixmaps/moneymouth.xpm" -#include "pixmaps/sad.xpm" -#include "pixmaps/scream.xpm" -#include "pixmaps/smile.xpm" -#include "pixmaps/smile8.xpm" -#include "pixmaps/think.xpm" -#include "pixmaps/tongue.xpm" -#include "pixmaps/wink.xpm" -#include "pixmaps/yell.xpm" #include "pixmaps/luke03.xpm" #include "pixmaps/oneeye.xpm" @@ -485,6 +469,8 @@ gtk_signal_emit_stop_by_name(GTK_OBJECT(entry), "key_press_event"); close_callback(c->window, c); } + } else if (event->keyval == GDK_F2) { + gtk_imhtml_show_comments(GTK_IMHTML(c->text), !(GTK_IMHTML(c->text))->comments); } else if (event->keyval == GDK_Return) { if (!(event->state & GDK_SHIFT_MASK) && (general_options & OPT_GEN_ENTER_SENDS)) { @@ -1120,130 +1106,6 @@ /*------------------------------------------------------------------------*/ -static GdkPixmap *is_smiley(GtkWidget *window, char *m, int *len, GdkColor * trans) -{ - GdkBitmap *mask; - GdkPixmap *face = NULL; - - if (strlen(m) < 2) - return face; - *len = 2; - if (!strncmp(m, ":)", 2)) { - face = gdk_pixmap_create_from_xpm_d(window->window, &mask, trans, smile_xpm); - } else if (!strncmp(m, ":(", 2)) { - face = gdk_pixmap_create_from_xpm_d(window->window, &mask, trans, sad_xpm); - } else if (!strncmp(m, ";)", 2)) { - face = gdk_pixmap_create_from_xpm_d(window->window, &mask, trans, wink_xpm); - } - - if (face || strlen(m) < 3) - return face; - *len = 3; - if (!strncmp(m, ":-)", 3)) { - face = gdk_pixmap_create_from_xpm_d(window->window, &mask, trans, smile_xpm); - } else if (!strncmp(m, "O-)", 3)) { - face = gdk_pixmap_create_from_xpm_d(window->window, &mask, trans, oneeye_xpm); - } else if (!strncmp(m, "C:)", 3)) { - face = gdk_pixmap_create_from_xpm_d(window->window, &mask, trans, luke03_xpm); - } else if (!strncmp(m, ":-(", 3)) { - face = gdk_pixmap_create_from_xpm_d(window->window, &mask, trans, sad_xpm); - } else if (!strncmp(m, ";-)", 3)) { - face = gdk_pixmap_create_from_xpm_d(window->window, &mask, trans, wink_xpm); - } else if (!strncmp(m, ":-p", 3) || !strncmp(m, ":-P", 3)) { - face = gdk_pixmap_create_from_xpm_d(window->window, &mask, trans, tongue_xpm); - } else if (!strncmp(m, "=-O", 3)) { - face = gdk_pixmap_create_from_xpm_d(window->window, &mask, trans, scream_xpm); - } else if (!strncmp(m, ":-*", 3)) { - face = gdk_pixmap_create_from_xpm_d(window->window, &mask, trans, kiss_xpm); - } else if (!strncmp(m, ">:o", 3)) { - face = gdk_pixmap_create_from_xpm_d(window->window, &mask, trans, yell_xpm); - } else if (!strncmp(m, "8-)", 3)) { - face = gdk_pixmap_create_from_xpm_d(window->window, &mask, trans, smile8_xpm); - } else if (!strncmp(m, ":-$", 3)) { - face = gdk_pixmap_create_from_xpm_d(window->window, &mask, trans, moneymouth_xpm); - } else if (!strncmp(m, ":-!", 3)) { - face = gdk_pixmap_create_from_xpm_d(window->window, &mask, trans, burp_xpm); - } else if (!strncmp(m, ":-[", 3)) { - face = gdk_pixmap_create_from_xpm_d(window->window, &mask, trans, embarrassed_xpm); - } else if (!strncmp(m, ":'(", 3)) { - face = gdk_pixmap_create_from_xpm_d(window->window, &mask, trans, cry_xpm); - } else if (!strncmp(m, ":-\\", 3) || !strncmp(m, ":-/", 3)) { - face = gdk_pixmap_create_from_xpm_d(window->window, &mask, trans, think_xpm); - } else if (!strncmp(m, ":-X", 3)) { - face = gdk_pixmap_create_from_xpm_d(window->window, &mask, trans, crossedlips_xpm); - } else if (!strncmp(m, ":-D", 3)) { - face = gdk_pixmap_create_from_xpm_d(window->window, &mask, trans, bigsmile_xpm); - } - - if (face || strlen(m) < 4) - return face; - *len = 4; - if (!strncmp(m, "O:-)", 4)) { - face = gdk_pixmap_create_from_xpm_d(window->window, &mask, trans, angel_xpm); - } else if (!strncmp(m, "C:-)", 4)) { - face = gdk_pixmap_create_from_xpm_d(window->window, &mask, trans, luke03_xpm); - } - - if (face || strlen(m) < 6) - return face; - *len = 6; - if (!strncmp(m, ">:o", 6)) { - face = gdk_pixmap_create_from_xpm_d(window->window, &mask, trans, yell_xpm); - } - - return face; -} - -void write_html_with_smileys(GtkWidget *window, GtkWidget *html, char *what) -{ - int y = 0; - char *buf2 = g_strdup(what); - int i; - GdkPixmap *face; - /* hopefully we can later use this for bgcolors in smileys */ - GdkColor *trans = &window->style->base[GTK_STATE_NORMAL]; - gboolean in_tag = FALSE; - int gtk_font_options = 0; - - if (display_options & OPT_DISP_IGNORE_COLOUR) - gtk_font_options = gtk_font_options ^ HTML_OPTION_NO_COLOURS; - - if (display_options & OPT_DISP_IGNORE_FONTS) - gtk_font_options = gtk_font_options ^ HTML_OPTION_NO_FONTS; - - for (i = 0; i < strlen(what); i++) { - if (!in_tag) { - int len; - if (what[i] == '<') { - buf2[y] = what[i]; - y++; - in_tag = TRUE; - } else if ((face = is_smiley(window, &what[i], &len, trans)) != NULL) { - - buf2[y] = 0; - gtk_html_append_text(GTK_HTML(html), buf2, gtk_font_options); - gtk_html_add_pixmap(GTK_HTML(html), face, 0, 0); - y = 0; - i += len - 1; - } else { - buf2[y] = what[i]; - y++; - } - } else { - buf2[y] = what[i]; - y++; - if (what[i] == '>') - in_tag = FALSE; - } - } - - if (y) { - buf2[y] = 0; - gtk_html_append_text(GTK_HTML(html), buf2, gtk_font_options); - } - g_free(buf2); -} - /* this is going to be interesting since the conversation could either be a * normal IM conversation or a chat window. but hopefully it won't matter */ void write_to_conv(struct conversation *c, char *what, int flags, char *who) @@ -1260,10 +1122,16 @@ int gtk_font_options = 0; if (display_options & OPT_DISP_IGNORE_COLOUR) - gtk_font_options = gtk_font_options ^ HTML_OPTION_NO_COLOURS; + gtk_font_options = gtk_font_options ^ GTK_IMHTML_NO_COLOURS; if (display_options & OPT_DISP_IGNORE_FONTS) - gtk_font_options = gtk_font_options ^ HTML_OPTION_NO_FONTS; + gtk_font_options = gtk_font_options ^ GTK_IMHTML_NO_FONTS; + + gtk_font_options = gtk_font_options ^ GTK_IMHTML_NO_COMMENTS; + gtk_font_options = gtk_font_options ^ GTK_IMHTML_NO_TITLE; + + if (display_options & OPT_DISP_IGNORE_SIZES) + gtk_font_options = gtk_font_options ^ GTK_IMHTML_NO_SIZES; if (!who) { @@ -1288,11 +1156,9 @@ if (flags & WFLAG_SYSTEM) { - gtk_html_freeze(GTK_HTML(c->text)); + gtk_imhtml_append_text(GTK_IMHTML(c->text), what, 0); - gtk_html_append_text(GTK_HTML(c->text), what, 0); - - gtk_html_append_text(GTK_HTML(c->text), "<BR>", 0); + gtk_imhtml_append_text(GTK_IMHTML(c->text), "<BR>", 0); if ((general_options & OPT_GEN_LOG_ALL) || find_log_info(c->name)) { char *t1; @@ -1358,34 +1224,16 @@ } } - if (display_options & OPT_DISP_SHOW_TIME) - g_snprintf(buf, BUF_LONG, "<FONT COLOR=\"%s\"><B>%s %s</B></FONT> ", colour, - date(), str); - else - g_snprintf(buf, BUF_LONG, "<FONT COLOR=\"%s\"><B>%s</B></FONT>", colour, str); + g_snprintf(buf, BUF_LONG, "<FONT COLOR=\"%s\"><FONT SIZE=\"2\"><!--(%s) --></FONT>" + "<B>%s</B></FONT> ", colour, date(), str); g_free(str); - gtk_html_freeze(GTK_HTML(c->text)); - - if (colorv != -1) { - sprintf(buf2, "<BODY BGCOLOR=\"#%x\">", colorv); - gtk_html_append_text(GTK_HTML(c->text), buf2, gtk_font_options); - } - - gtk_html_append_text(GTK_HTML(c->text), buf, 0); + gtk_imhtml_append_text(GTK_IMHTML(c->text), buf, 0); - if (display_options & OPT_DISP_SHOW_SMILEY) { - write_html_with_smileys(c->window, c->text, what); - } else { - gtk_html_append_text(GTK_HTML(c->text), what, gtk_font_options); - } + gtk_imhtml_append_text(GTK_IMHTML(c->text), what, gtk_font_options); - if (colorv != -1) { - gtk_html_append_text(GTK_HTML(c->text), "</BODY>", gtk_font_options); - } - gtk_html_append_text(GTK_HTML(c->text), "<BR>", gtk_font_options); - + gtk_imhtml_append_text(GTK_IMHTML(c->text), "<BR>", 0); if ((general_options & OPT_GEN_LOG_ALL) || find_log_info(c->name)) { char *t1, *t2; @@ -1430,8 +1278,6 @@ (!c->is_chat && (general_options & OPT_GEN_POPUP_WINDOWS))) gdk_window_show(c->window->window); - gtk_html_thaw(GTK_HTML(c->text)); - g_free(smiley); g_free(buf); } @@ -1866,12 +1712,19 @@ gtk_widget_set_usize(sw, 320, 175); gtk_widget_show(sw); - text = gtk_html_new(NULL, NULL); + text = gtk_imhtml_new(NULL, NULL); c->text = text; - gtk_html_set_editable(GTK_HTML(text), FALSE); gtk_container_add(GTK_CONTAINER(sw), text); - GTK_HTML(text)->hadj->step_increment = 10.0; - GTK_HTML(text)->vadj->step_increment = 10.0; + GTK_LAYOUT(text)->hadjustment->step_increment = 10.0; + GTK_LAYOUT(text)->vadjustment->step_increment = 10.0; + if (!(display_options & OPT_DISP_SHOW_SMILEY)) + gtk_imhtml_show_smileys(GTK_IMHTML(text), FALSE); + if (display_options & OPT_DISP_SHOW_TIME) + gtk_imhtml_show_comments(GTK_IMHTML(text), TRUE); + gtk_signal_connect(GTK_OBJECT(text), "url_clicked", GTK_SIGNAL_FUNC(open_url_nw), NULL); + gtk_imhtml_associate_smiley(GTK_IMHTML(text), "C:)", luke03_xpm); + gtk_imhtml_associate_smiley(GTK_IMHTML(text), "C:-)", luke03_xpm); + gtk_imhtml_associate_smiley(GTK_IMHTML(text), "O-)", oneeye_xpm); gtk_widget_show(text); vbox2 = gtk_vbox_new(FALSE, 5); @@ -2005,3 +1858,67 @@ con = con->next; } } + +void toggle_timestamps() +{ + GList *cnv = conversations; + GSList *cht; + struct conversation *c; + GSList *con = connections; + struct gaim_connection *gc; + + while (cnv) { + c = (struct conversation *)cnv->data; + if (display_options & OPT_DISP_SHOW_TIME) + gtk_imhtml_show_comments(GTK_IMHTML(c->text), TRUE); + else + gtk_imhtml_show_comments(GTK_IMHTML(c->text), FALSE); + cnv = cnv->next; + } + + while (con) { + gc = (struct gaim_connection *)con->data; + cht = gc->buddy_chats; + while (cht) { + c = (struct conversation *)cht->data; + if (display_options & OPT_DISP_SHOW_TIME) + gtk_imhtml_show_comments(GTK_IMHTML(c->text), TRUE); + else + gtk_imhtml_show_comments(GTK_IMHTML(c->text), FALSE); + cht = cht->next; + } + con = con->next; + } +} + +void toggle_smileys() +{ + GList *cnv = conversations; + GSList *cht; + struct conversation *c; + GSList *con = connections; + struct gaim_connection *gc; + + while (cnv) { + c = (struct conversation *)cnv->data; + if (display_options & OPT_DISP_SHOW_SMILEY) + gtk_imhtml_show_smileys(GTK_IMHTML(c->text), TRUE); + else + gtk_imhtml_show_smileys(GTK_IMHTML(c->text), FALSE); + cnv = cnv->next; + } + + while (con) { + gc = (struct gaim_connection *)con->data; + cht = gc->buddy_chats; + while (cht) { + c = (struct conversation *)cht->data; + if (display_options & OPT_DISP_SHOW_SMILEY) + gtk_imhtml_show_smileys(GTK_IMHTML(c->text), TRUE); + else + gtk_imhtml_show_smileys(GTK_IMHTML(c->text), FALSE); + cht = cht->next; + } + con = con->next; + } +}
--- a/src/dialogs.c Thu Jan 25 20:31:12 2001 +0000 +++ b/src/dialogs.c Fri Jan 26 02:02:36 2001 +0000 @@ -39,7 +39,7 @@ #include <gtk/gtk.h> #include "gaim.h" -#include "gtkhtml.h" +#include "gtkimhtml.h" #include "prpl.h" #include "pixmaps/gnome_preferences.xpm" @@ -56,7 +56,9 @@ #include "pixmaps/cry.xpm" #include "pixmaps/embarrassed.xpm" #include "pixmaps/kiss.xpm" +#include "pixmaps/luke03.xpm" #include "pixmaps/moneymouth.xpm" +#include "pixmaps/oneeye.xpm" #include "pixmaps/sad.xpm" #include "pixmaps/scream.xpm" #include "pixmaps/smile.xpm" @@ -66,6 +68,11 @@ #include "pixmaps/wink.xpm" #include "pixmaps/yell.xpm" +#include "pixmaps/aol_icon.xpm" +#include "pixmaps/free_icon.xpm" +#include "pixmaps/dt_icon.xpm" +#include "pixmaps/admin_icon.xpm" + #define DEFAULT_FONT_NAME "-adobe-helvetica-medium-r-normal--12-120-75-75-p-67-iso8859-1" #define PATHSIZE 1024 @@ -1756,6 +1763,19 @@ g_free(d); } +gchar **info_img_handler(gchar *url) +{ + if (!g_strcasecmp(url, "free_icon.gif")) + return free_icon_xpm; + if (!g_strcasecmp(url, "aol_icon.gif")) + return aol_icon_xpm; + if (!g_strcasecmp(url, "dt_icon.gif")) + return dt_icon_xpm; + if (!g_strcasecmp(url, "admin_icon.gif")) + return admin_icon_xpm; + return NULL; +} + void g_show_info_text(char *info) { GtkWidget *ok; @@ -1763,6 +1783,7 @@ GtkWidget *text; GtkWidget *bbox; GtkWidget *sw; + gint options = 0; struct info_dlg *b = g_new0(struct info_dlg, 1); @@ -1790,13 +1811,20 @@ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS); - text = gtk_html_new(NULL, NULL); + text = gtk_imhtml_new(NULL, NULL); b->text = text; gtk_container_add(GTK_CONTAINER(sw), text); - GTK_HTML (text)->hadj->step_increment = 10.0; - GTK_HTML (text)->vadj->step_increment = 10.0; + GTK_LAYOUT(text)->hadjustment->step_increment = 10.0; + GTK_LAYOUT(text)->vadjustment->step_increment = 10.0; gtk_widget_set_usize(sw, 300, 250); + gtk_imhtml_set_img_handler(GTK_IMHTML(text), info_img_handler); + if (!(display_options & OPT_DISP_SHOW_SMILEY)) + gtk_imhtml_show_smileys(GTK_IMHTML(b->text), FALSE); + gtk_signal_connect(GTK_OBJECT(text), "url_clicked", GTK_SIGNAL_FUNC(open_url_nw), NULL); + gtk_imhtml_associate_smiley(GTK_IMHTML(text), "C:)", luke03_xpm); + gtk_imhtml_associate_smiley(GTK_IMHTML(text), "C:-)", luke03_xpm); + gtk_imhtml_associate_smiley(GTK_IMHTML(text), "O-)", oneeye_xpm); gtk_box_pack_start(GTK_BOX(bbox), label, FALSE, FALSE, 0); gtk_box_pack_start(GTK_BOX(bbox), sw, TRUE, TRUE, 0); @@ -1805,11 +1833,16 @@ aol_icon(b->window->window); gtk_widget_show_all(b->window); - if (display_options & OPT_DISP_SHOW_SMILEY) - write_html_with_smileys(b->window, b->text, info); - else - gtk_html_append_text(GTK_HTML(b->text), info, (display_options & OPT_DISP_IGNORE_COLOUR) ? HTML_OPTION_NO_COLOURS : 0); - gtk_html_append_text(GTK_HTML(b->text), "</BODY>", 0); + if (display_options & OPT_DISP_IGNORE_COLOUR) + options ^= GTK_IMHTML_NO_COLOURS; + if (display_options & OPT_DISP_IGNORE_FONTS) + options ^= GTK_IMHTML_NO_FONTS; + options ^= GTK_IMHTML_NO_COMMENTS; + options ^= GTK_IMHTML_NO_TITLE; + options ^= GTK_IMHTML_NO_NEWLINE; + options ^= GTK_IMHTML_NO_SCROLL; + gtk_imhtml_append_text(GTK_IMHTML(b->text), info, options); + gtk_imhtml_append_text(GTK_IMHTML(b->text), "<BR>", 0); gtk_adjustment_set_value(gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(sw)), 0); }
--- a/src/gaim.h Thu Jan 25 20:31:12 2001 +0000 +++ b/src/gaim.h Fri Jan 26 02:02:36 2001 +0000 @@ -486,6 +486,7 @@ #define OPT_DISP_CHAT_BUTTON_XPM 0x00100000 #define OPT_DISP_SHOW_WARN 0x00200000 #define OPT_DISP_IGNORE_FONTS 0x00400000 +#define OPT_DISP_IGNORE_SIZES 0x00800000 extern int sound_options; #define OPT_SOUND_LOGIN 0x00000001
--- a/src/gtkhtml.c Thu Jan 25 20:31:12 2001 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,4448 +0,0 @@ - -/* - * gaim - * - * Copyright (C) 1998-1999, Mark Spencer <markster@marko.net> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#ifdef HAVE_CONFIG_H -#include "../config.h" -#endif -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <gtk/gtk.h> -#include <gdk/gdkprivate.h> -#include <gdk/gdkx.h> -#include <gdk/gdkkeysyms.h> -#include <ctype.h> - -#ifndef _WIN32 -#include <X11/Xlib.h> -#include <X11/Xatom.h> -#endif - -#include "gaim.h" -#include "gtkhtml.h" - -#include "pixmaps/aol_icon.xpm" -#include "pixmaps/admin_icon.xpm" -#include "pixmaps/free_icon.xpm" -#include "pixmaps/dt_icon.xpm" -#define MAX_SIZE 7 -#define MIN_HTML_WIDTH_LINES 20 -#define MIN_HTML_HEIGHT_LINES 10 -#define BORDER_WIDTH 2 -#define SCROLL_TIME 100 -#define SCROLL_PIXELS 5 -#define KEY_SCROLL_PIXELS 10 - -int font_sizes[] = { 80, 100, 120, 140, 200, 300, 400 }; - -/* -GdkFont *fixed_font[] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL }; -GdkFont *fixed_bold_font[] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL }; -GdkFont *fixed_italic_font[] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL }; -GdkFont *fixed_bold_italic_font[] = - { NULL, NULL, NULL, NULL, NULL, NULL, NULL }; -GdkFont *prop_font[] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL }; -GdkFont *prop_bold_font[] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL }; -GdkFont *prop_italic_font[] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL }; -GdkFont *prop_bold_italic_font[] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL }; -*/ - -GData * font_cache; -static gboolean cache_init = FALSE; - -struct font_state -{ - int size; - int owncolor; - int ownbg; - gchar font[1024]; - GdkColor *color; - GdkColor *bgcol; - struct font_state *next; -}; - -struct font_state *push_state(struct font_state *current) -{ - struct font_state *tmp; - tmp = (struct font_state *) g_new0(struct font_state, 1); - tmp->next = current; - tmp->color = current->color; - tmp->bgcol = current->bgcol; - tmp->size = current->size; - tmp->owncolor = 0; - tmp->ownbg = 0; - strcpy( tmp->font, current->font ); - return tmp; -} - -enum -{ - ARG_0, - ARG_HADJUSTMENT, - ARG_VADJUSTMENT, -}; - - -enum -{ - TARGET_STRING, - TARGET_TEXT, - TARGET_COMPOUND_TEXT -}; - - -static void gtk_html_class_init(GtkHtmlClass * klass); -static void gtk_html_set_arg(GtkObject * object, GtkArg * arg, guint arg_id); -static void gtk_html_get_arg(GtkObject * object, GtkArg * arg, guint arg_id); -static void gtk_html_init(GtkHtml * html); -static void gtk_html_destroy(GtkObject * object); -static void gtk_html_finalize(GtkObject * object); -static void gtk_html_realize(GtkWidget * widget); -static void gtk_html_unrealize(GtkWidget * widget); -static void gtk_html_style_set(GtkWidget * widget, GtkStyle * previous_style); -static void gtk_html_draw_focus(GtkWidget * widget); -static void gtk_html_size_request(GtkWidget * widget, - GtkRequisition * requisition); -static void gtk_html_size_allocate(GtkWidget * widget, - GtkAllocation * allocation); -static void gtk_html_adjustment(GtkAdjustment * adjustment, GtkHtml * html); -static void gtk_html_disconnect(GtkAdjustment * adjustment, GtkHtml * html); -static void gtk_html_add_seperator(GtkHtml *, GdkFont *, GdkColor *, GdkColor *); -static void gtk_html_add_text(GtkHtml * html, - GdkFont * font, - GdkColor * fore, - GdkColor * back, - gchar * chars, - gint length, - gint uline, gint strike, gchar * url); -static void gtk_html_draw_bit(GtkHtml * html, - GtkHtmlBit * htmlbit, gint redraw); -static void gtk_html_selection_get(GtkWidget * widget, - GtkSelectionData * selection_data, - guint sel_info, guint32 time); -static gint gtk_html_selection_clear(GtkWidget * widget, - GdkEventSelection * event); -static gint gtk_html_visibility_notify(GtkWidget * widget, - GdkEventVisibility * event); - - -/* Event handlers */ -static void gtk_html_draw(GtkWidget * widget, GdkRectangle * area); -static gint gtk_html_expose(GtkWidget * widget, GdkEventExpose * event); -static gint gtk_html_button_press(GtkWidget * widget, GdkEventButton * event); -static gint gtk_html_button_release(GtkWidget * widget, GdkEventButton * event); -static gint gtk_html_motion_notify(GtkWidget * widget, GdkEventMotion * event); -static gint gtk_html_key_press(GtkWidget * widget, GdkEventKey * event); -static gint gtk_html_leave_notify(GtkWidget * widget, GdkEventCrossing * event); - -static gint gtk_html_tooltip_timeout(gpointer data); - - -static void clear_area(GtkHtml * html, GdkRectangle * area); -static void expose_html(GtkHtml * html, GdkRectangle * area, gboolean cursor); -static void scroll_down(GtkHtml * html, gint diff0); -static void scroll_up(GtkHtml * html, gint diff0); - -static void adjust_adj(GtkHtml * html, GtkAdjustment * adj); -static void resize_html(GtkHtml * html); -static gint html_bit_is_onscreen(GtkHtml * html, GtkHtmlBit * hb); -static void draw_cursor(GtkHtml * html); -static void undraw_cursor(GtkHtml * html); - -static int get_line_height(GtkHtml *, GtkHtmlBit *); - -static GtkWidgetClass *parent_class = NULL; - -GtkType gtk_html_get_type(void) -{ - static GtkType html_type = 0; - - if (!html_type) - { - static const GtkTypeInfo html_info = { - "GtkHtml", - sizeof(GtkHtml), - sizeof(GtkHtmlClass), - (GtkClassInitFunc) gtk_html_class_init, - (GtkObjectInitFunc) gtk_html_init, - NULL, - NULL, - NULL, - }; - html_type = gtk_type_unique(GTK_TYPE_WIDGET, &html_info); - } - return html_type; -} - - -static void gtk_html_class_init(GtkHtmlClass * class) -{ - GtkObjectClass *object_class; - GtkWidgetClass *widget_class; - - object_class = (GtkObjectClass *) class; - widget_class = (GtkWidgetClass *) class; - parent_class = gtk_type_class(GTK_TYPE_WIDGET); - - - gtk_object_add_arg_type("GtkHtml::hadjustment", - GTK_TYPE_ADJUSTMENT, - GTK_ARG_READWRITE | GTK_ARG_CONSTRUCT, - ARG_HADJUSTMENT); - - gtk_object_add_arg_type("GtkHtml::vadjustment", - GTK_TYPE_ADJUSTMENT, - GTK_ARG_READWRITE | GTK_ARG_CONSTRUCT, - ARG_VADJUSTMENT); - - object_class->set_arg = gtk_html_set_arg; - object_class->get_arg = gtk_html_get_arg; - object_class->destroy = gtk_html_destroy; - object_class->finalize = gtk_html_finalize; - - widget_class->realize = gtk_html_realize; - widget_class->unrealize = gtk_html_unrealize; - widget_class->style_set = gtk_html_style_set; - widget_class->draw_focus = gtk_html_draw_focus; - widget_class->size_request = gtk_html_size_request; - widget_class->size_allocate = gtk_html_size_allocate; - widget_class->draw = gtk_html_draw; - widget_class->expose_event = gtk_html_expose; - widget_class->button_press_event = gtk_html_button_press; - widget_class->button_release_event = gtk_html_button_release; - widget_class->motion_notify_event = gtk_html_motion_notify; - widget_class->leave_notify_event = gtk_html_leave_notify; - widget_class->selection_get = gtk_html_selection_get; - widget_class->selection_clear_event = gtk_html_selection_clear; - widget_class->key_press_event = gtk_html_key_press; - widget_class->visibility_notify_event = gtk_html_visibility_notify; - - - widget_class->set_scroll_adjustments_signal = - gtk_signal_new("set_scroll_adjustments", - GTK_RUN_LAST, - object_class->type, - GTK_SIGNAL_OFFSET(GtkHtmlClass, set_scroll_adjustments), - gtk_marshal_NONE__POINTER_POINTER, - GTK_TYPE_NONE, 2, GTK_TYPE_ADJUSTMENT, - GTK_TYPE_ADJUSTMENT); - - - class->set_scroll_adjustments = gtk_html_set_adjustments; - -} - -static void gtk_html_set_arg(GtkObject * object, GtkArg * arg, guint arg_id) -{ - GtkHtml *html; - - html = GTK_HTML(object); - - switch (arg_id) - { - case ARG_HADJUSTMENT: - gtk_html_set_adjustments(html, GTK_VALUE_POINTER(*arg), html->vadj); - break; - case ARG_VADJUSTMENT: - gtk_html_set_adjustments(html, html->hadj, GTK_VALUE_POINTER(*arg)); - break; - default: - break; - } -} - -static void gtk_html_get_arg(GtkObject * object, GtkArg * arg, guint arg_id) -{ - GtkHtml *html; - - html = GTK_HTML(object); - - switch (arg_id) - { - case ARG_HADJUSTMENT: - GTK_VALUE_POINTER(*arg) = html->hadj; - break; - case ARG_VADJUSTMENT: - GTK_VALUE_POINTER(*arg) = html->vadj; - break; - default: - arg->type = GTK_TYPE_INVALID; - break; - } -} - -static void gtk_html_init(GtkHtml * html) -{ - static const GtkTargetEntry targets[] = { - {"STRING", 0, TARGET_STRING}, - {"TEXT", 0, TARGET_TEXT}, - {"COMPOUND_TEXT", 0, TARGET_COMPOUND_TEXT} - }; - - static const gint n_targets = sizeof(targets) / sizeof(targets[0]); - - GTK_WIDGET_SET_FLAGS(html, GTK_CAN_FOCUS); - - html->html_area = NULL; - html->hadj = NULL; - html->vadj = NULL; - html->current_x = 0; - html->current_y = 0; - html->start_sel = html->end_sel = NULL; - html->start_sel_x = html->start_sel_y = -1; - html->num_end = html->num_start = -1; - - html->html_bits = NULL; - html->urls = NULL; - html->selected_text = NULL; - html->tooltip_hb = NULL; - html->tooltip_timer = -1; - html->tooltip_window = NULL; - html->cursor_hb = NULL; - html->cursor_pos = 0; - - html->pm = NULL; - - html->editable = 0; - html->transparent = 0; - - html->frozen = 0; - - gtk_selection_add_targets(GTK_WIDGET(html), GDK_SELECTION_PRIMARY, - targets, n_targets); - - - -} - - -GtkWidget *gtk_html_new(GtkAdjustment * hadj, GtkAdjustment * vadj) -{ - GtkWidget *html; - if(!cache_init) - { - g_datalist_init(&font_cache); - cache_init = TRUE; - } - - if (hadj) - g_return_val_if_fail(GTK_IS_ADJUSTMENT(hadj), NULL); - if (vadj) - g_return_val_if_fail(GTK_IS_ADJUSTMENT(vadj), NULL); - - html = gtk_widget_new(GTK_TYPE_HTML, - "hadjustment", hadj, "vadjustment", vadj, NULL); - - return html; -} - - -void gtk_html_set_editable(GtkHtml * html, gboolean is_editable) -{ - g_return_if_fail(html != NULL); - g_return_if_fail(GTK_IS_HTML(html)); - - - html->editable = (is_editable != FALSE); - - if (is_editable) - draw_cursor(html); - else - undraw_cursor(html); - -} - -void gtk_html_set_transparent(GtkHtml * html, gboolean is_transparent) -{ - GdkRectangle rect; - gint width, - height; - GtkWidget *widget; - - g_return_if_fail(html != NULL); - g_return_if_fail(GTK_IS_HTML(html)); - - - widget = GTK_WIDGET(html); - html->transparent = (is_transparent != FALSE); - - if (!GTK_WIDGET_REALIZED(widget)) - return; - - html->bg_gc = NULL; - gdk_window_get_size(widget->window, &width, &height); - rect.x = 0; - rect.y = 0; - rect.width = width; - rect.height = height; - gdk_window_clear_area(widget->window, rect.x, rect.y, rect.width, - rect.height); - - expose_html(html, &rect, FALSE); - gtk_html_draw_focus((GtkWidget *) html); -} - - -void gtk_html_set_adjustments(GtkHtml * html, - GtkAdjustment * hadj, GtkAdjustment * vadj) -{ - g_return_if_fail(html != NULL); - g_return_if_fail(GTK_IS_HTML(html)); - if (hadj) - g_return_if_fail(GTK_IS_ADJUSTMENT(hadj)); - else - hadj = GTK_ADJUSTMENT(gtk_adjustment_new(0.0, 0.0, 0.0, 0.0, 0.0, 0.0)); - if (vadj) - g_return_if_fail(GTK_IS_ADJUSTMENT(vadj)); - else - vadj = GTK_ADJUSTMENT(gtk_adjustment_new(0.0, 0.0, 0.0, 0.0, 0.0, 0.0)); - - if (html->hadj && (html->hadj != hadj)) - { - gtk_signal_disconnect_by_data(GTK_OBJECT(html->hadj), html); - gtk_object_unref(GTK_OBJECT(html->hadj)); - } - - if (html->vadj && (html->vadj != vadj)) - { - gtk_signal_disconnect_by_data(GTK_OBJECT(html->vadj), html); - gtk_object_unref(GTK_OBJECT(html->vadj)); - } - - if (html->hadj != hadj) - { - html->hadj = hadj; - gtk_object_ref(GTK_OBJECT(html->hadj)); - gtk_object_sink(GTK_OBJECT(html->hadj)); - - gtk_signal_connect(GTK_OBJECT(html->hadj), "changed", - (GtkSignalFunc) gtk_html_adjustment, html); - gtk_signal_connect(GTK_OBJECT(html->hadj), "value_changed", - (GtkSignalFunc) gtk_html_adjustment, html); - gtk_signal_connect(GTK_OBJECT(html->hadj), "disconnect", - (GtkSignalFunc) gtk_html_disconnect, html); - gtk_html_adjustment(hadj, html); - } - - if (html->vadj != vadj) - { - html->vadj = vadj; - gtk_object_ref(GTK_OBJECT(html->vadj)); - gtk_object_sink(GTK_OBJECT(html->vadj)); - - gtk_signal_connect(GTK_OBJECT(html->vadj), "changed", - (GtkSignalFunc) gtk_html_adjustment, html); - gtk_signal_connect(GTK_OBJECT(html->vadj), "value_changed", - (GtkSignalFunc) gtk_html_adjustment, html); - gtk_signal_connect(GTK_OBJECT(html->vadj), "disconnect", - (GtkSignalFunc) gtk_html_disconnect, html); - gtk_html_adjustment(vadj, html); - } -} - - - -GdkColor *get_color(int colorv, GdkColormap * map) -{ - GdkColor *color; -#if 0 - fprintf(stdout, "color is %x\n", colorv); -#endif - color = (GdkColor *) g_new0(GdkColor, 1); - color->red = ((colorv & 0xff0000) >> 16) * 256; - color->green = ((colorv & 0xff00) >> 8) * 256; - color->blue = ((colorv & 0xff)) * 256; -#if 0 - fprintf(stdout, "Colors are %d, %d, %d\n", color->red, color->green, - color->blue); -#endif - gdk_color_alloc(map, color); - return color; -} - - -int load_font_with_cache(const char *name, const char *weight, char slant, - int size, GdkFont **font_return) -{ - gchar font_spec[1024]; - - if (size > 0) - g_snprintf(font_spec, sizeof font_spec, - "-*-%s-%s-%c-*-*-*-%d-*-*-*-*-*-*", - name, weight, slant, size); - else - g_snprintf(font_spec, sizeof font_spec, - "-*-%s-%s-%c-*-*-*-*-*-*-*-*-*-*", - name, weight, slant); - - if((*font_return = g_datalist_id_get_data(&font_cache, - g_quark_from_string(font_spec)))) { - return TRUE; - } else if ((*font_return = gdk_font_load(font_spec))) { - g_datalist_id_set_data(&font_cache, - g_quark_from_string(font_spec), *font_return); - return TRUE; - } else { - return FALSE; - } -} - - -GdkFont *getfont(const char *font, int bold, int italic, int fixed, int size) -{ - GdkFont *my_font = NULL; - gchar *weight, slant; - - if (!font || !strlen(font)) font = fixed ? "courier" : "helvetica"; - weight = bold ? "bold" : "medium"; - slant = italic ? 'i' : 'r'; - - if (size > MAX_SIZE) size = MAX_SIZE; - if (size < 1) size = 1; - size = font_sizes[size-1]; - - /* try both 'i'talic and 'o'blique for italic fonts */ - - if (load_font_with_cache(font, weight, slant, size, &my_font)) - return my_font; - if (load_font_with_cache(font, weight, 'o', size, &my_font)) - return my_font; - if (italic && load_font_with_cache(font, weight, slant, 0, &my_font)) - return my_font; - if (italic && load_font_with_cache(font, weight, 'o', 0, &my_font)) - return my_font; - - /* since we couldn't get the right font, fall back to the default fonts. */ - - font = fixed ? "courier" : "helvetica"; - if (load_font_with_cache(font, weight, slant, size, &my_font)) - return my_font; - if (load_font_with_cache(font, weight, slant, 0, &my_font)) - return my_font; - - font = fixed ? "helvetica" : "courier"; - if (load_font_with_cache(font, weight, slant, size, &my_font)) - return my_font; - if (load_font_with_cache(font, weight, slant, 0, &my_font)) - return my_font; - - /* whoops, couldn't do any of those. maybe they have a default outgoing - * font? maybe we can use that. */ - if (fontface[0]) { - /* woohoo! */ - if (load_font_with_cache(fontface, "medium", 'r', size, &my_font)) - return my_font; - if (load_font_with_cache(fontface, "medium", 'r', 0, &my_font)) - return my_font; - } - - /* ok, now we're in a pickle. if we can't do any of the above, let's - * try doing the most boring font we can find. */ - if (load_font_with_cache("helvetica", "medium", 'r', size, &my_font)) - return my_font; - if (load_font_with_cache("helvetica", "medium", 'r', 0, &my_font)) - return my_font; - - if (load_font_with_cache("courier", "medium", 'r', size, &my_font)) - return my_font; - if (load_font_with_cache("courier", "medium", 'r', 0, &my_font)) - return my_font; - - if (load_font_with_cache("times", "medium", 'r', size, &my_font)) - return my_font; - if (load_font_with_cache("times", "medium", 'r', 0, &my_font)) - return my_font; - - /* my god, how did we end up here. is there a 'generic font' function - * in gdk? that would be incredibly useful here. there's gotta be a - * better way to do this. */ - - /* well, if they can't do any of the fonts above, they'll take whatever - * they can get, and be happy about it, damn it. :) */ - load_font_with_cache("*", "*", '*', 0, &my_font); - return my_font; -} - - -/* 'Borrowed' from ETerm */ -GdkWindow *get_desktop_window(GtkWidget * widget) -{ -#ifndef _WIN32 - GdkAtom prop, - type, - prop2; - int format; - gint length; - guchar *data; - GtkWidget *w; - - prop = gdk_atom_intern("_XROOTPMAP_ID", 1); - prop2 = gdk_atom_intern("_XROOTCOLOR_PIXEL", 1); - - if (prop == None && prop2 == None) - { - return NULL; - } - - - - for (w = widget; w; w = w->parent) - { - - if (prop != None) - { - gdk_property_get(w->window, prop, AnyPropertyType, 0L, 1L, 0, - &type, &format, &length, &data); - } - else if (prop2 != None) - { - gdk_property_get(w->window, prop2, AnyPropertyType, 0L, 1L, 0, - &type, &format, &length, &data); - } - else - { - continue; - } - if (type != None) - { - return (w->window); - } - } -#endif - return NULL; - -} - - - -GdkPixmap *get_desktop_pixmap(GtkWidget * widget) -{ -#ifndef _WIN32 - GdkPixmap *p; - GdkAtom prop, - type, - prop2; - int format; - gint length; - guint32 id; - guchar *data; - - prop = gdk_atom_intern("_XROOTPMAP_ID", 1); - prop2 = gdk_atom_intern("_XROOTCOLOR_PIXEL", 1); - - - if (prop == None && prop2 == None) - { - return NULL; - } - - if (prop != None) - { - gdk_property_get(get_desktop_window(widget), prop, AnyPropertyType, 0L, - 1L, 0, &type, &format, &length, &data); - if (type == XA_PIXMAP) - { - id = data[0]; - id += data[1] << 8; - id += data[2] << 16; - id += data[3] << 24; - p = gdk_pixmap_foreign_new(id); - return p; - } - } - if (prop2 != None) - { - -/* XGetWindowProperty(Xdisplay, desktop_window, prop2, 0L, 1L, False, AnyPropertyType, - &type, &format, &length, &after, &data);*/ - -/* if (type == XA_CARDINAL) {*/ - /* - * D_PIXMAP((" Solid color not yet supported.\n")); - */ - -/* return NULL; - }*/ - } - /* - * D_PIXMAP(("No suitable attribute found.\n")); - */ -#endif - return NULL; -} - - -static void clear_focus_area(GtkHtml * html, - gint area_x, - gint area_y, gint area_width, gint area_height) -{ - GtkWidget *widget = GTK_WIDGET(html); - gint x, - y; - - gint ythick = BORDER_WIDTH + widget->style->klass->ythickness; - gint xthick = BORDER_WIDTH + widget->style->klass->xthickness; - - gint width, - height; - - if (html->frozen > 0) - return; - - if (html->transparent) - { - if (html->pm == NULL) - html->pm = get_desktop_pixmap(widget); - - if (html->pm == NULL) - return; - - if (html->bg_gc == NULL) - { - GdkGCValues values; - - values.tile = html->pm; - values.fill = GDK_TILED; - - html->bg_gc = gdk_gc_new_with_values(html->html_area, &values, - GDK_GC_FILL | GDK_GC_TILE); - - } - - gdk_window_get_deskrelative_origin(widget->window, &x, &y); - - gdk_draw_pixmap(widget->window, html->bg_gc, html->pm, - x + area_x, y + area_y, area_x, area_y, area_width, - area_height); - - - } - else - { - gdk_window_get_size(widget->style->bg_pixmap[GTK_STATE_NORMAL], &width, - &height); - - gdk_gc_set_ts_origin(html->bg_gc, - (-html->xoffset + xthick) % width, - (-html->yoffset + ythick) % height); - - gdk_draw_rectangle(widget->window, html->bg_gc, TRUE, - area_x, area_y, area_width, area_height); - } -} - -static void gtk_html_draw_focus(GtkWidget * widget) -{ - GtkHtml *html; - gint width, - height; - gint x, - y; - - g_return_if_fail(widget != NULL); - g_return_if_fail(GTK_IS_HTML(widget)); - - html = GTK_HTML(widget); - - if (GTK_WIDGET_DRAWABLE(widget)) - { - gint ythick = widget->style->klass->ythickness; - gint xthick = widget->style->klass->xthickness; - gint xextra = BORDER_WIDTH; - gint yextra = BORDER_WIDTH; - - x = 0; - y = 0; - width = widget->allocation.width; - height = widget->allocation.height; - - if (GTK_WIDGET_HAS_FOCUS(widget)) - { - x += 1; - y += 1; - width -= 2; - height -= 2; - xextra -= 1; - yextra -= 1; - - gtk_paint_focus(widget->style, widget->window, - NULL, widget, "text", - 0, 0, - widget->allocation.width - 1, - widget->allocation.height - 1); - } - - gtk_paint_shadow(widget->style, widget->window, - GTK_STATE_NORMAL, GTK_SHADOW_IN, - NULL, widget, "text", x, y, width, height); - - x += xthick; - y += ythick; - width -= 2 * xthick; - height -= 2 * ythick; - - - if (widget->style->bg_pixmap[GTK_STATE_NORMAL] || html->transparent) - { - /* - * top rect - */ - clear_focus_area(html, x, y, width, yextra); - /* - * left rect - */ - clear_focus_area(html, x, y + yextra, - xextra, y + height - 2 * yextra); - /* - * right rect - */ - clear_focus_area(html, x + width - xextra, y + yextra, - xextra, height - 2 * ythick); - /* - * bottom rect - */ - clear_focus_area(html, x, x + height - yextra, width, yextra); - } - } -} - -static void gtk_html_size_request(GtkWidget * widget, - GtkRequisition * requisition) -{ - gint xthickness; - gint ythickness; - gint char_height; - gint char_width; - - g_return_if_fail(widget != NULL); - g_return_if_fail(GTK_IS_HTML(widget)); - g_return_if_fail(requisition != NULL); - - xthickness = widget->style->klass->xthickness + BORDER_WIDTH; - ythickness = widget->style->klass->ythickness + BORDER_WIDTH; - - char_height = MIN_HTML_HEIGHT_LINES * (widget->style->font->ascent + - widget->style->font->descent); - - char_width = MIN_HTML_WIDTH_LINES * (gdk_text_width(widget->style->font, - "ABCDEFGHIJKLMNOPQRSTUVWXYZ", - 26) / 26); - - requisition->width = char_width + xthickness * 2; - requisition->height = char_height + ythickness * 2; -} - -static void gtk_html_size_allocate(GtkWidget * widget, - GtkAllocation * allocation) -{ - GtkHtml *html; - - g_return_if_fail(widget != NULL); - g_return_if_fail(GTK_IS_HTML(widget)); - g_return_if_fail(allocation != NULL); - - html = GTK_HTML(widget); - - widget->allocation = *allocation; - if (GTK_WIDGET_REALIZED(widget)) - { - gdk_window_move_resize(widget->window, - allocation->x, allocation->y, - allocation->width, allocation->height); - - gdk_window_move_resize(html->html_area, - widget->style->klass->xthickness + BORDER_WIDTH, - widget->style->klass->ythickness + BORDER_WIDTH, - MAX(1, (gint) widget->allocation.width - - (gint) (widget->style->klass->xthickness + - (gint) BORDER_WIDTH) * 2), - MAX(1, (gint) widget->allocation.height - - (gint) (widget->style->klass->ythickness + - (gint) BORDER_WIDTH) * 2)); - - resize_html(html); - } -} - -static void gtk_html_draw(GtkWidget * widget, GdkRectangle * area) -{ - g_return_if_fail(widget != NULL); - g_return_if_fail(GTK_IS_HTML(widget)); - g_return_if_fail(area != NULL); - - if (GTK_WIDGET_DRAWABLE(widget)) - { - expose_html(GTK_HTML(widget), area, TRUE); - gtk_widget_draw_focus(widget); - } -} - - -static gint gtk_html_expose(GtkWidget * widget, GdkEventExpose * event) -{ - GtkHtml *html; - - g_return_val_if_fail(widget != NULL, FALSE); - g_return_val_if_fail(GTK_IS_HTML(widget), FALSE); - g_return_val_if_fail(event != NULL, FALSE); - - html = GTK_HTML(widget); - - if (event->window == html->html_area) - { - expose_html(html, &event->area, TRUE); - } - else if (event->count == 0) - { - gtk_widget_draw_focus(widget); - } - - return FALSE; - -} - - -static gint gtk_html_selection_clear(GtkWidget * widget, - GdkEventSelection * event) -{ - GtkHtml *html; - - g_return_val_if_fail(widget != NULL, FALSE); - g_return_val_if_fail(GTK_IS_HTML(widget), FALSE); - g_return_val_if_fail(event != NULL, FALSE); - - /* - * Let the selection handling code know that the selection - * * has been changed, since we've overriden the default handler - */ - if (!gtk_selection_clear(widget, event)) - return FALSE; - - html = GTK_HTML(widget); - - if (event->selection == GDK_SELECTION_PRIMARY) - { - if (html->selected_text) - { - GList *hbits = html->html_bits; - GtkHtmlBit *hb; - - g_free(html->selected_text); - html->selected_text = NULL; - html->start_sel = NULL; - html->end_sel = NULL; - html->num_start = 0; - html->num_end = 0; - while (hbits) - { - hb = (GtkHtmlBit *) hbits->data; - if (hb->was_selected) - gtk_html_draw_bit(html, hb, 1); - hbits = hbits->prev; - } - hbits = g_list_last(html->html_bits); - } - } - - return TRUE; -} - - - -static void gtk_html_selection_get(GtkWidget * widget, - GtkSelectionData * selection_data, - guint sel_info, guint32 time) -{ - gchar *str; - gint len; - GtkHtml *html; - - g_return_if_fail(widget != NULL); - g_return_if_fail(GTK_IS_HTML(widget)); - - html = GTK_HTML(widget); - - - if (selection_data->selection != GDK_SELECTION_PRIMARY) - return; - - str = html->selected_text; - - if (!str) - return; - - len = strlen(str); - - if (sel_info == TARGET_STRING) - { - gtk_selection_data_set(selection_data, - GDK_SELECTION_TYPE_STRING, - 8 * sizeof(gchar), (guchar *) str, len); - } - else if ((sel_info == TARGET_TEXT) || (sel_info == TARGET_COMPOUND_TEXT)) - { - guchar *text; - GdkAtom encoding; - gint format; - gint new_length; - - gdk_string_to_compound_text(str, &encoding, &format, &text, - &new_length); - gtk_selection_data_set(selection_data, encoding, format, text, - new_length); - gdk_free_compound_text(text); - } - - - -} - -static void do_select(GtkHtml * html, int x, int y) -{ - GList *hbits = g_list_last(html->html_bits); - int epos, - spos; - GtkHtmlBit *hb; - - if (!hbits) - return; - - hb = (GtkHtmlBit *) hbits->data; - - while (hbits) - { - hb = (GtkHtmlBit *) hbits->data; - if (hb->type == HTML_BIT_TEXT) - break; - hbits = hbits->prev; - } - - if (!hb) - return; - - - if (y > hb->y) - { - html->num_end = strlen(hb->text) - 1; - html->end_sel = hb; - } - else if (y < 0) - { - html->num_end = 0; - html->end_sel = (GtkHtmlBit *) html->html_bits->data; - } - else - while (hbits) - { - hb = (GtkHtmlBit *) hbits->data; - if ((y < hb->y && y > (hb->y - hb->height)) && - (x > hb->x + hb->width)) - { - if (hb->type != HTML_BIT_TEXT) - { - html->num_end = 0; - html->end_sel = hb; - break; - } - - html->num_end = strlen(hb->text) - 1; - html->end_sel = hb; - break; - } - else if ((x > hb->x && x < (hb->x + hb->width)) && - (y < hb->y && y > (hb->y - hb->height))) - { - int i, - len; - int w = x - hb->x; - - if (hb->type != HTML_BIT_TEXT) - { - html->num_end = 0; - html->end_sel = hb; - break; - } - - len = strlen(hb->text); - - for (i = 1; i <= len; i++) - { - if (gdk_text_measure(hb->font, hb->text, i) > w) - { - html->num_end = i - 1; - html->end_sel = hb; - break; - } - } - break; - } - hbits = hbits->prev; - } - - if (html->end_sel == NULL) - return; - if (html->start_sel == NULL) - { - html->start_sel = html->end_sel; - html->num_start = html->num_end; - } - - epos = g_list_index(html->html_bits, html->end_sel); - spos = g_list_index(html->html_bits, html->start_sel); - g_free(html->selected_text); - html->selected_text = NULL; - - if (epos == spos) - { - char *str; - if (html->start_sel->type != HTML_BIT_TEXT) - { - html->selected_text = NULL; - return; - } - if (html->num_end == html->num_start) - { - str = g_malloc(2); - if (strlen(html->start_sel->text)) - str[0] = html->start_sel->text[html->num_end]; - else - str[0] = 0; - str[1] = 0; - gtk_html_draw_bit(html, html->start_sel, 0); - html->selected_text = str; - } - else - { - size_t st, - en; - char *str; - if (html->num_end > html->num_start) - { - en = html->num_end; - st = html->num_start; - } - else - { - en = html->num_start; - st = html->num_end; - } - - str = g_malloc(en - st + 2); - strncpy(str, html->start_sel->text + st, (en - st + 1)); - str[en - st + 1] = 0; - gtk_html_draw_bit(html, html->start_sel, 0); - html->selected_text = str; - - } - } - else - { - GtkHtmlBit *shb, - *ehb; - size_t en, - st; - int len, - nlen; - char *str; - if (epos > spos) - { - shb = html->start_sel; - ehb = html->end_sel; - en = html->num_end; - st = html->num_start; - } - else - { - shb = html->end_sel; - ehb = html->start_sel; - en = html->num_start; - st = html->num_end; - } - - hbits = g_list_find(html->html_bits, shb); - - if (!hbits) - return; - - if (shb->type == HTML_BIT_TEXT) - { - len = strlen(shb->text) - st + 1; - str = g_malloc(len); - strcpy(str, shb->text + st); - str[len - 1] = 0; - gtk_html_draw_bit(html, shb, 0); - if (shb->newline) - { - len += 1; - str = g_realloc(str, len); - str[len - 2] = '\n'; - str[len - 1] = 0; - } - } - else - { - len = 1; - str = g_malloc(1); - str[0] = 0; - } - if (hbits->next == NULL) - { - html->selected_text = str; - return; - } - - - hbits = hbits->next; - while (1) - { /* - * Yah I know is dangerous :P - */ - hb = (GtkHtmlBit *) hbits->data; - if (hb->type != HTML_BIT_TEXT) - { - if (hb == ehb) - break; - hbits = hbits->next; - continue; - } - if (hb != ehb) - { - nlen = len + strlen(hb->text); - str = g_realloc(str, nlen); - strcpy(str + (len - 1), hb->text); - len = nlen; - str[len - 1] = 0; - gtk_html_draw_bit(html, hb, 0); - if (hb->newline) - { - len += 1; - str = g_realloc(str, len); - str[len - 2] = '\n'; - str[len - 1] = 0; - } - } - else - { - nlen = len + en + 1; - str = g_realloc(str, nlen); - strncpy(str + (len - 1), hb->text, en + 1); - len = nlen; - str[len - 1] = 0; - - gtk_html_draw_bit(html, hb, 0); - if (hb->newline && en == strlen(hb->text)) - { - len += 1; - str = g_realloc(str, len); - str[len - 2] = '\n'; - str[len - 1] = 0; - } - break; - } - hbits = hbits->next; - } - html->selected_text = str; - } - -} - -static gint scroll_timeout(GtkHtml * html) -{ - GdkEventMotion event; - gint x, - y; - GdkModifierType mask; - - html->timer = 0; - gdk_window_get_pointer(html->html_area, &x, &y, &mask); - - if (mask & GDK_BUTTON1_MASK) - { - event.is_hint = 0; - event.x = x; - event.y = y; - event.state = mask; - - gtk_html_motion_notify(GTK_WIDGET(html), &event); - } - - return FALSE; - -} - - -static gint gtk_html_tooltip_paint_window(GtkHtml * html) -{ - GtkStyle *style; - gint y, - baseline_skip, - gap; - - style = html->tooltip_window->style; - - gap = (style->font->ascent + style->font->descent) / 4; - if (gap < 2) - gap = 2; - baseline_skip = style->font->ascent + style->font->descent + gap; - - if (!html->tooltip_hb) - return FALSE; - - gtk_paint_flat_box(style, html->tooltip_window->window, - GTK_STATE_NORMAL, GTK_SHADOW_OUT, - NULL, GTK_WIDGET(html->tooltip_window), "tooltip", - 0, 0, -1, -1); - - y = style->font->ascent + 4; - - gtk_paint_string(style, html->tooltip_window->window, - GTK_STATE_NORMAL, - NULL, GTK_WIDGET(html->tooltip_window), "tooltip", - 4, y, "HTML Link:"); - y += baseline_skip; - gtk_paint_string(style, html->tooltip_window->window, - GTK_STATE_NORMAL, - NULL, GTK_WIDGET(html->tooltip_window), "tooltip", - 4, y, html->tooltip_hb->url); - - return FALSE; - - -} - -static gint gtk_html_tooltip_timeout(gpointer data) -{ - GtkHtml *html = (GtkHtml *) data; - - - GDK_THREADS_ENTER(); - - if (html->tooltip_hb && GTK_WIDGET_DRAWABLE(GTK_WIDGET(html))) - { - GtkWidget *widget; - GtkStyle *style; - gint gap, - x, - y, - w, - h, - scr_w, - scr_h, - baseline_skip; - - if (html->tooltip_window) - gtk_widget_destroy(html->tooltip_window); - - html->tooltip_window = gtk_window_new(GTK_WINDOW_POPUP); - gtk_widget_set_app_paintable(html->tooltip_window, TRUE); - gtk_window_set_policy(GTK_WINDOW(html->tooltip_window), FALSE, FALSE, - TRUE); - gtk_widget_set_name(html->tooltip_window, "gtk-tooltips"); - gtk_signal_connect_object(GTK_OBJECT(html->tooltip_window), - "expose_event", - GTK_SIGNAL_FUNC - (gtk_html_tooltip_paint_window), - GTK_OBJECT(html)); - gtk_signal_connect_object(GTK_OBJECT(html->tooltip_window), "draw", - GTK_SIGNAL_FUNC - (gtk_html_tooltip_paint_window), - GTK_OBJECT(html)); - - gtk_widget_ensure_style(html->tooltip_window); - style = html->tooltip_window->style; - - widget = GTK_WIDGET(html); - - scr_w = gdk_screen_width(); - scr_h = gdk_screen_height(); - - gap = (style->font->ascent + style->font->descent) / 4; - if (gap < 2) - gap = 2; - baseline_skip = style->font->ascent + style->font->descent + gap; - - w = 8 + MAX(gdk_string_width(style->font, _("HTML Link:")), - gdk_string_width(style->font, html->tooltip_hb->url)); - ; - h = 8 - gap; - h += (baseline_skip * 2); - - gdk_window_get_pointer(NULL, &x, &y, NULL); - /* - * gdk_window_get_origin (widget->window, NULL, &y); - */ - if (GTK_WIDGET_NO_WINDOW(widget)) - y += widget->allocation.y; - - x -= ((w >> 1) + 4); - - if ((x + w) > scr_w) - x -= (x + w) - scr_w; - else if (x < 0) - x = 0; - - if (html->tooltip_hb->font) { - if ((y + h + 4) > scr_h) - y = - y - html->tooltip_hb->font->ascent + - html->tooltip_hb->font->descent; - else - y = - y + html->tooltip_hb->font->ascent + - html->tooltip_hb->font->descent; - } else { - if ((y + h + 4) > scr_h) - y = y - 10 + 4; - else - y = y + 10 + 4; - } - - gtk_widget_set_usize(html->tooltip_window, w, h); - gtk_widget_popup(html->tooltip_window, x, y); - - } - - html->tooltip_timer = -1; - - GDK_THREADS_LEAVE(); - - return FALSE; -} - - -static gint gtk_html_leave_notify(GtkWidget * widget, GdkEventCrossing * event) -{ - GtkHtml *html; - - html = GTK_HTML(widget); - - if (html->tooltip_timer != -1) - gtk_timeout_remove(html->tooltip_timer); - if (html->tooltip_window) - { - gtk_widget_destroy(html->tooltip_window); - html->tooltip_window = NULL; - } - - - html->tooltip_hb = NULL; - return TRUE; -} - - -static gint gtk_html_motion_notify(GtkWidget * widget, GdkEventMotion * event) -{ - int x, - y; - gint width, - height; - GdkModifierType state; - int realx, - realy; - GtkHtml *html = GTK_HTML(widget); - - if (event->is_hint) - gdk_window_get_pointer(event->window, &x, &y, &state); - else - { - x = event->x; - y = event->y; - state = event->state; - } - - gdk_window_get_size(html->html_area, &width, &height); - - realx = x; - realy = y + html->yoffset; - - - if (state & GDK_BUTTON1_MASK) - { - if (realx != html->start_sel_x || realy != html->start_sel_y) - { - char *tmp = NULL; - - if (y < 0 || y > height) - { - int diff; - if (html->timer == 0) - { - html->timer = gtk_timeout_add(100, - (GtkFunction) scroll_timeout, - html); - if (y < 0) - diff = y / 2; - else - diff = (y - height) / 2; - - if (html->vadj->value + diff > - html->vadj->upper - height + 20) - gtk_adjustment_set_value(html->vadj, - html->vadj->upper - height + - 20); - else - gtk_adjustment_set_value(html->vadj, - html->vadj->value + diff); - - } - } - - if (html->selected_text != NULL) - tmp = g_strdup(html->selected_text); - do_select(html, realx, realy); - if (tmp) - { - if (!html->selected_text || strcmp(tmp, html->selected_text)) - { - GtkHtmlBit *hb; - GList *hbits = html->html_bits; - while (hbits) - { - hb = (GtkHtmlBit *) hbits->data; - if (hb->was_selected) - gtk_html_draw_bit(html, hb, 0); - hbits = hbits->next; - } - } - g_free(tmp); - } - } - } - else - { - GtkHtmlBit *hb; - GList *urls; - - urls = html->urls; - while (urls) - { - hb = (GtkHtmlBit *) urls->data; - if ((realx > hb->x && realx < (hb->x + hb->width)) && - (realy < hb->y && realy > (hb->y - hb->height))) - { - GdkCursor *cursor = NULL; - - if (html->tooltip_hb != hb) - { - html->tooltip_hb = hb; - if (html->tooltip_timer != -1) - gtk_timeout_remove(html->tooltip_timer); - if (html->tooltip_window) - { - gtk_widget_destroy(html->tooltip_window); - html->tooltip_window = NULL; - } - html->tooltip_timer = - gtk_timeout_add(HTML_TOOLTIP_DELAY, - gtk_html_tooltip_timeout, html); - } - - cursor = gdk_cursor_new(GDK_HAND2); - gdk_window_set_cursor(html->html_area, cursor); - gdk_cursor_destroy(cursor); - - return TRUE; - } - urls = urls->next; - } - if (html->tooltip_timer != -1) - gtk_timeout_remove(html->tooltip_timer); - if (html->tooltip_window) - { - gtk_widget_destroy(html->tooltip_window); - html->tooltip_window = NULL; - } - - - html->tooltip_hb = NULL; - gdk_window_set_cursor(html->html_area, NULL); - - - } - - return TRUE; -} - -static gint gtk_html_button_release(GtkWidget * widget, GdkEventButton * event) -{ - GtkHtml *html; - - html = GTK_HTML(widget); - - if (html->frozen > 0) - return TRUE; - - if (event->button == 1) - { - int realx, - realy; - GtkHtmlBit *hb; - GList *urls = html->urls; - - realx = event->x; - realy = event->y + html->yoffset; - if (realx != html->start_sel_x || realy != html->start_sel_y) - { - if (gtk_selection_owner_set(widget, - GDK_SELECTION_PRIMARY, event->time)) - { - } - else - { - } - } - else - { - if (gdk_selection_owner_get(GDK_SELECTION_PRIMARY) == - widget->window) - gtk_selection_owner_set(NULL, GDK_SELECTION_PRIMARY, - event->time); - - - while (urls) - { - void open_url_nw(GtkWidget * w, char *url); - hb = (GtkHtmlBit *) urls->data; - if ((realx > hb->x && realx < (hb->x + hb->width)) && - (realy < hb->y && realy > (hb->y - hb->height))) - { - open_url_nw(NULL, hb->url); - // else - // open_url(NULL, hb->url); - break; - } - urls = urls->next; - } - } - } - return TRUE; -} - - - -static gint gtk_html_button_press(GtkWidget * widget, GdkEventButton * event) -{ - GtkHtml *html; - gfloat value; - - - html = GTK_HTML(widget); - value = html->vadj->value; - - if (html->frozen > 0) - return TRUE; - - if (event->button == 4) - { - value -= html->vadj->step_increment; - if (value < html->vadj->lower) - value = html->vadj->lower; - gtk_adjustment_set_value(html->vadj, value); - } - else if (event->button == 5) - { - value += html->vadj->step_increment; - if (value > html->vadj->upper) - value = html->vadj->upper; - gtk_adjustment_set_value(html->vadj, value); - - } - else if (event->button == 1) - { - GList *hbits = g_list_last(html->html_bits); - int realx, - realy; - GtkHtmlBit *hb; - - realx = event->x; - realy = event->y + html->yoffset; - - html->start_sel_x = realx; - html->start_sel_y = realy; - - if (!hbits) - return TRUE; - - if (html->selected_text) - { - gboolean forcedraw = FALSE; - hbits = html->html_bits; - g_free(html->selected_text); - html->selected_text = NULL; - html->start_sel = NULL; - html->end_sel = NULL; - html->num_start = 0; - html->num_end = 0; - while (hbits) - { - hb = (GtkHtmlBit *) hbits->data; - if (hb->was_selected || forcedraw) { - gtk_html_draw_bit(html, hb, 1); - forcedraw = TRUE; - } - hbits = hbits->next; - } - hbits = g_list_last(html->html_bits); - } - - hb = (GtkHtmlBit *) hbits->data; - if (realy > hb->y) - { - if (hb->text) - html->num_start = strlen(hb->text) - 1; - else - html->num_start = 0; - html->start_sel = hb; - } - else - while (hbits) - { - hb = (GtkHtmlBit *) hbits->data; - if ((realy < hb->y && realy > (hb->y - hb->height)) && - (realx > hb->x + hb->width)) - { - if (hb->type != HTML_BIT_TEXT) - { - html->num_end = 0; - html->end_sel = hb; - break; - } - - if (hb->text) - html->num_start = strlen(hb->text) - 1; - else - html->num_start = 0; - - html->start_sel = hb; - break; - } - else if ((realx > hb->x && realx < (hb->x + hb->width)) && - (realy < hb->y && realy > (hb->y - hb->height))) - { - int i, - len; - int w = realx - hb->x; - - if (hb->type != HTML_BIT_TEXT) - { - html->num_end = 0; - html->end_sel = hb; - break; - } - - if (hb->text) - len = strlen(hb->text); - else - len = 0; - - for (i = 1; i <= len; i++) - { - if (gdk_text_measure(hb->font, hb->text, i) > w) - { - html->num_start = i - 1; - html->start_sel = hb; - break; - } - } - break; - } - hbits = hbits->prev; - } - } - else if (event->button == 3 && event->type == GDK_BUTTON_PRESS) - { - GtkHtmlBit *hb = NULL; - int realx, - realy; - GList *urls; - - realx = event->x; - realy = event->y + html->yoffset; - - urls = html->urls; - while (urls) - { - hb = (GtkHtmlBit *) urls->data; - if ((realx > hb->x && realx < (hb->x + hb->width)) && - (realy < hb->y && realy > (hb->y - hb->height))) - { - break; - } - urls = urls->next; - hb = NULL; - } - - if (hb != NULL) - { - - GtkWidget *menu, *button; - - menu = gtk_menu_new(); - - if (web_browser == BROWSER_NETSCAPE) { - - button = gtk_menu_item_new_with_label(_("Open URL in existing window")); - gtk_signal_connect(GTK_OBJECT(button), "activate", - GTK_SIGNAL_FUNC(open_url), hb->url); - gtk_menu_append(GTK_MENU(menu), button); - gtk_widget_show(button); - - } - - - button = gtk_menu_item_new_with_label(_("Open URL in new window")); - gtk_signal_connect(GTK_OBJECT(button), "activate", - GTK_SIGNAL_FUNC(open_url_nw), hb->url); - gtk_menu_append(GTK_MENU(menu), button); - gtk_widget_show(button); - - if (web_browser == BROWSER_NETSCAPE) { - - button = gtk_menu_item_new_with_label(_("Add URL as bookmark")); - gtk_signal_connect(GTK_OBJECT(button), "activate", - GTK_SIGNAL_FUNC(add_bookmark), hb->url); - gtk_menu_append(GTK_MENU(menu), button); - gtk_widget_show(button); - - } - - gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, - event->button, event->time); - - } - } - - return TRUE; -} - - -static void gtk_html_draw_bit(GtkHtml * html, GtkHtmlBit * hb, int redraw) -{ - int mypos, - epos, - spos; - GdkGC *gc = html->gc; - int shift; - GtkStateType selected_state; - GtkWidget *widget = GTK_WIDGET(html); - GdkRectangle area; - GList *hbits; - - if (html->frozen > 0) - return; - - hbits = g_list_find(html->html_bits, hb); - - if (hb->type == HTML_BIT_TEXT) - { - - if (!(hb->text)) - return; - /* this is possible, don't comment it out >:P */ - if (!strlen(hb->text)) - return; - - mypos = g_list_index(html->html_bits, hb); - epos = g_list_index(html->html_bits, html->end_sel); - spos = g_list_index(html->html_bits, html->start_sel); - - if (((html->end_sel == NULL) || (html->start_sel == NULL)) || - ((epos < mypos) && (spos < mypos)) || - ((epos > mypos) && (spos > mypos))) - { - selected_state = GTK_STATE_NORMAL; - } - else - { - selected_state = GTK_STATE_SELECTED; - } - - gdk_text_extents(hb->font, hb->text, 1, &shift, NULL, NULL, NULL, NULL); - - if (selected_state == GTK_STATE_SELECTED) - { - int schar = 0, - echar = 0; - int startx = 0, - xwidth = 0; - - if (epos > spos || - (epos == spos && html->num_end >= html->num_start)) - { - if (mypos == epos) - { - echar = html->num_end; - xwidth = - gdk_text_width(hb->font, hb->text, html->num_end + 1); - } - else - { - echar = strlen(hb->text); - xwidth = hb->width; - } - if (mypos == spos) - { - schar = html->num_start; - startx = - gdk_text_width(hb->font, hb->text, html->num_start); - xwidth -= startx; - } - } - else - { - if (mypos == spos) - { - echar = html->num_start; - xwidth = - gdk_text_width(hb->font, hb->text, - html->num_start + 1); - } - else - { - echar = strlen(hb->text); - xwidth = hb->width; - } - if (mypos == epos) - { - schar = html->num_end; - startx = - gdk_text_width(hb->font, hb->text, html->num_end); - xwidth -= startx; - } - } - - if (!redraw && echar == hb->sel_e && schar == hb->sel_s) - return; - - hb->sel_e = echar; - hb->sel_s = schar; - - startx += hb->x; - - area.x = hb->x - html->xoffset; - area.y = hb->y - hb->height + 3 - html->yoffset; - area.width = hb->width + 2; - area.height = hb->height; - clear_area(html, &area); - - gtk_paint_flat_box(widget->style, html->html_area, - selected_state, GTK_SHADOW_NONE, - NULL, widget, "text", - startx, - hb->y - hb->height + 3 - html->yoffset, - xwidth + 2, hb->height); - hb->was_selected = 1; - } - else if (hb->was_selected) - { - area.x = hb->x - html->xoffset; - area.y = hb->y - hb->height + 3 - html->yoffset; - area.width = hb->width + 2; - area.height = hb->height; - clear_area(html, &area); - - hb->sel_e = -1; - hb->sel_s = -1; - - hb->was_selected = 0; - } - - if (selected_state == GTK_STATE_SELECTED && (mypos == epos - || mypos == spos)) - { - char *s = hb->text; - int num = 0, - width = 0, - fsel = 0, - esel = strlen(hb->text); - int lbearing, - rbearing, - w; - - if (epos > spos || - (epos == spos && html->num_end >= html->num_start)) - { - if (mypos == epos) - esel = html->num_end; - if (mypos == spos) - fsel = html->num_start; - } - else - { - if (mypos == spos) - esel = html->num_start; - if (mypos == epos) - fsel = html->num_end; - } - - while (*s) - { - - if (num < fsel || num > esel) - selected_state = GTK_STATE_NORMAL; - else - selected_state = GTK_STATE_SELECTED; - if (hb->fore != NULL) - gdk_gc_set_foreground(gc, hb->fore); - else - gdk_gc_set_foreground(gc, - &widget->style->fg[selected_state]); - - gdk_gc_set_font(gc, hb->font); - - gdk_text_extents(hb->font, s, 1, &lbearing, &rbearing, &w, NULL, - NULL); - - gdk_draw_text(html->html_area, hb->font, gc, - shift + hb->x + width, hb->y - html->yoffset, s, - 1); - - if (hb->uline) - gdk_draw_line(html->html_area, gc, shift + hb->x + width, - hb->y - html->yoffset, - shift + hb->x + width + w, - hb->y - html->yoffset); - - if (hb->strike) - gdk_draw_line(html->html_area, gc, shift + hb->x + width, - hb->y - html->yoffset - (hb->height / 3), - shift + hb->x + width + w, - hb->y - html->yoffset - (hb->height / 3)); - - width += w; - - s++; - num++; - } - - - } - else - { - /*my stuff here*/ - - if(!hb->was_selected) - { - area.x = hb->x - html->xoffset; - area.y = hb->y - hb->height + 3 - html->yoffset; - area.width = hb->width + 2; - area.height = hb->height; - clear_area(html, &area); - } - - /*end my stuff*/ - - if (hb->text && hb->back != NULL) { - int hwidth, hheight, hei, tmpcnt; - hei = get_line_height(html, hb); - gdk_window_get_size(html->html_area, &hwidth, &hheight); - gdk_gc_set_foreground(gc, hb->back); - /* we use a 2-pixel window border */ - if (hb->x < 2) - hb->x = 2; - gdk_draw_rectangle(html->html_area, gc, TRUE /* filled */, - hb->x, hb->y - html->yoffset - hei - 6, - hwidth - shift - hb->x + 1, hei + hei + 2); - for (tmpcnt = 1; tmpcnt < hb->newline; tmpcnt++) { - int eoff = hei + hei + 2; - eoff *= tmpcnt; - gdk_draw_rectangle(html->html_area, gc, TRUE, - 2, hb->y - html->yoffset - hei - 6 + eoff, - hwidth, hei + hei + 2); - } - } - - if (hb->fore != NULL) - gdk_gc_set_foreground(gc, hb->fore); - else - gdk_gc_set_foreground(gc, &widget->style->fg[selected_state]); - - gdk_gc_set_font(gc, hb->font); - - gdk_draw_string(html->html_area, hb->font, gc, shift + hb->x, - hb->y - html->yoffset, hb->text); - if (hb->uline) - gdk_draw_line(html->html_area, gc, shift + hb->x, - hb->y - html->yoffset, - hb->x + gdk_string_measure(hb->font, hb->text), - hb->y - html->yoffset); - - if (hb->strike) - gdk_draw_line(html->html_area, gc, shift + hb->x, - hb->y - html->yoffset - (hb->height / 3), - hb->x + gdk_string_measure(hb->font, hb->text), - hb->y - html->yoffset - (hb->height / 3)); - - } - } - else if (hb->type == HTML_BIT_SEP) - { - if (hb->back != NULL) { - int hwidth, hheight, hei, tmpcnt; - hei = get_line_height(html, hb); - gdk_window_get_size(html->html_area, &hwidth, &hheight); - gdk_gc_set_foreground(gc, hb->back); - gdk_draw_rectangle(html->html_area, gc, TRUE, - 2, hb->y - html->yoffset, - hwidth, 5); - for (tmpcnt = 0; tmpcnt < hb->newline; tmpcnt++) { - int eoff = hei + hei + 2; - eoff *= tmpcnt; - eoff += 5; - gdk_draw_rectangle(html->html_area, gc, TRUE, - 2, hb->y - html->yoffset + eoff, - hwidth, hei + hei + 2); - } - } - - if (hb->fore != NULL) - gdk_gc_set_foreground(gc, hb->fore); - else - gdk_gc_set_foreground(gc, &widget->style->fg[GTK_STATE_NORMAL]); - - gdk_draw_line(html->html_area, gc, hb->x + 2, - hb->y - html->yoffset - (hb->height / 2 - 1), - hb->x + hb->width, - hb->y - html->yoffset - (hb->height / 2 - 1)); - - } - else if (hb->type == HTML_BIT_PIXMAP) - { - area.x = hb->x - html->xoffset; - area.y = hb->y - hb->height + 5 - html->yoffset; - area.width = hb->width; - area.height = hb->height; - clear_area(html, &area); - if (hb->back != NULL) { - int hwidth, hheight, hei, tmpcnt; - hei = get_line_height(html, hb); - gdk_window_get_size(html->html_area, &hwidth, &hheight); - gdk_gc_set_foreground(gc, hb->back); - for (tmpcnt = 0; tmpcnt < hb->newline; tmpcnt++) { - int eoff = hei + hei + 2; - eoff *= tmpcnt; - eoff += 5; - gdk_draw_rectangle(html->html_area, gc, TRUE, - 2, hb->y - html->yoffset + eoff, - hwidth, hei + hei + 2); - } - } - - if (hb->fore != NULL) - gdk_gc_set_foreground(gc, hb->fore); - else - gdk_gc_set_foreground(gc, &widget->style->fg[GTK_STATE_NORMAL]); - - gdk_draw_pixmap(html->html_area, gc, hb->pm, 0, 0, hb->x, - hb->y - html->yoffset - (hb->height) + 4, -1, -1); - } -} - - - -gint compare_types(GtkHtmlBit * hb, GtkHtmlBit * hb2) -{ - /* - * In this function, it's OK to accidently return a - * * 0, but will cause problems on an accidental 1 - */ - - if (!hb || !hb2) - return 0; - - - if (hb->uline != hb2->uline) - return 0; - if (hb->strike != hb2->strike) - return 0; - if (hb->font && hb2->font) - { - if (!gdk_font_equal(hb->font, hb2->font)) - return 0; - } - else if (hb->font && !hb2->font) - { - return 0; - } - else if (!hb->font && hb2->font) - { - return 0; - } - if (hb->type != hb2->type) - return 0; - - if (hb->fore && hb2->fore) - { - if (!gdk_color_equal(hb->fore, hb2->fore)) - return 0; - } - else if (hb->fore && !hb2->fore) - { - return 0; - } - else if (!hb->fore && hb2->fore) - { - return 0; - } - - if (hb->back && hb2->back) - { - if (!gdk_color_equal(hb->back, hb2->back)) - return 0; - } - else if (hb->back && !hb2->back) - { - return 0; - } - else if (!hb->back && hb2->back) - { - return 0; - } - - if ((hb->url != NULL && hb2->url == NULL) || - (hb->url == NULL && hb2->url != NULL)) - return 0; - - if (hb->url != NULL && hb2->url != NULL) - if (strcasecmp(hb->url, hb2->url)) - return 0; - - return 1; -} - -static gint html_bit_is_onscreen(GtkHtml * html, GtkHtmlBit * hb) -{ - gint width, - height; - - gdk_window_get_size(html->html_area, &width, &height); - - if (hb->y < html->yoffset) - { - return 0; - } - - if ((hb->y - hb->height) > (html->yoffset + height)) - { - return 0; - } - return 1; -} - -static void draw_cursor(GtkHtml * html) -{ - if (html->editable && - html->cursor_hb && - GTK_WIDGET_DRAWABLE(html) && - html_bit_is_onscreen(html, html->cursor_hb)) - { - gint x, - y; - gint width; - - GdkFont *font = html->cursor_hb->font; - - gdk_text_extents(font, html->cursor_hb->text, html->cursor_pos, NULL, - NULL, &width, NULL, NULL); - - gdk_gc_set_foreground(html->gc, - >K_WIDGET(html)->style->text[GTK_STATE_NORMAL]); - - y = html->cursor_hb->y - html->yoffset; - x = html->cursor_hb->x + width; - - - gdk_draw_line(html->html_area, html->gc, x, y, x, y - font->ascent); - - } -} - -static void undraw_cursor(GtkHtml * html) -{ - if (html->editable && - html->cursor_hb && - GTK_WIDGET_DRAWABLE(html) && - html_bit_is_onscreen(html, html->cursor_hb)) - { - gint x, - y; - gint width; - GdkRectangle area; - - GdkFont *font = html->cursor_hb->font; - - gdk_text_extents(font, html->cursor_hb->text, html->cursor_pos, NULL, - NULL, &width, NULL, NULL); - - y = html->cursor_hb->y - html->yoffset; - x = html->cursor_hb->x + width; - - area.x = x; - area.y = y - font->ascent; - area.height = font->ascent + 1; - area.width = 1; - - - clear_area(html, &area); - - gtk_html_draw_bit(html, html->cursor_hb, 1); - - - } -} - - -static void expose_html(GtkHtml * html, GdkRectangle * area, gboolean cursor) -{ - GList *hbits; - GtkHtmlBit *hb; - gint width, - height; - gint realy; - - - if (html->frozen > 0) - return; - - - hbits = html->html_bits; - - gdk_window_get_size(html->html_area, &width, &height); - - realy = area->y + html->yoffset; - - /* this is needed since background colors draw across the entire window width - if anyone knows of a cleaner way to work bg colors, please submit code =) */ - area->x = 0; - area->width = width; - - clear_area(html, area); - - while (hbits) - { - - hb = (GtkHtmlBit *) hbits->data; - - if (html_bit_is_onscreen(html, hb)) - gtk_html_draw_bit(html, hb, 1); - - - hbits = hbits->next; - } -} - -static void resize_html(GtkHtml * html) -{ - GList *hbits = html->html_bits; - GList *html_bits = html->html_bits; - GtkHtmlBit *hb, - *hb2; - char *str; - gint height; - - if (!hbits) - return; - - - html->html_bits = NULL; - - html->current_x = 0; - html->current_y = 0; - - html->vadj->upper = 0; - - gtk_html_freeze(html); - - while (hbits) - { - hb = (GtkHtmlBit *) hbits->data; - if (hb->type == HTML_BIT_SEP) - { - - gtk_html_add_seperator(html, hb->font, hb->fore, hb->back); - - g_free(hb); - - hbits = hbits->next; - continue; - } - if (hb->type == HTML_BIT_PIXMAP) - { - - gtk_html_add_pixmap(html, hb->pm, hb->fit, hb->newline); - - g_free(hb); - - hbits = hbits->next; - continue; - } - - if (hb->newline) - { - int i; - - if (!hb->text) - { - hb->text = g_malloc(1); - hb->text[0] = 0; - } - for (i = 0; i < hb->newline; i++) - { - str = hb->text; - hb->text = g_strconcat(str, "\n", NULL); - if (str) g_free(str); - } - } - - if (hbits->next) - { - hb2 = (GtkHtmlBit *) hbits->next->data; - } - else - { - hb2 = NULL; - } - - - - if (!hb->newline && compare_types(hb, hb2)) - { - str = hb2->text; - hb2->text = g_strconcat(hb->text, hb2->text, NULL); - if (str) g_free(str); - hb2 = NULL; - } - else if (hb->text) - { - gtk_html_add_text(html, hb->font, hb->fore, hb->back, - hb->text, strlen(hb->text), hb->uline, hb->strike, - hb->url); - } - - - - /* - * Font stays, so do colors (segfaults if I free) - */ - if (hb->fore) - gdk_color_free(hb->fore); - if (hb->back) - gdk_color_free(hb->back); - if (hb->text) - g_free(hb->text); - if (hb->url) - g_free(hb->url); - - g_free(hb); - - hbits = hbits->next; - } - - g_list_free(html_bits); - - - gtk_html_thaw(html); - - gdk_window_get_size(html->html_area, NULL, &height); - gtk_adjustment_set_value(html->vadj, html->vadj->upper - height); - -} - -static GdkGC *create_bg_gc(GtkHtml * html) -{ - GdkGCValues values; - - values.tile = GTK_WIDGET(html)->style->bg_pixmap[GTK_STATE_NORMAL]; - values.fill = GDK_TILED; - - return gdk_gc_new_with_values(html->html_area, &values, - GDK_GC_FILL | GDK_GC_TILE); -} - -static void clear_area(GtkHtml * html, GdkRectangle * area) -{ - GtkWidget *widget = GTK_WIDGET(html); - gint x, - y; - - - if (html->transparent) - { - if (html->pm == NULL) - html->pm = get_desktop_pixmap(widget); - - if (html->pm == NULL) - return; - - if (html->bg_gc == NULL) - { - GdkGCValues values; - - values.tile = html->pm; - values.fill = GDK_TILED; - - html->bg_gc = gdk_gc_new_with_values(html->html_area, &values, - GDK_GC_FILL | GDK_GC_TILE); - - } - - gdk_window_get_deskrelative_origin(html->html_area, &x, &y); - - gdk_draw_pixmap(html->html_area, html->bg_gc, html->pm, - x + area->x, y + area->y, area->x, area->y, area->width, - area->height); - - return; - - } - if (html->bg_gc) - { - - gint width, - height; - - gdk_window_get_size(widget->style->bg_pixmap[GTK_STATE_NORMAL], &width, - &height); - - gdk_gc_set_ts_origin(html->bg_gc, - (-html->xoffset) % width, - (-html->yoffset) % height); - - gdk_draw_rectangle(html->html_area, html->bg_gc, TRUE, - area->x, area->y, area->width, area->height); - } - else - gdk_window_clear_area(html->html_area, area->x, area->y, area->width, - area->height); -} - - - - -static void gtk_html_destroy(GtkObject * object) -{ - GtkHtml *html; - - g_return_if_fail(object != NULL); - g_return_if_fail(GTK_IS_HTML(object)); - - html = (GtkHtml *) object; - - - gtk_signal_disconnect_by_data(GTK_OBJECT(html->hadj), html); - gtk_signal_disconnect_by_data(GTK_OBJECT(html->vadj), html); - - if (html->timer) - { - gtk_timeout_remove(html->timer); - html->timer = 0; - } - - if (html->tooltip_timer) - { - gtk_timeout_remove(html->tooltip_timer); - html->tooltip_timer = -1; - } - - - GTK_OBJECT_CLASS(parent_class)->destroy(object); - -} - -static void gtk_html_finalize(GtkObject * object) -{ - GList *hbits; - GtkHtml *html; - GtkHtmlBit *hb; - - - g_return_if_fail(object != NULL); - g_return_if_fail(GTK_IS_HTML(object)); - - html = (GtkHtml *) object; - - gtk_object_unref(GTK_OBJECT(html->hadj)); - gtk_object_unref(GTK_OBJECT(html->vadj)); - - hbits = html->html_bits; - - while (hbits) - { - hb = (GtkHtmlBit *) hbits->data; - if (hb->fore) - gdk_color_free(hb->fore); - if (hb->back) - gdk_color_free(hb->back); - if (hb->text) - g_free(hb->text); - if (hb->url) - g_free(hb->url); - if (hb->pm) - gdk_pixmap_unref(hb->pm); - - g_free(hb); - hbits = hbits->next; - } - if (html->html_bits) - g_list_free(html->html_bits); - - if (html->urls) - g_list_free(html->urls); - - if (html->selected_text) - g_free(html->selected_text); - - if (html->gc) - gdk_gc_destroy(html->gc); - - if (html->bg_gc) - gdk_gc_destroy(html->bg_gc); - - if (html->tooltip_window) - gtk_widget_destroy(html->tooltip_window); - - GTK_OBJECT_CLASS(parent_class)->finalize(object); -} - -static void gtk_html_realize(GtkWidget * widget) -{ - GtkHtml *html; - GdkWindowAttr attributes; - gint attributes_mask; - - g_return_if_fail(widget != NULL); - g_return_if_fail(GTK_IS_HTML(widget)); - - html = GTK_HTML(widget); - GTK_WIDGET_SET_FLAGS(html, GTK_REALIZED); - - attributes.window_type = GDK_WINDOW_CHILD; - attributes.x = widget->allocation.x; - attributes.y = widget->allocation.y; - attributes.width = widget->allocation.width; - attributes.height = widget->allocation.height; - attributes.wclass = GDK_INPUT_OUTPUT; - attributes.visual = gtk_widget_get_visual(widget); - attributes.colormap = gtk_widget_get_colormap(widget); - attributes.event_mask = gtk_widget_get_events(widget); - attributes.event_mask |= (GDK_EXPOSURE_MASK | - GDK_BUTTON_PRESS_MASK | - GDK_BUTTON_RELEASE_MASK | - GDK_BUTTON_MOTION_MASK | - GDK_ENTER_NOTIFY_MASK | - GDK_LEAVE_NOTIFY_MASK | - GDK_POINTER_MOTION_MASK | - GDK_POINTER_MOTION_HINT_MASK | - GDK_VISIBILITY_NOTIFY_MASK | GDK_KEY_PRESS_MASK); - - attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP; - - widget->window = - gdk_window_new(gtk_widget_get_parent_window(widget), &attributes, - attributes_mask); - gdk_window_set_user_data(widget->window, html); - - attributes.x = (widget->style->klass->xthickness + BORDER_WIDTH); - attributes.y = (widget->style->klass->ythickness + BORDER_WIDTH); - attributes.width = - MAX(1, (gint) widget->allocation.width - (gint) attributes.x * 2); - attributes.height = - MAX(1, (gint) widget->allocation.height - (gint) attributes.y * 2); - - html->html_area = - gdk_window_new(widget->window, &attributes, attributes_mask); - gdk_window_set_user_data(html->html_area, html); - - widget->style = gtk_style_attach(widget->style, widget->window); - - /* - * Can't call gtk_style_set_background here because it's handled specially - */ - gdk_window_set_background(widget->window, - &widget->style->base[GTK_STATE_NORMAL]); - gdk_window_set_background(html->html_area, - &widget->style->base[GTK_STATE_NORMAL]); - - if (widget->style->bg_pixmap[GTK_STATE_NORMAL]) - html->bg_gc = create_bg_gc(html); - - html->gc = gdk_gc_new(html->html_area); - gdk_gc_set_exposures(html->gc, TRUE); - gdk_gc_set_foreground(html->gc, &widget->style->text[GTK_STATE_NORMAL]); - - gdk_window_show(html->html_area); - -} - -static void gtk_html_style_set(GtkWidget * widget, GtkStyle * previous_style) -{ - GtkHtml *html; - - g_return_if_fail(widget != NULL); - g_return_if_fail(GTK_IS_HTML(widget)); - - html = GTK_HTML(widget); - if (GTK_WIDGET_REALIZED(widget)) - { - gdk_window_set_background(widget->window, - &widget->style->base[GTK_STATE_NORMAL]); - gdk_window_set_background(html->html_area, - &widget->style->base[GTK_STATE_NORMAL]); - - if (html->bg_gc) - { - gdk_gc_destroy(html->bg_gc); - html->bg_gc = NULL; - } - - if (widget->style->bg_pixmap[GTK_STATE_NORMAL]) - { - html->bg_gc = create_bg_gc(html); - } - - } -} - -static void gtk_html_unrealize(GtkWidget * widget) -{ - GtkHtml *html; - - g_return_if_fail(widget != NULL); - g_return_if_fail(GTK_IS_HTML(widget)); - - html = GTK_HTML(widget); - - gdk_window_set_user_data(html->html_area, NULL); - gdk_window_destroy(html->html_area); - html->html_area = NULL; - - gdk_gc_destroy(html->gc); - html->gc = NULL; - - if (html->bg_gc) - { - gdk_gc_destroy(html->bg_gc); - html->bg_gc = NULL; - } - - if (GTK_WIDGET_CLASS(parent_class)->unrealize) - (*GTK_WIDGET_CLASS(parent_class)->unrealize) (widget); -} - - -void gtk_html_add_pixmap(GtkHtml * html, GdkPixmap * pm, int fit, int newline) -{ - GtkHtmlBit *last_hb; - GtkHtmlBit *hb = g_new0(GtkHtmlBit, 1); - GdkWindowPrivate *private = (GdkWindowPrivate *) pm; - int width, height; - - last_hb = (GtkHtmlBit *) g_list_last(html->html_bits)->data; - - /* wrap pixmaps */ - gdk_window_get_size(html->html_area, &width, &height); - if ((html->current_x + private->width) >= width) { - html->current_x = 0; - } - - hb->fit = fit; - html->current_x += 2; - hb->x = html->current_x; - hb->y = html->current_y; - if (fit) - hb->height = last_hb->height; - else - hb->height = private->height; - hb->type = HTML_BIT_PIXMAP; - hb->width = private->width; - hb->text = NULL; - hb->url = NULL; - if (last_hb->fore) hb->fore = gdk_color_copy(last_hb->fore); - else hb->fore = NULL; - if (last_hb->back) hb->back = gdk_color_copy(last_hb->back); - else hb->back = NULL; - hb->font = last_hb->font; - hb->uline = 0; - hb->strike = 0; - hb->was_selected = 0; - hb->newline = newline; - hb->pm = pm; - - if (html->current_x == BORDER_WIDTH) - { - html->current_y += hb->height + 3; - hb->y += hb->height + 3; - } - - - html->current_x += hb->width + 1; - - gtk_html_draw_bit(html, hb, 1); - - if (hb->newline) - html->current_x = 0; - - html->html_bits = g_list_append(html->html_bits, hb); - - -} - -static void gtk_html_add_seperator(GtkHtml * html, GdkFont *font, GdkColor *fore, GdkColor *back) -{ - GtkHtmlBit *hb = g_new0(GtkHtmlBit, 1); - gint width, - height; - - html->current_x = 0; - html->current_y += 5; - - gdk_window_get_size(html->html_area, &width, &height); - - hb->x = html->current_x; - hb->y = html->current_y; - hb->height = 5; - hb->type = HTML_BIT_SEP; - hb->width = - width - - GTK_SCROLLED_WINDOW(GTK_WIDGET(html)->parent)->vscrollbar->allocation. - width - 10; - hb->text = NULL; - hb->url = NULL; - if (fore) - hb->fore = gdk_color_copy(fore); - else - hb->fore = NULL; - - if (back) - hb->back = gdk_color_copy(back); - else - hb->back = NULL; - hb->font = font; - hb->uline = 0; - hb->strike = 0; - hb->was_selected = 0; - hb->newline = 0; - hb->pm = NULL; - - gtk_html_draw_bit(html, hb, 1); - - html->html_bits = g_list_append(html->html_bits, hb); - -} - -static void gtk_html_add_text(GtkHtml * html, - GdkFont * cfont, - GdkColor * fore, - GdkColor * back, - char *chars, - gint length, gint uline, gint strike, char *url) -{ - char *nextline = NULL, - *c, - *text, - *tmp; - GdkGC *gc; - int nl = 0, - nl2 = 0; - int maxwidth; - gint lb; - GList *hbits; - size_t num = 0; - int i, - height; - GtkHtmlBit *hb; - gint hwidth, - hheight; - - if (length == 1 && chars[0] == '\n') - { - GtkHtmlBit *h; - hbits = g_list_last(html->html_bits); - if (!hbits) - return; - /* - * I realize this loses a \n sometimes - * * if it's the first thing in the widget. - * * so fucking what. - */ - - h = (GtkHtmlBit *) hbits->data; - h->newline++; - if (html->current_x > 0) - html->current_x = 0; - else - html->current_y += cfont->ascent + cfont->descent + 5; - return; - } - - - - c = text = g_malloc(length + 2); - strncpy(text, chars, length); - text[length] = 0; - - - gc = html->gc; - - if (gc == NULL) - gc = html->gc = gdk_gc_new(html->html_area); - - gdk_gc_set_font(gc, cfont); - - - while (*c) - { - if (*c == '\n') - { - if (*(c + 1) == '\0') - { - nl = 1; - length--; - c[0] = '\0'; - break; - } - if (*c) - { - gtk_html_add_text(html, cfont, fore, back, text, num + 1, uline, - strike, url); - tmp = text; - length -= (num + 1); - text = g_malloc(length + 2); - strncpy(text, (c + 1), length); - text[length] = 0; - c = text; - num = 0; - g_free(tmp); - continue; - } - } - - num++; - c++; - } - - /* - * Note, yG is chosen because G is damn high, and y is damn low, - */ - /* - * it should be just fine. :) - */ - - gdk_window_get_size(html->html_area, &hwidth, &hheight); - - num = strlen(text); - - while (GTK_WIDGET(html)->allocation.width < 20) - { - while (gtk_events_pending()) - gtk_main_iteration(); - } - - maxwidth = (hwidth - html->current_x - 8); - /* - * HTK_SCROLLED_WINDOW(GTK_WIDGET(layout)->parent)->vscrollbar->allocation.width) - 8; - */ - - if ((maxwidth == (hwidth - 8) && gdk_text_measure(cfont, text, num) > 2 * maxwidth) || - gdk_text_measure(cfont, text, num) < 0) { - int pos = num / 2; - static int count = 0; - count ++; - while (pos < num && (!isspace(text[pos]) || text[pos] == '\n')) pos++; - if (pos >= num - 1) { - pos = num/2; - while (pos > 0 && (!isspace(text[pos]) || text[pos] == '\n')) pos--; - if (!pos) pos = num / 2; - } - gtk_html_add_text(html, cfont, fore, back, text, pos + 1, uline, strike, url); - gtk_html_add_text(html, cfont, fore, back, &text[pos+1], 1, uline, strike, url); - gtk_html_add_text(html, cfont, fore, back, &text[pos+2], num - pos + 1, uline, strike, url); - g_free(text); - count--; - if (!count) { - /* FIXME : sometimes we need to add newline, sometimes we don't */ - hbits = g_list_last(html->html_bits); - if (!hbits) return; /* does this ever happen? */ - hb = (GtkHtmlBit *)hbits->data; - hb->newline++; - } - return; - } - - while (gdk_text_measure(cfont, text, num) > maxwidth) - { - if (num > 1) - num--; - else - { - if (html->current_x != 0) { - html->current_x = 0; - if (nl) { - text[length] = '\n'; - length++; - } - gtk_html_add_text(html, cfont, fore, back, text, length, uline, strike, url); - g_free(text); - return; - } else { - num = strlen (text); - break; - } - } - - } - - height = cfont->ascent + cfont->descent + 2; - - - if ((int) (html->vadj->upper - html->current_y) < (int) (height * 2)) - { - int val; - val = (height * 2) + html->current_y; - html->vadj->upper = val; - adjust_adj(html, html->vadj); - } - - - if (html->current_x == 0) - { - html->current_y += height + 3; - gdk_text_extents(cfont, text, 1, &lb, NULL, NULL, NULL, NULL); - html->current_x += (2 - lb); - } - else if ((hbits = g_list_last(html->html_bits)) != NULL) - { - int diff, - y; - hb = (GtkHtmlBit *) hbits->data; - if (height > hb->height) - { - diff = height - hb->height; - y = hb->y; - html->current_y += diff; - while (hbits) - { - hb = (GtkHtmlBit *) hbits->data; - if (hb->y != y) - break; - if (hb->type != HTML_BIT_PIXMAP) - hb->height = height; - hb->y += diff; ////////////my thing here ///////////////// - gtk_html_draw_bit(html, hb, FALSE); - - hbits = hbits->prev; - } - } - } - - - - - if (num != strlen(text)) - { - /* - * This is kinda cheesy but it may make things - * * much better lookin - */ - - for (i=2; (num > i); i++) { - if (text[num - i] == ' ') { - num = num - (i - 1); - nl2 = 1; - break; - } - } - - nextline = g_malloc(length - num + 2); - strncpy(nextline, (char *) (text + num), length - num); - nextline[length - num] = 0; - if (nl) - { - nextline[length - num] = '\n'; - nextline[length - num + 1] = 0; - nl = 0; - } - - - text[num] = 0; - } - - - if (url != NULL) { - fore = get_color(3355647, gdk_window_get_colormap(html->html_area)); - } - - hb = g_new0(GtkHtmlBit, 1); - - hb->text = g_strdup(text); - - if (fore) - hb->fore = gdk_color_copy(fore); - else - hb->fore = NULL; - - if (back) - hb->back = gdk_color_copy(back); - else - hb->back = NULL; - hb->font = cfont; - hb->uline = uline; - hb->strike = strike; - hb->height = height; - gdk_text_extents(cfont, text, num, &lb, NULL, &hb->width, NULL, NULL); - hb->x = html->current_x; - hb->y = html->current_y; - hb->type = HTML_BIT_TEXT; - hb->pm = NULL; - if (url != NULL) - { - uline = 1; - hb->uline = 1; - hb->url = g_strdup(url); - } - else - { - hb->url = NULL; - } - html->current_x += hb->width; - - html->html_bits = g_list_append(html->html_bits, hb); - if (url != NULL) - { - html->urls = g_list_append(html->urls, hb); - } - - - - gtk_html_draw_bit(html, hb, 1); - - if (nl || nl2) - { - if (nl) - hb->newline = 1; - html->current_x = 0; - } - else - hb->newline = 0; - - - if (nextline != NULL) - { - gtk_html_add_text(html, cfont, fore, back, nextline, strlen(nextline), - uline, strike, url); - g_free(nextline); - } - - g_free(text); - if (url != NULL) - g_free(fore); -} - -static char * html_strtok( char * input, char delim ) -{ - static char * end; - static char * curr_offset; - int i; - int num_quotes=0; - - if( input != NULL) - { - curr_offset = input; - end = input+strlen(input); - } - else - { - if( curr_offset + strlen(curr_offset) < end ) - { - curr_offset += strlen(curr_offset) + 1; - } - else - { - return NULL; - } - } - for( i=0; curr_offset+i < end && - (curr_offset[i] != delim || num_quotes != 0) - ; i++ ) - { - if( curr_offset[i] == '\"' ) - { - num_quotes = (num_quotes+1)%2; - } - } - curr_offset[i] = '\0'; - return curr_offset; -} - - -void gtk_html_append_text(GtkHtml * html, char *text, gint options) -{ - GdkColormap *map; - GdkFont *cfont; - GdkRectangle area; - char *ws, - *tag, - *c, - *url = NULL; - gint intag = 0, - wpos = 0, - tpos = 0; - static gint colorv, - bold = 0, - italic = 0, - fixed = 0, - uline = 0, - strike = 0, - title = 0, - height; - static struct font_state *current = NULL, - *tmp; - static struct font_state def_state = { 3, 0, 0, "", NULL, NULL, NULL }; - gboolean scrolldown = TRUE; - - if (html->vadj->upper > (html->vadj->value + html->vadj->page_size)) - scrolldown = FALSE; - - if (text == NULL) { - bold = 0; - italic = 0; - fixed = 0; - uline = 0; - strike = 0; - title = 0; - while (current->next) - { - if (current->ownbg) - g_free(current->bgcol); - if (current->owncolor) - g_free(current->color); - tmp = current; - current = current->next; - g_free(tmp); - } - return; - } - - if (!current) current = &def_state; - map = gdk_window_get_colormap(html->html_area); - cfont = getfont(current->font, bold, italic, fixed, current->size); - c = text; - - ws = g_malloc(strlen(text) + 2); - tag = g_malloc(strlen(text) + 2); - - while (*c) - { - if (*c == '<') - { - if (!intag) - { - ws[wpos] = 0; - if (wpos) - { - if (title) - { - if (html->title) - g_free(html->title); - html->title = g_strdup(ws); - } - else - gtk_html_add_text(html, cfont, current->color, - current->bgcol, ws, strlen(ws), uline, - strike, url); - } - wpos = 0; - intag = 1; - } - else - { - /* - * Assuming you NEVER have nested tags - * * (and I mean <tag <tag>> by this, not - * * <tag><tag2></tag2><tag>.. - */ - tag[tpos] = 0; - gtk_html_add_text(html, cfont, current->color, current->bgcol, - "<", 1, 0, 0, NULL); - gtk_html_add_text(html, cfont, current->color, current->bgcol, - tag, strlen(tag), 0, 0, NULL); - tpos = 0; - - tag[0] = *c; - } - } - else if (*c == '>') - { - if (intag) - { - tag[tpos] = 0; - if (!strcasecmp(tag, "B")) - bold = 1; - else if (!strcasecmp(tag, "STRIKE") || !strcasecmp(tag, "S")) - strike = 1; - else if (!strcasecmp(tag, "I")) - italic = 1; - else if (!strcasecmp(tag, "U")) - uline = 1; - else if (!strcasecmp(tag, "PRE")) - fixed = 1; - else if (!strcasecmp(tag, "HR")) - gtk_html_add_seperator(html, cfont, current->color, current->bgcol); - else if (!strcasecmp(tag, "/B")) - bold = 0; - else if (!strcasecmp(tag, "/STRIKE") || !strcasecmp(tag, "/S")) - strike = 0; - else if (!strcasecmp(tag, "/I")) - italic = 0; - else if (!strcasecmp(tag, "/U")) - uline = 0; - else if (!strcasecmp(tag, "/PRE")) - fixed = 0; - else if (!strcasecmp(tag, "TITLE")) - title = 1; - else if (!strcasecmp(tag, "/TITLE")) - title = 0; - else if (!strncasecmp(tag, "IMG", 3)) - { - GdkPixmap *legend_i; - GdkBitmap *legend_m; - - if (strstr(tag, "SRC=\"aol_icon.gif\"") != NULL) - { - legend_i = gdk_pixmap_create_from_xpm_d(GTK_WIDGET(html)->window, &legend_m, NULL, aol_icon_xpm); - gtk_html_add_pixmap(html, legend_i, 0, 0); - } - - if (strstr(tag, "SRC=\"admin_icon.gif\"") != NULL) - { - legend_i = gdk_pixmap_create_from_xpm_d(GTK_WIDGET(html)->window, &legend_m, NULL, admin_icon_xpm); - gtk_html_add_pixmap(html, legend_i, 0, 0); - } - if (strstr(tag, "SRC=\"dt_icon.gif\"") != NULL) - { - legend_i = gdk_pixmap_create_from_xpm_d(GTK_WIDGET(html)->window, &legend_m, NULL, dt_icon_xpm); - gtk_html_add_pixmap(html, legend_i, 0, 0); - } - if (strstr(tag, "SRC=\"free_icon.gif\"") != NULL) - { - legend_i = gdk_pixmap_create_from_xpm_d(GTK_WIDGET(html)->window, &legend_m, NULL, free_icon_xpm); - gtk_html_add_pixmap(html, legend_i, 0, 0); - } - } - else if (!strcasecmp(tag, "H3")) - { - current = push_state(current); - current->size = 4; - } - else if (!strcasecmp(tag, "/H3")) - { - gtk_html_add_text(html, cfont, current->color, - current->bgcol, "\n", 1, 0, 0, NULL); - - if (current->next) - { - if (current->ownbg) - g_free(current->bgcol); - if (current->owncolor) - g_free(current->color); - tmp = current; - current = current->next; - g_free(tmp); - } - } - else if (!strcasecmp(tag, "TABLE")) - { - } - else if (!strcasecmp(tag, "/TABLE")) - { - } - else if (!strcasecmp(tag, "TR")) - { - } - else if (!strcasecmp(tag, "/TR")) - { - } - else if (!strcasecmp(tag, "/TD")) - { - } - else if (!strcasecmp(tag, "TD")) - { - gtk_html_add_text(html, cfont, current->color, - current->bgcol, " ", 2, 0, 0, NULL); - } - else if (!strncasecmp(tag, "A ", 2)) - { - char *d; - char *temp = d = g_strdup(tag); - int flag = 0; - strtok(tag, " "); - while ((d = strtok(NULL, " "))) - { - if (strlen(d) < 7) - break; - if (!strncasecmp(d, "HREF=\"", strlen("HREF=\""))) - { - d += strlen("HREF=\""); - d[strlen(d) - 1] = 0; - url = g_malloc(strlen(d) + 1); - strcpy(url, d); - flag = 1; - } - } - g_free(temp); - if (!flag) - { - gtk_html_add_text(html, cfont, current->color, - current->bgcol, "<", 1, 0, 0, NULL); - gtk_html_add_text(html, cfont, current->color, - current->bgcol, tag, strlen(tag), 0, - 0, NULL); - gtk_html_add_text(html, cfont, current->color, - current->bgcol, ">", 1, 0, 0, NULL); - } - } - else if (!strcasecmp(tag, "/A")) - { - if (url) - { - g_free(url); - url = NULL; - } - } - else if (!strncasecmp(tag, "FONT", strlen("FONT"))) - { - char *d; - /* - * Push a new state onto the stack, based on the old state - */ - current = push_state(current); - html_strtok(tag, ' '); - while ((d = html_strtok(NULL, ' '))) - { - if (!strncasecmp(d, "style=", strlen("style="))) - { - d += strlen("style="); - if (*d == '\"') { - d++; - while (*d != '\0' && *d != '\"') d++; - if (*d == '\0') - html_strtok(tag, ' '); - } - } - if (!strncasecmp(d, "COLOR=", strlen("COLOR="))) - { - d += strlen("COLOR="); - if (*d == '\"') - { - d++; - } - if (*d == '#') - d++; - if (d[strlen(d) - 1] == '\"') - d[strlen(d) - 1] = 0; - if (sscanf(d, "%x", &colorv) - && !(options & HTML_OPTION_NO_COLOURS)) - { - current->color = get_color(colorv, map); - current->owncolor = 1; - } - else - { - } - } - if (!strncasecmp(d, "FACE=", strlen("FACE="))) - { - d += strlen("FACE="); - if (*d == '\"') - { - d++; - } - if (d[strlen(d) - 1] == '\"') - d[strlen(d) - 1] = 0; - - if (!(options & HTML_OPTION_NO_FONTS)) { - strcpy(current->font, d); - } - } - else if (!strncasecmp(d, "BACK=", strlen("BACK="))) - { - d += strlen("BACK="); - if (*d == '\"') - d++; - if (*d == '#') - d++; - if (d[strlen(d) - 1] == '\"') - d[strlen(d) - 1] = 0; - if (sscanf(d, "%x", &colorv) - && !(options & HTML_OPTION_NO_COLOURS)) - { - if (colorv != 0xffffff || - !(display_options & OPT_DISP_IGN_WHITE)) { - current->bgcol = get_color(colorv, map); - current->ownbg = 1; - } - } - else - { - } - } - else if (!strncasecmp(d, "SIZE=", strlen("SIZE="))) - { - d += strlen("SIZE="); - if (*d == '\"') - d++; - if (*d == '+') - d++; - if (sscanf(d, "%d", &colorv)) - { - current->size = colorv; - } - else - { - } - } - else if (strncasecmp(d, "PTSIZE=", strlen("PTSIZE="))) - { - } - } - } - else if (!strncasecmp(tag, "BODY", strlen("BODY"))) - { - - char *d; - current = push_state(current); - html_strtok(tag, ' '); - while ((d = html_strtok(NULL, ' '))) - { - if (!strncasecmp(d, "BGCOLOR=", strlen("BGCOLOR="))) - { - d += strlen("BGCOLOR="); - if (*d == '\"') - d++; - if (*d == '#') - d++; - if (d[strlen(d) - 1] == '\"') - d[strlen(d) - 1] = 0; - if (sscanf(d, "%x", &colorv) - && !(options & HTML_OPTION_NO_COLOURS)) - { - if (colorv != 0xffffff || - !(display_options & OPT_DISP_IGN_WHITE)) { - current->bgcol = get_color(colorv, map); - current->ownbg = 1; - } - } - } - } - } - else if (!strncasecmp(tag, "/FONT", strlen("/FONT"))) - { - /* - * Pop a font state off the list if possible, freeing - * any resources it used - */ - if (current->next) - { - if (current->ownbg) - g_free(current->bgcol); - if (current->owncolor) - g_free(current->color); - tmp = current; - current = current->next; - g_free(tmp); - } - - } - else if (!strcasecmp(tag, "/BODY")) - { - if (current->next) - { - if (current->ownbg) - g_free(current->bgcol); - if (current->owncolor) - g_free(current->color); - tmp = current; - current = current->next; - g_free(tmp); - } /* - * tags we ignore below - */ - } - else if (!strncasecmp(tag, "BR", 2)) - { - gtk_html_add_text(html, cfont, current->color, - current->bgcol, "\n", 1, 0, 0, NULL); - } - else if (strncasecmp(tag, "HTML", 4) - && strncasecmp(tag, "/HTML", 5) - && strncasecmp(tag, "BODY", 4) - && strncasecmp(tag, "/BODY", 5) - && (strncasecmp(tag, "P", 1) || tag[1] != '>') - && (strncasecmp(tag, "/P", 2) || tag[3] != '>') - && strncasecmp(tag, "HEAD", 4) - && strncasecmp(tag, "/HEAD", 5)) - { - if (tpos) - { - gtk_html_add_text(html, cfont, current->color, - current->bgcol, "<", 1, 0, 0, NULL); - gtk_html_add_text(html, cfont, current->color, - current->bgcol, tag, strlen(tag), 0, - 0, NULL); - gtk_html_add_text(html, cfont, current->color, - current->bgcol, ">", 1, 0, 0, NULL); - - } - } - cfont = getfont(current->font, bold, italic, fixed, current->size); - tpos = 0; - intag = 0; - } - else - { - ws[wpos++] = *c; - } - } - else if (!intag && *c == '&') - { - if (!strncasecmp(c, "&", 5)) - { - ws[wpos++] = '&'; - c += 4; - } - else if (!strncasecmp(c, "<", 4)) - { - ws[wpos++] = '<'; - c += 3; - } - else if (!strncasecmp(c, ">", 4)) - { - ws[wpos++] = '>'; - c += 3; - } - else if (!strncasecmp(c, " ", 6)) - { - ws[wpos++] = ' '; - c += 5; - } - else if (!strncasecmp(c, "©", 6)) - { - ws[wpos++] = '©'; - c += 5; - } - else if (!strncasecmp(c, """, 6)) - { - ws[wpos++] = '\"'; - c += 5; - } - else if (!strncasecmp(c, "®", 5)) - { - ws[wpos++] = 174; - c += 4; - } - else if (*(c + 1) == '#') - { - int pound = 0; - debug_printf("got &#;\n"); - if (sscanf(c, "&#%d;", £) > 0) { - ws[wpos++] = (char)pound; - c += 2; - while (isdigit(*c)) c++; - if (*c != ';') c--; - } else { - ws[wpos++] = *c; - } - } - else - { - ws[wpos++] = *c; - } - } - else - { - if (intag) - { - tag[tpos++] = *c; - } - else - { - ws[wpos++] = *c; - } - } - c++; - } - ws[wpos] = 0; - tag[tpos] = 0; - if (wpos) - { - gtk_html_add_text(html, cfont, current->color, current->bgcol, ws, - strlen(ws), uline, strike, url); - } - if (tpos) - { - gtk_html_add_text(html, cfont, current->color, current->bgcol, "<", 1, - 0, 0, NULL); - gtk_html_add_text(html, cfont, current->color, current->bgcol, tag, - strlen(tag), 0, 0, NULL); -/* gtk_html_add_text(html, cfont, current->color, current->bgcol, ">", 1, - 0, 0, NULL); -*/ } - - - g_free(ws); - g_free(tag); - - gdk_window_get_size(html->html_area, NULL, &height); - area.height = height; - if (scrolldown) - gtk_adjustment_set_value(html->vadj, html->vadj->upper - area.height); - - return; -} - - -static void adjust_adj(GtkHtml * html, GtkAdjustment * adj) -{ - gint height; - - gdk_window_get_size(html->html_area, NULL, &height); - - adj->step_increment = MIN(adj->upper, (float) SCROLL_PIXELS); - adj->page_increment = MIN(adj->upper, height - (float) KEY_SCROLL_PIXELS); - adj->page_size = MIN(adj->upper, height); - adj->value = MIN(adj->value, adj->upper - adj->page_size); - adj->value = MAX(adj->value, 0.0); - - gtk_signal_emit_by_name(GTK_OBJECT(adj), "changed"); -} - - -static void scroll_down(GtkHtml * html, gint diff0) -{ - GdkRectangle rect; - gint width, - height; - - html->yoffset += diff0; - - gdk_window_get_size(html->html_area, &width, &height); - - if (html->transparent) - { - rect.x = 0; - rect.y = 0; - rect.width = width; - rect.height = height; - } - else - { - - - if (height > diff0 && !html->transparent) - gdk_draw_pixmap(html->html_area, - html->gc, - html->html_area, - 0, diff0, 0, 0, width, height - diff0); - - rect.x = 0; - rect.y = MAX(0, height - diff0); - rect.width = width; - rect.height = MIN(height, diff0); - } - - expose_html(html, &rect, FALSE); - gtk_html_draw_focus((GtkWidget *) html); - -} - -static void scroll_up(GtkHtml * html, gint diff0) -{ - GdkRectangle rect; - gint width, - height; - - html->yoffset -= diff0; - - - gdk_window_get_size(html->html_area, &width, &height); - - if (html->transparent) - { - rect.x = 0; - rect.y = 0; - rect.width = width; - rect.height = height; - } - else - { - - if (height > diff0) - gdk_draw_pixmap(html->html_area, - html->gc, - html->html_area, - 0, 0, 0, diff0, width, height - diff0); - - rect.x = 0; - rect.y = 0; - rect.width = width; - rect.height = MIN(height, diff0); - } - - expose_html(html, &rect, FALSE); - gtk_html_draw_focus((GtkWidget *) html); - -} - - - -static void gtk_html_adjustment(GtkAdjustment * adjustment, GtkHtml * html) -{ - g_return_if_fail(adjustment != NULL); - g_return_if_fail(GTK_IS_ADJUSTMENT(adjustment)); - g_return_if_fail(html != NULL); - g_return_if_fail(GTK_IS_HTML(html)); - - /* - * Just ignore it if we haven't been size-allocated and realized yet - */ - if (html->html_area == NULL) - return; - - if (adjustment == html->hadj) - { - g_warning("horizontal scrolling not implemented"); - } - else - { - gint diff = ((gint) adjustment->value) - html->last_ver_value; - - if (diff != 0) - { - /* - * undraw_cursor (text, FALSE); - */ - - if (diff > 0) - { - scroll_down(html, diff); - } - else - { /* - * if (diff < 0) - */ - scroll_up(html, -diff); - } - /* - * draw_cursor (text, FALSE); - */ - - html->last_ver_value = adjustment->value; - } - } -} - -static gint gtk_html_visibility_notify(GtkWidget * widget, - GdkEventVisibility * event) -{ - GtkHtml *html; - GdkRectangle rect; - gint width, - height; - - g_return_val_if_fail(widget != NULL, FALSE); - g_return_val_if_fail(GTK_IS_HTML(widget), FALSE); - - html = GTK_HTML(widget); - - if (GTK_WIDGET_REALIZED(widget) && html->transparent) - { - gdk_window_get_size(html->html_area, &width, &height); - rect.x = 0; - rect.y = 0; - rect.width = width; - rect.height = height; - expose_html(html, &rect, FALSE); - gtk_html_draw_focus((GtkWidget *) html); - } - else - { - } - - - return FALSE; -} - - - -static void gtk_html_disconnect(GtkAdjustment * adjustment, GtkHtml * html) -{ - g_return_if_fail(adjustment != NULL); - g_return_if_fail(GTK_IS_ADJUSTMENT(adjustment)); - g_return_if_fail(html != NULL); - g_return_if_fail(GTK_IS_HTML(html)); - - if (adjustment == html->hadj) - gtk_html_set_adjustments(html, NULL, html->vadj); - if (adjustment == html->vadj) - gtk_html_set_adjustments(html, html->hadj, NULL); -} - -static void move_cursor_ver(GtkHtml * html, int count) -{ - GList *hbits = g_list_find(html->html_bits, html->cursor_hb); - GtkHtmlBit *hb = NULL, - *hb2 = NULL; - gint y; - size_t len, - len2 = 0; - - undraw_cursor(html); - - if (!html->html_bits) - return; - - if (!html->cursor_hb) - html->cursor_hb = (GtkHtmlBit *) html->html_bits->data; - - hb = html->cursor_hb; - - len = html->cursor_pos; - hbits = hbits->prev; - while (hbits) - { - hb2 = (GtkHtmlBit *) hbits->data; - - if (hb2->y != hb->y) - break; - - len += strlen(hb2->text); - - hbits = hbits->prev; - } - - hbits = g_list_find(html->html_bits, html->cursor_hb); - - if (count < 0) - { - while (hbits) - { - hb2 = (GtkHtmlBit *) hbits->data; - - if (hb2->y != hb->y) - break; - - hbits = hbits->prev; - } - if (!hbits) - { - draw_cursor(html); - return; - } - y = hb2->y; - hb = hb2; - while (hbits) - { - hb2 = (GtkHtmlBit *) hbits->data; - - if (hb2->y != y) - break; - - hb = hb2; - - hbits = hbits->prev; - } - hbits = g_list_find(html->html_bits, hb); - while (hbits) - { - hb2 = (GtkHtmlBit *) hbits->data; - - if (hb->y != hb2->y) - { - html->cursor_hb = hb; - html->cursor_pos = strlen(hb->text); - break; - } - - - if (len < len2 + strlen(hb2->text)) - { - html->cursor_hb = hb2; - html->cursor_pos = len - len2; - break; - } - - len2 += strlen(hb2->text); - - hb = hb2; - - hbits = hbits->next; - } - } - else - { - while (hbits) - { - hb2 = (GtkHtmlBit *) hbits->data; - - if (hb2->y != hb->y) - break; - - hbits = hbits->next; - } - if (!hbits) - { - draw_cursor(html); - return; - } - hb = hb2; - while (hbits) - { - hb2 = (GtkHtmlBit *) hbits->data; - - if (hb->y != hb2->y) - { - html->cursor_hb = hb; - html->cursor_pos = strlen(hb->text); - break; - } - - - if (len < len2 + strlen(hb2->text)) - { - html->cursor_hb = hb2; - html->cursor_pos = len - len2; - break; - } - - len2 += strlen(hb2->text); - - hb = hb2; - - hbits = hbits->next; - } - } - - draw_cursor(html); - -} - -static void move_cursor_hor(GtkHtml * html, int count) -{ - GList *hbits = g_list_find(html->html_bits, html->cursor_hb); - GtkHtmlBit *hb, - *hb2; - - undraw_cursor(html); - - if (!html->html_bits) - return; - - if (!html->cursor_hb) - html->cursor_hb = (GtkHtmlBit *) html->html_bits->data; - - html->cursor_pos += count; - - if (html->cursor_pos < 0) - { - if (hbits->prev) - { - gint diff; - hb = html->cursor_hb; - hb2 = (GtkHtmlBit *) hbits->prev->data; - diff = html->cursor_pos + strlen(hb2->text) + 1; - if (hb->y == hb2->y) - --diff; - - html->cursor_pos = diff; - - html->cursor_hb = (GtkHtmlBit *) hbits->prev->data; - } - else - { - html->cursor_pos = 0; - } - } - else if ((unsigned) html->cursor_pos > strlen(html->cursor_hb->text)) - { - if (hbits->next) - { - gint diff; - hb = html->cursor_hb; - hb2 = (GtkHtmlBit *) hbits->next->data; - - diff = html->cursor_pos - strlen(html->cursor_hb->text) - 1; - if (hb->y == hb2->y) - ++diff; - html->cursor_pos = diff; - html->cursor_hb = (GtkHtmlBit *) hbits->next->data; - } - else - { - html->cursor_pos = strlen(html->cursor_hb->text); - } - - } - - draw_cursor(html); -} - -static void move_beginning_of_line(GtkHtml * html) -{ - GList *hbits = g_list_find(html->html_bits, html->cursor_hb); - GtkHtmlBit *hb = NULL; - gint y; - - undraw_cursor(html); - - if (!html->html_bits) - return; - - if (!html->cursor_hb) - html->cursor_hb = (GtkHtmlBit *) html->html_bits->data; - - y = html->cursor_hb->y; - - while (hbits) - { - hb = (GtkHtmlBit *) hbits->data; - - if (y != hb->y) - { - hb = (GtkHtmlBit *) hbits->next->data; - break; - } - - hbits = hbits->prev; - } - if (!hbits) - html->cursor_hb = (GtkHtmlBit *) html->html_bits->data; - else - html->cursor_hb = hb; - - html->cursor_pos = 0; - - - draw_cursor(html); - - -} - -static void move_end_of_line(GtkHtml * html) -{ - GList *hbits = g_list_find(html->html_bits, html->cursor_hb); - GtkHtmlBit *hb = NULL; - gint y; - - undraw_cursor(html); - - if (!html->html_bits) - return; - - if (!html->cursor_hb) - html->cursor_hb = (GtkHtmlBit *) html->html_bits->data; - - y = html->cursor_hb->y; - - while (hbits) - { - hb = (GtkHtmlBit *) hbits->data; - - if (y != hb->y) - { - hb = (GtkHtmlBit *) hbits->prev->data; - break; - } - - hbits = hbits->next; - } - if (!hbits) - html->cursor_hb = (GtkHtmlBit *) g_list_last(html->html_bits)->data; - else - html->cursor_hb = hb; - - html->cursor_pos = strlen(html->cursor_hb->text); - - - draw_cursor(html); - - -} - - - -static gint gtk_html_key_press(GtkWidget * widget, GdkEventKey * event) -{ - GtkHtml *html; - gchar key; - gint return_val; - - g_return_val_if_fail(widget != NULL, FALSE); - g_return_val_if_fail(GTK_IS_HTML(widget), FALSE); - g_return_val_if_fail(event != NULL, FALSE); - - return_val = FALSE; - - html = GTK_HTML(widget); - - key = event->keyval; - return_val = TRUE; - - - if (html->editable == FALSE) - { - /* - * switch (event->keyval) { - * case GDK_Home: - * if (event->state & GDK_CONTROL_MASK) - * scroll_int (text, -text->vadj->value); - * else - * return_val = FALSE; - * break; - * case GDK_End: - * if (event->state & GDK_CONTROL_MASK) - * scroll_int (text, +text->vadj->upper); - * else - * return_val = FALSE; - * break; - * case GDK_Page_Up: scroll_int (text, -text->vadj->page_increment); break; - * case GDK_Page_Down: scroll_int (text, +text->vadj->page_increment); break; - * case GDK_Up: scroll_int (text, -KEY_SCROLL_PIXELS); break; - * case GDK_Down: scroll_int (text, +KEY_SCROLL_PIXELS); break; - * case GDK_Return: - * if (event->state & GDK_CONTROL_MASK) - * gtk_signal_emit_by_name (GTK_OBJECT (text), "activate"); - * else - * return_val = FALSE; - * break; - * default: - * return_val = FALSE; - * break; - * } - */ - } - else - { - - switch (event->keyval) - { - case GDK_Home: - move_beginning_of_line(html); - break; - case GDK_End: - move_end_of_line(html); - break; - /* - * case GDK_Page_Up: - * move_cursor_page_ver (html, -1); - * break; - * case GDK_Page_Down: - * move_cursor_page_ver (html, +1); - * break; - */ - /* - * CUA has Ctrl-Up/Ctrl-Down as paragraph up down - */ - case GDK_Up: - move_cursor_ver(html, -1); - break; - case GDK_Down: - move_cursor_ver(html, +1); - break; - case GDK_Left: - move_cursor_hor(html, -1); - break; - case GDK_Right: - move_cursor_hor(html, +1); - break; -#if 0 - case GDK_BackSpace: - if (event->state & GDK_CONTROL_MASK) - gtk_text_delete_backward_word(text); - else - gtk_text_delete_backward_character(text); - break; - case GDK_Clear: - gtk_text_delete_line(text); - break; - case GDK_Insert: - if (event->state & GDK_SHIFT_MASK) - { - extend_selection = FALSE; - gtk_editable_paste_clipboard(editable); - } - else if (event->state & GDK_CONTROL_MASK) - { - gtk_editable_copy_clipboard(editable); - } - else - { - /* - * gtk_toggle_insert(text) -- IMPLEMENT - */ - } - break; - case GDK_Delete: - if (event->state & GDK_CONTROL_MASK) - gtk_text_delete_forward_word(text); - else if (event->state & GDK_SHIFT_MASK) - { - extend_selection = FALSE; - gtk_editable_cut_clipboard(editable); - } - else - gtk_text_delete_forward_character(text); - break; - case GDK_Tab: - position = text->point.index; - gtk_editable_insert_text(editable, "\t", 1, &position); - break; - case GDK_Return: - if (event->state & GDK_CONTROL_MASK) - gtk_signal_emit_by_name(GTK_OBJECT(text), "activate"); - else - { - position = text->point.index; - gtk_editable_insert_text(editable, "\n", 1, &position); - } - break; - case GDK_Escape: - /* - * Don't insert literally - */ - return_val = FALSE; - break; -#endif - default: - return_val = FALSE; - -#if 0 - if (event->state & GDK_CONTROL_MASK) - { - if ((key >= 'A') && (key <= 'Z')) - key -= 'A' - 'a'; - - if ((key >= 'a') && (key <= 'z') - && control_keys[(int) (key - 'a')]) - { - (*control_keys[(int) (key - 'a')]) (editable, event->time); - return_val = TRUE; - } - - break; - } - else if (event->state & GDK_MOD1_MASK) - { - if ((key >= 'A') && (key <= 'Z')) - key -= 'A' - 'a'; - - if ((key >= 'a') && (key <= 'z') && alt_keys[(int) (key - 'a')]) - { - (*alt_keys[(int) (key - 'a')]) (editable, event->time); - return_val = TRUE; - } - break; - } -#endif - /* - * if (event->length > 0) { - * html->cursor_pos++; - * gtk_editable_insert_text (editable, event->string, event->length, &position); - * - * return_val = TRUE; - * } - * else - * return_val = FALSE; - */ - } - - } - - return return_val; -} - -void gtk_html_freeze(GtkHtml * html) -{ - g_return_if_fail(html != NULL); - g_return_if_fail(GTK_IS_HTML(html)); - - html->frozen++; -} - -void gtk_html_thaw(GtkHtml * html) -{ - GdkRectangle area; - - g_return_if_fail(html != NULL); - g_return_if_fail(GTK_IS_HTML(html)); - - gtk_html_append_text(html, NULL, 0); - - html->frozen--; - - if (html->frozen < 0) - html->frozen = 0; - - if (html->frozen == 0) - { - if (html->html_area) - { - gint width, - height; - area.x = 0; - area.y = 0; - - gdk_window_get_size(html->html_area, &width, &height); - - area.width = width; - area.height = height; - - expose_html(html, &area, TRUE); - } - } -} - -static int get_line_height(GtkHtml *html, GtkHtmlBit *start) -{ - int height = 1, max_height = 0; - GList *hbits = html->html_bits; - GtkHtmlBit *hbit = start; /* default this in case hbits is NULL */ - - hbits = g_list_find(hbits, start); - - while (hbits) - { - hbit = hbits->data; - if (hbit->font) - height = gdk_text_height(hbit->font, "C", 1); - - if (max_height < height) - max_height = height; - if (hbit->newline) - break; - hbits = hbits->next; - } - - if (max_height == 0) - max_height = gdk_text_height(hbit->font, "C", 1); - - return max_height; -}
--- a/src/gtkhtml.h Thu Jan 25 20:31:12 2001 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,153 +0,0 @@ -/* - * gaim - * - * Copyright (C) 1998-1999, Mark Spencer <markster@marko.net> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#ifndef __GTK_HTML_H__ -#define __GTK_HTML_H__ - -#include <gdk/gdk.h> -#include <gtk/gtkwidget.h> - -#ifdef __cplusplus -/*extern "C" {*/ -#endif /* __cplusplus */ - -#define GTK_TYPE_HTML (gtk_html_get_type()) -#define GTK_HTML(obj) (GTK_CHECK_CAST ((obj), GTK_TYPE_HTML, GtkHtml)) -#define GTK_HTML_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), GTK_TYPE_HTML, GtkHtmlClass)) -#define GTK_IS_HTML(obj) (GTK_CHECK_TYPE ((obj), GTK_TYPE_HTML)) -#define GTK_IS_HTML_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GTK_TYPE_HTML) - -typedef struct _GtkHtml GtkHtml; -typedef struct _GtkHtmlClass GtkHtmlClass; -typedef struct _GtkHtmlBit GtkHtmlBit; - - -struct _GtkHtmlBit { - int type; - GdkColor *fore; - GdkColor *back; - GdkFont *font; - int uline; - int strike; - int width, height; - int x, y; - char *url; - int was_selected; - int sel_s, sel_e; - int newline; - char *text; - GdkPixmap *pm; - int fit; -}; - - -struct _GtkHtml { - GtkWidget widget; - - GdkWindow *html_area; - - GtkAdjustment *hadj; - GtkAdjustment *vadj; - - gint xoffset; - gint yoffset; - - int current_x; - int current_y; - GdkGC *gc; - GdkGC *bg_gc; - GList *html_bits; - GList *urls; - int start_sel_x, start_sel_y; - GtkHtmlBit *start_sel, *end_sel; - int num_start, num_end; - char *selected_text; - gint editable; - gint transparent; - gint timer; - gint last_ver_value; - char *title; - gint frozen; - GtkHtmlBit *cursor_hb; - GtkWidget *tooltip_window; - GtkHtmlBit *tooltip_hb; - int tooltip_timer; - int cursor_pos; - GdkPixmap *pm; -}; - - -struct _GtkHtmlClass { - GtkWidgetClass parent_class; - - void (*set_scroll_adjustments) (GtkHtml *html, - GtkAdjustment *hadjustment, - GtkAdjustment *vadjustment); - -}; - - -#define HTML_BIT_TEXT 0 -#define HTML_BIT_PIXMAP 1 -#define HTML_BIT_SEP 2 - -#define HTML_OPTION_NO_COLOURS 0x01 -#define HTML_OPTION_NO_FONTS 0x02 - -#define STYLE_ITALIC 0x01000000 -#define STYLE_BOLD 0x020000000 - -#define FIXED_FONT "-*-courier-medium-r-*-*-*-%d-*-*-*-*-*-*" -#define FIXED_BOLD_FONT "-*-courier-bold-r-*-*-*-%d-*-*-*-*-*-*" -#define FIXED_ITALIC_FONT "-*-courier-medium-o-*-*-*-%d-*-*-*-*-*-*" -#define FIXED_BOLD_ITALIC_FONT "-*-courier-bold-o-*-*-*-%d-*-*-*-*-*-*" -#define PROP_FONT "-*-helvetica-medium-r-*-*-*-%d-*-*-*-*-*-*" -#define PROP_BOLD_FONT "-*-helvetica-bold-r-*-*-*-%d-*-*-*-*-*-*" -#define PROP_ITALIC_FONT "-*-helvetica-medium-o-*-*-*-%d-*-*-*-*-*-*" -#define PROP_BOLD_ITALIC_FONT "-*-helvetica-bold-o-*-*-*-%d-*-*-*-*-*-*" - -#define HTML_TOOLTIP_DELAY 500 - -GtkType gtk_html_get_type (void); -GtkWidget* gtk_html_new (GtkAdjustment *hadj, - GtkAdjustment *vadj); -void gtk_html_set_editable (GtkHtml *html, - gboolean is_editable); -void gtk_html_set_transparent (GtkHtml *html, - gboolean is_transparent); -void gtk_html_set_adjustments (GtkHtml *html, - GtkAdjustment *hadj, - GtkAdjustment *vadj); -void gtk_html_append_text (GtkHtml *html, - char *text, gint options); -void gtk_html_freeze (GtkHtml *html); -void gtk_html_thaw (GtkHtml *html); -void gtk_html_add_pixmap (GtkHtml * html, GdkPixmap *pm, - gint fint, gint newline); - -#ifdef __cplusplus -/*}*/ -#endif /* __cplusplus */ - -#endif /* __GTK_HTML_H__ */ - - -
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/gtkimhtml.c Fri Jan 26 02:02:36 2001 +0000 @@ -0,0 +1,2918 @@ +/* + * GtkIMHtml + * + * Copyright (C) 2000, Eric Warmenhoven <warmenhoven@yahoo.com> + * + * This program is free software; you can redistribute it and/or modify + * under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "gtkimhtml.h" +#include <gtk/gtk.h> +#include <string.h> +#include <ctype.h> +#include <stdio.h> +#include <math.h> + +#include "pixmaps/angel.xpm" +#include "pixmaps/bigsmile.xpm" +#include "pixmaps/burp.xpm" +#include "pixmaps/crossedlips.xpm" +#include "pixmaps/cry.xpm" +#include "pixmaps/embarrassed.xpm" +#include "pixmaps/kiss.xpm" +#include "pixmaps/moneymouth.xpm" +#include "pixmaps/sad.xpm" +#include "pixmaps/scream.xpm" +#include "pixmaps/smile.xpm" +#include "pixmaps/smile8.xpm" +#include "pixmaps/think.xpm" +#include "pixmaps/tongue.xpm" +#include "pixmaps/wink.xpm" +#include "pixmaps/yell.xpm" + +#define DEFAULT_FONT_NAME "helvetica" +#define MAX_SIZE 7 + +gint font_sizes [] = { 80, 100, 120, 140, 200, 300, 400 }; + +#define BORDER_SIZE 3 +#define MIN_HEIGHT 20 +#define HR_HEIGHT 2 + +#define TYPE_TEXT 0 +#define TYPE_SMILEY 1 +#define TYPE_IMG 2 +#define TYPE_SEP 3 +#define TYPE_BR 4 +#define TYPE_COMMENT 5 + +typedef struct _GtkIMHtmlBit GtkIMHtmlBit; +typedef struct _FontDetail FontDetail; + +struct _GtkIMHtmlBit { + gint type; + + gchar *text; + GdkPixmap *pm; + GdkBitmap *bm; + + GdkFont *font; + GdkColor *fore; + GdkColor *back; + GdkColor *bg; + gboolean underline; + gboolean strike; + gchar *url; + + GList *chunks; +}; + +struct _FontDetail { + gushort size; + gchar *face; + GdkColor *fore; + GdkColor *back; +}; + +struct line_info { + gint x; + gint y; + gint width; + gint height; + gint ascent; + + gboolean selected; + gchar *sel_start; + gchar *sel_end; + + gchar *text; + GtkIMHtmlBit *bit; +}; + +struct url_widget { + gint x; + gint y; + gint width; + gint height; + gchar *url; +}; + +static GtkLayoutClass *parent_class = NULL; + +enum { + TARGET_STRING, + TARGET_TEXT, + TARGET_COMPOUND_TEXT +}; + +enum { + URL_CLICKED, + LAST_SIGNAL +}; +static guint signals [LAST_SIGNAL] = { 0 }; + +static void gtk_imhtml_draw_bit (GtkIMHtml *, GtkIMHtmlBit *); +static GdkColor *gtk_imhtml_get_color (const gchar *); +static gint gtk_imhtml_motion_notify_event (GtkWidget *, GdkEventMotion *); + +static void +gtk_imhtml_destroy (GtkObject *object) +{ + GtkIMHtml *imhtml; + + imhtml = GTK_IMHTML (object); + + while (imhtml->bits) { + GtkIMHtmlBit *bit = imhtml->bits->data; + imhtml->bits = g_list_remove (imhtml->bits, bit); + if (bit->text) + g_free (bit->text); + if (bit->font) + gdk_font_unref (bit->font); + if (bit->fore) + gdk_color_free (bit->fore); + if (bit->back) + gdk_color_free (bit->back); + if (bit->bg) + gdk_color_free (bit->bg); + if (bit->url) + g_free (bit->url); + if (bit->pm) + gdk_pixmap_unref (bit->pm); + if (bit->bm) + gdk_bitmap_unref (bit->bm); + while (bit->chunks) { + GtkObject *obj = bit->chunks->data; + struct line_info *li = gtk_object_get_user_data (obj); + if (li->text) + g_free (li->text); + g_free (li); + bit->chunks = g_list_remove (bit->chunks, obj); + } + g_free (bit); + } + + while (imhtml->urls) { + g_free (imhtml->urls->data); + imhtml->urls = g_list_remove (imhtml->urls, imhtml->urls->data); + } + + if (imhtml->selected_text) + g_string_free (imhtml->selected_text, TRUE); + + gdk_font_unref (imhtml->default_font); + gdk_color_free (imhtml->default_fg_color); + + gdk_cursor_destroy (imhtml->hand_cursor); + gdk_cursor_destroy (imhtml->arrow_cursor); + + g_hash_table_destroy (imhtml->smiley_hash); + + if (GTK_OBJECT_CLASS (parent_class)->destroy != NULL) + (* GTK_OBJECT_CLASS (parent_class)->destroy) (object); +} + +static void +gtk_imhtml_realize (GtkWidget *widget) +{ + GtkIMHtml *imhtml; + + g_return_if_fail (widget != NULL); + g_return_if_fail (GTK_IS_IMHTML (widget)); + + imhtml = GTK_IMHTML (widget); + + if (GTK_WIDGET_CLASS (parent_class)->realize) + (* GTK_WIDGET_CLASS (parent_class)->realize) (widget); + + widget->style = gtk_style_attach (widget->style, widget->window); + gdk_window_set_events (imhtml->layout.bin_window, + (gdk_window_get_events (imhtml->layout.bin_window) + | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + | GDK_POINTER_MOTION_MASK | GDK_EXPOSURE_MASK)); + + gdk_window_set_cursor (widget->window, imhtml->arrow_cursor); + + gdk_window_set_background (GTK_LAYOUT (imhtml)->bin_window, >K_WIDGET (imhtml)->style->white); +} + +static void +gtk_imhtml_unrealize (GtkWidget *widget) +{ + if (GTK_WIDGET_CLASS (parent_class)->unrealize) + (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget); +} + +static void +gtk_imhtml_draw (GtkWidget *widget, + GdkRectangle *area) +{ + if (GTK_WIDGET_CLASS (parent_class)->draw) + (* GTK_WIDGET_CLASS (parent_class)->draw) (widget, area); +} + +static void +gtk_imhtml_style_set (GtkWidget *widget, + GtkStyle *style) +{ + GtkIMHtml *imhtml; + + g_return_if_fail (widget != NULL); + g_return_if_fail (GTK_IS_IMHTML (widget)); + if (!GTK_WIDGET_REALIZED (widget)) + return; + + imhtml = GTK_IMHTML (widget); + + gdk_window_set_background (GTK_LAYOUT (imhtml)->bin_window, >K_WIDGET (imhtml)->style->white); +} + +static gint +gtk_imhtml_expose (GtkWidget *widget, + GdkEventExpose *event) +{ + if (GTK_WIDGET_CLASS (parent_class)->expose_event) + (* GTK_WIDGET_CLASS (parent_class)->expose_event) (widget, event); + + return TRUE; +} + +static void +gtk_imhtml_redraw_all (GtkIMHtml *imhtml) +{ + GList *b; + GtkIMHtmlBit *bit; + GtkAdjustment *vadj; + gfloat oldvalue; + + vadj = GTK_LAYOUT (imhtml)->vadjustment; + oldvalue = vadj->value / vadj->upper; + + b = imhtml->bits; + while (b) { + bit = b->data; + b = g_list_next (b); + while (bit->chunks) { + GtkObject *obj = bit->chunks->data; + struct line_info *li = gtk_object_get_user_data (obj); + if (li->text) + g_free (li->text); + g_free (li); + bit->chunks = g_list_remove (bit->chunks, obj); + } + } + + g_list_free (imhtml->line); + imhtml->line = NULL; + + while (imhtml->urls) { + g_free (imhtml->urls->data); + imhtml->urls = g_list_remove (imhtml->urls, imhtml->urls->data); + } + + while (GTK_LAYOUT (imhtml)->children) + gtk_container_remove (GTK_CONTAINER (imhtml), + *(GtkWidget **)GTK_LAYOUT (imhtml)->children->data); + + imhtml->x = BORDER_SIZE; + imhtml->y = BORDER_SIZE + 10; + imhtml->llheight = 0; + imhtml->llascent = 0; + + b = imhtml->bits; + while (b) { + gtk_imhtml_draw_bit (imhtml, b->data); + b = g_list_next (b); + } + + gtk_adjustment_set_value (vadj, vadj->upper * oldvalue); +} + +static void +gtk_imhtml_size_allocate (GtkWidget *widget, + GtkAllocation *allocation) +{ + GtkIMHtml *imhtml; + + g_return_if_fail (widget != NULL); + g_return_if_fail (GTK_IS_IMHTML (widget)); + g_return_if_fail (allocation != NULL); + + imhtml = GTK_IMHTML (widget); + + if (GTK_WIDGET_CLASS (parent_class)->size_allocate) + ( *GTK_WIDGET_CLASS (parent_class)->size_allocate) (widget, allocation); + + if (allocation->width == imhtml->xsize) + return; + + imhtml->x = BORDER_SIZE; + imhtml->y = BORDER_SIZE + 10; + imhtml->llheight = 0; + imhtml->llascent = 0; + + imhtml->xsize = allocation->width; + + gtk_imhtml_redraw_all (imhtml); +} + +static void +gtk_imhtml_select_none (GtkIMHtml *imhtml) +{ + GList *bits; + GList *chunks; + GtkIMHtmlBit *bit; + struct line_info *chunk; + GtkWidget *darea; + + g_return_if_fail (GTK_IS_IMHTML (imhtml)); + + bits = imhtml->bits; + while (bits) { + bit = bits->data; + chunks = bit->chunks; + + while (chunks) { + darea = chunks->data; + chunk = gtk_object_get_user_data (GTK_OBJECT (darea)); + + if (chunk->selected) + gtk_widget_queue_draw (darea); + chunk->selected = FALSE; + chunk->sel_start = chunk->sel_end = NULL; + + chunks = g_list_next (chunks); + } + + bits = g_list_next (bits); + } +} + +static gchar* +get_position (struct line_info *chunk, + gint x, + gboolean smileys) +{ + gint width = x - chunk->x; + gchar *text; + gchar *pos; + guint total = 0; + + switch (chunk->bit->type) { + case TYPE_TEXT: + case TYPE_COMMENT: + text = chunk->text; + break; + case TYPE_SMILEY: + if (smileys) + return NULL; + else + text = chunk->text; + break; + default: + return NULL; + break; + } + + if (width <= 0) + return text; + + for (pos = text; *pos != '\0'; pos++) { + gint char_width = gdk_text_width (chunk->bit->font, pos, 1); + if ((width > total) && (width <= total + char_width)) { + if (width < total + (char_width >> 1)) + return pos; + else + return ++pos; + } + total += char_width; + } + + return pos; +} + +static GString* +append_to_sel (GString *string, + struct line_info *chunk, + gboolean smileys) +{ + GString *new_string; + gchar *buf; + gchar *start; + gint length; + + switch (chunk->bit->type) { + case TYPE_TEXT: + case TYPE_COMMENT: + start = (chunk->sel_start == NULL) ? chunk->text : chunk->sel_start; + length = (chunk->sel_end == NULL) ? strlen (start) : chunk->sel_end - start; + if (length <= 0) + return string; + buf = g_strndup (start, length); + break; + case TYPE_SMILEY: + if (smileys) { + start = (chunk->sel_start == NULL) ? chunk->bit->text : chunk->sel_start; + length = (chunk->sel_end == NULL) ? strlen (start) : chunk->sel_end - start; + if (length <= 0) + return string; + buf = g_strndup (start, length); + } else { + start = (chunk->sel_start == NULL) ? chunk->text : chunk->sel_start; + length = (chunk->sel_end == NULL) ? strlen (start) : chunk->sel_end - start; + if (length <= 0) + return string; + buf = g_strndup (start, length); + } + break; + case TYPE_BR: + buf = g_strdup ("\n"); + break; + default: + return string; + break; + } + + new_string = g_string_append (string, buf); + g_free (buf); + + return new_string; +} + +#define COORDS_IN_CHUNK(xx, yy) (((xx) < chunk->x + chunk->width) && \ + ((yy) < chunk->y + chunk->height)) + +static void +gtk_imhtml_select_bits (GtkIMHtml *imhtml) +{ + GList *bits; + GList *chunks; + GtkIMHtmlBit *bit; + struct line_info *chunk; + GtkWidget *darea; + + guint startx = imhtml->sel_startx, + starty = imhtml->sel_starty, + endx = imhtml->sel_endx, + endy = imhtml->sel_endy; + gchar *new_pos; + gint selection = 0; + gboolean smileys = imhtml->smileys; + gboolean redraw = FALSE; + gboolean got_start = FALSE; + gboolean got_end = FALSE; + + g_return_if_fail (GTK_IS_IMHTML (imhtml)); + + if (!imhtml->selection) + return; + + if (imhtml->selected_text) { + g_string_free (imhtml->selected_text, TRUE); + imhtml->selected_text = g_string_new (""); + } + + bits = imhtml->bits; + while (bits) { + bit = bits->data; + chunks = bit->chunks; + + while (chunks) { + darea = chunks->data; + chunk = gtk_object_get_user_data (GTK_OBJECT (darea)); + + switch (selection) { + case 0: + if (COORDS_IN_CHUNK (startx, starty)) { + new_pos = get_position (chunk, startx, smileys); + if ( !chunk->selected || + (chunk->sel_start != new_pos) || + (chunk->sel_end != NULL)) + redraw = TRUE; + chunk->selected = TRUE; + chunk->sel_start = new_pos; + chunk->sel_end = NULL; + selection++; + got_start = TRUE; + } + + if (COORDS_IN_CHUNK (endx, endy)) { + if (got_start) { + new_pos = get_position (chunk, endx, smileys); + if (chunk->sel_end != new_pos) + redraw = TRUE; + if (chunk->sel_start > new_pos) { + chunk->sel_end = chunk->sel_start; + chunk->sel_start = new_pos; + } else + chunk->sel_end = new_pos; + selection = 2; + got_end = TRUE; + } else { + new_pos = get_position (chunk, endx, smileys); + if ( !chunk->selected || + (chunk->sel_start != new_pos) || + (chunk->sel_end != NULL)) + redraw = TRUE; + chunk->selected = TRUE; + chunk->sel_start = new_pos; + chunk->sel_end = NULL; + selection++; + got_end = TRUE; + } + } else if (!COORDS_IN_CHUNK (startx, starty) && !got_start) { + if (chunk->selected) + redraw = TRUE; + chunk->selected = FALSE; + chunk->sel_start = chunk->text; + chunk->sel_end = NULL; + } + + break; + case 1: + if (!got_start && COORDS_IN_CHUNK (startx, starty)) { + new_pos = get_position (chunk, startx, smileys); + if ( !chunk->selected || + (chunk->sel_end != new_pos) || + (chunk->sel_start != chunk->text)) + redraw = TRUE; + chunk->selected = TRUE; + chunk->sel_start = chunk->text; + chunk->sel_end = new_pos; + selection++; + got_start = TRUE; + } else if (!got_end && COORDS_IN_CHUNK (endx, endy)) { + new_pos = get_position (chunk, endx, smileys); + if ( !chunk->selected || + (chunk->sel_end != new_pos) || + (chunk->sel_start != chunk->text)) + redraw = TRUE; + chunk->selected = TRUE; + chunk->sel_start = chunk->text; + chunk->sel_end = new_pos; + selection++; + got_end = TRUE; + } else { + if ( !chunk->selected || + (chunk->sel_end != new_pos) || + (chunk->sel_start != NULL)) + redraw = TRUE; + chunk->selected = TRUE; + chunk->sel_start = chunk->text; + chunk->sel_end = NULL; + } + + break; + case 2: + if ( chunk->selected || + (chunk->sel_start != chunk->text) || + (chunk->sel_end != NULL)) + redraw = TRUE; + chunk->selected = FALSE; + chunk->sel_start = chunk->text; + chunk->sel_end = NULL; + break; + } + + if (chunk->selected == TRUE) + imhtml->selected_text = append_to_sel (imhtml->selected_text, + chunk, smileys); + + if (redraw) { + gtk_widget_queue_draw (darea); + redraw = FALSE; + } + + chunks = g_list_next (chunks); + } + + bits = g_list_next (bits); + } +} + +static gint +scroll_timeout (GtkIMHtml *imhtml) +{ + GdkEventMotion event; + gint x, y; + GdkModifierType mask; + + imhtml->scroll_timer = 0; + + gdk_window_get_pointer (imhtml->layout.bin_window, &x, &y, &mask); + + if (mask & GDK_BUTTON1_MASK) { + event.is_hint = 0; + event.x = x; + event.y = y; + event.state = mask; + + gtk_imhtml_motion_notify_event (GTK_WIDGET (imhtml), &event); + } + + return FALSE; +} + +static gint +gtk_imhtml_motion_notify_event (GtkWidget *widget, + GdkEventMotion *event) +{ + gint x, y; + GdkModifierType state; + GtkIMHtml *imhtml = GTK_IMHTML (widget); + GtkAdjustment *vadj = GTK_LAYOUT (widget)->vadjustment; + GtkAdjustment *hadj = GTK_LAYOUT (widget)->hadjustment; + + if (event->is_hint) + gdk_window_get_pointer (event->window, &x, &y, &state); + else { + x = event->x + hadj->value; + y = event->y + vadj->value; + state = event->state; + } + + if (state & GDK_BUTTON1_MASK) { + gint diff; + gint height = vadj->page_size; + gint yy = y - vadj->value; + + if (((yy < 0) || (yy > height)) && + (imhtml->scroll_timer == 0) && + (vadj->upper > vadj->page_size)) { + imhtml->scroll_timer = gtk_timeout_add (100, + (GtkFunction) scroll_timeout, + imhtml); + diff = (yy < 0) ? (yy >> 1) : ((yy - height) >> 1); + gtk_adjustment_set_value (vadj, + MIN (vadj->value + diff, vadj->upper - height + 20)); + } + + if (imhtml->selection) { + imhtml->sel_endx = MAX (x, 0); + imhtml->sel_endy = MAX (y, 0); + gtk_imhtml_select_bits (imhtml); + } + } else { + GList *urls = imhtml->urls; + struct url_widget *uw; + + while (urls) { + uw = (struct url_widget *) urls->data; + if ((x > uw->x) && (x < uw->x + uw->width) && + (y > uw->y) && (y < uw->y + uw->height)) { + gdk_window_set_cursor (imhtml->layout.bin_window, imhtml->hand_cursor); + return TRUE; + } + urls = g_list_next (urls); + } + } + + gdk_window_set_cursor (imhtml->layout.bin_window, imhtml->arrow_cursor); + + return TRUE; +} + +static gint +gtk_imhtml_button_press_event (GtkWidget *widget, + GdkEventButton *event) +{ + GtkIMHtml *imhtml = GTK_IMHTML (widget); + GtkAdjustment *vadj = GTK_LAYOUT (widget)->vadjustment; + GtkAdjustment *hadj = GTK_LAYOUT (widget)->hadjustment; + gint x, y; + + if (event->button == 1) { + x = event->x + hadj->value; + y = event->y + vadj->value; + + imhtml->sel_startx = x; + imhtml->sel_starty = y; + imhtml->selection = TRUE; + gtk_imhtml_select_none (imhtml); + } + + return TRUE; +} + +static gint +gtk_imhtml_button_release_event (GtkWidget *widget, + GdkEventButton *event) +{ + GtkIMHtml *imhtml = GTK_IMHTML (widget); + GtkAdjustment *vadj = GTK_LAYOUT (widget)->vadjustment; + GtkAdjustment *hadj = GTK_LAYOUT (widget)->hadjustment; + gint x, y; + + if ((event->button == 1) && imhtml->selection) { + x = event->x + hadj->value; + y = event->y + vadj->value; + + if ((x == imhtml->sel_startx) && (y == imhtml->sel_starty)) { + imhtml->sel_startx = imhtml->sel_starty = 0; + imhtml->selection = FALSE; + gtk_imhtml_select_none (imhtml); + } else { + imhtml->sel_endx = MAX (x, 0); + imhtml->sel_endy = MAX (y, 0); + gtk_imhtml_select_bits (imhtml); + } + + gtk_selection_owner_set (widget, GDK_SELECTION_PRIMARY, event->time); + } + + return TRUE; +} + +static void +gtk_imhtml_selection_get (GtkWidget *widget, + GtkSelectionData *sel_data, + guint sel_info, + guint32 time) +{ + GtkIMHtml *imhtml; + gchar *string; + gint length; + + g_return_if_fail (widget != NULL); + g_return_if_fail (GTK_IS_IMHTML (widget)); + g_return_if_fail (sel_data->selection == GDK_SELECTION_PRIMARY); + + imhtml = GTK_IMHTML (widget); + + g_return_if_fail (imhtml->selected_text != NULL); + g_return_if_fail (imhtml->selected_text->str != NULL); + + if (imhtml->selected_text->len <= 0) { + string = NULL; + length = 0; + } else { + string = g_strdup (imhtml->selected_text->str); + length = strlen (string); + } + + if (sel_info == TARGET_STRING) { + gtk_selection_data_set (sel_data, + GDK_SELECTION_TYPE_STRING, + 8 * sizeof (gchar), + (guchar *) string, + length); + } else if ((sel_info == TARGET_TEXT) || (sel_info == TARGET_COMPOUND_TEXT)) { + guchar *text; + GdkAtom encoding; + gint format; + gint new_length; + + gdk_string_to_compound_text (string, &encoding, &format, &text, &new_length); + gtk_selection_data_set (sel_data, encoding, format, text, new_length); + gdk_free_compound_text (text); + } + + if (string) + g_free (string); +} + +static gint +gtk_imhtml_selection_clear_event (GtkWidget *widget, + GdkEventSelection *event) +{ + GtkIMHtml *imhtml; + + g_return_val_if_fail (widget != NULL, FALSE); + g_return_val_if_fail (GTK_IS_IMHTML (widget), FALSE); + g_return_val_if_fail (event != NULL, FALSE); + g_return_val_if_fail (event->selection == GDK_SELECTION_PRIMARY, TRUE); + + if (!gtk_selection_clear (widget, event)) + return FALSE; + + imhtml = GTK_IMHTML (widget); + + gtk_imhtml_select_none (imhtml); + + return TRUE; +} + +static void +gtk_imhtml_set_scroll_adjustments (GtkLayout *layout, + GtkAdjustment *hadj, + GtkAdjustment *vadj) +{ + if (parent_class->set_scroll_adjustments) + (* parent_class->set_scroll_adjustments) (layout, hadj, vadj); +} + +static void +gtk_imhtml_class_init (GtkIMHtmlClass *class) +{ + GtkObjectClass *object_class; + GtkWidgetClass *widget_class; + GtkLayoutClass *layout_class; + + object_class = (GtkObjectClass*) class; + widget_class = (GtkWidgetClass*) class; + layout_class = (GtkLayoutClass*) class; + + parent_class = gtk_type_class (GTK_TYPE_LAYOUT); + + signals [URL_CLICKED] = + gtk_signal_new ("url_clicked", + GTK_RUN_FIRST, + object_class->type, + GTK_SIGNAL_OFFSET (GtkIMHtmlClass, url_clicked), + gtk_marshal_NONE__POINTER, + GTK_TYPE_NONE, 1, + GTK_TYPE_POINTER); + + gtk_object_class_add_signals (object_class, signals, LAST_SIGNAL); + + object_class->destroy = gtk_imhtml_destroy; + + widget_class->realize = gtk_imhtml_realize; + widget_class->unrealize = gtk_imhtml_unrealize; + widget_class->draw = gtk_imhtml_draw; + widget_class->style_set = gtk_imhtml_style_set; + widget_class->expose_event = gtk_imhtml_expose; + widget_class->size_allocate = gtk_imhtml_size_allocate; + widget_class->motion_notify_event = gtk_imhtml_motion_notify_event; + widget_class->button_press_event = gtk_imhtml_button_press_event; + widget_class->button_release_event = gtk_imhtml_button_release_event; + widget_class->selection_get = gtk_imhtml_selection_get; + widget_class->selection_clear_event = gtk_imhtml_selection_clear_event; + + layout_class->set_scroll_adjustments = gtk_imhtml_set_scroll_adjustments; +} + +static GdkFont* +gtk_imhtml_font_load (GtkIMHtml *imhtml, + gchar *name, + gboolean bold, + gboolean italics, + gint fontsize) +{ + gchar buf [16 * 1024]; + GdkFont *font; + gint size = fontsize ? font_sizes [MIN (fontsize, MAX_SIZE) - 1] : 120; + + g_snprintf (buf, sizeof (buf), "-*-%s-%s-%c-*-*-*-%d-*-*-*-*-*-*", + name ? name : DEFAULT_FONT_NAME, + bold ? "bold" : "medium", + italics ? 'i' : 'r', + size); + font = gdk_font_load (buf); + if (font) + return font; + + if (italics) { + g_snprintf (buf, sizeof (buf), "-*-%s-%s-%c-*-*-*-%d-*-*-*-*-*-*", + name ? name : DEFAULT_FONT_NAME, + bold ? "bold" : "medium", + 'o', + size); + font = gdk_font_load (buf); + if (font) + return font; + + if (bold) { + g_snprintf (buf, sizeof (buf), "-*-%s-%s-%c-*-*-*-%d-*-*-*-*-*-*", + name ? name : DEFAULT_FONT_NAME, + "bold", + 'r', + size); + font = gdk_font_load (buf); + if (font) + return font; + } + } + + if (bold) { + g_snprintf (buf, sizeof (buf), "-*-%s-%s-%c-*-*-*-%d-*-*-*-*-*-*", + name ? name : DEFAULT_FONT_NAME, + "medium", + italics ? 'i' : 'r', + size); + font = gdk_font_load (buf); + if (font) + return font; + + if (italics) { + g_snprintf (buf, sizeof (buf), "-*-%s-%s-%c-*-*-*-%d-*-*-*-*-*-*", + name ? name : DEFAULT_FONT_NAME, + "medium", + 'o', + size); + font = gdk_font_load (buf); + if (font) + return font; + } + } + + if (!bold && !italics) { + g_snprintf (buf, sizeof (buf), "-*-%s-medium-r-*-*-*-%d-*-*-*-*-*-*", + name ? name : DEFAULT_FONT_NAME, + size); + font = gdk_font_load (buf); + if (font) + return font; + } + + g_snprintf (buf, sizeof (buf), "-*-%s-medium-r-*-*-*-%d-*-*-*-*-*-*", + DEFAULT_FONT_NAME, + size); + font = gdk_font_load (buf); + if (font) + return font; + + if (imhtml->default_font) + return gdk_font_ref (imhtml->default_font); + + return NULL; +} + +static void +gtk_imhtml_init (GtkIMHtml *imhtml) +{ + static const GtkTargetEntry targets [] = { + { "STRING", 0, TARGET_STRING }, + { "TEXT", 0, TARGET_TEXT }, + { "COMPOUND_TEXT", 0, TARGET_COMPOUND_TEXT } + }; + + imhtml->default_font = gtk_imhtml_font_load (imhtml, NULL, FALSE, FALSE, 0); + if (imhtml->default_font == NULL) + g_warning ("GtkIMHtml: Could not load default font!"); + imhtml->default_fg_color = gdk_color_copy (>K_WIDGET (imhtml)->style->black); + imhtml->hand_cursor = gdk_cursor_new (GDK_HAND2); + imhtml->arrow_cursor = gdk_cursor_new (GDK_LEFT_PTR); + + GTK_WIDGET_SET_FLAGS (GTK_WIDGET (imhtml), GTK_CAN_FOCUS); + gtk_selection_add_targets (GTK_WIDGET (imhtml), GDK_SELECTION_PRIMARY, targets, 3); +} + +GtkType +gtk_imhtml_get_type (void) +{ + static GtkType imhtml_type = 0; + + if (!imhtml_type) { + static const GtkTypeInfo imhtml_info = { + "GtkIMHtml", + sizeof (GtkIMHtml), + sizeof (GtkIMHtmlClass), + (GtkClassInitFunc) gtk_imhtml_class_init, + (GtkObjectInitFunc) gtk_imhtml_init, + NULL, + NULL, + NULL + }; + + imhtml_type = gtk_type_unique (GTK_TYPE_LAYOUT, &imhtml_info); + } + + return imhtml_type; +} + +static void +gtk_imhtml_init_smiley_hash (GtkIMHtml *imhtml) +{ + g_return_if_fail (imhtml != NULL); + g_return_if_fail (GTK_IS_IMHTML (imhtml)); + + imhtml->smiley_hash = g_hash_table_new (g_str_hash, g_str_equal); + + gtk_imhtml_associate_smiley (imhtml, ":)", smile_xpm); + gtk_imhtml_associate_smiley (imhtml, ":-)", smile_xpm); + + gtk_imhtml_associate_smiley (imhtml, ":(", sad_xpm); + gtk_imhtml_associate_smiley (imhtml, ":-(", sad_xpm); + + gtk_imhtml_associate_smiley (imhtml, ";)", wink_xpm); + gtk_imhtml_associate_smiley (imhtml, ";-)", wink_xpm); + + gtk_imhtml_associate_smiley (imhtml, ":-p", tongue_xpm); + gtk_imhtml_associate_smiley (imhtml, ":-P", tongue_xpm); + + gtk_imhtml_associate_smiley (imhtml, "=-O", scream_xpm); + gtk_imhtml_associate_smiley (imhtml, ":-*", kiss_xpm); + gtk_imhtml_associate_smiley (imhtml, ">:o", yell_xpm); + gtk_imhtml_associate_smiley (imhtml, "8-)", smile8_xpm); + gtk_imhtml_associate_smiley (imhtml, ":-$", moneymouth_xpm); + gtk_imhtml_associate_smiley (imhtml, ":-!", burp_xpm); + gtk_imhtml_associate_smiley (imhtml, ":-[", embarrassed_xpm); + gtk_imhtml_associate_smiley (imhtml, ":'(", cry_xpm); + + gtk_imhtml_associate_smiley (imhtml, ":-/", think_xpm); + gtk_imhtml_associate_smiley (imhtml, ":-\\", think_xpm); + + gtk_imhtml_associate_smiley (imhtml, ":-X", crossedlips_xpm); + gtk_imhtml_associate_smiley (imhtml, ":-D", bigsmile_xpm); + gtk_imhtml_associate_smiley (imhtml, "O:-)", angel_xpm); +} + +GtkWidget* +gtk_imhtml_new (GtkAdjustment *hadj, + GtkAdjustment *vadj) +{ + GtkIMHtml *imhtml = gtk_type_new (GTK_TYPE_IMHTML); + + gtk_imhtml_set_adjustments (imhtml, hadj, vadj); + + imhtml->bits = NULL; + imhtml->urls = NULL; + + imhtml->x = BORDER_SIZE; + imhtml->y = BORDER_SIZE + 10; + imhtml->llheight = 0; + imhtml->llascent = 0; + imhtml->line = NULL; + + imhtml->selected_text = g_string_new (""); + imhtml->scroll_timer = 0; + + imhtml->img = NULL; + + imhtml->smileys = TRUE; + imhtml->comments = FALSE; + + imhtml->smin = G_MAXINT; + imhtml->smax = 0; + gtk_imhtml_init_smiley_hash (imhtml); + + return GTK_WIDGET (imhtml); +} + +void +gtk_imhtml_set_adjustments (GtkIMHtml *imhtml, + GtkAdjustment *hadj, + GtkAdjustment *vadj) +{ + gtk_layout_set_hadjustment (GTK_LAYOUT (imhtml), hadj); + gtk_layout_set_vadjustment (GTK_LAYOUT (imhtml), vadj); +} + +void +gtk_imhtml_set_defaults (GtkIMHtml *imhtml, + GdkFont *font, + GdkColor *fg_color) +{ + g_return_if_fail (imhtml != NULL); + g_return_if_fail (GTK_IS_IMHTML (imhtml)); + + if (font) { + if (imhtml->default_font) + gdk_font_unref (imhtml->default_font); + imhtml->default_font = gdk_font_ref (font); + } + + if (fg_color) { + if (imhtml->default_fg_color) + gdk_color_free (imhtml->default_fg_color); + imhtml->default_fg_color = gdk_color_copy (fg_color); + } +} + +void +gtk_imhtml_set_img_handler (GtkIMHtml *imhtml, + GtkIMHtmlImage handler) +{ + g_return_if_fail (imhtml != NULL); + g_return_if_fail (GTK_IS_IMHTML (imhtml)); + + imhtml->img = handler; +} + +void +gtk_imhtml_associate_smiley (GtkIMHtml *imhtml, + gchar *text, + gchar **xpm) +{ + g_return_if_fail (imhtml != NULL); + g_return_if_fail (GTK_IS_IMHTML (imhtml)); + g_return_if_fail (text != NULL); + + if (strlen (text) < imhtml->smin) + imhtml->smin = strlen (text); + + if (strlen (text) > imhtml->smax) + imhtml->smax = strlen (text); + + if (xpm == NULL) + g_hash_table_remove (imhtml->smiley_hash, text); + else + g_hash_table_insert (imhtml->smiley_hash, text, xpm); +} + +static gint +draw_text (GtkWidget *widget, + GdkEvent *event, + gpointer data) +{ + GtkIMHtmlBit *bit; + struct line_info *line; + GdkGC *gc; + GdkColormap *cmap; + + line = gtk_object_get_user_data (GTK_OBJECT (widget)); + bit = line->bit; + gc = gdk_gc_new (widget->window); + cmap = gdk_colormap_new (gdk_visual_get_best (), FALSE); + + if (bit->bg != NULL) { + gdk_color_alloc (cmap, bit->bg); + gdk_gc_set_foreground (gc, bit->bg); + } else + gdk_gc_copy (gc, widget->style->white_gc); + + gdk_draw_rectangle (widget->window, gc, TRUE, 0, 0, + widget->allocation.width, + widget->allocation.height); + + if (!line->text) { + gdk_colormap_unref (cmap); + gdk_gc_unref (gc); + return TRUE; + } + + if (bit->back != NULL) { + gdk_color_alloc (cmap, bit->back); + gdk_gc_set_foreground (gc, bit->back); + gdk_draw_rectangle (widget->window, gc, TRUE, 0, 0, + gdk_string_width (bit->font, line->text), + widget->allocation.height); + } + + if (line->selected) { + gint width, x; + gchar *start, *end; + GdkColor col; + + if ((line->sel_start > line->sel_end) && (line->sel_end != NULL)) { + start = line->sel_end; + end = line->sel_start; + } else { + start = line->sel_start; + end = line->sel_end; + } + + if (start == NULL) + x = 0; + else + x = gdk_text_width (bit->font, line->text, start - line->text); + + if (end == NULL) + width = gdk_string_width (bit->font, line->text) - x; + else + width = gdk_text_width (bit->font, line->text, end - line->text) - x; + + col.red = col.green = col.blue = 0xc000; + gdk_color_alloc (cmap, &col); + gdk_gc_set_foreground (gc, &col); + + gdk_draw_rectangle (widget->window, gc, TRUE, x, 0, + width, widget->allocation.height); + } + + if (bit->url) { + GdkColor *tc = gtk_imhtml_get_color ("#0000a0"); + gdk_color_alloc (cmap, tc); + gdk_gc_set_foreground (gc, tc); + gdk_color_free (tc); + } else if (bit->fore) { + gdk_color_alloc (cmap, bit->fore); + gdk_gc_set_foreground (gc, bit->fore); + } else + gdk_gc_copy (gc, widget->style->black_gc); + + gdk_draw_string (widget->window, bit->font, gc, + 0, line->ascent, line->text); + + if (bit->underline || bit->url) + gdk_draw_rectangle (widget->window, gc, TRUE, + 0, line->ascent + 1, + gdk_string_width (bit->font, line->text), 1); + if (bit->strike) + gdk_draw_rectangle (widget->window, gc, TRUE, + 0, line->ascent - (bit->font->ascent >> 1), + gdk_string_width (bit->font, line->text), 1); + + gdk_colormap_unref (cmap); + gdk_gc_unref (gc); + + return TRUE; +} + +static gint +draw_img (GtkWidget *widget, + GdkEvent *event, + gpointer data) +{ + GtkIMHtmlBit *bit; + struct line_info *line; + GdkGC *gc; + GdkColormap *cmap; + gint width, height, hoff; + + line = gtk_object_get_user_data (GTK_OBJECT (widget)); + bit = line->bit; + gdk_window_get_size (bit->pm, &width, &height); + hoff = (widget->allocation.height - height) / 2; + gc = gdk_gc_new (widget->window); + cmap = gdk_colormap_new (gdk_visual_get_best (), FALSE); + + if (bit->bg != NULL) { + gdk_color_alloc (cmap, bit->bg); + gdk_gc_set_foreground (gc, bit->bg); + } else + gdk_gc_copy (gc, widget->style->white_gc); + + gdk_draw_rectangle (widget->window, gc, TRUE, 0, 0, + widget->allocation.width, + widget->allocation.height); + + if (bit->back != NULL) { + gdk_color_alloc (cmap, bit->back); + gdk_gc_set_foreground (gc, bit->back); + gdk_draw_rectangle (widget->window, gc, TRUE, 0, 0, + width, widget->allocation.height); + } + + gdk_draw_pixmap (widget->window, gc, bit->pm, 0, 0, 0, hoff, -1, -1); + + gdk_colormap_unref (cmap); + gdk_gc_unref (gc); + + return TRUE; +} + +static gint +draw_line (GtkWidget *widget, + GdkEvent *event, + gpointer data) +{ + GtkIMHtmlBit *bit; + GdkDrawable *drawable; + GdkColormap *cmap; + GdkGC *gc; + guint max_width; + guint max_height; + + bit = data; + drawable = widget->window; + cmap = gdk_colormap_new (gdk_visual_get_best (), FALSE); + gc = gdk_gc_new (drawable); + + if (bit->bg != NULL) { + gdk_color_alloc (cmap, bit->bg); + gdk_gc_set_foreground (gc, bit->bg); + + gdk_draw_rectangle (widget->window, gc, TRUE, 0, 0, + widget->allocation.width, + widget->allocation.height); + } + + gdk_gc_copy (gc, widget->style->black_gc); + + max_width = widget->allocation.width; + max_height = widget->allocation.height / 2; + + gdk_draw_rectangle (drawable, gc, + TRUE, + 0, max_height / 2, + max_width, max_height); + + gdk_colormap_unref (cmap); + gdk_gc_unref (gc); + + return TRUE; +} + +static gint +click_event_box (GtkBin *bin, + GdkEventButton *event, + GtkIMHtml *imhtml) +{ + struct line_info *li = gtk_object_get_user_data (GTK_OBJECT (bin->child)); + + if (event->button == 1 && event->type == GDK_BUTTON_PRESS) + gtk_signal_emit (GTK_OBJECT (imhtml), signals [URL_CLICKED], li->bit->url); + + return TRUE; +} + +static void +new_line (GtkIMHtml *imhtml) +{ + GList *last = g_list_last (imhtml->line); + GtkWidget *widget; + struct line_info *li; + + if (last) { + widget = last->data; + li = gtk_object_get_user_data (GTK_OBJECT (widget)); + if (li->x + li->width != imhtml->xsize - BORDER_SIZE) { + li->width = imhtml->xsize - BORDER_SIZE - li->x; + gtk_widget_set_usize (widget, li->width, li->height); + } + } + + last = imhtml->line; + if (last) { + widget = last->data; + li = gtk_object_get_user_data (GTK_OBJECT (widget)); + if (li->height < MIN_HEIGHT) { + while (last) { + gint diff; + widget = last->data; + li = gtk_object_get_user_data (GTK_OBJECT (widget)); + diff = MIN_HEIGHT - li->height; + li->height = MIN_HEIGHT; + gtk_widget_set_usize (widget, li->width, li->height); + li->ascent += diff >> 1; + last = g_list_next (last); + } + imhtml->llheight = MIN_HEIGHT; + } + } + + g_list_free (imhtml->line); + imhtml->line = NULL; + + imhtml->x = BORDER_SIZE; + imhtml->y += imhtml->llheight; +} + +static void +backwards_update (GtkIMHtml *imhtml, + GtkIMHtmlBit *bit, + gint height, + gint ascent) +{ + gint diff; + GList *ls = NULL; + struct line_info *li; + struct url_widget *uw; + + if (height > imhtml->llheight) { + diff = height - imhtml->llheight; + + ls = imhtml->line; + while (ls) { + GtkWidget *data = ls->data; + li = gtk_object_get_user_data (GTK_OBJECT (data)); + li->height += diff; + if (ascent) + li->ascent = ascent; + else + li->ascent += diff >> 1; + gtk_widget_set_usize (data, li->width, li->height); + ls = g_list_next (ls); + } + + ls = imhtml->urls; + while (ls) { + uw = ls->data; + if (uw->y + diff > imhtml->y) + uw->y += diff; + ls = g_list_next (ls); + } + + imhtml->llheight = height; + if (ascent) + imhtml->llascent = ascent; + else + imhtml->llascent += diff >> 1; + } +} + +static GtkTooltips *tips = NULL; + +static void +add_text_renderer (GtkIMHtml *imhtml, + GtkIMHtmlBit *bit, + gchar *text) +{ + GtkWidget *darea; + GtkWidget *eventbox; + struct line_info *li; + struct url_widget *uw; + gint width; + + if (text) + width = gdk_string_width (bit->font, text); + else + width = 0; + + darea = gtk_drawing_area_new (); + gtk_widget_set_usize (darea, width, imhtml->llheight); + gtk_signal_connect (GTK_OBJECT (darea), "expose_event", GTK_SIGNAL_FUNC (draw_text), NULL); + + li = g_new0 (struct line_info, 1); + li->x = imhtml->x; + li->y = imhtml->y; + li->width = width; + li->height = imhtml->llheight; + if (text) + li->ascent = MAX (imhtml->llascent, bit->font->ascent); + else + li->ascent = 0; + li->text = text; + li->bit = bit; + + gtk_object_set_user_data (GTK_OBJECT (darea), li); + + if (bit->url) { + eventbox = gtk_event_box_new (); + gtk_layout_put (GTK_LAYOUT (imhtml), eventbox, imhtml->x, imhtml->y); + gtk_signal_connect (GTK_OBJECT (eventbox), "button_press_event", + GTK_SIGNAL_FUNC (click_event_box), imhtml); + gtk_widget_show (eventbox); + + gtk_container_add (GTK_CONTAINER (eventbox), darea); + + uw = g_new0 (struct url_widget, 1); + uw->x = imhtml->x; + uw->y = imhtml->y; + uw->width = width; + uw->height = imhtml->llheight; + uw->url = bit->url; + imhtml->urls = g_list_append (imhtml->urls, uw); + + if (!tips) + tips = gtk_tooltips_new (); + gtk_tooltips_set_tip (tips, eventbox, bit->url, ""); + } else { + gtk_layout_put (GTK_LAYOUT (imhtml), darea, imhtml->x, imhtml->y); + } + + bit->chunks = g_list_append (bit->chunks, darea); + imhtml->line = g_list_append (imhtml->line, darea); + gtk_widget_show (darea); + + if (bit->bg) { + GdkColormap *cmap; + + cmap = gdk_colormap_new (gdk_visual_get_best (), FALSE); + gdk_color_alloc (cmap, bit->bg); + gdk_window_set_background (darea->window, bit->bg); + gdk_colormap_unref (cmap); + } else + gdk_window_set_background (darea->window, &darea->style->white); +} + +static void +add_img_renderer (GtkIMHtml *imhtml, + GtkIMHtmlBit *bit) +{ + GtkWidget *darea; + GtkWidget *eventbox; + struct line_info *li; + struct url_widget *uw; + gint width; + + gdk_window_get_size (bit->pm, &width, NULL); + + darea = gtk_drawing_area_new (); + gtk_widget_set_usize (darea, width, imhtml->llheight); + gtk_signal_connect (GTK_OBJECT (darea), "expose_event", GTK_SIGNAL_FUNC (draw_img), NULL); + + li = g_new0 (struct line_info, 1); + li->x = imhtml->x; + li->y = imhtml->y; + li->width = width; + li->height = imhtml->llheight; + li->ascent = 0; + li->bit = bit; + + gtk_object_set_user_data (GTK_OBJECT (darea), li); + + if (bit->url) { + eventbox = gtk_event_box_new (); + gtk_layout_put (GTK_LAYOUT (imhtml), eventbox, imhtml->x, imhtml->y); + gtk_signal_connect (GTK_OBJECT (eventbox), "button_press_event", + GTK_SIGNAL_FUNC (click_event_box), imhtml); + gtk_widget_show (eventbox); + + gtk_container_add (GTK_CONTAINER (eventbox), darea); + + uw = g_new0 (struct url_widget, 1); + uw->x = imhtml->x; + uw->y = imhtml->y; + uw->width = width; + uw->height = imhtml->llheight; + uw->url = bit->url; + imhtml->urls = g_list_append (imhtml->urls, uw); + + if (!tips) + tips = gtk_tooltips_new (); + gtk_tooltips_set_tip (tips, eventbox, bit->url, ""); + } else { + gtk_layout_put (GTK_LAYOUT (imhtml), darea, imhtml->x, imhtml->y); + } + + bit->chunks = g_list_append (bit->chunks, darea); + imhtml->line = g_list_append (imhtml->line, darea); + gtk_widget_show (darea); + + if (bit->bg) { + GdkColormap *cmap; + + cmap = gdk_colormap_new (gdk_visual_get_best (), FALSE); + gdk_color_alloc (cmap, bit->bg); + gdk_window_set_background (darea->window, bit->bg); + gdk_colormap_unref (cmap); + } else + gdk_window_set_background (darea->window, &darea->style->white); + + imhtml->x += width; +} + +static void +gtk_imhtml_draw_bit (GtkIMHtml *imhtml, + GtkIMHtmlBit *bit) +{ + gint width, height; + GdkWindow *window; + GdkGC *gc; + + g_return_if_fail (imhtml != NULL); + g_return_if_fail (GTK_IS_IMHTML (imhtml)); + g_return_if_fail (bit != NULL); + + window = GTK_LAYOUT (imhtml)->bin_window; + gc = gdk_gc_new (window); + + if ( (bit->type == TYPE_TEXT) || + ((bit->type == TYPE_SMILEY) && !imhtml->smileys) || + ((bit->type == TYPE_COMMENT) && imhtml->comments)) { + gchar *copy = g_strdup (bit->text); + gint pos = 0; + gboolean seenspace = FALSE; + gchar *tmp; + + height = bit->font->ascent + bit->font->descent; + width = gdk_string_width (bit->font, bit->text); + + if ((imhtml->x != BORDER_SIZE) && + ((imhtml->x + width + BORDER_SIZE + BORDER_SIZE + 5) > imhtml->xsize)) { + gint remain = imhtml->xsize - imhtml->x - BORDER_SIZE - BORDER_SIZE - 5; + while (gdk_text_width (bit->font, copy, pos) < remain) { + if (copy [pos] == ' ') + seenspace = TRUE; + pos++; + } + if (seenspace) { + while (copy [pos - 1] != ' ') pos--; + + tmp = g_strndup (copy, pos); + + backwards_update (imhtml, bit, height, bit->font->ascent); + add_text_renderer (imhtml, bit, tmp); + } else + pos = 0; + seenspace = FALSE; + new_line (imhtml); + imhtml->llheight = 0; + imhtml->llascent = 0; + } + + backwards_update (imhtml, bit, height, bit->font->ascent); + + while (pos < strlen (bit->text)) { + width = gdk_string_width (bit->font, copy + pos); + if (imhtml->x + width + BORDER_SIZE + BORDER_SIZE + 5 > imhtml->xsize) { + gint newpos = 0; + gint remain = imhtml->xsize - imhtml->x - BORDER_SIZE - BORDER_SIZE - 5; + while (gdk_text_width (bit->font, copy + pos, newpos) < remain) { + if (copy [pos + newpos] == ' ') + seenspace = TRUE; + newpos++; + } + + if (seenspace) + while (copy [pos + newpos - 1] != ' ') newpos--; + + if (newpos == 0) + break; + + tmp = g_strndup (copy + pos, newpos); + pos += newpos; + + add_text_renderer (imhtml, bit, tmp); + + seenspace = FALSE; + new_line (imhtml); + } else { + tmp = g_strdup (copy + pos); + + add_text_renderer (imhtml, bit, tmp); + + pos = strlen (bit->text); + + imhtml->x += width; + } + } + + g_free (copy); + } else if ((bit->type == TYPE_SMILEY) || (bit->type == TYPE_IMG)) { + gdk_window_get_size (bit->pm, &width, &height); + + if ((imhtml->x != BORDER_SIZE) && + ((imhtml->x + width + BORDER_SIZE + BORDER_SIZE + 5) > imhtml->xsize)) { + new_line (imhtml); + imhtml->llheight = 0; + imhtml->llascent = 0; + } else + backwards_update (imhtml, bit, height, height * 3 / 4); + + add_img_renderer (imhtml, bit); + } else if (bit->type == TYPE_BR) { + new_line (imhtml); + imhtml->llheight = 0; + imhtml->llascent = 0; + add_text_renderer (imhtml, bit, NULL); + } else if (bit->type == TYPE_SEP) { + GtkWidget *darea; + if (imhtml->llheight) { + new_line (imhtml); + imhtml->llheight = 0; + imhtml->llascent = 0; + } + darea = gtk_drawing_area_new (); + gtk_widget_set_usize (darea, imhtml->xsize - (BORDER_SIZE * 2), HR_HEIGHT * 2); + gtk_layout_put (GTK_LAYOUT (imhtml), darea, imhtml->x, imhtml->y); + gtk_signal_connect (GTK_OBJECT (darea), "expose_event", + GTK_SIGNAL_FUNC (draw_line), bit); + gtk_widget_show (darea); + imhtml->llheight = HR_HEIGHT * 2; + new_line (imhtml); + imhtml->llheight = 0; + imhtml->llascent = 0; + add_text_renderer (imhtml, bit, NULL); + } + + gtk_layout_set_size (GTK_LAYOUT (imhtml), imhtml->xsize, imhtml->y + 5); + + gdk_gc_destroy (gc); +} + +void +gtk_imhtml_show_smileys (GtkIMHtml *imhtml, + gboolean show) +{ + g_return_if_fail (imhtml != NULL); + g_return_if_fail (GTK_IS_IMHTML (imhtml)); + + imhtml->smileys = show; + + if (GTK_WIDGET_VISIBLE (GTK_WIDGET (imhtml))) + gtk_imhtml_redraw_all (imhtml); +} + +void +gtk_imhtml_show_comments (GtkIMHtml *imhtml, + gboolean show) +{ + g_return_if_fail (imhtml != NULL); + g_return_if_fail (GTK_IS_IMHTML (imhtml)); + + imhtml->comments = show; + + if (GTK_WIDGET_VISIBLE (GTK_WIDGET (imhtml))) + gtk_imhtml_redraw_all (imhtml); +} + +static GdkColor * +gtk_imhtml_get_color (const gchar *color) +{ + GdkColor c; + gboolean valid = TRUE; + + g_return_val_if_fail (color != NULL, NULL); + + c.red = 0; c.green = 0; c.blue = 0; + + if (!g_strcasecmp (color, "aliceblue")) { + c.red = 0xf000; c.green = 0xf800; c.blue = 0xff00; + } else if (!g_strcasecmp (color, "antiquewhite")) { + c.red = 0xfa00; c.green = 0xeb00; c.blue = 0xd700; + } else if (!g_strcasecmp (color, "aqua")) { + c.red = 0; c.green = 0xff00; c.blue = 0xff00; + } else if (!g_strcasecmp (color, "aquamarine")) { + c.red = 0; c.green = 0xff00; c.blue = 0xff00; + } else if (!g_strcasecmp (color, "azure")) { + c.red = 0xf000; c.green = 0xff00; c.blue = 0xff00; + } else if (!g_strcasecmp (color, "beige")) { + c.red = 0xf500; c.green = 0xf500; c.blue = 0xdc00; + } else if (!g_strcasecmp (color, "bisque")) { + c.red = 0xff00; c.green = 0xe400; c.blue = 0xc400; + } else if (!g_strcasecmp (color, "black")) { + c.red = 0; c.green = 0; c.blue = 0; + } else if (!g_strcasecmp (color, "blanchedalmond")) { + c.red = 0xff00; c.green = 0xeb00; c.blue = 0xcd00; + } else if (!g_strcasecmp (color, "blue")) { + c.red = 0; c.green = 0; c.blue = 0xff00; + } else if (!g_strcasecmp (color, "blueviolet")) { + c.red = 0; c.green = 0; c.blue = 0xff00; + } else if (!g_strcasecmp (color, "brown")) { + c.red = 0xa500; c.green = 0x2a00; c.blue = 0x2a00; + } else if (!g_strcasecmp (color, "burlywood")) { + c.red = 0xde00; c.green = 0xb800; c.blue = 0x8700; + } else if (!g_strcasecmp (color, "cadetblue")) { + c.red = 0x5f00; c.green = 0x9e00; c.blue = 0xa000; + } else if (!g_strcasecmp (color, "chartreuse")) { + c.red = 0x7f00; c.green = 0xff00; c.blue = 0; + } else if (!g_strcasecmp (color, "chocolate")) { + c.red = 0xd200; c.green = 0x6900; c.blue = 0x1e00; + } else if (!g_strcasecmp (color, "coral")) { + c.red = 0xff00; c.green = 0x7f00; c.blue = 0x5000; + } else if (!g_strcasecmp (color, "cornflowerblue")) { + c.red = 0x6400; c.green = 0x9500; c.blue = 0xed00; + } else if (!g_strcasecmp (color, "cornsilk")) { + c.red = 0xff00; c.green = 0xf800; c.blue = 0xdc00; + } else if (!g_strcasecmp (color, "crimson")) { + c.red = 0xdc00; c.green = 0x1400; c.blue = 0x3c00; + } else if (!g_strcasecmp (color, "cyan")) { + c.red = 0; c.green = 0xff00; c.blue = 0xff00; + } else if (!g_strcasecmp (color, "darkblue")) { + c.red = 0; c.green = 0; c.blue = 0x8b00; + } else if (!g_strcasecmp (color, "darkcyan")) { + c.red = 0; c.green = 0x8b00; c.blue = 0x8b00; + } else if (!g_strcasecmp (color, "darkgoldenrod")) { + c.red = 0xb800; c.green = 0x8600; c.blue = 0x0b00; + } else if (!g_strcasecmp (color, "darkgray")) { + c.red = 0xa900; c.green = 0xa900; c.blue = 0xa900; + } else if (!g_strcasecmp (color, "darkgreen")) { + c.red = 0; c.green = 0x6400; c.blue = 0; + } else if (!g_strcasecmp (color, "darkkhaki")) { + c.red = 0xbd00; c.green = 0xb700; c.blue = 0x6b00; + } else if (!g_strcasecmp (color, "darkmagenta")) { + c.red = 0x8b00; c.green = 0; c.blue = 0x8b00; + } else if (!g_strcasecmp (color, "darkolivegreen")) { + c.red = 0x5500; c.green = 0x6b00; c.blue = 0x2f00; + } else if (!g_strcasecmp (color, "darkorange")) { + c.red = 0xff00; c.green = 0x8c00; c.blue = 0; + } else if (!g_strcasecmp (color, "darkorchid")) { + c.red = 0x9900; c.green = 0x3200; c.blue = 0xcc00; + } else if (!g_strcasecmp (color, "darkred")) { + c.red = 0x8b00; c.green = 0; c.blue = 0; + } else if (!g_strcasecmp (color, "darksalmon")) { + c.red = 0xe900; c.green = 0x9600; c.blue = 0x7a00; + } else if (!g_strcasecmp (color, "darkseagreen")) { + c.red = 0x8f00; c.green = 0xbc00; c.blue = 0x8f00; + } else if (!g_strcasecmp (color, "darkslateblue")) { + c.red = 0x4800; c.green = 0x3d00; c.blue = 0x8b00; + } else if (!g_strcasecmp (color, "darkslategray")) { + c.red = 0x2f00; c.green = 0x4f00; c.blue = 0x4f00; + } else if (!g_strcasecmp (color, "darkturquoise")) { + c.red = 0; c.green = 0xce00; c.blue = 0xd100; + } else if (!g_strcasecmp (color, "darkviolet")) { + c.red = 0x9400; c.green = 0; c.blue = 0xd300; + } else if (!g_strcasecmp (color, "deeppink")) { + c.red = 0xff00; c.green = 0x1400; c.blue = 0x9300; + } else if (!g_strcasecmp (color, "deepskyblue")) { + c.red = 0; c.green = 0xbf00; c.blue = 0xff00; + } else if (!g_strcasecmp (color, "dimgray")) { + c.red = 0x6900; c.green = 0x6900; c.blue = 0x6900; + } else if (!g_strcasecmp (color, "dodgerblue")) { + c.red = 0x1e00; c.green = 0x9000; c.blue = 0xff00; + } else if (!g_strcasecmp (color, "firebrick")) { + c.red = 0xb200; c.green = 0x2200; c.blue = 0x2200; + } else if (!g_strcasecmp (color, "floralwhite")) { + c.red = 0xff00; c.green = 0xfa00; c.blue = 0xf000; + } else if (!g_strcasecmp (color, "forestgreen")) { + c.red = 0x2200; c.green = 0x8b00; c.blue = 0x2200; + } else if (!g_strcasecmp (color, "fuchsia")) { + c.red = 0xff00; c.green = 0; c.blue = 0xff00; + } else if (!g_strcasecmp (color, "gainsboro")) { + c.red = 0xdc00; c.green = 0xdc00; c.blue = 0xdc00; + } else if (!g_strcasecmp (color, "ghostwhite")) { + c.red = 0xf800; c.green = 0xf800; c.blue = 0xff00; + } else if (!g_strcasecmp (color, "gold")) { + c.red = 0xff00; c.green = 0xd700; c.blue = 0; + } else if (!g_strcasecmp (color, "goldenrod")) { + c.red = 0xff00; c.green = 0xd700; c.blue = 0; + } else if (!g_strcasecmp (color, "gray")) { + c.red = 0x8000; c.green = 0x8000; c.blue = 0x8000; + } else if (!g_strcasecmp (color, "green")) { + c.red = 0; c.green = 0x8000; c.blue = 0; + } else if (!g_strcasecmp (color, "greenyellow")) { + c.red = 0; c.green = 0x8000; c.blue = 0; + } else if (!g_strcasecmp (color, "honeydew")) { + c.red = 0xf000; c.green = 0xff00; c.blue = 0xf000; + } else if (!g_strcasecmp (color, "hotpink")) { + c.red = 0xff00; c.green = 0x6900; c.blue = 0xb400; + } else if (!g_strcasecmp (color, "indianred")) { + c.red = 0xcd00; c.green = 0x5c00; c.blue = 0x5c00; + } else if (!g_strcasecmp (color, "indigo")) { + c.red = 0x4b00; c.green = 0; c.blue = 0x8200; + } else if (!g_strcasecmp (color, "ivory")) { + c.red = 0xff00; c.green = 0xff00; c.blue = 0xf000; + } else if (!g_strcasecmp (color, "khaki")) { + c.red = 0xf000; c.green = 0xe600; c.blue = 0x8c00; + } else if (!g_strcasecmp (color, "lavender")) { + c.red = 0xe600; c.green = 0xe600; c.blue = 0xfa00; + } else if (!g_strcasecmp (color, "lavenderblush")) { + c.red = 0xe600; c.green = 0xe600; c.blue = 0xfa00; + } else if (!g_strcasecmp (color, "lawngreen")) { + c.red = 0x7c00; c.green = 0xfc00; c.blue = 0; + } else if (!g_strcasecmp (color, "lemonchiffon")) { + c.red = 0xff00; c.green = 0xfa00; c.blue = 0xcd00; + } else if (!g_strcasecmp (color, "lightblue")) { + c.red = 0xad00; c.green = 0xd800; c.blue = 0xe600; + } else if (!g_strcasecmp (color, "lightcoral")) { + c.red = 0xf000; c.green = 0x8000; c.blue = 0x8000; + } else if (!g_strcasecmp (color, "lightcyan")) { + c.red = 0xe000; c.green = 0xff00; c.blue = 0xff00; + } else if (!g_strcasecmp (color, "lightgoldenrodyellow")) { + c.red = 0xfa00; c.green = 0xfa00; c.blue = 0xd200; + } else if (!g_strcasecmp (color, "lightgreen")) { + c.red = 0x9000; c.green = 0xee00; c.blue = 0x9000; + } else if (!g_strcasecmp (color, "lightgray")) { + c.red = 0xd300; c.green = 0xd300; c.blue = 0xd300; + } else if (!g_strcasecmp (color, "lightpink")) { + c.red = 0xff00; c.green = 0xb600; c.blue = 0xc100; + } else if (!g_strcasecmp (color, "lightsalmon")) { + c.red = 0xff00; c.green = 0xa000; c.blue = 0x7a00; + } else if (!g_strcasecmp (color, "lightseagreen")) { + c.red = 0x2000; c.green = 0xb200; c.blue = 0xaa00; + } else if (!g_strcasecmp (color, "lightskyblue")) { + c.red = 0x8700; c.green = 0xce00; c.blue = 0xfa00; + } else if (!g_strcasecmp (color, "lightslategray")) { + c.red = 0x7700; c.green = 0x8800; c.blue = 0x9900; + } else if (!g_strcasecmp (color, "lightsteelblue")) { + c.red = 0xb000; c.green = 0xc400; c.blue = 0xde00; + } else if (!g_strcasecmp (color, "lightyellow")) { + c.red = 0xff00; c.green = 0xff00; c.blue = 0xe000; + } else if (!g_strcasecmp (color, "lime")) { + c.red = 0; c.green = 0xff00; c.blue = 0; + } else if (!g_strcasecmp (color, "limegreen")) { + c.red = 0; c.green = 0xff00; c.blue = 0; + } else if (!g_strcasecmp (color, "linen")) { + c.red = 0xfa00; c.green = 0xf000; c.blue = 0xe600; + } else if (!g_strcasecmp (color, "magenta")) { + c.red = 0xff00; c.green = 0; c.blue = 0xff00; + } else if (!g_strcasecmp (color, "maroon")) { + c.red = 0x8000; c.green = 0; c.blue = 0; + } else if (!g_strcasecmp (color, "mediumaquamarine")) { + c.red = 0x6600; c.green = 0xcd00; c.blue = 0xaa00; + } else if (!g_strcasecmp (color, "mediumblue")) { + c.red = 0; c.green = 0; c.blue = 0xcd00; + } else if (!g_strcasecmp (color, "mediumorchid")) { + c.red = 0xba00; c.green = 0x5500; c.blue = 0xd300; + } else if (!g_strcasecmp (color, "mediumpurple")) { + c.red = 0x93; c.green = 0x7000; c.blue = 0xdb00; + } else if (!g_strcasecmp (color, "mediumseagreen")) { + c.red = 0x3c00; c.green = 0xb300; c.blue = 0x7100; + } else if (!g_strcasecmp (color, "mediumslateblue")) { + c.red = 0x7b00; c.green = 0x6800; c.blue = 0xee00; + } else if (!g_strcasecmp (color, "mediumspringgreen")) { + c.red = 0; c.green = 0xfa00; c.blue = 0x9a00; + } else if (!g_strcasecmp (color, "mediumturquoise")) { + c.red = 0x4800; c.green = 0xd100; c.blue = 0xcc00; + } else if (!g_strcasecmp (color, "mediumvioletred")) { + c.red = 0xc700; c.green = 0x1500; c.blue = 0x8500; + } else if (!g_strcasecmp (color, "midnightblue")) { + c.red = 0x1900; c.green = 0x1900; c.blue = 0x7000; + } else if (!g_strcasecmp (color, "mintcream")) { + c.red = 0xf500; c.green = 0xff00; c.blue = 0xfa00; + } else if (!g_strcasecmp (color, "mistyrose")) { + c.red = 0xff00; c.green = 0xe400; c.blue = 0xe100; + } else if (!g_strcasecmp (color, "moccasin")) { + c.red = 0xff00; c.green = 0xe400; c.blue = 0xb500; + } else if (!g_strcasecmp (color, "navajowhite")) { + c.red = 0xff00; c.green = 0xde00; c.blue = 0xad00; + } else if (!g_strcasecmp (color, "navy")) { + c.red = 0; c.green = 0x8000; c.blue = 0; + } else if (!g_strcasecmp (color, "oldlace")) { + c.red = 0xfd00; c.green = 0xf500; c.blue = 0xe600; + } else if (!g_strcasecmp (color, "olive")) { + c.red = 0x8000; c.green = 0x8000; c.blue = 0; + } else if (!g_strcasecmp (color, "olivedrab")) { + c.red = 0x8000; c.green = 0x8000; c.blue = 0; + } else if (!g_strcasecmp (color, "orange")) { + c.red = 0xff00; c.green = 0xa500; c.blue = 0; + } else if (!g_strcasecmp (color, "orangered")) { + c.red = 0xff00; c.green = 0xa500; c.blue = 0; + } else if (!g_strcasecmp (color, "orchid")) { + c.red = 0xda00; c.green = 0x7000; c.blue = 0xd600; + } else if (!g_strcasecmp (color, "palegoldenrod")) { + c.red = 0xee00; c.green = 0xe800; c.blue = 0xaa00; + } else if (!g_strcasecmp (color, "palegreen")) { + c.red = 0x9800; c.green = 0xfb00; c.blue = 0x9800; + } else if (!g_strcasecmp (color, "paleturquoise")) { + c.red = 0xaf00; c.green = 0xee00; c.blue = 0xee00; + } else if (!g_strcasecmp (color, "palevioletred")) { + c.red = 0xdb00; c.green = 0x7000; c.blue = 0x9300; + } else if (!g_strcasecmp (color, "papayawhip")) { + c.red = 0xff00; c.green = 0xef00; c.blue = 0xd500; + } else if (!g_strcasecmp (color, "peachpuff")) { + c.red = 0xff00; c.green = 0xda00; c.blue = 0xb900; + } else if (!g_strcasecmp (color, "peru")) { + c.red = 0xcd00; c.green = 0x8500; c.blue = 0x3f00; + } else if (!g_strcasecmp (color, "pink")) { + c.red = 0xff00; c.green = 0xc000; c.blue = 0xcb00; + } else if (!g_strcasecmp (color, "plum")) { + c.red = 0xdd00; c.green = 0xa000; c.blue = 0xdd00; + } else if (!g_strcasecmp (color, "powderblue")) { + c.red = 0xb000; c.green = 0xe000; c.blue = 0xe600; + } else if (!g_strcasecmp (color, "purple")) { + c.red = 0x8000; c.green = 0; c.blue = 0x8000; + } else if (!g_strcasecmp (color, "red")) { + c.red = 0xff00; c.green = 0; c.blue = 0; + } else if (!g_strcasecmp (color, "rosybrown")) { + c.red = 0xbc00; c.green = 0x8f00; c.blue = 0x8f00; + } else if (!g_strcasecmp (color, "royalblue")) { + c.red = 0x4100; c.green = 0x6900; c.blue = 0xe100; + } else if (!g_strcasecmp (color, "saddlebrown")) { + c.red = 0x8b00; c.green = 0x4500; c.blue = 0x1300; + } else if (!g_strcasecmp (color, "salmon")) { + c.red = 0xfa00; c.green = 0x8000; c.blue = 0x7200; + } else if (!g_strcasecmp (color, "sandybrown")) { + c.red = 0xf400; c.green = 0xa400; c.blue = 0x6000; + } else if (!g_strcasecmp (color, "seagreen")) { + c.red = 0x2e00; c.green = 0x8b00; c.blue = 0x5700; + } else if (!g_strcasecmp (color, "seashell")) { + c.red = 0xff00; c.green = 0xf500; c.blue = 0xee00; + } else if (!g_strcasecmp (color, "sienna")) { + c.red = 0xa000; c.green = 0x5200; c.blue = 0x2d00; + } else if (!g_strcasecmp (color, "silver")) { + c.red = 0xc000; c.green = 0xc000; c.blue = 0xc000; + } else if (!g_strcasecmp (color, "skyblue")) { + c.red = 0x8700; c.green = 0xce00; c.blue = 0xeb00; + } else if (!g_strcasecmp (color, "slateblue")) { + c.red = 0x6a00; c.green = 0x5a00; c.blue = 0xcd00; + } else if (!g_strcasecmp (color, "slategray")) { + c.red = 0x7000; c.green = 0x8000; c.blue = 0x9000; + } else if (!g_strcasecmp (color, "springgreen")) { + c.red = 0; c.green = 0xff00; c.blue = 0x7f00; + } else if (!g_strcasecmp (color, "steelblue")) { + c.red = 0x4600; c.green = 0x8200; c.blue = 0xb400; + } else if (!g_strcasecmp (color, "teal")) { + c.red = 0; c.green = 0x8000; c.blue = 0x8000; + } else if (!g_strcasecmp (color, "thistle")) { + c.red = 0xd800; c.green = 0xbf00; c.blue = 0xd800; + } else if (!g_strcasecmp (color, "tomato")) { + c.red = 0xff00; c.green = 0x6300; c.blue = 0x4700; + } else if (!g_strcasecmp (color, "turquoise")) { + c.red = 0x4000; c.green = 0xe000; c.blue = 0xd000; + } else if (!g_strcasecmp (color, "violet")) { + c.red = 0xee00; c.green = 0x8200; c.blue = 0xee00; + } else if (!g_strcasecmp (color, "wheat")) { + c.red = 0xf500; c.green = 0xde00; c.blue = 0xb300; + } else if (!g_strcasecmp (color, "white")) { + c.red = 0xfe00; c.green = 0xfe00; c.blue = 0xfe00; + } else if (!g_strcasecmp (color, "whitesmoke")) { + c.red = 0xfe00; c.green = 0xfe00; c.blue = 0xfe00; + } else if (!g_strcasecmp (color, "yellow")) { + c.red = 0xff00; c.green = 0xff00; c.blue = 0; + } else if (!g_strcasecmp (color, "yellowgreen")) { + c.red = 0xff00; c.green = 0xff00; c.blue = 0; + } else { + const gchar *hex; + guint32 value; + + if (color [0] == '#') + hex = color + 1; + else + hex = color; + + if (strlen (hex) == 6) { + gint i = 0; + for ( ; i < 6; i++) + if (!isxdigit ((gint) hex [i])) + break; + if (i == 6) { + sscanf (hex, "%x", &value); + c.red = (value & 0xff0000) >> 8; + c.green = value & 0xff00; + c.blue = (value & 0xff) << 8; + } else { + valid = FALSE; + } + } else { + valid = FALSE; + } + } + + if (valid) + return gdk_color_copy (&c); + + return NULL; +} + +static gint +gtk_imhtml_is_smiley (GtkIMHtml *imhtml, + const gchar *text) +{ + gchar *tmp; + gint i; + + g_return_val_if_fail (imhtml != NULL, 0); + g_return_val_if_fail (GTK_IS_IMHTML (imhtml), 0); + g_return_val_if_fail (text != NULL, 0); + + tmp = g_malloc (imhtml->smax + 1); + + for (i = imhtml->smin; i <= imhtml->smax; i++) { + if (strlen (text) < i) { + g_free (tmp); + return 0; + } + g_snprintf (tmp, i + 1, "%s", text); + if (g_hash_table_lookup (imhtml->smiley_hash, tmp)) { + g_free (tmp); + return i; + } + } + + g_free (tmp); + return 0; +} + +static GtkIMHtmlBit * +gtk_imhtml_new_bit (GtkIMHtml *imhtml, + gint type, + gchar *text, + gint bold, + gint italics, + gint underline, + gint strike, + FontDetail *font, + GdkColor *bg, + gchar *url) +{ + GtkIMHtmlBit *bit = NULL; + + g_return_val_if_fail (imhtml != NULL, NULL); + g_return_val_if_fail (GTK_IS_IMHTML (imhtml), NULL); + + if ((type == TYPE_TEXT) && ((text == NULL) || (strlen (text) == 0))) + return NULL; + + bit = g_new0 (GtkIMHtmlBit, 1); + bit->type = type; + + if ((text != NULL) && (strlen (text) != 0)) + bit->text = g_strdup (text); + + if ((font != NULL) || bold || italics) { + if (font && (bold || italics || font->size || font->face)) { + bit->font = gtk_imhtml_font_load (imhtml, font->face, bold, italics, font->size); + } else if (bold || italics) { + bit->font = gtk_imhtml_font_load (imhtml, NULL, bold, italics, 0); + } + + if (font && (type != TYPE_BR)) { + if (font->fore != NULL) + bit->fore = gdk_color_copy (font->fore); + + if (font->back != NULL) + bit->back = gdk_color_copy (font->back); + } + } + + if (((bit->type == TYPE_TEXT) || (bit->type == TYPE_SMILEY) || (bit->type == TYPE_COMMENT)) && + (bit->font == NULL)) + bit->font = gdk_font_ref (imhtml->default_font); + + if (bg != NULL) + bit->bg = gdk_color_copy (bg); + + bit->underline = underline; + bit->strike = strike; + + if (url != NULL) + bit->url = g_strdup (url); + + if (type == TYPE_SMILEY) { + GdkColor *clr; + + if ((font != NULL) && (font->back != NULL)) + clr = font->back; + else + clr = (bg != NULL) ? bg : >K_WIDGET (imhtml)->style->white; + + bit->pm = gdk_pixmap_create_from_xpm_d (GTK_WIDGET (imhtml)->window, + &bit->bm, + clr, + g_hash_table_lookup (imhtml->smiley_hash, text)); + } + + return bit; +} + +#define NEW_TEXT_BIT gtk_imhtml_new_bit (imhtml, TYPE_TEXT, ws, bold, italics, underline, strike, \ + fonts ? fonts->data : NULL, bg, url) +#define NEW_SMILEY_BIT gtk_imhtml_new_bit (imhtml, TYPE_SMILEY, ws, bold, italics, underline, strike, \ + fonts ? fonts->data : NULL, bg, url) +#define NEW_SEP_BIT gtk_imhtml_new_bit (imhtml, TYPE_SEP, NULL, 0, 0, 0, 0, NULL, bg, NULL) +#define NEW_BR_BIT gtk_imhtml_new_bit (imhtml, TYPE_BR, NULL, 0, 0, 0, 0, \ + fonts ? fonts->data : NULL, bg, NULL) +#define NEW_COMMENT_BIT gtk_imhtml_new_bit (imhtml, TYPE_COMMENT, ws, bold, italics, underline, strike, \ + fonts ? fonts->data : NULL, bg, url) + +#define NEW_BIT(bit) { GtkIMHtmlBit *tmp = bit; if (tmp != NULL) \ + newbits = g_list_append (newbits, tmp); } + +#define UPDATE_BG_COLORS \ + { \ + GdkColormap *cmap; \ + GList *rev; \ + cmap = gdk_colormap_new (gdk_visual_get_best (), FALSE); \ + rev = g_list_last (newbits); \ + while (rev) { \ + GtkIMHtmlBit *bit = rev->data; \ + if (bit->type == TYPE_BR) \ + break; \ + if (bit->bg) \ + gdk_color_free (bit->bg); \ + bit->bg = gdk_color_copy (bg); \ + rev = g_list_previous (rev); \ + } \ + if (!rev) { \ + rev = g_list_last (imhtml->bits); \ + while (rev) { \ + GList *ln; \ + GtkIMHtmlBit *bit = rev->data; \ + if (bit->type == TYPE_BR) \ + break; \ + if (bit->bg) \ + gdk_color_free (bit->bg); \ + bit->bg = gdk_color_copy (bg); \ + gdk_color_alloc (cmap, bit->bg); \ + ln = bit->chunks; \ + while (ln) { \ + GtkWidget *widget = ln->data; \ + gdk_window_set_background (widget->window, bit->bg); \ + ln = g_list_next (ln); \ + } \ + rev = g_list_previous (rev); \ + } \ + gdk_colormap_unref (cmap); \ + } \ + } + +GString* +gtk_imhtml_append_text (GtkIMHtml *imhtml, + const gchar *text, + GtkIMHtmlOptions options) +{ + const gchar *c; + gboolean intag = FALSE; + gboolean tagquote = FALSE; + gboolean incomment = FALSE; + gchar *ws; + gchar *tag; + gint wpos = 0; + gint tpos = 0; + int smilelen; + GList *newbits = NULL; + + guint bold = 0, + italics = 0, + underline = 0, + strike = 0, + sub = 0, + sup = 0, + title = 0; + GSList *fonts = NULL; + GdkColor *bg = NULL; + gchar *url = NULL; + + GtkAdjustment *vadj; + gboolean scrolldown = TRUE; + + GString *retval = 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); + + if (options & GTK_IMHTML_RETURN_LOG) + retval = g_string_new (""); + + vadj = GTK_LAYOUT (imhtml)->vadjustment; + if ((vadj->value < imhtml->y + 5 - GTK_WIDGET (imhtml)->allocation.height) && + (vadj->upper >= GTK_WIDGET (imhtml)->allocation.height)) + scrolldown = FALSE; + + c = text; + ws = g_malloc (strlen (text) + 1); + tag = g_malloc (strlen (text) + 1); + + ws [0] = '\0'; + + while (*c) { + if (*c == '<') { + if (intag) { + ws [wpos] = 0; + tag [tpos] = 0; + tpos = 0; + strcat (ws, tag); + wpos = strlen (ws); + } + + if (incomment) { + ws [wpos++] = *c++; + continue; + } + + if (!g_strncasecmp (c, "<!--", strlen ("<!--"))) { + if (!(options & GTK_IMHTML_NO_COMMENTS)) { + ws [wpos] = 0; + wpos = 0; + tag [tpos] = 0; + strcat (tag, ws); + incomment = TRUE; + intag = FALSE; + } + ws [wpos++] = *c++; + ws [wpos++] = *c++; + ws [wpos++] = *c++; + ws [wpos++] = *c++; + continue; + } + + tag [tpos++] = *c++; + intag = TRUE; + } else if (incomment && (*c == '-') && !g_strncasecmp (c, "-->", strlen ("-->"))) { + gchar *tmp; + ws [wpos] = 0; + wpos = 0; + tmp = g_strdup (ws); + ws [wpos] = 0; + strcat (ws, tag); + NEW_BIT (NEW_TEXT_BIT); + ws [wpos] = 0; + strcat (ws, tmp + strlen ("<!--")); + g_free (tmp); + NEW_BIT (NEW_COMMENT_BIT); + incomment = FALSE; + c += strlen ("-->"); + } else if (*c == '>' && intag && !tagquote) { + gboolean got_tag = FALSE; + tag [tpos++] = *c++; + tag [tpos] = 0; + ws [wpos] = 0; + + if (!g_strcasecmp (tag, "<B>") || !g_strcasecmp (tag, "<BOLD>")) { + got_tag = TRUE; + NEW_BIT (NEW_TEXT_BIT); + bold++; + } else if (!g_strcasecmp (tag, "</B>") || !g_strcasecmp (tag, "</BOLD>")) { + got_tag = TRUE; + if (bold) { + NEW_BIT (NEW_TEXT_BIT); + bold--; + } + } else if (!g_strcasecmp (tag, "<I>") || !g_strcasecmp (tag, "<ITALIC>")) { + got_tag = TRUE; + NEW_BIT (NEW_TEXT_BIT); + italics++; + } else if (!g_strcasecmp (tag, "</I>") || !g_strcasecmp (tag, "</ITALIC>")) { + got_tag = TRUE; + if (italics) { + NEW_BIT (NEW_TEXT_BIT); + italics--; + } + } else if (!g_strcasecmp (tag, "<U>") || !g_strcasecmp (tag, "<UNDERLINE>")) { + got_tag = TRUE; + NEW_BIT (NEW_TEXT_BIT); + underline++; + } else if (!g_strcasecmp (tag, "</U>") || !g_strcasecmp (tag, "</UNDERLINE>")) { + got_tag = TRUE; + if (underline) { + NEW_BIT (NEW_TEXT_BIT); + underline--; + } + } else if (!g_strcasecmp (tag, "<S>") || !g_strcasecmp (tag, "<STRIKE>")) { + got_tag = TRUE; + NEW_BIT (NEW_TEXT_BIT); + strike++; + } else if (!g_strcasecmp (tag, "</S>") || !g_strcasecmp (tag, "</STRIKE>")) { + got_tag = TRUE; + if (strike) { + NEW_BIT (NEW_TEXT_BIT); + strike--; + } + } else if (!g_strcasecmp (tag, "<SUB>")) { + got_tag = TRUE; + sub++; + } else if (!g_strcasecmp (tag, "</SUB>")) { + got_tag = TRUE; + if (sub) { + sub--; + } + } else if (!g_strcasecmp (tag, "<SUP>")) { + got_tag = TRUE; + sup++; + } else if (!g_strcasecmp (tag, "</SUP>")) { + got_tag = TRUE; + if (sup) { + sup--; + } + } else if (!g_strcasecmp (tag, "<TITLE>")) { + if (options & GTK_IMHTML_NO_TITLE) { + got_tag = TRUE; + title++; + } else { + intag = FALSE; + tpos = 0; + continue; + } + } else if (!g_strcasecmp (tag, "</TITLE>")) { + if (title) { + got_tag = TRUE; + wpos = 0; + ws [wpos] = '\0'; + title--; + } else { + intag = FALSE; + tpos = 0; + continue; + } + } else if (!g_strcasecmp (tag, "<BR>")) { + got_tag = TRUE; + NEW_BIT (NEW_TEXT_BIT); + NEW_BIT (NEW_BR_BIT); + } else if (!g_strcasecmp (tag, "<HR>") || + !g_strncasecmp (tag, "<HR ", strlen ("<HR "))) { + got_tag = TRUE; + NEW_BIT (NEW_TEXT_BIT); + NEW_BIT (NEW_SEP_BIT); + } else if (!g_strncasecmp (tag, "<FONT ", strlen ("<FONT "))) { + gchar *t, *e, *a, *value; + FontDetail *font = NULL; + GdkColor *clr; + gint saw; + gint i; + + t = tag + strlen ("<FONT "); + + while (*t != '\0') { + value = NULL; + saw = 0; + + while (g_strncasecmp (t, "COLOR=", strlen ("COLOR=")) + && g_strncasecmp (t, "BACK=", strlen ("BACK=")) + && g_strncasecmp (t, "FACE=", strlen ("FACE=")) + && g_strncasecmp (t, "SIZE=", strlen ("SIZE="))) { + gboolean quote = FALSE; + if (*t == '\0') break; + while (*t && !((*t == ' ') && !quote)) { + if (*t == '\"') + quote = ! quote; + t++; + } + while (*t && (*t == ' ')) t++; + } + + if (!g_strncasecmp (t, "COLOR=", strlen ("COLOR="))) { + t += strlen ("COLOR="); + saw = 1; + } else if (!g_strncasecmp (t, "BACK=", strlen ("BACK="))) { + t += strlen ("BACK="); + saw = 2; + } else if (!g_strncasecmp (t, "FACE=", strlen ("FACE="))) { + t += strlen ("FACE="); + saw = 3; + } else if (!g_strncasecmp (t, "SIZE=", strlen ("SIZE="))) { + t += strlen ("SIZE="); + saw = 4; + } + + if (!saw) + continue; + + if ((*t == '\"') || (*t == '\'')) { + e = a = ++t; + while (*e && (*e != *(t - 1))) e++; + if (*e != '\0') { + *e = '\0'; + t = e + 1; + value = g_strdup (a); + } else { + *t = '\0'; + } + } else { + e = a = t; + while (*e && !isspace ((gint) *e)) e++; + if (*e == '\0') e--; + *e = '\0'; + t = e + 1; + value = g_strdup (a); + } + + if (value == NULL) + continue; + + if (font == NULL) + font = g_new0 (FontDetail, 1); + + switch (saw) { + case 1: + clr = gtk_imhtml_get_color (value); + if (clr != NULL) { + if ( (font->fore == NULL) && + !(options & GTK_IMHTML_NO_COLOURS)) + font->fore = clr; + } + break; + case 2: + clr = gtk_imhtml_get_color (value); + if (clr != NULL) { + if ( (font->back == NULL) && + !(options & GTK_IMHTML_NO_COLOURS)) + font->back = clr; + } + break; + case 3: + if ( (font->face == NULL) && + !(options & GTK_IMHTML_NO_FONTS)) + font->face = g_strdup (value); + break; + case 4: + if ((font->size != 0) || + (options & GTK_IMHTML_NO_SIZES)) + break; + + if (isdigit ((gint) value [0])) { + for (i = 0; i < strlen (value); i++) + if (!isdigit ((gint) value [i])) + break; + if (i != strlen (value)) + break; + + sscanf (value, "%hd", &font->size); + break; + } + + if ((value [0] == '+') && (value [1] != '\0')) { + for (i = 1; i < strlen (value); i++) + if (!isdigit ((gint) value [i])) + break; + if (i != strlen (value)) + break; + + sscanf (value + 1, "%hd", &font->size); + font->size += 3; + break; + } + + if ((value [0] == '-') && (value [1] != '\0')) { + for (i = 1; i < strlen (value); i++) + if (!isdigit ((gint) value [i])) + break; + if (i != strlen (value)) + break; + + sscanf (value + 1, "%hd", &font->size); + font->size = MIN (font->size, 2); + font->size = 3 - font->size; + break; + } + + break; + } + + g_free (value); + } + + if (!font) { + intag = FALSE; + tpos = 0; + continue; + } + + if (!(font->size || font->face || font->fore || font->back)) { + g_free (font); + intag = FALSE; + tpos = 0; + continue; + } + + NEW_BIT (NEW_TEXT_BIT); + + if (fonts) { + FontDetail *oldfont = fonts->data; + if (!font->size) + font->size = oldfont->size; + if (!font->face) + font->face = g_strdup (oldfont->face); + if (!font->fore && oldfont->fore) + font->fore = gdk_color_copy (oldfont->fore); + if (!font->back && oldfont->back) + font->back = gdk_color_copy (oldfont->back); + } else { + if (!font->size) + font->size = 3; + if (!font->face) + font->face = g_strdup ("helvetica"); + } + + fonts = g_slist_prepend (fonts, font); + got_tag = TRUE; + } else if (!g_strcasecmp (tag, "</FONT>")) { + FontDetail *font; + + if (fonts) { + got_tag = TRUE; + NEW_BIT (NEW_TEXT_BIT); + font = fonts->data; + fonts = g_slist_remove (fonts, font); + g_free (font->face); + if (font->fore) + gdk_color_free (font->fore); + if (font->back) + gdk_color_free (font->back); + g_free (font); + } else { + intag = FALSE; + tpos = 0; + continue; + } + } else if (!g_strncasecmp (tag, "<BODY ", strlen ("<BODY "))) { + gchar *t, *e, *color = NULL; + GdkColor *tmp; + + got_tag = TRUE; + + if (!(options & GTK_IMHTML_NO_COLOURS)) { + t = tag + strlen ("<BODY"); + do { + gboolean quote = FALSE; + if (*t == '\0') break; + while (*t && !((*t == ' ') && !quote)) { + if (*t == '\"') + quote = ! quote; + t++; + } + while (*t && (*t == ' ')) t++; + } while (g_strncasecmp (t, "BGCOLOR=", strlen ("BGCOLOR="))); + + if (!g_strncasecmp (t, "BGCOLOR=", strlen ("BGCOLOR="))) { + t += strlen ("BGCOLOR="); + if ((*t == '\"') || (*t == '\'')) { + e = ++t; + while (*e && (*e != *(t - 1))) e++; + if (*e != '\0') { + *e = '\0'; + color = g_strdup (t); + } + } else { + e = t; + while (*e && !isspace ((gint) *e)) e++; + if (*e == '\0') e--; + *e = '\0'; + color = g_strdup (t); + } + + if (color != NULL) { + tmp = gtk_imhtml_get_color (color); + g_free (color); + if (tmp != NULL) { + NEW_BIT (NEW_TEXT_BIT); + bg = tmp; + UPDATE_BG_COLORS; + } + } + } + } + } else if (!g_strncasecmp (tag, "<A ", strlen ("<A "))) { + gchar *t, *e; + + got_tag = TRUE; + NEW_BIT (NEW_TEXT_BIT); + + if (url != NULL) + g_free (url); + url = NULL; + + t = tag + strlen ("<A"); + do { + gboolean quote = FALSE; + if (*t == '\0') break; + while (*t && !((*t == ' ') && !quote)) { + if (*t == '\"') + quote = ! quote; + t++; + } + while (*t && (*t == ' ')) t++; + } while (g_strncasecmp (t, "HREF=", strlen ("HREF="))); + + if (!g_strncasecmp (t, "HREF=", strlen ("HREF="))) { + t += strlen ("HREF="); + if ((*t == '\"') || (*t == '\'')) { + e = ++t; + while (*e && (*e != *(t - 1))) e++; + if (*e != '\0') { + *e = '\0'; + url = g_strdup (t); + } + } else { + e = t; + while (*e && !isspace ((gint) *e)) e++; + if (*e == '\0') e--; + *e = '\0'; + url = g_strdup (t); + } + } + } else if (!g_strcasecmp (tag, "</A>")) { + got_tag = TRUE; + if (url != NULL) { + NEW_BIT (NEW_TEXT_BIT); + g_free (url); + } + url = NULL; + } else if (!g_strncasecmp (tag, "<IMG ", strlen ("<IMG "))) { + gchar *t, *e, *src = NULL; + gchar *copy = g_strdup (tag); + gchar **xpm; + GdkColor *clr = NULL; + GtkIMHtmlBit *bit; + + intag = FALSE; + tpos = 0; + + if (imhtml->img == NULL) + continue; + + t = tag + strlen ("<IMG"); + do { + gboolean quote = FALSE; + if (*t == '\0') break; + while (*t && !((*t == ' ') && !quote)) { + if (*t == '\"') + quote = ! quote; + t++; + } + while (*t && (*t == ' ')) t++; + } while (g_strncasecmp (t, "SRC=", strlen ("SRC="))); + + if (!g_strncasecmp (t, "SRC=", strlen ("SRC="))) { + t += strlen ("SRC="); + if ((*t == '\"') || (*t == '\'')) { + e = ++t; + while (*e && (*e != *(t - 1))) e++; + if (*e != '\0') { + *e = '\0'; + src = g_strdup (t); + } + } else { + e = t; + while (*e && !isspace ((gint) *e)) e++; + if (*e == '\0') e--; + *e = '\0'; + src = g_strdup (t); + } + } + + if (src == NULL) { + ws [wpos] = 0; + strcat (ws, copy); + wpos = strlen (ws); + g_free (copy); + continue; + } + + xpm = (* imhtml->img) (src); + if (xpm == NULL) { + g_free (src); + ws [wpos] = 0; + strcat (ws, copy); + wpos = strlen (ws); + g_free (copy); + continue; + } + + g_free (copy); + + if (!fonts || ((clr = ((FontDetail *)fonts->data)->back) == NULL)) + clr = (bg != NULL) ? bg : >K_WIDGET (imhtml)->style->white; + + if (!GTK_WIDGET_REALIZED (imhtml)) + gtk_widget_realize (GTK_WIDGET (imhtml)); + + bit = g_new0 (GtkIMHtmlBit, 1); + bit->type = TYPE_IMG; + bit->pm = gdk_pixmap_create_from_xpm_d (GTK_WIDGET (imhtml)->window, + &bit->bm, + clr, + xpm); + if (url) + bit->url = g_strdup (url); + + NEW_BIT (bit); + + g_free (src); + + continue; + } else if (!g_strcasecmp (tag, "<P>") || + !g_strcasecmp (tag, "</P>") || + !g_strncasecmp (tag, "<P ", strlen ("<P ")) || + !g_strcasecmp (tag, "<PRE>") || + !g_strcasecmp (tag, "</PRE>") || + !g_strcasecmp (tag, "<HTML>") || + !g_strcasecmp (tag, "</HTML>") || + !g_strcasecmp (tag, "<BODY>") || + !g_strcasecmp (tag, "</BODY>") || + !g_strcasecmp (tag, "<FONT>") || + !g_strcasecmp (tag, "<HEAD>") || + !g_strcasecmp (tag, "</HEAD>")) { + intag = FALSE; + tpos = 0; + continue; + } + + if (!got_tag) { + ws [wpos] = 0; + strcat (ws, tag); + wpos = strlen (ws); + } else { + wpos = 0; + } + intag = FALSE; + tpos = 0; + } else if (*c == '&' && !intag) { + if (!g_strncasecmp (c, "&", 5)) { + ws [wpos++] = '&'; + c += 5; + } else if (!g_strncasecmp (c, "<", 4)) { + ws [wpos++] = '<'; + c += 4; + } else if (!g_strncasecmp (c, ">", 4)) { + ws [wpos++] = '>'; + c += 4; + } else if (!g_strncasecmp (c, " ", 6)) { + ws [wpos++] = ' '; + c += 6; + } else if (!g_strncasecmp (c, "©", 6)) { + ws [wpos++] = '©'; + c += 6; + } else if (!g_strncasecmp (c, """, 6)) { + ws [wpos++] = '\"'; + c += 6; + } else if (!g_strncasecmp (c, "®", 5)) { + ws [wpos++] = '®'; + c += 5; + } else if (*(c + 1) == '#') { + gint pound = 0; + if (sscanf (c, "&#%d;", £) == 1) { + if (*(c + 3 + (gint)log10 (pound)) != ';') { + ws [wpos++] = *c++; + continue; + } + ws [wpos++] = (gchar)pound; + c += 2; + while (isdigit ((gint) *c)) c++; + if (*c == ';') c++; + } else { + ws [wpos++] = *c++; + } + } else { + ws [wpos++] = *c++; + } + } else if (intag) { + if (*c == '\"') + tagquote = !tagquote; + tag [tpos++] = *c++; + } else if (incomment) { + ws [wpos++] = *c++; + } else if (((smilelen = gtk_imhtml_is_smiley (imhtml, c)) != 0)) { + ws [wpos] = 0; + wpos = 0; + NEW_BIT (NEW_TEXT_BIT); + g_snprintf (ws, smilelen + 1, "%s", c); + NEW_BIT (NEW_SMILEY_BIT); + c += smilelen; + } else if (*c == '\n') { + if (!(options & GTK_IMHTML_NO_NEWLINE)) { + ws [wpos] = 0; + wpos = 0; + NEW_BIT (NEW_TEXT_BIT); + NEW_BIT (NEW_BR_BIT); + } + c++; + } else { + ws [wpos++] = *c++; + } + } + + if (intag) { + tag [tpos] = 0; + c = tag; + while (*c) { + if ((smilelen = gtk_imhtml_is_smiley (imhtml, c)) != 0) { + ws [wpos] = 0; + wpos = 0; + NEW_BIT (NEW_TEXT_BIT); + g_snprintf (ws, smilelen + 1, "%s", c); + NEW_BIT (NEW_SMILEY_BIT); + c += smilelen; + } else { + ws [wpos++] = *c++; + } + } + } else if (incomment) { + ws [wpos] = 0; + wpos = 0; + strcat (tag, ws); + ws [wpos] = 0; + c = tag; + while (*c) { + if ((smilelen = gtk_imhtml_is_smiley (imhtml, c)) != 0) { + ws [wpos] = 0; + wpos = 0; + NEW_BIT (NEW_TEXT_BIT); + g_snprintf (ws, smilelen + 1, "%s", c); + NEW_BIT (NEW_SMILEY_BIT); + c += smilelen; + } else { + ws [wpos++] = *c++; + } + } + } + + ws [wpos] = 0; + NEW_BIT (NEW_TEXT_BIT); + + while (newbits) { + GtkIMHtmlBit *bit = newbits->data; + imhtml->bits = g_list_append (imhtml->bits, bit); + newbits = g_list_remove (newbits, bit); + gtk_imhtml_draw_bit (imhtml, bit); + } + + gtk_widget_set_usize (GTK_WIDGET (imhtml), -1, imhtml->y + 5); + + if (!(options & GTK_IMHTML_NO_SCROLL) && + scrolldown && + (imhtml->y + 5 >= GTK_WIDGET (imhtml)->allocation.height)) + gtk_adjustment_set_value (vadj, imhtml->y + 5 - GTK_WIDGET (imhtml)->allocation.height); + + if (url) { + g_free (url); + if (retval) + retval = g_string_append (retval, "</A>"); + } + if (bg) + gdk_color_free (bg); + while (fonts) { + FontDetail *font = fonts->data; + fonts = g_slist_remove (fonts, font); + g_free (font->face); + if (font->fore) + gdk_color_free (font->fore); + if (font->back) + gdk_color_free (font->back); + g_free (font); + if (retval) + retval = g_string_append (retval, "</FONT>"); + } + if (retval) { + while (bold) { + retval = g_string_append (retval, "</B>"); + bold--; + } + while (italics) { + retval = g_string_append (retval, "</I>"); + italics--; + } + while (underline) { + retval = g_string_append (retval, "</U>"); + underline--; + } + while (strike) { + retval = g_string_append (retval, "</S>"); + strike--; + } + while (sub) { + retval = g_string_append (retval, "</SUB>"); + sub--; + } + while (sup) { + retval = g_string_append (retval, "</SUP>"); + sup--; + } + while (title) { + retval = g_string_append (retval, "</TITLE>"); + title--; + } + } + g_free (ws); + g_free (tag); + + return retval; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/gtkimhtml.h Fri Jan 26 02:02:36 2001 +0000 @@ -0,0 +1,127 @@ +/* + * GtkIMHtml + * + * Copyright (C) 2000, Eric Warmenhoven <warmenhoven@yahoo.com> + * + * This program is free software; you can redistribute it and/or modify + * under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef __GTK_IMHTML_H +#define __GTK_IMHTML_H + +#include <gdk/gdk.h> +#include <gtk/gtklayout.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#define GTK_TYPE_IMHTML (gtk_imhtml_get_type ()) +#define GTK_IMHTML(obj) (GTK_CHECK_CAST ((obj), GTK_TYPE_IMHTML, GtkIMHtml)) +#define GTK_IMHTML_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), GTK_TYPE_IMHTML, GtkIMHtmlClass)) +#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)) + +typedef gchar** (*GtkIMHtmlImage) (gchar *url); + +typedef struct _GtkIMHtml GtkIMHtml; +typedef struct _GtkIMHtmlClass GtkIMHtmlClass; + +struct _GtkIMHtml { + GtkLayout layout; + + GdkFont *default_font; + GdkColor *default_fg_color; + + GdkCursor *hand_cursor; + GdkCursor *arrow_cursor; + + GList *bits; + GList *urls; + + guint sel_startx, sel_starty; + guint sel_endx, sel_endy; + gboolean selection; + GString *selected_text; + guint scroll_timer; + + guint x, y; + guint xsize; + guint llheight; + guint llascent; + GList *line; + + GtkIMHtmlImage img; + + gboolean smileys; + gboolean comments; + + GHashTable *smiley_hash; + gint smin, smax; +}; + +struct _GtkIMHtmlClass { + GtkLayoutClass parent_class; + + void (*url_clicked) (GtkIMHtml *, const gchar *); +}; + +typedef enum +{ + GTK_IMHTML_NO_COLOURS = 1 << 0, + GTK_IMHTML_NO_FONTS = 1 << 1, + GTK_IMHTML_NO_COMMENTS = 1 << 2, + GTK_IMHTML_NO_TITLE = 1 << 3, + GTK_IMHTML_NO_NEWLINE = 1 << 4, + GTK_IMHTML_NO_SIZES = 1 << 5, + GTK_IMHTML_NO_SCROLL = 1 << 6, + GTK_IMHTML_RETURN_LOG = 1 << 7 +} GtkIMHtmlOptions; + +GtkType gtk_imhtml_get_type (void); +GtkWidget* gtk_imhtml_new (GtkAdjustment *hadj, + GtkAdjustment *vadj); + +void gtk_imhtml_set_adjustments (GtkIMHtml *imhtml, + GtkAdjustment *hadj, + GtkAdjustment *vadj); + +void gtk_imhtml_set_defaults (GtkIMHtml *imhtml, + GdkFont *font, + GdkColor *fg_color); + +void gtk_imhtml_set_img_handler (GtkIMHtml *imhtml, + GtkIMHtmlImage handler); + +void gtk_imhtml_associate_smiley (GtkIMHtml *imhtml, + gchar *text, + gchar **xpm); + +void gtk_imhtml_show_smileys (GtkIMHtml *imhtml, + gboolean show); + +void gtk_imhtml_show_comments (GtkIMHtml *imhtml, + gboolean show); + +GString* gtk_imhtml_append_text (GtkIMHtml *imhtml, + const gchar *text, + GtkIMHtmlOptions options); + +#ifdef __cplusplus +} +#endif + +#endif
--- a/src/prefs.c Thu Jan 25 20:31:12 2001 +0000 +++ b/src/prefs.c Fri Jan 26 02:02:36 2001 +0000 @@ -251,9 +251,9 @@ gaim_button(_("Show graphical smileys"), &display_options, OPT_DISP_SHOW_SMILEY, box); gaim_button(_("Show timestamp on messages"), &display_options, OPT_DISP_SHOW_TIME, box); - gaim_button(_("Ignore incoming colors"), &display_options, OPT_DISP_IGNORE_COLOUR, box); - gaim_button(_("Ignore incoming fonts"), &display_options, OPT_DISP_IGNORE_FONTS, box); - gaim_button(_("Ignore white backgrounds"), &display_options, OPT_DISP_IGN_WHITE, box); + gaim_button(_("Ignore colors"), &display_options, OPT_DISP_IGNORE_COLOUR, box); + gaim_button(_("Ignore font faces"), &display_options, OPT_DISP_IGNORE_FONTS, box); + gaim_button(_("Ignore font sizes"), &display_options, OPT_DISP_IGNORE_SIZES, box); sep = gtk_hseparator_new(); gtk_box_pack_start(GTK_BOX(box), sep, FALSE, FALSE, 5); @@ -2066,6 +2066,12 @@ if (blist && ((int)option == OPT_DISP_SHOW_BUTTON_XPM)) update_button_pix(); + if ((int)option == OPT_DISP_SHOW_SMILEY) + toggle_smileys(); + + if ((int)option == OPT_DISP_SHOW_TIME) + toggle_timestamps(); + #ifdef USE_APPLET update_pixmaps(); #endif