pidgin/pidgin

Remove GtkIMHtml

2014-02-02, Tomasz Wasilczyk
424f080c23e8
Parents 9f6d0e63e80a
Children c651682dada0
Remove GtkIMHtml
--- a/pidgin/Makefile.am Sun Feb 02 02:00:31 2014 +0100
+++ b/pidgin/Makefile.am Sun Feb 02 02:11:58 2014 +0100
@@ -60,8 +60,6 @@
gtkicon-theme.c \
gtkicon-theme-loader.c \
gtkidle.c \
- gtkimhtml.c \
- gtkimhtmltoolbar.c \
gtklog.c \
gtkmain.c \
gtkmedia.c \
@@ -114,8 +112,6 @@
gtkicon-theme.h \
gtkicon-theme-loader.h \
gtkidle.h \
- gtkimhtml.h \
- gtkimhtmltoolbar.h \
gtklog.h \
gtkmedia.h \
gtkmenutray.h \
--- a/pidgin/Makefile.mingw Sun Feb 02 02:00:31 2014 +0100
+++ b/pidgin/Makefile.mingw Sun Feb 02 02:11:58 2014 +0100
@@ -76,8 +76,6 @@
gtkicon-theme-loader.c \
gtkicon-theme.c \
gtkidle.c \
- gtkimhtml.c \
- gtkimhtmltoolbar.c \
gtklog.c \
gtkmain.c \
gtkmedia.c \
--- a/pidgin/gtkimhtml.c Sun Feb 02 02:00:31 2014 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,6092 +0,0 @@
-/*
- * @file gtkimhtml.c GTK+ IMHtml
- * @ingroup pidgin
- */
-
-/* pidgin
- *
- * Pidgin is the legal property of its developers, whose names are too numerous
- * to list here. Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
- *
- */
-#define _PIDGIN_GTKIMHTML_C_
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include "internal.h"
-#include "pidgin.h"
-#include "pidginstock.h"
-#include "gtkutils.h"
-#include "smiley.h"
-#include "imgstore.h"
-
-#include "debug.h"
-#include "util.h"
-#include "gtkimhtml.h"
-#include "gtksmiley.h"
-#include "gtksourceiter.h"
-#include "gtksourceundomanager.h"
-#include "gtksourceview-marshal.h"
-#include <gtk/gtk.h>
-#include <glib.h>
-#include <gdk/gdkkeysyms.h>
-#include <string.h>
-#include <ctype.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <math.h>
-#ifdef HAVE_LANGINFO_CODESET
-#include <langinfo.h>
-#include <locale.h>
-#endif
-#ifdef _WIN32
-#include <gdk/gdkwin32.h>
-#include <windows.h>
-#endif
-
-#include <pango/pango-font.h>
-
-#define TOOLTIP_TIMEOUT 500
-
-#include "gtk3compat.h"
-
-static GtkTextViewClass *parent_class = NULL;
-
-struct scalable_data {
- GtkIMHtmlScalable *scalable;
- GtkTextMark *mark;
-};
-
-typedef struct {
- GtkIMHtmlScalable *image;
- gpointer data;
- gsize datasize;
-} GtkIMHtmlImageSave;
-
-struct im_image_data {
- int id;
- GtkTextMark *mark;
-};
-
-struct _GtkIMHtmlScalable {
- void (*scale)(struct _GtkIMHtmlScalable *, int, int);
- void (*add_to)(struct _GtkIMHtmlScalable *, GtkIMHtml *, GtkTextIter *);
- void (*free)(struct _GtkIMHtmlScalable *);
-};
-
-struct _GtkIMHtmlHr {
- GtkIMHtmlScalable scalable;
- GtkWidget *sep;
-};
-
-struct _GtkIMHtmlImage {
- GtkIMHtmlScalable scalable;
- GtkImage *image; /**< Contains the scaled version of this pixbuf. */
- GdkPixbuf *pixbuf; /**< The original pixbuf, before any scaling. */
- GtkTextMark *mark;
- gchar *filename;
- int width;
- int height;
- int id;
- GtkWidget *filesel;
-};
-
-struct _GtkIMHtmlAnimation {
- GtkIMHtmlImage imhtmlimage;
- GdkPixbufAnimation *anim; /**< The original animation, before any scaling. */
- GdkPixbufAnimationIter *iter;
- guint timer;
-};
-
-struct _GtkIMHtmlLink
-{
- GtkIMHtml *imhtml;
- gchar *url;
- GtkTextTag *tag;
-};
-
-struct _GtkSmileyTree {
- GString *values;
- GtkSmileyTree **children;
- GtkIMHtmlSmiley *image;
-};
-
-typedef struct {
- char *name;
- int length;
-
- gboolean (*activate)(GtkIMHtml *imhtml, GtkIMHtmlLink *link);
- gboolean (*context_menu)(GtkIMHtml *imhtml, GtkIMHtmlLink *link, GtkWidget *menu);
-} GtkIMHtmlProtocol;
-
-/* The five elements contained in a FONT tag */
-typedef struct {
- gushort size;
- gchar *face;
- gchar *fore;
- gchar *back;
- gchar *bg;
- gchar *sml;
- gboolean underline;
- gboolean strike;
- gshort bold;
-} GtkIMHtmlFontDetail;
-
-static gboolean
-gtk_text_view_drag_motion (GtkWidget *widget,
- GdkDragContext *context,
- gint x,
- gint y,
- guint time);
-
-static void preinsert_cb(GtkTextBuffer *buffer, GtkTextIter *iter, gchar *text, gint len, GtkIMHtml *imhtml);
-static void insert_cb(GtkTextBuffer *buffer, GtkTextIter *iter, gchar *text, gint len, GtkIMHtml *imhtml);
-static void delete_cb(GtkTextBuffer *buffer, GtkTextIter *iter, GtkTextIter *end, GtkIMHtml *imhtml);
-static void insert_ca_cb(GtkTextBuffer *buffer, GtkTextIter *arg1, GtkTextChildAnchor *arg2, gpointer user_data);
-static void gtk_imhtml_apply_tags_on_insert(GtkIMHtml *imhtml, GtkTextIter *start, GtkTextIter *end);
-void gtk_imhtml_close_tags(GtkIMHtml *imhtml, GtkTextIter *iter);
-static void gtk_imhtml_link_drop_cb(GtkWidget *widget, GdkDragContext *context, gint x, gint y, guint time, gpointer user_data);
-static void gtk_imhtml_link_drag_rcv_cb(GtkWidget *widget, GdkDragContext *dc, guint x, guint y, GtkSelectionData *sd, guint info, guint t, GtkIMHtml *imhtml);
-static void mark_set_cb(GtkTextBuffer *buffer, GtkTextIter *arg1, GtkTextMark *mark, GtkIMHtml *imhtml);
-static void hijack_menu_cb(GtkIMHtml *imhtml, GtkMenu *menu, gpointer data);
-static void paste_received_cb (GtkClipboard *clipboard, GtkSelectionData *selection_data, gpointer data);
-static void paste_plaintext_received_cb (GtkClipboard *clipboard, const gchar *text, gpointer data);
-static void imhtml_paste_insert(GtkIMHtml *imhtml, const char *text, gboolean plaintext);
-static void imhtml_toggle_bold(GtkIMHtml *imhtml);
-static void imhtml_toggle_italic(GtkIMHtml *imhtml);
-static void imhtml_toggle_strike(GtkIMHtml *imhtml);
-static void imhtml_toggle_underline(GtkIMHtml *imhtml);
-static void imhtml_font_grow(GtkIMHtml *imhtml);
-static void imhtml_font_shrink(GtkIMHtml *imhtml);
-static void imhtml_clear_formatting(GtkIMHtml *imhtml);
-static int gtk_imhtml_is_protocol(const char *text);
-static void gtk_imhtml_activate_tag(GtkIMHtml *imhtml, GtkTextTag *tag);
-static void gtk_imhtml_link_destroy(GtkIMHtmlLink *link);
-
-/* POINT_SIZE converts from AIM font sizes to a point size scale factor. */
-#define MAX_FONT_SIZE 7
-#define POINT_SIZE(x) (_point_sizes [MIN ((x > 0 ? x : 1), MAX_FONT_SIZE) - 1])
-static const gdouble _point_sizes [] = { .85, .95, 1, 1.2, 1.44, 1.728, 2.0736};
-
-enum {
- TARGET_HTML,
- TARGET_UTF8_STRING,
- TARGET_COMPOUND_TEXT,
- TARGET_STRING,
- TARGET_TEXT
-};
-
-enum {
- URL_CLICKED,
- BUTTONS_UPDATE,
- TOGGLE_FORMAT,
- CLEAR_FORMAT,
- UPDATE_FORMAT,
- MESSAGE_SEND,
- UNDO,
- REDO,
- PASTE,
- LAST_SIGNAL
-};
-static guint signals [LAST_SIGNAL] = { 0 };
-
-static char *html_clipboard = NULL;
-static char *text_clipboard = NULL;
-static GtkClipboard *clipboard_selection = NULL;
-
-static const GtkTargetEntry selection_targets[] = {
-#ifndef _WIN32
- { "text/html", 0, TARGET_HTML },
-#else
- { "HTML Format", 0, TARGET_HTML },
-#endif
- { "UTF8_STRING", 0, TARGET_UTF8_STRING },
- { "COMPOUND_TEXT", 0, TARGET_COMPOUND_TEXT },
- { "STRING", 0, TARGET_STRING },
- { "TEXT", 0, TARGET_TEXT}};
-
-static const GtkTargetEntry link_drag_drop_targets[] = {
- GTK_IMHTML_DND_TARGETS
-};
-
-#ifdef _WIN32
-static gchar *
-clipboard_win32_to_html(char *clipboard) {
- const char *header;
- const char *begin, *end;
- gint start = 0;
- gint finish = 0;
- gchar *html;
- gchar **split;
- int clipboard_length = 0;
-
-#if 0 /* Debugging for Windows clipboard */
- FILE *fd;
-
- purple_debug_info("imhtml clipboard", "from clipboard: %s\n", clipboard);
-
- fd = g_fopen("c:\\purplecb.txt", "wb");
- fprintf(fd, "%s", clipboard);
- fclose(fd);
-#endif
-
- clipboard_length = strlen(clipboard);
-
- if (!(header = strstr(clipboard, "StartFragment:")) || (header - clipboard) >= clipboard_length)
- return NULL;
- sscanf(header, "StartFragment:%d", &start);
-
- if (!(header = strstr(clipboard, "EndFragment:")) || (header - clipboard) >= clipboard_length)
- return NULL;
- sscanf(header, "EndFragment:%d", &finish);
-
- if (finish > clipboard_length)
- finish = clipboard_length;
-
- if (start > finish)
- start = finish;
-
- begin = clipboard + start;
-
- end = clipboard + finish;
-
- html = g_strndup(begin, end - begin);
-
- /* any newlines in the string will now be \r\n, so we need to strip out the \r */
- split = g_strsplit(html, "\r\n", 0);
- g_free(html);
- html = g_strjoinv("\n", split);
- g_strfreev(split);
-
-#if 0 /* Debugging for Windows clipboard */
- purple_debug_info("imhtml clipboard", "HTML fragment: '%s'\n", html);
-#endif
-
- return html;
-}
-
-static gchar *
-clipboard_html_to_win32(char *html) {
- int length;
- GString *clipboard;
-
- if (html == NULL)
- return NULL;
-
- length = strlen(html);
- clipboard = g_string_new ("Version:1.0\r\n");
- g_string_append(clipboard, "StartHTML:0000000105\r\n");
- g_string_append_printf(clipboard, "EndHTML:%010d\r\n", 147 + length);
- g_string_append(clipboard, "StartFragment:0000000127\r\n");
- g_string_append_printf(clipboard, "EndFragment:%010d\r\n", 127 + length);
- g_string_append(clipboard, "<!--StartFragment-->\r\n");
- g_string_append(clipboard, html);
- g_string_append(clipboard, "\r\n<!--EndFragment-->");
-
- return g_string_free(clipboard, FALSE);
-}
-
-static gboolean clipboard_paste_html_win32(GtkIMHtml *imhtml) {
- gboolean pasted = FALSE;
-
- /* Win32 clipboard format value, and functions to convert back and
- * forth between HTML and the clipboard format.
- */
- static UINT win_html_fmt = 0;
-
- /* Register HTML Format as desired clipboard format */
- if (!win_html_fmt)
- win_html_fmt = RegisterClipboardFormat("HTML Format");
-
- if (gtk_text_view_get_editable(GTK_TEXT_VIEW(imhtml))
- && IsClipboardFormatAvailable(win_html_fmt)) {
- gboolean error_reading_clipboard = FALSE;
- HWND hwnd = GDK_WINDOW_HWND(GTK_WIDGET(imhtml)->window);
-
- if (OpenClipboard(hwnd)) {
- HGLOBAL hdata = GetClipboardData(win_html_fmt);
- if (hdata == NULL) {
- if (GetLastError() != ERROR_SUCCESS)
- error_reading_clipboard = TRUE;
- } else {
- char *buffer = GlobalLock(hdata);
- if (buffer == NULL) {
- error_reading_clipboard = TRUE;
- } else {
- char *text = clipboard_win32_to_html(
- buffer);
- imhtml_paste_insert(imhtml, text,
- FALSE);
- g_free(text);
- pasted = TRUE;
- }
- GlobalUnlock(hdata);
- }
-
- CloseClipboard();
- } else {
- error_reading_clipboard = TRUE;
- }
-
- if (error_reading_clipboard) {
- gchar *err_msg = g_win32_error_message(GetLastError());
- purple_debug_info("html clipboard",
- "Unable to read clipboard data: %s\n",
- err_msg ? err_msg : "Unknown Error");
- g_free(err_msg);
- }
- }
-
- return pasted;
-}
-#endif
-
-static GtkSmileyTree*
-gtk_smiley_tree_new (void)
-{
- return g_new0 (GtkSmileyTree, 1);
-}
-
-static void
-gtk_smiley_tree_insert (GtkSmileyTree *tree,
- GtkIMHtmlSmiley *smiley)
-{
- GtkSmileyTree *t = tree;
- const gchar *x = smiley->smile;
-
- if (!(*x))
- return;
-
- do {
- gchar *pos;
- gint index;
-
- if (!t->values)
- t->values = g_string_new ("");
-
- pos = strchr (t->values->str, *x);
- if (!pos) {
- t->values = g_string_append_c (t->values, *x);
- index = t->values->len - 1;
- t->children = g_realloc (t->children, t->values->len * sizeof (GtkSmileyTree *));
- t->children [index] = g_new0 (GtkSmileyTree, 1);
- } else
- index = GPOINTER_TO_INT(pos) - GPOINTER_TO_INT(t->values->str);
-
- t = t->children [index];
-
- x++;
- } while (*x);
-
- t->image = smiley;
-}
-
-
-static void
-gtk_smiley_tree_destroy (GtkSmileyTree *tree)
-{
- GSList *list = g_slist_prepend (NULL, tree);
-
- while (list) {
- GtkSmileyTree *t = list->data;
- gsize i;
- list = g_slist_remove(list, t);
- if (t && t->values) {
- for (i = 0; i < t->values->len; i++)
- list = g_slist_prepend (list, t->children [i]);
- g_string_free (t->values, TRUE);
- g_free (t->children);
- }
-
- g_free (t);
- }
-}
-
-static void (*parent_size_allocate)(GtkWidget *widget, GtkAllocation *alloc);
-
-static void gtk_imhtml_size_allocate(GtkWidget *widget, GtkAllocation *alloc)
-{
- GtkIMHtml *imhtml = GTK_IMHTML(widget);
- GdkRectangle rect;
- int xminus;
- int height = 0, y = 0;
- GtkTextIter iter;
- gboolean scroll = TRUE;
-
- gtk_text_buffer_get_end_iter(imhtml->text_buffer, &iter);
-
- gtk_text_view_get_visible_rect(GTK_TEXT_VIEW(widget), &rect);
- gtk_text_view_get_line_yrange(GTK_TEXT_VIEW(imhtml), &iter, &y, &height);
-
- if (((y + height) - (rect.y + rect.height)) > height &&
- gtk_text_buffer_get_char_count(imhtml->text_buffer)) {
- scroll = FALSE;
- }
-
- if(imhtml->old_rect.width != rect.width || imhtml->old_rect.height != rect.height) {
- GList *iter = GTK_IMHTML(widget)->scalables;
-
- xminus = gtk_text_view_get_left_margin(GTK_TEXT_VIEW(widget)) +
- gtk_text_view_get_right_margin(GTK_TEXT_VIEW(widget));
-
- while(iter){
- struct scalable_data *sd = iter->data;
- GtkIMHtmlScalable *scale = GTK_IMHTML_SCALABLE(sd->scalable);
- scale->scale(scale, rect.width - xminus, rect.height);
-
- iter = iter->next;
- }
- }
-
- imhtml->old_rect = rect;
- parent_size_allocate(widget, alloc);
-
- /* Don't scroll here if we're in the middle of a smooth scroll */
- if (scroll && imhtml->scroll_time == NULL &&
- gtk_widget_get_realized(GTK_WIDGET(imhtml)))
- gtk_imhtml_scroll_to_end(imhtml, FALSE);
-}
-
-#define DEFAULT_SEND_COLOR "#204a87"
-#define DEFAULT_RECV_COLOR "#cc0000"
-#define DEFAULT_HIGHLIGHT_COLOR "#AF7F00"
-#define DEFAULT_ACTION_COLOR "#062585"
-#define DEFAULT_WHISPER_ACTION_COLOR "#6C2585"
-#define DEFAULT_WHISPER_COLOR "#00FF00"
-
-static void (*parent_style_set)(GtkWidget *widget, GtkStyle *prev_style);
-
-static void
-gtk_imhtml_style_set(GtkWidget *widget, GtkStyle *prev_style)
-{
- int i;
- struct {
- const char *tag;
- const char *color;
- const char *def;
- } styles[] = {
- {"send-name", "send-name-color", DEFAULT_SEND_COLOR},
- {"receive-name", "receive-name-color", DEFAULT_RECV_COLOR},
- {"highlight-name", "highlight-name-color", DEFAULT_HIGHLIGHT_COLOR},
- {"action-name", "action-name-color", DEFAULT_ACTION_COLOR},
- {"whisper-action-name", "whisper-action-name-color", DEFAULT_WHISPER_ACTION_COLOR},
- {"whisper-name", "whisper-name-color", DEFAULT_WHISPER_COLOR},
- {NULL, NULL, NULL}
- };
- GtkIMHtml *imhtml = GTK_IMHTML(widget);
- GtkTextTagTable *table = gtk_text_buffer_get_tag_table(imhtml->text_buffer);
-
- for (i = 0; styles[i].tag; i++) {
- GdkColor *color = NULL;
- GtkTextTag *tag = gtk_text_tag_table_lookup(table, styles[i].tag);
- if (!tag) {
- purple_debug_warning("gtkimhtml", "Cannot find tag '%s'. This should never happen. Please file a bug.\n", styles[i].tag);
- continue;
- }
- gtk_widget_style_get(widget, styles[i].color, &color, NULL);
- if (color) {
- g_object_set(tag, "foreground-gdk", color, NULL);
- gdk_color_free(color);
- } else {
- GdkColor defcolor;
- gdk_color_parse(styles[i].def, &defcolor);
- g_object_set(tag, "foreground-gdk", &defcolor, NULL);
- }
- }
- parent_style_set(widget, prev_style);
-}
-
-static gboolean
-imhtml_get_iter_bounds(GtkIMHtml *imhtml, GtkTextIter *start, GtkTextIter *end)
-{
- if (imhtml->wbfo) {
- gtk_text_buffer_get_bounds(imhtml->text_buffer, start, end);
- return TRUE;
- } else if (imhtml->editable) {
- if (!gtk_text_buffer_get_selection_bounds(imhtml->text_buffer, start, end)) {
- GtkTextMark *mark = gtk_text_buffer_get_insert(imhtml->text_buffer);
- gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, start, mark);
- *end = *start;
- }
- return TRUE;
- }
-
- return FALSE;
-}
-
-static void
-gtk_imhtml_set_link_color(GtkIMHtml *imhtml, GtkTextTag *tag)
-{
- GdkColor *color = NULL;
- gboolean visited = !!g_object_get_data(G_OBJECT(tag), "visited");
- gtk_widget_style_get(GTK_WIDGET(imhtml), visited ? "hyperlink-visited-color" : "hyperlink-color", &color, NULL);
- if (color) {
- g_object_set(G_OBJECT(tag), "foreground-gdk", color, NULL);
- gdk_color_free(color);
- } else {
- g_object_set(G_OBJECT(tag), "foreground", visited ? "#800000" : "blue", NULL);
- }
-}
-
-#if GTK_CHECK_VERSION(3,0,0)
-static gboolean
-gtk_imhtml_tip_paint(GtkIMHtml *imhtml, cairo_t *cr, GtkWidget *w)
-#else
-static gint
-gtk_imhtml_tip_paint(GtkIMHtml *imhtml, GdkEvent *event, GtkWidget *w)
-#endif
-{
- PangoLayout *layout;
- GtkStyle *style;
-
- g_return_val_if_fail(GTK_IS_IMHTML(imhtml), FALSE);
-
- /* We set the text in a separate function call so we can specify a
- max length. This is important so the tooltip isn't too wide for
- the screen, and also because some X library function exits the
- process when it can't allocate enough memory for a super wide
- tooltip. */
- layout = gtk_widget_create_pango_layout(imhtml->tip_window, NULL);
- pango_layout_set_text(layout, imhtml->tip, 200);
- style = gtk_widget_get_style(imhtml->tip_window);
-
-#if GTK_CHECK_VERSION(3,0,0)
- gtk_paint_flat_box(style, cr,
- GTK_STATE_NORMAL, GTK_SHADOW_OUT,
- imhtml->tip_window, "tooltip",
- 0, 0, -1, -1);
-
- gtk_paint_layout(style, cr,
- GTK_STATE_NORMAL, TRUE, imhtml->tip_window, NULL,
- 4, 4, layout);
-#else
- gtk_paint_flat_box(style, gtk_widget_get_window(imhtml->tip_window),
- GTK_STATE_NORMAL, GTK_SHADOW_OUT,
- NULL, imhtml->tip_window,
- "tooltip", 0, 0, -1, -1);
-
- gtk_paint_layout(style, gtk_widget_get_window(imhtml->tip_window),
- GTK_STATE_NORMAL, FALSE, NULL, imhtml->tip_window, NULL,
- 4, 4, layout);
-#endif
-
- g_object_unref(layout);
- return FALSE;
-}
-
-static gint
-gtk_imhtml_tip (gpointer data)
-{
- GtkIMHtml *imhtml = data;
- PangoFontMetrics *font_metrics;
- PangoLayout *layout;
- PangoFont *font;
- GtkStyle *style = gtk_widget_get_style(imhtml->tip_window);
- GtkAllocation allocation;
- gint gap, x, y, h, w, scr_w, baseline_skip;
-
- g_return_val_if_fail(GTK_IS_IMHTML(imhtml), FALSE);
-
- gtk_widget_get_allocation(GTK_WIDGET(imhtml), &allocation);
-
- if (!imhtml->tip || !gtk_widget_is_drawable(GTK_WIDGET(imhtml))) {
- imhtml->tip_timer = 0;
- return FALSE;
- }
-
- if (imhtml->tip_window){
- gtk_widget_destroy (imhtml->tip_window);
- imhtml->tip_window = NULL;
- }
-
- imhtml->tip_timer = 0;
- imhtml->tip_window = gtk_window_new (GTK_WINDOW_POPUP);
- gtk_widget_set_app_paintable (imhtml->tip_window, TRUE);
- gtk_window_set_title(GTK_WINDOW(imhtml->tip_window), "GtkIMHtml");
- gtk_window_set_resizable (GTK_WINDOW (imhtml->tip_window), FALSE);
- gtk_widget_set_name (imhtml->tip_window, "gtk-tooltips");
- gtk_window_set_type_hint (GTK_WINDOW (imhtml->tip_window),
- GDK_WINDOW_TYPE_HINT_TOOLTIP);
-#if GTK_CHECK_VERSION(3,0,0)
- g_signal_connect_swapped(G_OBJECT(imhtml->tip_window), "draw",
- G_CALLBACK(gtk_imhtml_tip_paint), imhtml);
-#else
- g_signal_connect_swapped(G_OBJECT(imhtml->tip_window), "expose_event",
- G_CALLBACK(gtk_imhtml_tip_paint), imhtml);
-#endif
-
- gtk_widget_ensure_style (imhtml->tip_window);
-
- /* We set the text in a separate function call so we can specify a
- max length. This is important so the tooltip isn't too wide for
- the screen, and also because some X library function exits the
- process when it can't allocate enough memory for a super wide
- tooltip. */
- layout = gtk_widget_create_pango_layout(imhtml->tip_window, NULL);
- pango_layout_set_text(layout, imhtml->tip, 200);
-
- font = pango_context_load_font(pango_layout_get_context(layout),
- style->font_desc);
-
- if (font == NULL) {
- char *tmp = pango_font_description_to_string(style->font_desc);
-
- purple_debug(PURPLE_DEBUG_ERROR, "gtk_imhtml_tip",
- "pango_context_load_font() couldn't load font: '%s'\n",
- tmp);
- g_free(tmp);
-
- g_object_unref(layout);
- return FALSE;
- }
-
- font_metrics = pango_font_get_metrics(font, NULL);
-
- pango_layout_get_pixel_size(layout, &scr_w, NULL);
- gap = PANGO_PIXELS((pango_font_metrics_get_ascent(font_metrics) +
- pango_font_metrics_get_descent(font_metrics))/ 4);
-
- if (gap < 2)
- gap = 2;
- baseline_skip = PANGO_PIXELS(pango_font_metrics_get_ascent(font_metrics) +
- pango_font_metrics_get_descent(font_metrics));
- w = 8 + scr_w;
- h = 8 + baseline_skip;
-
- gdk_window_get_pointer (NULL, &x, &y, NULL);
- if ((!gtk_widget_get_has_window(GTK_WIDGET(imhtml))))
- y += allocation.y;
-
- scr_w = gdk_screen_width();
-
- x -= ((w >> 1) + 4);
-
- if ((x + w) > scr_w)
- x -= (x + w) - scr_w;
- else if (x < 0)
- x = 0;
-
- y = y + PANGO_PIXELS(pango_font_metrics_get_ascent(font_metrics) +
- pango_font_metrics_get_descent(font_metrics));
-
- gtk_widget_set_size_request (imhtml->tip_window, w, h);
- gtk_window_move (GTK_WINDOW(imhtml->tip_window), x, y);
- gtk_widget_show (imhtml->tip_window);
-
- pango_font_metrics_unref(font_metrics);
- g_object_unref(font);
- g_object_unref(layout);
-
- return FALSE;
-}
-
-static gboolean
-gtk_motion_event_notify(GtkWidget *imhtml, GdkEventMotion *event, gpointer data)
-{
- GtkTextIter iter;
- GdkWindow *win = event->window;
- int x, y;
- char *tip = NULL;
- GSList *tags = NULL, *templist = NULL;
- GtkTextTag *tag = NULL, *oldprelit_tag;
- GtkTextChildAnchor* anchor;
- gboolean hand = TRUE;
- GdkCursor *cursor = NULL;
-
- oldprelit_tag = GTK_IMHTML(imhtml)->prelit_tag;
-
- gdk_window_get_pointer(gtk_widget_get_window(GTK_WIDGET(imhtml)), NULL, NULL, NULL);
- gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(imhtml), GTK_TEXT_WINDOW_WIDGET,
- event->x, event->y, &x, &y);
- gtk_text_view_get_iter_at_location(GTK_TEXT_VIEW(imhtml), &iter, x, y);
- tags = gtk_text_iter_get_tags(&iter);
-
- templist = tags;
- while (templist) {
- tag = templist->data;
- tip = g_object_get_data(G_OBJECT(tag), "link_url");
- if (tip)
- break;
- templist = templist->next;
- }
-
- if (tip && (!tag || !g_object_get_data(G_OBJECT(tag), "visited"))) {
- GTK_IMHTML(imhtml)->prelit_tag = tag;
- if (tag != oldprelit_tag) {
- GdkColor *pre = NULL;
- gtk_widget_style_get(GTK_WIDGET(imhtml), "hyperlink-prelight-color", &pre, NULL);
- if (pre) {
- g_object_set(G_OBJECT(tag), "foreground-gdk", pre, NULL);
- gdk_color_free(pre);
- } else
- g_object_set(G_OBJECT(tag), "foreground", "#70a0ff", NULL);
- }
- } else {
- GTK_IMHTML(imhtml)->prelit_tag = NULL;
- }
-
- if ((oldprelit_tag != NULL) && (GTK_IMHTML(imhtml)->prelit_tag != oldprelit_tag)) {
- gtk_imhtml_set_link_color(GTK_IMHTML(imhtml), oldprelit_tag);
- }
-
- if (GTK_IMHTML(imhtml)->tip) {
- if (tip == GTK_IMHTML(imhtml)->tip) {
- g_slist_free(tags);
- return FALSE;
- }
- /* We've left the cell. Remove the timeout and create a new one below */
- if (GTK_IMHTML(imhtml)->tip_window) {
- gtk_widget_destroy(GTK_IMHTML(imhtml)->tip_window);
- GTK_IMHTML(imhtml)->tip_window = NULL;
- }
- if (GTK_IMHTML(imhtml)->editable)
- cursor = GTK_IMHTML(imhtml)->text_cursor;
- else
- cursor = GTK_IMHTML(imhtml)->arrow_cursor;
- if (GTK_IMHTML(imhtml)->tip_timer)
- g_source_remove(GTK_IMHTML(imhtml)->tip_timer);
- GTK_IMHTML(imhtml)->tip_timer = 0;
- }
-
- /* If we don't have a tip from a URL, let's see if we have a tip from a smiley */
- anchor = gtk_text_iter_get_child_anchor(&iter);
- if (anchor) {
- tip = g_object_get_data(G_OBJECT(anchor), "gtkimhtml_tiptext");
- hand = FALSE;
- }
-
- if (tip && *tip) {
- GTK_IMHTML(imhtml)->tip_timer = g_timeout_add (TOOLTIP_TIMEOUT,
- gtk_imhtml_tip, imhtml);
- } else if (!tip) {
- hand = FALSE;
- for (templist = tags; templist; templist = templist->next) {
- tag = templist->data;
- if ((tip = g_object_get_data(G_OBJECT(tag), "cursor"))) {
- hand = TRUE;
- break;
- }
- }
- }
-
- if (hand && !(GTK_IMHTML(imhtml)->editable))
- cursor = GTK_IMHTML(imhtml)->hand_cursor;
-
- if (cursor)
- gdk_window_set_cursor(win, cursor);
-
- GTK_IMHTML(imhtml)->tip = tip;
- g_slist_free(tags);
- return FALSE;
-}
-
-static gboolean
-gtk_enter_event_notify(GtkWidget *imhtml, GdkEventCrossing *event, gpointer data)
-{
- if (GTK_IMHTML(imhtml)->editable)
- gdk_window_set_cursor(
- gtk_text_view_get_window(GTK_TEXT_VIEW(imhtml),
- GTK_TEXT_WINDOW_TEXT),
- GTK_IMHTML(imhtml)->text_cursor);
- else
- gdk_window_set_cursor(
- gtk_text_view_get_window(GTK_TEXT_VIEW(imhtml),
- GTK_TEXT_WINDOW_TEXT),
- GTK_IMHTML(imhtml)->arrow_cursor);
-
- /* propagate the event normally */
- return FALSE;
-}
-
-static gboolean
-gtk_leave_event_notify(GtkWidget *imhtml, GdkEventCrossing *event, gpointer data)
-{
- /* when leaving the widget, clear any current & pending tooltips and restore the cursor */
- if (GTK_IMHTML(imhtml)->prelit_tag) {
- gtk_imhtml_set_link_color(GTK_IMHTML(imhtml), GTK_IMHTML(imhtml)->prelit_tag);
- GTK_IMHTML(imhtml)->prelit_tag = NULL;
- }
-
- if (GTK_IMHTML(imhtml)->tip_window) {
- gtk_widget_destroy(GTK_IMHTML(imhtml)->tip_window);
- GTK_IMHTML(imhtml)->tip_window = NULL;
- }
- if (GTK_IMHTML(imhtml)->tip_timer) {
- g_source_remove(GTK_IMHTML(imhtml)->tip_timer);
- GTK_IMHTML(imhtml)->tip_timer = 0;
- }
- gdk_window_set_cursor(
- gtk_text_view_get_window(GTK_TEXT_VIEW(imhtml),
- GTK_TEXT_WINDOW_TEXT), NULL);
-
- /* propagate the event normally */
- return FALSE;
-}
-
-/* TODO: I think this can be removed for GTK+ 3.0... */
-#if !GTK_CHECK_VERSION(3,0,0)
-static gint
-gtk_imhtml_expose_event (GtkWidget *widget,
- GdkEventExpose *event)
-{
- GtkTextIter start, end, cur;
- int buf_x, buf_y;
- GdkRectangle visible_rect;
- cairo_t *cr = gdk_cairo_create(GDK_DRAWABLE(event->window));
- GdkColor gcolor;
-
- gtk_text_view_get_visible_rect(GTK_TEXT_VIEW(widget), &visible_rect);
- gtk_text_view_buffer_to_window_coords(GTK_TEXT_VIEW(widget),
- GTK_TEXT_WINDOW_TEXT,
- visible_rect.x,
- visible_rect.y,
- &visible_rect.x,
- &visible_rect.y);
-
- gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(widget), GTK_TEXT_WINDOW_TEXT,
- event->area.x, event->area.y, &buf_x, &buf_y);
-
- if (GTK_IMHTML(widget)->editable || GTK_IMHTML(widget)->wbfo) {
-
- if (GTK_IMHTML(widget)->edit.background) {
- gdk_color_parse(GTK_IMHTML(widget)->edit.background, &gcolor);
- gdk_cairo_set_source_color(cr, &gcolor);
- } else {
- gdk_cairo_set_source_color(cr,
- &(gtk_widget_get_style(widget)->base[gtk_widget_get_state(widget)]));
- }
-
- cairo_rectangle(cr,
- visible_rect.x, visible_rect.y,
- visible_rect.width, visible_rect.height);
- cairo_fill(cr);
- cairo_destroy(cr);
-
- if (GTK_WIDGET_CLASS (parent_class)->expose_event)
- return (* GTK_WIDGET_CLASS (parent_class)->expose_event)
- (widget, event);
- return FALSE;
-
- }
-
- gtk_text_view_get_iter_at_location(GTK_TEXT_VIEW(widget), &start, buf_x, buf_y);
- gtk_text_view_get_iter_at_location(GTK_TEXT_VIEW(widget), &end,
- buf_x + event->area.width, buf_y + event->area.height);
-
- gtk_text_iter_order(&start, &end);
-
- cur = start;
-
- while (gtk_text_iter_in_range(&cur, &start, &end)) {
- GSList *tags = gtk_text_iter_get_tags(&cur);
- GSList *l;
-
- for (l = tags; l; l = l->next) {
- GtkTextTag *tag = l->data;
- GdkRectangle rect;
- GdkRectangle tag_area;
- const char *color;
-
- if (strncmp(tag->name, "BACKGROUND ", 11))
- continue;
-
- if (gtk_text_iter_ends_tag(&cur, tag))
- continue;
-
- gtk_text_view_get_iter_location(GTK_TEXT_VIEW(widget), &cur, &tag_area);
- gtk_text_view_buffer_to_window_coords(GTK_TEXT_VIEW(widget),
- GTK_TEXT_WINDOW_TEXT,
- tag_area.x,
- tag_area.y,
- &tag_area.x,
- &tag_area.y);
- rect.x = visible_rect.x;
- rect.y = tag_area.y;
- rect.width = visible_rect.width;
-
- do
- gtk_text_iter_forward_to_tag_toggle(&cur, tag);
- while (!gtk_text_iter_is_end(&cur) && gtk_text_iter_begins_tag(&cur, tag));
-
- gtk_text_view_get_iter_location(GTK_TEXT_VIEW(widget), &cur, &tag_area);
- gtk_text_view_buffer_to_window_coords(GTK_TEXT_VIEW(widget),
- GTK_TEXT_WINDOW_TEXT,
- tag_area.x,
- tag_area.y,
- &tag_area.x,
- &tag_area.y);
-
-
- rect.height = tag_area.y + tag_area.height - rect.y
- + gtk_text_view_get_pixels_above_lines(GTK_TEXT_VIEW(widget))
- + gtk_text_view_get_pixels_below_lines(GTK_TEXT_VIEW(widget));
-
- color = tag->name + 11;
-
- if (!gdk_color_parse(color, &gcolor)) {
- gchar tmp[8];
- tmp[0] = '#';
- strncpy(&tmp[1], color, 7);
- tmp[7] = '\0';
- if (!gdk_color_parse(tmp, &gcolor))
- gdk_color_parse("white", &gcolor);
- }
- gdk_cairo_set_source_color(cr, &gcolor);
-
- cairo_rectangle(cr,
- rect.x, rect.y,
- rect.width, rect.height);
- cairo_fill(cr);
- gtk_text_iter_backward_char(&cur); /* go back one, in case the end is the begining is the end
- * note that above, we always moved cur ahead by at least
- * one character */
- break;
- }
-
- g_slist_free(tags);
-
- /* loop until another tag begins, or no tag begins */
- while (gtk_text_iter_forward_to_tag_toggle(&cur, NULL) &&
- !gtk_text_iter_is_end(&cur) &&
- !gtk_text_iter_begins_tag(&cur, NULL));
- }
-
- cairo_destroy(cr);
-
- if (GTK_WIDGET_CLASS (parent_class)->expose_event)
- return (* GTK_WIDGET_CLASS (parent_class)->expose_event)
- (widget, event);
-
- return FALSE;
-}
-#endif
-
-
-static void paste_unformatted_cb(GtkMenuItem *menu, GtkIMHtml *imhtml)
-{
- GtkClipboard *clipboard = gtk_widget_get_clipboard(GTK_WIDGET(imhtml), GDK_SELECTION_CLIPBOARD);
-
- gtk_clipboard_request_text(clipboard, paste_plaintext_received_cb, imhtml);
-
-}
-
-static void clear_formatting_cb(GtkMenuItem *menu, GtkIMHtml *imhtml)
-{
- gtk_imhtml_clear_formatting(imhtml);
-}
-
-static void disable_smiley_selected(GtkMenuItem *item, GtkIMHtml *imhtml)
-{
- GtkTextIter start, end;
- GtkTextMark *mark;
- char *text;
-
- if (!gtk_text_buffer_get_selection_bounds(imhtml->text_buffer, &start, &end))
- return;
-
- text = gtk_imhtml_get_markup_range(imhtml, &start, &end);
-
- mark = gtk_text_buffer_get_selection_bound(imhtml->text_buffer);
- gtk_text_buffer_delete_selection(imhtml->text_buffer, FALSE, FALSE);
-
- gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &start, mark);
- gtk_imhtml_insert_html_at_iter(imhtml, text, GTK_IMHTML_NO_NEWLINE | GTK_IMHTML_NO_SMILEY, &start);
-
- g_free(text);
-}
-
-static void hijack_menu_cb(GtkIMHtml *imhtml, GtkMenu *menu, gpointer data)
-{
- GtkWidget *menuitem;
- GtkTextIter start, end;
-
- menuitem = gtk_menu_item_new_with_mnemonic(_("Paste as Plain _Text"));
- gtk_widget_show(menuitem);
- /*
- * TODO: gtk_clipboard_wait_is_text_available() iterates the glib
- * mainloop, which tends to be a source of bugs. It would
- * be good to audit this or change it to not wait.
- */
- gtk_widget_set_sensitive(menuitem,
- (imhtml->editable &&
- gtk_clipboard_wait_is_text_available(
- gtk_widget_get_clipboard(GTK_WIDGET(imhtml), GDK_SELECTION_CLIPBOARD))));
- /* put it after "Paste" */
- gtk_menu_shell_insert(GTK_MENU_SHELL(menu), menuitem, 3);
-
- g_signal_connect(G_OBJECT(menuitem), "activate",
- G_CALLBACK(paste_unformatted_cb), imhtml);
-
- menuitem = gtk_menu_item_new_with_mnemonic(_("_Reset formatting"));
- gtk_widget_show(menuitem);
- gtk_widget_set_sensitive(menuitem, imhtml->editable);
- /* put it after Delete */
- gtk_menu_shell_insert(GTK_MENU_SHELL(menu), menuitem, 5);
-
- g_signal_connect(G_OBJECT(menuitem), "activate", G_CALLBACK(clear_formatting_cb), imhtml);
-
- menuitem = gtk_menu_item_new_with_mnemonic(_("Disable _smileys in selected text"));
- gtk_widget_show(menuitem);
- if (gtk_text_buffer_get_selection_bounds(imhtml->text_buffer, &start, &end)) {
- g_signal_connect(G_OBJECT(menuitem), "activate", G_CALLBACK(disable_smiley_selected), imhtml);
- } else {
- gtk_widget_set_sensitive(menuitem, FALSE);
- }
- gtk_menu_shell_insert(GTK_MENU_SHELL(menu), menuitem, 6);
-}
-
-static char *
-ucs2_order(gboolean swap)
-{
- gboolean be;
-
- be = G_BYTE_ORDER == G_BIG_ENDIAN;
- be = swap ? be : !be;
-
- if (be)
- return "UTF-16BE";
- else
- return "UTF-16LE";
-
-}
-
-/* Convert from UTF-16LE to UTF-8, stripping the BOM if one is present.*/
-static gchar *
-utf16_to_utf8_with_bom_check(gchar *data, guint len) {
- char *fromcode = NULL;
- GError *error = NULL;
- guint16 c;
- gchar *utf8_ret;
-
- /*
- * Unicode Techinical Report 20
- * ( http://www.unicode.org/unicode/reports/tr20/ ) says to treat an
- * initial 0xfeff (ZWNBSP) as a byte order indicator so that is
- * what we do. If there is no indicator assume it is in the default
- * order
- */
-
- memcpy(&c, data, 2);
- switch (c) {
- case 0xfeff:
- case 0xfffe:
- fromcode = ucs2_order(c == 0xfeff);
- data += 2;
- len -= 2;
- break;
- default:
- fromcode = "UTF-16";
- break;
- }
-
- utf8_ret = g_convert(data, len, "UTF-8", fromcode, NULL, NULL, &error);
-
- if (error) {
- purple_debug_warning("gtkimhtml", "g_convert error: %s\n", error->message);
- g_error_free(error);
- }
- return utf8_ret;
-}
-
-
-static void gtk_imhtml_clipboard_get(GtkClipboard *clipboard, GtkSelectionData *selection_data, guint info, GtkIMHtml *imhtml) {
- char *text = NULL;
- gboolean primary = (clipboard != clipboard_selection);
- GtkTextIter start, end;
-
- if (primary) {
- GtkTextMark *sel = NULL, *ins = NULL;
-
- g_return_if_fail(imhtml != NULL);
-
- ins = gtk_text_buffer_get_insert(imhtml->text_buffer);
- sel = gtk_text_buffer_get_selection_bound(imhtml->text_buffer);
- gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &start, sel);
- gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &end, ins);
- }
-
- if (info == TARGET_HTML) {
- char *selection;
-#ifndef _WIN32
- gsize len;
- if (primary) {
- text = gtk_imhtml_get_markup_range(imhtml, &start, &end);
- } else
- text = html_clipboard;
-
- /* Mozilla asks that we start our text/html with the Unicode byte order mark */
- selection = g_convert(text, -1, "UTF-16", "UTF-8", NULL, &len, NULL);
- gtk_selection_data_set(selection_data, gdk_atom_intern("text/html", FALSE), 16, (const guchar *)selection, len);
-#else
- selection = clipboard_html_to_win32(html_clipboard);
- gtk_selection_data_set(selection_data, gdk_atom_intern("HTML Format", FALSE), 8, (const guchar *)selection, strlen(selection));
-#endif
- g_free(selection);
- } else {
- if (primary) {
- text = gtk_imhtml_get_text(imhtml, &start, &end);
- } else
- text = text_clipboard;
- gtk_selection_data_set_text(selection_data, text, strlen(text));
- }
- if (primary) /* This was allocated here */
- g_free(text);
-}
-
-static void gtk_imhtml_primary_clipboard_clear(GtkClipboard *clipboard, GtkIMHtml *imhtml)
-{
- GtkTextIter insert;
- GtkTextIter selection_bound;
-
- gtk_text_buffer_get_iter_at_mark (imhtml->text_buffer, &insert,
- gtk_text_buffer_get_mark (imhtml->text_buffer, "insert"));
- gtk_text_buffer_get_iter_at_mark (imhtml->text_buffer, &selection_bound,
- gtk_text_buffer_get_mark (imhtml->text_buffer, "selection_bound"));
-
- if (!gtk_text_iter_equal (&insert, &selection_bound))
- gtk_text_buffer_move_mark (imhtml->text_buffer,
- gtk_text_buffer_get_mark (imhtml->text_buffer, "selection_bound"),
- &insert);
-}
-
-static void gtk_imhtml_clipboard_clear (GtkClipboard *clipboard, GtkSelectionData *sel_data,
- guint info, gpointer user_data_or_owner)
-{
-}
-
-static void copy_clipboard_cb(GtkIMHtml *imhtml, gpointer unused)
-{
- GtkTextIter start, end;
- if (gtk_text_buffer_get_selection_bounds(imhtml->text_buffer, &start, &end)) {
- if (!clipboard_selection)
- clipboard_selection = gtk_widget_get_clipboard(GTK_WIDGET(imhtml), GDK_SELECTION_CLIPBOARD);
- gtk_clipboard_set_with_data(clipboard_selection,
- selection_targets, sizeof(selection_targets) / sizeof(GtkTargetEntry),
- (GtkClipboardGetFunc)gtk_imhtml_clipboard_get,
- (GtkClipboardClearFunc)gtk_imhtml_clipboard_clear, NULL);
-
- g_free(html_clipboard);
- g_free(text_clipboard);
-
- html_clipboard = gtk_imhtml_get_markup_range(imhtml, &start, &end);
- text_clipboard = gtk_imhtml_get_text(imhtml, &start, &end);
- }
-
- g_signal_stop_emission_by_name(imhtml, "copy-clipboard");
-}
-
-static void cut_clipboard_cb(GtkIMHtml *imhtml, gpointer unused)
-{
- GtkTextIter start, end;
- if (gtk_text_buffer_get_selection_bounds(imhtml->text_buffer, &start, &end)) {
- if (!clipboard_selection)
- clipboard_selection = gtk_widget_get_clipboard(GTK_WIDGET(imhtml), GDK_SELECTION_CLIPBOARD);
- gtk_clipboard_set_with_data(clipboard_selection,
- selection_targets, sizeof(selection_targets) / sizeof(GtkTargetEntry),
- (GtkClipboardGetFunc)gtk_imhtml_clipboard_get,
- (GtkClipboardClearFunc)gtk_imhtml_clipboard_clear, NULL);
-
- g_free(html_clipboard);
- g_free(text_clipboard);
-
- html_clipboard = gtk_imhtml_get_markup_range(imhtml, &start, &end);
- text_clipboard = gtk_imhtml_get_text(imhtml, &start, &end);
-
- if (imhtml->editable)
- gtk_text_buffer_delete_selection(imhtml->text_buffer, FALSE, FALSE);
- }
-
- g_signal_stop_emission_by_name(imhtml, "cut-clipboard");
-}
-
-static void imhtml_paste_insert(GtkIMHtml *imhtml, const char *text, gboolean plaintext)
-{
- GtkTextIter iter;
- GtkIMHtmlOptions flags = plaintext ? GTK_IMHTML_NO_SMILEY : (GTK_IMHTML_NO_NEWLINE | GTK_IMHTML_NO_COMMENTS);
-
- /* Delete any currently selected text */
- gtk_text_buffer_delete_selection(imhtml->text_buffer, TRUE, TRUE);
-
- gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &iter, gtk_text_buffer_get_insert(imhtml->text_buffer));
- if (!imhtml->wbfo && !plaintext)
- gtk_imhtml_close_tags(imhtml, &iter);
-
- gtk_imhtml_insert_html_at_iter(imhtml, text, flags, &iter);
- gtk_text_buffer_move_mark_by_name(imhtml->text_buffer, "insert", &iter);
- gtk_text_view_scroll_to_mark(GTK_TEXT_VIEW(imhtml), gtk_text_buffer_get_insert(imhtml->text_buffer),
- 0, FALSE, 0.0, 0.0);
- if (!imhtml->wbfo && !plaintext)
- gtk_imhtml_close_tags(imhtml, &iter);
-
-}
-
-static void paste_plaintext_received_cb (GtkClipboard *clipboard, const gchar *text, gpointer data)
-{
- char *tmp;
-
- if (text == NULL || !(*text))
- return;
-
- tmp = g_markup_escape_text(text, -1);
- imhtml_paste_insert(data, tmp, TRUE);
- g_free(tmp);
-}
-
-static void paste_received_cb (GtkClipboard *clipboard, GtkSelectionData *selection_data, gpointer data)
-{
- char *text;
- GtkIMHtml *imhtml = data;
- gint length = gtk_selection_data_get_length(selection_data);
-
- if (!gtk_text_view_get_editable(GTK_TEXT_VIEW(imhtml)))
- return;
-
- if (imhtml->wbfo || length <= 0) {
- gtk_clipboard_request_text(clipboard, paste_plaintext_received_cb, imhtml);
- return;
- } else {
-#if 0
- /* Here's some debug code, for figuring out what sent to us over the clipboard. */
- {
- int i;
-
- purple_debug_misc("gtkimhtml", "In paste_received_cb():\n\tformat = %d, length = %d\n\t",
- selection_data->format, selection_data->length);
-
- for (i = 0; i < (/*(selection_data->format / 8) **/ selection_data->length); i++) {
- if ((i % 70) == 0)
- printf("\n\t");
- if (selection_data->data[i] == '\0')
- printf(".");
- else
- printf("%c", selection_data->data[i]);
- }
- printf("\n");
- }
-#endif
-
- text = g_malloc(length + 1);
- memcpy(text, gtk_selection_data_get_data(selection_data), length);
- /* Make sure the paste data is null-terminated. Given that
- * we're passed length (but assume later that it is
- * null-terminated), this seems sensible to me.
- */
- text[length] = '\0';
- }
-
-#ifdef _WIN32
- if (gtk_selection_data_get_data_type(selection_data) == gdk_atom_intern("HTML Format", FALSE)) {
- char *tmp = clipboard_win32_to_html(text);
- g_free(text);
- text = tmp;
- }
-#endif
-
- if (length >= 2 &&
- (*(guint16 *)text == 0xfeff || *(guint16 *)text == 0xfffe)) {
- /* This is UTF-16 */
- char *utf8 = utf16_to_utf8_with_bom_check(text, length);
- g_free(text);
- text = utf8;
- if (!text) {
- purple_debug_warning("gtkimhtml", "g_convert from UTF-16 failed in paste_received_cb\n");
- return;
- }
- }
-
- if (!(*text) || !g_utf8_validate(text, -1, NULL)) {
- purple_debug_warning("gtkimhtml", "empty string or invalid UTF-8 in paste_received_cb\n");
- g_free(text);
- return;
- }
-
- imhtml_paste_insert(imhtml, text, FALSE);
- g_free(text);
-}
-
-
-static void smart_backspace_cb(GtkIMHtml *imhtml, gpointer blah)
-{
- GtkTextIter iter;
- GtkTextChildAnchor* anchor;
- char * text;
- gint offset;
-
- if (!imhtml->editable)
- return;
-
- gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &iter, gtk_text_buffer_get_insert(imhtml->text_buffer));
-
- /* Get the character before the insertion point */
- offset = gtk_text_iter_get_offset(&iter);
- if (offset <= 0)
- return;
-
- gtk_text_iter_backward_char(&iter);
- anchor = gtk_text_iter_get_child_anchor(&iter);
-
- if (!anchor)
- return; /* No smiley here */
-
- text = g_object_get_data(G_OBJECT(anchor), "gtkimhtml_plaintext");
- if (!text)
- return;
-
- /* ok, then we need to insert the image buffer text before the anchor */
- gtk_text_buffer_insert(imhtml->text_buffer, &iter, text, -1);
-}
-
-static void paste_clipboard_cb(GtkIMHtml *imhtml, gpointer blah)
-{
-#ifdef _WIN32
- /* If we're on windows, let's see if we can get data from the HTML Format
- clipboard before we try to paste from the GTK buffer */
- if (!clipboard_paste_html_win32(imhtml) && gtk_text_view_get_editable(GTK_TEXT_VIEW(imhtml))) {
- GtkClipboard *clipboard = gtk_widget_get_clipboard(GTK_WIDGET(imhtml), GDK_SELECTION_CLIPBOARD);
- gtk_clipboard_request_text(clipboard, paste_plaintext_received_cb, imhtml);
-
- }
-#else
- GtkClipboard *clipboard = gtk_widget_get_clipboard(GTK_WIDGET(imhtml), GDK_SELECTION_CLIPBOARD);
- gtk_clipboard_request_contents(clipboard, gdk_atom_intern("text/html", FALSE),
- paste_received_cb, imhtml);
-#endif
- g_signal_stop_emission_by_name(imhtml, "paste-clipboard");
-}
-
-static void imhtml_realized_remove_primary(GtkIMHtml *imhtml, gpointer unused)
-{
- gtk_text_buffer_remove_selection_clipboard(GTK_IMHTML(imhtml)->text_buffer,
- gtk_widget_get_clipboard(GTK_WIDGET(imhtml), GDK_SELECTION_PRIMARY));
-
-}
-
-static void imhtml_destroy_add_primary(GtkIMHtml *imhtml, gpointer unused)
-{
- gtk_text_buffer_add_selection_clipboard(GTK_IMHTML(imhtml)->text_buffer,
- gtk_widget_get_clipboard(GTK_WIDGET(imhtml), GDK_SELECTION_PRIMARY));
-}
-
-static void mark_set_so_update_selection_cb(GtkTextBuffer *buffer, GtkTextIter *arg1, GtkTextMark *mark, GtkIMHtml *imhtml)
-{
- if (gtk_text_buffer_get_selection_bounds(buffer, NULL, NULL)) {
- gtk_clipboard_set_with_owner(gtk_widget_get_clipboard(GTK_WIDGET(imhtml), GDK_SELECTION_PRIMARY),
- selection_targets, sizeof(selection_targets) / sizeof(GtkTargetEntry),
- (GtkClipboardGetFunc)gtk_imhtml_clipboard_get,
- (GtkClipboardClearFunc)gtk_imhtml_primary_clipboard_clear, G_OBJECT(imhtml));
- }
-}
-
-static gboolean gtk_imhtml_button_press_event(GtkIMHtml *imhtml, GdkEventButton *event, gpointer unused)
-{
- if (event->button == 2) {
- int x, y;
- GtkTextIter iter;
- GtkClipboard *clipboard = gtk_widget_get_clipboard(GTK_WIDGET(imhtml), GDK_SELECTION_PRIMARY);
-
- if (!imhtml->editable)
- return FALSE;
-
- gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(imhtml),
- GTK_TEXT_WINDOW_TEXT,
- event->x,
- event->y,
- &x,
- &y);
- gtk_text_view_get_iter_at_location(GTK_TEXT_VIEW(imhtml), &iter, x, y);
- gtk_text_buffer_place_cursor(imhtml->text_buffer, &iter);
-
- gtk_clipboard_request_contents(clipboard, gdk_atom_intern("text/html", FALSE),
- paste_received_cb, imhtml);
-
- return TRUE;
- }
-
- return FALSE;
-}
-
-static void
-gtk_imhtml_undo(GtkIMHtml *imhtml)
-{
- g_return_if_fail(GTK_IS_IMHTML(imhtml));
- if (imhtml->editable &&
- gtk_source_undo_manager_can_undo(imhtml->undo_manager))
- gtk_source_undo_manager_undo(imhtml->undo_manager);
-}
-
-static void
-gtk_imhtml_redo(GtkIMHtml *imhtml)
-{
- g_return_if_fail(GTK_IS_IMHTML(imhtml));
- if (imhtml->editable &&
- gtk_source_undo_manager_can_redo(imhtml->undo_manager))
- gtk_source_undo_manager_redo(imhtml->undo_manager);
-
-}
-
-static gboolean imhtml_message_send(GtkIMHtml *imhtml)
-{
- return FALSE;
-}
-
-static void
-imhtml_paste_cb(GtkIMHtml *imhtml, const char *str)
-{
- if (!gtk_text_view_get_editable(GTK_TEXT_VIEW(imhtml)))
- return;
-
- if (!str || !*str || !strcmp(str, "html"))
- g_signal_emit_by_name(imhtml, "paste_clipboard");
- else if (!strcmp(str, "text"))
- paste_unformatted_cb(NULL, imhtml);
-}
-
-static void imhtml_toggle_format(GtkIMHtml *imhtml, GtkIMHtmlButtons buttons)
-{
- /* since this function is the handler for the formatting keystrokes,
- we need to check here that the formatting attempted is permitted */
- buttons &= imhtml->format_functions;
-
- switch (buttons) {
- case GTK_IMHTML_BOLD:
- imhtml_toggle_bold(imhtml);
- break;
- case GTK_IMHTML_ITALIC:
- imhtml_toggle_italic(imhtml);
- break;
- case GTK_IMHTML_UNDERLINE:
- imhtml_toggle_underline(imhtml);
- break;
- case GTK_IMHTML_STRIKE:
- imhtml_toggle_strike(imhtml);
- break;
- case GTK_IMHTML_SHRINK:
- imhtml_font_shrink(imhtml);
- break;
- case GTK_IMHTML_GROW:
- imhtml_font_grow(imhtml);
- break;
- default:
- break;
- }
-}
-
-static void
-gtk_imhtml_finalize (GObject *object)
-{
- GtkIMHtml *imhtml = GTK_IMHTML(object);
- GList *scalables;
- GSList *l;
-
- if (imhtml->scroll_src)
- g_source_remove(imhtml->scroll_src);
- if (imhtml->scroll_time)
- g_timer_destroy(imhtml->scroll_time);
-
- g_hash_table_destroy(imhtml->smiley_data);
- gtk_smiley_tree_destroy(imhtml->default_smilies);
- gdk_cursor_unref(imhtml->hand_cursor);
- gdk_cursor_unref(imhtml->arrow_cursor);
- gdk_cursor_unref(imhtml->text_cursor);
-
- if(imhtml->tip_window){
- gtk_widget_destroy(imhtml->tip_window);
- }
- if(imhtml->tip_timer)
- g_source_remove(imhtml->tip_timer);
-
- for(scalables = imhtml->scalables; scalables; scalables = scalables->next) {
- struct scalable_data *sd = scalables->data;
- GtkIMHtmlScalable *scale = GTK_IMHTML_SCALABLE(sd->scalable);
- scale->free(scale);
- g_free(sd);
- }
-
- for (l = imhtml->im_images; l; l = l->next) {
- struct im_image_data *img_data = l->data;
- if (imhtml->funcs->image_unref)
- imhtml->funcs->image_unref(img_data->id);
- g_free(img_data);
- }
-
- g_list_free(imhtml->scalables);
- g_slist_free(imhtml->im_images);
- g_queue_free(imhtml->animations);
- g_free(imhtml->protocol_name);
- g_free(imhtml->search_string);
- g_object_unref(imhtml->undo_manager);
- G_OBJECT_CLASS(parent_class)->finalize (object);
-
-}
-
-static GtkIMHtmlProtocol *
-imhtml_find_protocol(const char *url, gboolean reverse)
-{
- GtkIMHtmlClass *klass;
- GList *iter;
- GtkIMHtmlProtocol *proto = NULL;
- gssize length = reverse ? (gssize)strlen(url) : -1;
-
- klass = g_type_class_ref(GTK_TYPE_IMHTML);
- for (iter = klass->protocols; iter; iter = iter->next) {
- proto = iter->data;
- if (g_ascii_strncasecmp(url, proto->name, reverse ? MIN(length, proto->length) : proto->length) == 0) {
- return proto;
- }
- }
- return NULL;
-}
-
-static void
-imhtml_url_clicked(GtkIMHtml *imhtml, const char *url)
-{
- GtkIMHtmlProtocol *proto = imhtml_find_protocol(url, FALSE);
- GtkIMHtmlLink *link;
- if (!proto)
- return;
- link = g_new0(GtkIMHtmlLink, 1);
- link->imhtml = g_object_ref(imhtml);
- link->url = g_strdup(url);
- proto->activate(imhtml, link); /* XXX: Do something with the return value? */
- gtk_imhtml_link_destroy(link);
-}
-
-/* Boring GTK+ stuff */
-static void gtk_imhtml_class_init (GtkIMHtmlClass *klass)
-{
- GtkWidgetClass *widget_class = (GtkWidgetClass *) klass;
- GObjectClass *gobject_class;
- gobject_class = (GObjectClass*) klass;
- parent_class = g_type_class_ref(GTK_TYPE_TEXT_VIEW);
- signals[URL_CLICKED] = g_signal_new("url_clicked",
- G_TYPE_FROM_CLASS(gobject_class),
- G_SIGNAL_RUN_FIRST,
- G_STRUCT_OFFSET(GtkIMHtmlClass, url_clicked),
- NULL,
- 0,
- g_cclosure_marshal_VOID__POINTER,
- G_TYPE_NONE, 1,
- G_TYPE_POINTER);
- signals[BUTTONS_UPDATE] = g_signal_new("format_buttons_update",
- G_TYPE_FROM_CLASS(gobject_class),
- G_SIGNAL_RUN_FIRST,
- G_STRUCT_OFFSET(GtkIMHtmlClass, buttons_update),
- NULL,
- 0,
- g_cclosure_marshal_VOID__INT,
- G_TYPE_NONE, 1,
- G_TYPE_INT);
- signals[TOGGLE_FORMAT] = g_signal_new("format_function_toggle",
- G_TYPE_FROM_CLASS(gobject_class),
- G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
- G_STRUCT_OFFSET(GtkIMHtmlClass, toggle_format),
- NULL,
- 0,
- g_cclosure_marshal_VOID__INT,
- G_TYPE_NONE, 1,
- G_TYPE_INT);
- signals[CLEAR_FORMAT] = g_signal_new("format_function_clear",
- G_TYPE_FROM_CLASS(gobject_class),
- G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
- G_STRUCT_OFFSET(GtkIMHtmlClass, clear_format),
- NULL,
- 0,
- g_cclosure_marshal_VOID__VOID,
- G_TYPE_NONE, 0);
- signals[UPDATE_FORMAT] = g_signal_new("format_function_update",
- G_TYPE_FROM_CLASS(gobject_class),
- G_SIGNAL_RUN_FIRST,
- G_STRUCT_OFFSET(GtkIMHtmlClass, update_format),
- NULL,
- 0,
- g_cclosure_marshal_VOID__VOID,
- G_TYPE_NONE, 0);
- signals[MESSAGE_SEND] = g_signal_new("message_send",
- G_TYPE_FROM_CLASS(gobject_class),
- G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
- G_STRUCT_OFFSET(GtkIMHtmlClass, message_send),
- NULL,
- 0, g_cclosure_marshal_VOID__VOID,
- G_TYPE_NONE, 0);
- signals[PASTE] = g_signal_new("paste",
- G_TYPE_FROM_CLASS(gobject_class),
- G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
- 0,
- NULL,
- 0, g_cclosure_marshal_VOID__STRING,
- G_TYPE_NONE, 1, G_TYPE_STRING);
- signals [UNDO] = g_signal_new ("undo",
- G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
- G_STRUCT_OFFSET (GtkIMHtmlClass, undo),
- NULL,
- NULL,
- gtksourceview_marshal_VOID__VOID,
- G_TYPE_NONE,
- 0);
- signals [REDO] = g_signal_new ("redo",
- G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
- G_STRUCT_OFFSET (GtkIMHtmlClass, redo),
- NULL,
- NULL,
- gtksourceview_marshal_VOID__VOID,
- G_TYPE_NONE,
- 0);
-
-
-
- klass->toggle_format = imhtml_toggle_format;
- klass->message_send = imhtml_message_send;
- klass->clear_format = imhtml_clear_formatting;
- klass->url_clicked = imhtml_url_clicked;
- klass->undo = gtk_imhtml_undo;
- klass->redo = gtk_imhtml_redo;
-
- gobject_class->finalize = gtk_imhtml_finalize;
- widget_class->drag_motion = gtk_text_view_drag_motion;
- /* TODO: I _think_ this should be removed for GTK+ 3.0 */
-#if !GTK_CHECK_VERSION(3,0,0)
- widget_class->expose_event = gtk_imhtml_expose_event;
-#endif
- parent_size_allocate = widget_class->size_allocate;
- widget_class->size_allocate = gtk_imhtml_size_allocate;
- parent_style_set = widget_class->style_set;
- widget_class->style_set = gtk_imhtml_style_set;
-
- gtk_widget_class_install_style_property(widget_class, g_param_spec_boxed("hyperlink-color",
- "Hyperlink color",
- "Color to draw hyperlinks.",
- GDK_TYPE_COLOR, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
- gtk_widget_class_install_style_property(widget_class, g_param_spec_boxed("hyperlink-visited-color",
- "Hyperlink visited color",
- "Color to draw hyperlink after it has been visited (or activated).",
- GDK_TYPE_COLOR, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
- gtk_widget_class_install_style_property(widget_class, g_param_spec_boxed("hyperlink-prelight-color",
- "Hyperlink prelight color",
- "Color to draw hyperlinks when mouse is over them.",
- GDK_TYPE_COLOR, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
- gtk_widget_class_install_style_property(widget_class, g_param_spec_boxed("send-name-color",
- "Sent Message Name Color",
- "Color to draw the name of a message you sent.",
- GDK_TYPE_COLOR, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
- gtk_widget_class_install_style_property(widget_class, g_param_spec_boxed("receive-name-color",
- "Received Message Name Color",
- "Color to draw the name of a message you received.",
- GDK_TYPE_COLOR, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
- gtk_widget_class_install_style_property(widget_class, g_param_spec_boxed("highlight-name-color",
- "\"Attention\" Name Color",
- "Color to draw the name of a message you received containing your name.",
- GDK_TYPE_COLOR, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
- gtk_widget_class_install_style_property(widget_class, g_param_spec_boxed("action-name-color",
- "Action Message Name Color",
- "Color to draw the name of an action message.",
- GDK_TYPE_COLOR, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
- gtk_widget_class_install_style_property(widget_class, g_param_spec_boxed("whisper-action-name-color",
- "Action Message Name Color for Whispered Message",
- "Color to draw the name of a whispered action message.",
- GDK_TYPE_COLOR, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
- gtk_widget_class_install_style_property(widget_class, g_param_spec_boxed("whisper-name-color",
- "Whisper Message Name Color",
- "Color to draw the name of a whispered message.",
- GDK_TYPE_COLOR, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
-
- /* Customizable typing notification ... sort of. Example:
- * GtkIMHtml::typing-notification-font = "monospace italic light 8.0"
- * GtkIMHtml::typing-notification-color = "#ff0000"
- * GtkIMHtml::typing-notification-enable = 1
- */
- gtk_widget_class_install_style_property(widget_class, g_param_spec_boxed("typing-notification-color",
- "Typing notification color",
- "The color to use for the typing notification",
- GDK_TYPE_COLOR, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
- gtk_widget_class_install_style_property(widget_class, g_param_spec_string("typing-notification-font",
- "Typing notification font",
- "The font to use for the typing notification",
- "light 8.0", G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
- gtk_widget_class_install_style_property(widget_class, g_param_spec_boolean("typing-notification-enable",
- "Enable typing notification",
- "Enable typing notification",
- TRUE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
-
-#if 0
- GtkBindingSet *binding_set;
- binding_set = gtk_binding_set_by_class (parent_class);
- gtk_binding_entry_add_signal (binding_set, GDK_KEY_b, GDK_CONTROL_MASK, "format_function_toggle", 1, G_TYPE_INT, GTK_IMHTML_BOLD);
- gtk_binding_entry_add_signal (binding_set, GDK_KEY_i, GDK_CONTROL_MASK, "format_function_toggle", 1, G_TYPE_INT, GTK_IMHTML_ITALIC);
- gtk_binding_entry_add_signal (binding_set, GDK_KEY_u, GDK_CONTROL_MASK, "format_function_toggle", 1, G_TYPE_INT, GTK_IMHTML_UNDERLINE);
- gtk_binding_entry_add_signal (binding_set, GDK_KEY_plus, GDK_CONTROL_MASK, "format_function_toggle", 1, G_TYPE_INT, GTK_IMHTML_GROW);
- gtk_binding_entry_add_signal (binding_set, GDK_KEY_equal, GDK_CONTROL_MASK, "format_function_toggle", 1, G_TYPE_INT, GTK_IMHTML_GROW);
- gtk_binding_entry_add_signal (binding_set, GDK_KEY_minus, GDK_CONTROL_MASK, "format_function_toggle", 1, G_TYPE_INT, GTK_IMHTML_SHRINK);
- binding_set = gtk_binding_set_by_class(klass);
- gtk_binding_entry_add_signal (binding_set, GDK_KEY_r, GDK_CONTROL_MASK, "format_function_clear", 0);
- gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Enter, 0, "message_send", 0);
- gtk_binding_entry_add_signal (binding_set, GDK_KEY_Return, 0, "message_send", 0);
- gtk_binding_entry_add_signal (binding_set, GDK_KEY_z, GDK_CONTROL_MASK, "undo", 0);
- gtk_binding_entry_add_signal (binding_set, GDK_KEY_z, GDK_CONTROL_MASK | GDK_SHIFT_MASK, "redo", 0);
- gtk_binding_entry_add_signal (binding_set, GDK_KEY_F14, 0, "undo", 0);
- gtk_binding_entry_add_signal(binding_set, GDK_KEY_v, GDK_CONTROL_MASK | GDK_SHIFT_MASK, "paste", 1, G_TYPE_STRING, "text");
-#endif
-}
-
-static void gtk_imhtml_init (GtkIMHtml *imhtml)
-{
- imhtml->text_buffer = gtk_text_buffer_new(NULL);
- imhtml->undo_manager = gtk_source_undo_manager_new(imhtml->text_buffer);
- gtk_text_view_set_buffer(GTK_TEXT_VIEW(imhtml), imhtml->text_buffer);
- gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(imhtml), GTK_WRAP_WORD_CHAR);
- gtk_text_view_set_pixels_above_lines(GTK_TEXT_VIEW(imhtml), 2);
- gtk_text_view_set_pixels_below_lines(GTK_TEXT_VIEW(imhtml), 3);
- gtk_text_view_set_left_margin(GTK_TEXT_VIEW(imhtml), 2);
- gtk_text_view_set_right_margin(GTK_TEXT_VIEW(imhtml), 2);
- /*gtk_text_view_set_indent(GTK_TEXT_VIEW(imhtml), -15);*/
- /*gtk_text_view_set_justification(GTK_TEXT_VIEW(imhtml), GTK_JUSTIFY_FILL);*/
-
- /* These tags will be used often and can be reused--we create them on init and then apply them by name
- * other tags (color, size, face, etc.) will have to be created and applied dynamically
- * Note that even though we created SUB, SUP, and PRE tags here, we don't really
- * apply them anywhere yet. */
- gtk_text_buffer_create_tag(imhtml->text_buffer, "BOLD", "weight", PANGO_WEIGHT_BOLD, NULL);
- gtk_text_buffer_create_tag(imhtml->text_buffer, "ITALICS", "style", PANGO_STYLE_ITALIC, NULL);
- gtk_text_buffer_create_tag(imhtml->text_buffer, "UNDERLINE", "underline", PANGO_UNDERLINE_SINGLE, NULL);
- gtk_text_buffer_create_tag(imhtml->text_buffer, "STRIKE", "strikethrough", TRUE, NULL);
- gtk_text_buffer_create_tag(imhtml->text_buffer, "SUB", "rise", -5000, NULL);
- gtk_text_buffer_create_tag(imhtml->text_buffer, "SUP", "rise", 5000, NULL);
- gtk_text_buffer_create_tag(imhtml->text_buffer, "PRE", "family", "Monospace", NULL);
- gtk_text_buffer_create_tag(imhtml->text_buffer, "search", "background", "#22ff00", "weight", "bold", NULL);
- gtk_text_buffer_create_tag(imhtml->text_buffer, "comment", "weight", PANGO_WEIGHT_NORMAL,
-#if FALSE
- "invisible", FALSE,
-#endif
- NULL);
-
- gtk_text_buffer_create_tag(imhtml->text_buffer, "send-name", "weight", PANGO_WEIGHT_BOLD, NULL);
- gtk_text_buffer_create_tag(imhtml->text_buffer, "receive-name", "weight", PANGO_WEIGHT_BOLD, NULL);
- gtk_text_buffer_create_tag(imhtml->text_buffer, "highlight-name", "weight", PANGO_WEIGHT_BOLD, NULL);
- gtk_text_buffer_create_tag(imhtml->text_buffer, "action-name", "weight", PANGO_WEIGHT_BOLD, NULL);
- gtk_text_buffer_create_tag(imhtml->text_buffer, "whisper-action-name", "weight", PANGO_WEIGHT_BOLD, NULL);
- gtk_text_buffer_create_tag(imhtml->text_buffer, "whisper-name", "weight", PANGO_WEIGHT_BOLD, NULL);
-
- /* When hovering over a link, we show the hand cursor--elsewhere we show the plain ol' pointer cursor */
- imhtml->hand_cursor = gdk_cursor_new (GDK_HAND2);
- imhtml->arrow_cursor = gdk_cursor_new (GDK_LEFT_PTR);
- imhtml->text_cursor = gdk_cursor_new (GDK_XTERM);
-
- imhtml->show_comments = TRUE;
-
- imhtml->smiley_data = g_hash_table_new_full(g_str_hash, g_str_equal,
- g_free, (GDestroyNotify)gtk_smiley_tree_destroy);
- imhtml->default_smilies = gtk_smiley_tree_new();
-
- g_signal_connect(G_OBJECT(imhtml), "motion-notify-event", G_CALLBACK(gtk_motion_event_notify), NULL);
- g_signal_connect(G_OBJECT(imhtml), "leave-notify-event", G_CALLBACK(gtk_leave_event_notify), NULL);
- g_signal_connect(G_OBJECT(imhtml), "enter-notify-event", G_CALLBACK(gtk_enter_event_notify), NULL);
- g_signal_connect(G_OBJECT(imhtml), "button_press_event", G_CALLBACK(gtk_imhtml_button_press_event), NULL);
- g_signal_connect(G_OBJECT(imhtml->text_buffer), "insert-text", G_CALLBACK(preinsert_cb), imhtml);
- g_signal_connect(G_OBJECT(imhtml->text_buffer), "delete_range", G_CALLBACK(delete_cb), imhtml);
- g_signal_connect_after(G_OBJECT(imhtml->text_buffer), "insert-text", G_CALLBACK(insert_cb), imhtml);
- g_signal_connect_after(G_OBJECT(imhtml->text_buffer), "insert-child-anchor", G_CALLBACK(insert_ca_cb), imhtml);
- gtk_drag_dest_set(GTK_WIDGET(imhtml), 0,
- link_drag_drop_targets, sizeof(link_drag_drop_targets) / sizeof(GtkTargetEntry),
- GDK_ACTION_COPY);
- g_signal_connect(G_OBJECT(imhtml), "drag_data_received", G_CALLBACK(gtk_imhtml_link_drag_rcv_cb), imhtml);
- g_signal_connect(G_OBJECT(imhtml), "drag_drop", G_CALLBACK(gtk_imhtml_link_drop_cb), imhtml);
-
- g_signal_connect(G_OBJECT(imhtml), "copy-clipboard", G_CALLBACK(copy_clipboard_cb), NULL);
- g_signal_connect(G_OBJECT(imhtml), "cut-clipboard", G_CALLBACK(cut_clipboard_cb), NULL);
- g_signal_connect(G_OBJECT(imhtml), "paste-clipboard", G_CALLBACK(paste_clipboard_cb), NULL);
- g_signal_connect_after(G_OBJECT(imhtml), "realize", G_CALLBACK(imhtml_realized_remove_primary), NULL);
- g_signal_connect(G_OBJECT(imhtml), "unrealize", G_CALLBACK(imhtml_destroy_add_primary), NULL);
- g_signal_connect(G_OBJECT(imhtml), "paste", G_CALLBACK(imhtml_paste_cb), NULL);
-
-#ifndef _WIN32
- g_signal_connect_after(G_OBJECT(GTK_IMHTML(imhtml)->text_buffer), "mark-set",
- G_CALLBACK(mark_set_so_update_selection_cb), imhtml);
-#endif
-
- gtk_widget_add_events(GTK_WIDGET(imhtml),
- GDK_LEAVE_NOTIFY_MASK | GDK_ENTER_NOTIFY_MASK);
-
- imhtml->tip = NULL;
- imhtml->tip_timer = 0;
- imhtml->tip_window = NULL;
-
- imhtml->edit.bold = FALSE;
- imhtml->edit.italic = FALSE;
- imhtml->edit.underline = FALSE;
- imhtml->edit.forecolor = NULL;
- imhtml->edit.backcolor = NULL;
- imhtml->edit.fontface = NULL;
- imhtml->edit.fontsize = 0;
- imhtml->edit.link = NULL;
-
-
- imhtml->scalables = NULL;
- imhtml->animations = g_queue_new();
- gtk_imhtml_set_editable(imhtml, FALSE);
- g_signal_connect(G_OBJECT(imhtml), "populate-popup",
- G_CALLBACK(hijack_menu_cb), NULL);
-}
-
-GtkWidget *gtk_imhtml_new(void *a, void *b)
-{
- return GTK_WIDGET(g_object_new(gtk_imhtml_get_type(), NULL));
-}
-
-GType gtk_imhtml_get_type()
-{
- static GType imhtml_type = 0;
-
- if (!imhtml_type) {
- static const GTypeInfo imhtml_info = {
- sizeof(GtkIMHtmlClass),
- NULL,
- NULL,
- (GClassInitFunc) gtk_imhtml_class_init,
- NULL,
- NULL,
- sizeof (GtkIMHtml),
- 0,
- (GInstanceInitFunc) gtk_imhtml_init,
- NULL
- };
-
- imhtml_type = g_type_register_static(gtk_text_view_get_type(),
- "GtkIMHtml", &imhtml_info, 0);
- }
-
- return imhtml_type;
-}
-
-static void gtk_imhtml_link_destroy(GtkIMHtmlLink *link)
-{
- if (link->imhtml)
- g_object_unref(link->imhtml);
- if (link->tag)
- g_object_unref(link->tag);
- g_free(link->url);
- g_free(link);
-}
-
-/* The callback for an event on a link tag. */
-static gboolean tag_event(GtkTextTag *tag, GObject *imhtml, GdkEvent *event, GtkTextIter *arg2, gpointer unused)
-{
- GdkEventButton *event_button = (GdkEventButton *) event;
- if (GTK_IMHTML(imhtml)->editable)
- return FALSE;
- if (event->type == GDK_BUTTON_RELEASE) {
- if ((event_button->button == 1) || (event_button->button == 2)) {
- GtkTextIter start, end;
- /* we shouldn't open a URL if the user has selected something: */
- if (gtk_text_buffer_get_selection_bounds(
- gtk_text_iter_get_buffer(arg2), &start, &end))
- return FALSE;
- gtk_imhtml_activate_tag(GTK_IMHTML(imhtml), tag);
- return FALSE;
- } else if(event_button->button == 3) {
- GList *children;
- GtkWidget *menu;
- GtkIMHtmlProtocol *proto;
- GtkIMHtmlLink *link = g_new(GtkIMHtmlLink, 1);
- link->imhtml = g_object_ref(imhtml);
- link->url = g_strdup(g_object_get_data(G_OBJECT(tag), "link_url"));
- link->tag = g_object_ref(tag);
-
- /* Don't want the tooltip around if user right-clicked on link */
- if (GTK_IMHTML(imhtml)->tip_window) {
- gtk_widget_destroy(GTK_IMHTML(imhtml)->tip_window);
- GTK_IMHTML(imhtml)->tip_window = NULL;
- }
- if (GTK_IMHTML(imhtml)->tip_timer) {
- g_source_remove(GTK_IMHTML(imhtml)->tip_timer);
- GTK_IMHTML(imhtml)->tip_timer = 0;
- }
- if (GTK_IMHTML(imhtml)->editable)
- gdk_window_set_cursor(event_button->window, GTK_IMHTML(imhtml)->text_cursor);
- else
- gdk_window_set_cursor(event_button->window, GTK_IMHTML(imhtml)->arrow_cursor);
- menu = gtk_menu_new();
- g_object_set_data_full(G_OBJECT(menu), "x-imhtml-url-data", link,
- (GDestroyNotify)gtk_imhtml_link_destroy);
-
- proto = imhtml_find_protocol(link->url, FALSE);
-
- if (proto && proto->context_menu) {
- proto->context_menu(GTK_IMHTML(link->imhtml), link, menu);
- }
-
- children = gtk_container_get_children(GTK_CONTAINER(menu));
- if (!children) {
- GtkWidget *item = gtk_menu_item_new_with_label(_("No actions available"));
- gtk_widget_show(item);
- gtk_widget_set_sensitive(item, FALSE);
- gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
- } else {
- g_list_free(children);
- }
-
-
- gtk_widget_show_all(menu);
- gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL,
- event_button->button, event_button->time);
-
- return TRUE;
- }
- }
- if(event->type == GDK_BUTTON_PRESS && event_button->button == 3)
- return TRUE; /* Clicking the right mouse button on a link shouldn't
- be caught by the regular GtkTextView menu */
- else
- return FALSE; /* Let clicks go through if we didn't catch anything */
-}
-
-static gboolean
-gtk_text_view_drag_motion (GtkWidget *widget,
- GdkDragContext *context,
- gint x,
- gint y,
- guint time)
-{
- GdkDragAction suggested_action = 0;
-
- if (gtk_drag_dest_find_target (widget, context, NULL) == GDK_NONE) {
- /* can't accept any of the offered targets */
- } else {
- GtkWidget *source_widget;
- suggested_action = gdk_drag_context_get_suggested_action(context);
- source_widget = gtk_drag_get_source_widget (context);
- if (source_widget == widget) {
- /* Default to MOVE, unless the user has
- * pressed ctrl or alt to affect available actions
- */
- if ((gdk_drag_context_get_actions(context) & GDK_ACTION_MOVE) != 0)
- suggested_action = GDK_ACTION_MOVE;
- }
- }
-
- gdk_drag_status (context, suggested_action, time);
-
- /* TRUE return means don't propagate the drag motion to parent
- * widgets that may also be drop sites.
- */
- return TRUE;
-}
-
-static void
-gtk_imhtml_link_drop_cb(GtkWidget *widget, GdkDragContext *context, gint x, gint y, guint time, gpointer user_data)
-{
- GdkAtom target = gtk_drag_dest_find_target (widget, context, NULL);
-
- if (target != GDK_NONE)
- gtk_drag_get_data (widget, context, target, time);
- else
- gtk_drag_finish (context, FALSE, FALSE, time);
-
- return;
-}
-
-static void
-gtk_imhtml_link_drag_rcv_cb(GtkWidget *widget, GdkDragContext *dc, guint x, guint y,
- GtkSelectionData *sd, guint info, guint t, GtkIMHtml *imhtml)
-{
- gchar **links;
- gchar *link;
- char *text = (char *) gtk_selection_data_get_data(sd);
- GtkTextMark *mark = gtk_text_buffer_get_insert(imhtml->text_buffer);
- GtkTextIter iter;
- gint i = 0;
- gint length = gtk_selection_data_get_length(sd);
-
- gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &iter, mark);
-
- if (gtk_imhtml_get_editable(imhtml) && text) {
- switch (info) {
- case GTK_IMHTML_DRAG_URL:
- /* TODO: Is it really ok to change sd->data...? */
- purple_str_strip_char(text, '\r');
-
- links = g_strsplit(text, "\n", 0);
- while ((link = links[i]) != NULL) {
- if (gtk_imhtml_is_protocol(link)) {
- gchar *label;
-
- if(links[i + 1])
- i++;
-
- label = links[i];
-
- gtk_imhtml_insert_link(imhtml, mark, link, label);
- } else if (*link == '\0') {
- /* Ignore blank lines */
- } else {
- /* Special reasons, aka images being put in via other tag, etc. */
- /* ... don't pretend we handled it if we didn't */
- gtk_drag_finish(dc, FALSE, FALSE, t);
- g_strfreev(links);
- return;
- }
-
- i++;
- }
- g_strfreev(links);
- break;
- case GTK_IMHTML_DRAG_HTML:
- {
- char *utf8 = NULL;
- /* Ewww. This is all because mozilla thinks that text/html is 'for internal use only.'
- * as explained by this comment in gtkhtml:
- *
- * FIXME This hack decides the charset of the selection. It seems that
- * mozilla/netscape alway use ucs2 for text/html
- * and openoffice.org seems to always use utf8 so we try to validate
- * the string as utf8 and if that fails we assume it is ucs2
- *
- * See also the comment on text/html here:
- * http://mail.gnome.org/archives/gtk-devel-list/2001-September/msg00114.html
- */
- if (length >= 2 && !g_utf8_validate(text, length - 1, NULL)) {
- utf8 = utf16_to_utf8_with_bom_check(text, length);
-
- if (!utf8) {
- purple_debug_warning("gtkimhtml", "g_convert from UTF-16 failed in drag_rcv_cb\n");
- return;
- }
- } else if (!(*text) || !g_utf8_validate(text, -1, NULL)) {
- purple_debug_warning("gtkimhtml", "empty string or invalid UTF-8 in drag_rcv_cb\n");
- return;
- }
-
- gtk_imhtml_insert_html_at_iter(imhtml, utf8 ? utf8 : text, 0, &iter);
- g_free(utf8);
- break;
- }
- case GTK_IMHTML_DRAG_TEXT:
- if (!(*text) || !g_utf8_validate(text, -1, NULL)) {
- purple_debug_warning("gtkimhtml", "empty string or invalid UTF-8 in drag_rcv_cb\n");
- return;
- } else {
- char *tmp = g_markup_escape_text(text, -1);
- gtk_imhtml_insert_html_at_iter(imhtml, tmp, 0, &iter);
- g_free(tmp);
- }
- break;
- default:
- gtk_drag_finish(dc, FALSE, FALSE, t);
- return;
- }
- gtk_drag_finish(dc, TRUE,
- gdk_drag_context_get_actions(dc) == GDK_ACTION_MOVE, t);
- } else {
- gtk_drag_finish(dc, FALSE, FALSE, t);
- }
-}
-
-static void gtk_smiley_tree_remove (GtkSmileyTree *tree,
- GtkIMHtmlSmiley *smiley)
-{
- GtkSmileyTree *t = tree;
- const gchar *x = smiley->smile;
- gint len = 0;
-
- while (*x) {
- gchar *pos;
-
- if (!t->values)
- return;
-
- pos = strchr (t->values->str, *x);
- if (pos)
- t = t->children [pos - t->values->str];
- else
- return;
-
- x++; len++;
- }
-
- if (t->image) {
- t->image = NULL;
- }
-}
-
-static gint
-gtk_smiley_tree_lookup (GtkSmileyTree *tree,
- const gchar *text)
-{
- GtkSmileyTree *t = tree;
- const gchar *x = text;
- gint len = 0;
- const gchar *amp;
- gint alen;
-
- while (*x) {
- gchar *pos;
-
- if (!t->values)
- break;
-
- if(*x == '&' && (amp = purple_markup_unescape_entity(x, &alen))) {
- gboolean matched = TRUE;
- /* Make sure all chars of the unescaped value match */
- while (*(amp + 1)) {
- pos = strchr (t->values->str, *amp);
- if (pos)
- t = t->children [GPOINTER_TO_INT(pos) - GPOINTER_TO_INT(t->values->str)];
- else {
- matched = FALSE;
- break;
- }
- amp++;
- }
- if (!matched)
- break;
-
- pos = strchr (t->values->str, *amp);
- }
- else if (*x == '<') /* Because we're all WYSIWYG now, a '<'
- * char should only appear as the start of a tag. Perhaps a safer (but costlier)
- * check would be to call gtk_imhtml_is_tag on it */
- break;
- else {
- alen = 1;
- pos = strchr (t->values->str, *x);
- }
-
- if (pos)
- t = t->children [GPOINTER_TO_INT(pos) - GPOINTER_TO_INT(t->values->str)];
- else
- break;
-
- x += alen;
- len += alen;
- }
-
- if (t->image)
- return len;
-
- return 0;
-}
-
-static void
-gtk_imhtml_disassociate_smiley_foreach(gpointer key, gpointer value,
- gpointer user_data)
-{
- GtkSmileyTree *tree = (GtkSmileyTree *) value;
- GtkIMHtmlSmiley *smiley = (GtkIMHtmlSmiley *) user_data;
- gtk_smiley_tree_remove(tree, smiley);
-}
-
-static void
-gtk_imhtml_disconnect_smiley(GtkIMHtml *imhtml, GtkIMHtmlSmiley *smiley)
-{
- smiley->imhtml = NULL;
- g_signal_handlers_disconnect_matched(imhtml, G_SIGNAL_MATCH_DATA, 0, 0,
- NULL, NULL, smiley);
-}
-
-static void
-gtk_imhtml_disassociate_smiley(GtkIMHtmlSmiley *smiley)
-{
- if (smiley->imhtml) {
- gtk_smiley_tree_remove(smiley->imhtml->default_smilies, smiley);
- g_hash_table_foreach(smiley->imhtml->smiley_data,
- gtk_imhtml_disassociate_smiley_foreach, smiley);
- g_signal_handlers_disconnect_matched(smiley->imhtml, G_SIGNAL_MATCH_DATA,
- 0, 0, NULL, NULL, smiley);
- smiley->imhtml = NULL;
- }
-}
-
-void
-gtk_imhtml_associate_smiley (GtkIMHtml *imhtml,
- const gchar *sml,
- GtkIMHtmlSmiley *smiley)
-{
- GtkSmileyTree *tree;
- g_return_if_fail (imhtml != NULL);
- g_return_if_fail (GTK_IS_IMHTML (imhtml));
-
- if (sml == NULL)
- tree = imhtml->default_smilies;
- else if (!(tree = g_hash_table_lookup(imhtml->smiley_data, sml))) {
- tree = gtk_smiley_tree_new();
- g_hash_table_insert(imhtml->smiley_data, g_strdup(sml), tree);
- }
-
- /* need to disconnect old imhtml, if there is one */
- if (smiley->imhtml) {
- g_signal_handlers_disconnect_matched(smiley->imhtml, G_SIGNAL_MATCH_DATA,
- 0, 0, NULL, NULL, smiley);
- }
-
- smiley->imhtml = imhtml;
-
- gtk_smiley_tree_insert (tree, smiley);
-
- /* connect destroy signal for the imhtml */
- g_signal_connect(imhtml, "destroy", G_CALLBACK(gtk_imhtml_disconnect_smiley),
- smiley);
-}
-
-static gboolean
-gtk_imhtml_is_smiley (GtkIMHtml *imhtml,
- GSList *fonts,
- const gchar *text,
- gint *len)
-{
- GtkSmileyTree *tree;
- GtkIMHtmlFontDetail *font;
- char *sml = NULL;
-
- if (fonts) {
- font = fonts->data;
- sml = font->sml;
- }
-
- if (!sml)
- sml = imhtml->protocol_name;
-
- if (!sml || !(tree = g_hash_table_lookup(imhtml->smiley_data, sml)))
- tree = imhtml->default_smilies;
-
- if (tree == NULL)
- return FALSE;
-
- *len = gtk_smiley_tree_lookup (tree, text);
- return (*len > 0);
-}
-
-static GtkIMHtmlSmiley *gtk_imhtml_smiley_get_from_tree(GtkSmileyTree *t, const gchar *text)
-{
- const gchar *x = text;
- gchar *pos;
-
- if (t == NULL)
- return NULL;
-
- while (*x) {
- if (!t->values)
- return NULL;
-
- pos = strchr(t->values->str, *x);
- if (!pos)
- return NULL;
-
- t = t->children[GPOINTER_TO_INT(pos) - GPOINTER_TO_INT(t->values->str)];
- x++;
- }
-
- return t->image;
-}
-
-GtkIMHtmlSmiley *
-gtk_imhtml_smiley_get(GtkIMHtml *imhtml, const gchar *sml, const gchar *text)
-{
- GtkIMHtmlSmiley *ret;
-
- /* Look for custom smileys first */
- if (sml != NULL) {
- ret = gtk_imhtml_smiley_get_from_tree(g_hash_table_lookup(imhtml->smiley_data, sml), text);
- if (ret != NULL)
- return ret;
- }
-
- /* Fall back to check for default smileys */
- return gtk_imhtml_smiley_get_from_tree(imhtml->default_smilies, text);
-}
-
-static GdkPixbufAnimation *
-gtk_smiley_get_image(GtkIMHtmlSmiley *smiley)
-{
- if (!smiley->icon) {
- if (smiley->file) {
- smiley->icon = gdk_pixbuf_animation_new_from_file(smiley->file, NULL);
- } else if (smiley->loader) {
- smiley->icon = gdk_pixbuf_loader_get_animation(smiley->loader);
- if (smiley->icon)
- g_object_ref(G_OBJECT(smiley->icon));
- }
- }
-
- return smiley->icon;
-}
-
-#define VALID_TAG(x) do { \
- if (!g_ascii_strncasecmp (string, x ">", strlen (x ">"))) { \
- if (tag) *tag = g_strndup (string, strlen (x)); \
- if (len) *len = strlen (x) + 1; \
- return TRUE; \
- } \
- if (type) (*type)++; \
- } while (0)
-
-#define VALID_OPT_TAG(x) do { \
- if (!g_ascii_strncasecmp (string, x " ", strlen (x " "))) { \
- const gchar *c = string + strlen (x " "); \
- gchar e = '"'; \
- gboolean quote = FALSE; \
- while (*c) { \
- if (*c == '"' || *c == '\'') { \
- if (quote && (*c == e)) \
- quote = !quote; \
- else if (!quote) { \
- quote = !quote; \
- e = *c; \
- } \
- } else if (!quote && (*c == '>')) \
- break; \
- c++; \
- } \
- if (*c) { \
- if (tag) *tag = g_strndup (string, c - string); \
- if (len) *len = c - string + 1; \
- return TRUE; \
- } \
- } \
- if (type) (*type)++; \
- } while (0)
-
-
-static gboolean
-gtk_imhtml_is_tag (const gchar *string,
- gchar **tag,
- gint *len,
- gint *type)
-{
- char *close;
- if (type)
- *type = 1;
-
- if (!(close = strchr (string, '>')))
- return FALSE;
-
- VALID_TAG ("B");
- VALID_TAG ("BOLD");
- VALID_TAG ("/B");
- VALID_TAG ("/BOLD");
- VALID_TAG ("I");
- VALID_TAG ("ITALIC");
- VALID_TAG ("/I");
- VALID_TAG ("/ITALIC");
- VALID_TAG ("U");
- VALID_TAG ("UNDERLINE");
- VALID_TAG ("/U");
- VALID_TAG ("/UNDERLINE");
- VALID_TAG ("S");
- VALID_TAG ("STRIKE");
- VALID_TAG ("/S");
- VALID_TAG ("/STRIKE");
- VALID_TAG ("SUB");
- VALID_TAG ("/SUB");
- VALID_TAG ("SUP");
- VALID_TAG ("/SUP");
- VALID_TAG ("PRE");
- VALID_TAG ("/PRE");
- VALID_TAG ("TITLE");
- VALID_TAG ("/TITLE");
- VALID_TAG ("BR");
- VALID_TAG ("HR");
- VALID_TAG ("/FONT");
- VALID_TAG ("/A");
- VALID_TAG ("P");
- VALID_TAG ("/P");
- VALID_TAG ("H3");
- VALID_TAG ("/H3");
- VALID_TAG ("HTML");
- VALID_TAG ("/HTML");
- VALID_TAG ("BODY");
- VALID_TAG ("/BODY");
- VALID_TAG ("FONT");
- VALID_TAG ("HEAD");
- VALID_TAG ("/HEAD");
- VALID_TAG ("BINARY");
- VALID_TAG ("/BINARY");
-
- VALID_OPT_TAG ("HR");
- VALID_OPT_TAG ("FONT");
- VALID_OPT_TAG ("BODY");
- VALID_OPT_TAG ("A");
- VALID_OPT_TAG ("IMG");
- VALID_OPT_TAG ("P");
- VALID_OPT_TAG ("H3");
- VALID_OPT_TAG ("HTML");
-
- VALID_TAG ("CITE");
- VALID_TAG ("/CITE");
- VALID_TAG ("EM");
- VALID_TAG ("/EM");
- VALID_TAG ("STRONG");
- VALID_TAG ("/STRONG");
-
- VALID_OPT_TAG ("SPAN");
- VALID_TAG ("/SPAN");
- VALID_TAG ("BR/"); /* hack until gtkimhtml handles things better */
- VALID_TAG ("IMG");
- VALID_TAG("SPAN");
- VALID_OPT_TAG("BR");
-
- if (!g_ascii_strncasecmp(string, "!--", strlen ("!--"))) {
- gchar *e = strstr (string + strlen("!--"), "-->");
- if (e) {
- if (len) {
- *len = e - string + strlen ("-->");
- if (tag)
- *tag = g_strndup (string + strlen ("!--"), *len - strlen ("!---->"));
- }
- return TRUE;
- }
- }
-
- if (type)
- *type = -1;
- if (len)
- *len = close - string + 1;
- if (tag)
- *tag = g_strndup(string, close - string);
- return TRUE;
-}
-
-static gchar*
-gtk_imhtml_get_html_opt (gchar *tag,
- const gchar *opt)
-{
- gchar *t = tag;
- gchar *e, *a;
- gchar *val;
- gint len;
- const gchar *c;
- GString *ret;
-
- while (g_ascii_strncasecmp (t, opt, strlen (opt))) {
- gboolean quote = FALSE;
- if (*t == '\0') break;
- while (*t && !((*t == ' ') && !quote)) {
- if (*t == '\"')
- quote = ! quote;
- t++;
- }
- while (*t && (*t == ' ')) t++;
- }
-
- if (!g_ascii_strncasecmp (t, opt, strlen (opt))) {
- t += strlen (opt);
- } else {
- return NULL;
- }
-
- if ((*t == '\"') || (*t == '\'')) {
- e = a = ++t;
- while (*e && (*e != *(t - 1))) e++;
- if (*e == '\0') {
- return NULL;
- } else
- val = g_strndup(a, e - a);
- } else {
- e = a = t;
- while (*e && !isspace ((gint) *e)) e++;
- val = g_strndup(a, e - a);
- }
-
- ret = g_string_new("");
- e = val;
- while(*e) {
- if((c = purple_markup_unescape_entity(e, &len))) {
- ret = g_string_append(ret, c);
- e += len;
- } else {
- gunichar uni = g_utf8_get_char(e);
- ret = g_string_append_unichar(ret, uni);
- e = g_utf8_next_char(e);
- }
- }
-
- g_free(val);
-
- return g_string_free(ret, FALSE);
-}
-
-/* returns if the beginning of the text is a protocol. If it is the protocol, returns the length so
- the caller knows how long the protocol string is. */
-static int gtk_imhtml_is_protocol(const char *text)
-{
- GtkIMHtmlProtocol *proto = imhtml_find_protocol(text, FALSE);
- return proto ? proto->length : 0;
-}
-
-static gboolean smooth_scroll_cb(gpointer data);
-
-/*
- <KingAnt> marv: The two IM image functions in oscar are purple_odc_send_im and purple_odc_incoming
-
-
-[19:58] <Robot101> marv: images go into the imgstore, a refcounted... well.. hash. :)
-[19:59] <KingAnt> marv: I think the image tag used by the core is something like <img id="#"/>
-[19:59] Ro0tSiEgE robert42 RobFlynn Robot101 ross22 roz
-[20:00] <KingAnt> marv: Where the ID is the what is returned when you add the image to the imgstore using purple_imgstore_new
-[20:00] <marv> Robot101: so how does the image get passed to serv_got_im() and serv_send_im()? just as the <img id="#" and then the prpl looks it up from the store?
-[20:00] <KingAnt> marv: Right
-[20:00] <marv> alright
-
-Here's my plan with IMImages. make gtk_imhtml_[append|insert]_text_with_images instead just
-gtkimhtml_[append|insert]_text (hrm maybe it should be called html instead of text), add a
-function for purple to register for look up images, i.e. gtk_imhtml_set_get_img_fnc, so that
-images can be looked up like that, instead of passing a GSList of them.
- */
-
-void gtk_imhtml_append_text_with_images (GtkIMHtml *imhtml,
- const gchar *text,
- GtkIMHtmlOptions options,
- GSList *unused)
-{
- GtkTextIter iter, ins, sel;
- int ins_offset = 0, sel_offset = 0;
- gboolean fixins = FALSE, fixsel = FALSE;
-
- g_return_if_fail (imhtml != NULL);
- g_return_if_fail (GTK_IS_IMHTML (imhtml));
- g_return_if_fail (text != NULL);
-
-
- gtk_text_buffer_get_end_iter(imhtml->text_buffer, &iter);
- gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &ins, gtk_text_buffer_get_insert(imhtml->text_buffer));
- if (gtk_text_iter_equal(&iter, &ins) && gtk_text_buffer_get_selection_bounds(imhtml->text_buffer, NULL, NULL)) {
- fixins = TRUE;
- ins_offset = gtk_text_iter_get_offset(&ins);
- }
-
- gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &sel, gtk_text_buffer_get_selection_bound(imhtml->text_buffer));
- if (gtk_text_iter_equal(&iter, &sel) && gtk_text_buffer_get_selection_bounds(imhtml->text_buffer, NULL, NULL)) {
- fixsel = TRUE;
- sel_offset = gtk_text_iter_get_offset(&sel);
- }
-
- if (!(options & GTK_IMHTML_NO_SCROLL)) {
- GdkRectangle rect;
- int y, height;
-
- gtk_text_view_get_visible_rect(GTK_TEXT_VIEW(imhtml), &rect);
- gtk_text_view_get_line_yrange(GTK_TEXT_VIEW(imhtml), &iter, &y, &height);
-
- if (((y + height) - (rect.y + rect.height)) > height &&
- gtk_text_buffer_get_char_count(imhtml->text_buffer)) {
- /* If we are in the middle of smooth-scrolling, then take a scroll step.
- * If we are not in the middle of smooth-scrolling, that means we were
- * not looking at the end of the buffer before the new text was added,
- * so do not scroll. */
- if (imhtml->scroll_time)
- smooth_scroll_cb(imhtml);
- else
- options |= GTK_IMHTML_NO_SCROLL;
- }
- }
-
- gtk_imhtml_insert_html_at_iter(imhtml, text, options, &iter);
-
- if (fixins) {
- gtk_text_buffer_get_iter_at_offset(imhtml->text_buffer, &ins, ins_offset);
- gtk_text_buffer_move_mark(imhtml->text_buffer, gtk_text_buffer_get_insert(imhtml->text_buffer), &ins);
- }
-
- if (fixsel) {
- gtk_text_buffer_get_iter_at_offset(imhtml->text_buffer, &sel, sel_offset);
- gtk_text_buffer_move_mark(imhtml->text_buffer, gtk_text_buffer_get_selection_bound(imhtml->text_buffer), &sel);
- }
-
- if (!(options & GTK_IMHTML_NO_SCROLL)) {
- gtk_imhtml_scroll_to_end(imhtml, (options & GTK_IMHTML_USE_SMOOTHSCROLLING));
- }
-}
-
-#define MAX_SCROLL_TIME 0.4 /* seconds */
-#define SCROLL_DELAY 33 /* milliseconds */
-
-/*
- * Smoothly scroll a GtkIMHtml.
- *
- * @return TRUE if the window needs to be scrolled further, FALSE if we're at the bottom.
- */
-static gboolean smooth_scroll_cb(gpointer data)
-{
- GtkIMHtml *imhtml = data;
- GtkAdjustment *adj = gtk_text_view_get_vadjustment(GTK_TEXT_VIEW(imhtml));
- gdouble max_val = gtk_adjustment_get_upper(adj) - gtk_adjustment_get_page_size(adj);
- gdouble scroll_val = gtk_adjustment_get_value(adj) + ((max_val - gtk_adjustment_get_value(adj)) / 3);
-
- g_return_val_if_fail(imhtml->scroll_time != NULL, FALSE);
-
- if (g_timer_elapsed(imhtml->scroll_time, NULL) > MAX_SCROLL_TIME || scroll_val >= max_val) {
- /* time's up. jump to the end and kill the timer */
- gtk_adjustment_set_value(adj, max_val);
- g_timer_destroy(imhtml->scroll_time);
- imhtml->scroll_time = NULL;
- g_source_remove(imhtml->scroll_src);
- imhtml->scroll_src = 0;
- return FALSE;
- }
-
- /* scroll by 1/3rd the remaining distance */
- gtk_adjustment_set_value(adj, scroll_val);
- return TRUE;
-}
-
-static gboolean scroll_idle_cb(gpointer data)
-{
- GtkIMHtml *imhtml = data;
- GtkAdjustment *adj = gtk_text_view_get_vadjustment(GTK_TEXT_VIEW(imhtml));
- if (adj) {
- gtk_adjustment_set_value(adj, gtk_adjustment_get_upper(adj) -
- gtk_adjustment_get_page_size(adj));
- }
- imhtml->scroll_src = 0;
- return FALSE;
-}
-
-void gtk_imhtml_scroll_to_end(GtkIMHtml *imhtml, gboolean smooth)
-{
- if (imhtml->scroll_time)
- g_timer_destroy(imhtml->scroll_time);
- if (imhtml->scroll_src)
- g_source_remove(imhtml->scroll_src);
- if(smooth) {
- imhtml->scroll_time = g_timer_new();
- imhtml->scroll_src = g_timeout_add_full(G_PRIORITY_LOW, SCROLL_DELAY, smooth_scroll_cb, imhtml, NULL);
- } else {
- imhtml->scroll_time = NULL;
- imhtml->scroll_src = g_idle_add_full(G_PRIORITY_LOW, scroll_idle_cb, imhtml, NULL);
- }
-}
-
-/* CSS colors are either rgb (x,y,z) or #hex
- * we need to convert to hex if it is RGB */
-static gchar*
-parse_css_color(gchar *in_color)
-{
- char *tmp = in_color;
-
- if (*tmp == 'r' && *(++tmp) == 'g' && *(++tmp) == 'b' && *(++tmp)) {
- int rgbval[] = {0, 0, 0};
- int count = 0;
- const char *v_start;
-
- while (*tmp && g_ascii_isspace(*tmp))
- tmp++;
- if (*tmp != '(') {
- /* We don't support rgba() */
- purple_debug_warning("gtkimhtml", "Invalid rgb CSS color in '%s'!\n", in_color);
- return in_color;
- }
- tmp++;
-
- while (count < 3) {
- /* Skip any leading spaces */
- while (*tmp && g_ascii_isspace(*tmp))
- tmp++;
-
- /* Find the subsequent contiguous digits */
- v_start = tmp;
- if (*v_start == '-')
- tmp++;
- while (*tmp && g_ascii_isdigit(*tmp))
- tmp++;
-
- if (tmp != v_start) {
- char prev = *tmp;
- *tmp = '\0';
- rgbval[count] = atoi(v_start);
- *tmp = prev;
-
- /* deal with % */
- while (*tmp && g_ascii_isspace(*tmp))
- tmp++;
- if (*tmp == '%') {
- rgbval[count] = (rgbval[count] / 100.0) * 255;
- tmp++;
- }
- } else {
- purple_debug_warning("gtkimhtml", "Invalid rgb CSS color in '%s'!\n", in_color);
- return in_color;
- }
-
- if (rgbval[count] > 255) {
- rgbval[count] = 255;
- } else if (rgbval[count] < 0) {
- rgbval[count] = 0;
- }
-
- while (*tmp && g_ascii_isspace(*tmp))
- tmp++;
- if (*tmp == ',')
- tmp++;
-
- count++;
- }
-
- g_free(in_color);
- return g_strdup_printf("#%02X%02X%02X", rgbval[0], rgbval[1], rgbval[2]);
- }
-
- return in_color;
-}
-
-void gtk_imhtml_insert_html_at_iter(GtkIMHtml *imhtml,
- const gchar *text,
- GtkIMHtmlOptions options,
- GtkTextIter *iter)
-{
- GdkRectangle rect;
- gint pos = 0;
- gchar *ws;
- gchar *tag;
- gchar *bg = NULL;
- gint len;
- gint tlen, smilelen, wpos=0;
- gint type;
- const gchar *c;
- const gchar *amp;
- gint len_protocol;
-
- guint bold = 0,
- italics = 0,
- underline = 0,
- strike = 0,
- sub = 0,
- sup = 0,
- title = 0,
- pre = 0;
-
- gboolean br = FALSE;
- gboolean align_right = FALSE;
- gboolean rtl_direction = FALSE;
- gint align_line = 0;
-
- GSList *fonts = NULL;
- GObject *object;
- GtkIMHtmlScalable *scalable = NULL;
-
- g_return_if_fail (imhtml != NULL);
- g_return_if_fail (GTK_IS_IMHTML (imhtml));
- g_return_if_fail (text != NULL);
- c = text;
- len = strlen(text);
- ws = g_malloc(len + 1);
- ws[0] = '\0';
-
- g_object_set_data(G_OBJECT(imhtml), "gtkimhtml_numsmileys_thismsg", GINT_TO_POINTER(0));
-
- gtk_text_buffer_begin_user_action(imhtml->text_buffer);
- while (pos < len) {
- if (*c == '<' && gtk_imhtml_is_tag (c + 1, &tag, &tlen, &type)) {
- c++;
- pos++;
- ws[wpos] = '\0';
- br = FALSE;
- switch (type)
- {
- case 1: /* B */
- case 2: /* BOLD */
- case 54: /* STRONG */
- if (!(options & GTK_IMHTML_NO_FORMATTING)) {
- gtk_text_buffer_insert(imhtml->text_buffer, iter, ws, wpos);
-
- if ((bold == 0) && (imhtml->format_functions & GTK_IMHTML_BOLD))
- gtk_imhtml_toggle_bold(imhtml);
- bold++;
- ws[0] = '\0'; wpos = 0;
- }
- break;
- case 3: /* /B */
- case 4: /* /BOLD */
- case 55: /* /STRONG */
- if (!(options & GTK_IMHTML_NO_FORMATTING)) {
- gtk_text_buffer_insert(imhtml->text_buffer, iter, ws, wpos);
- ws[0] = '\0'; wpos = 0;
-
- if (bold) {
- bold--;
- if ((bold == 0) && (imhtml->format_functions & GTK_IMHTML_BOLD) && !imhtml->wbfo)
- gtk_imhtml_toggle_bold(imhtml);
- }
- }
- break;
- case 5: /* I */
- case 6: /* ITALIC */
- case 52: /* EM */
- if (!(options & GTK_IMHTML_NO_FORMATTING)) {
- gtk_text_buffer_insert(imhtml->text_buffer, iter, ws, wpos);
- ws[0] = '\0'; wpos = 0;
- if ((italics == 0) && (imhtml->format_functions & GTK_IMHTML_ITALIC))
- gtk_imhtml_toggle_italic(imhtml);
- italics++;
- }
- break;
- case 7: /* /I */
- case 8: /* /ITALIC */
- case 53: /* /EM */
- if (!(options & GTK_IMHTML_NO_FORMATTING)) {
- gtk_text_buffer_insert(imhtml->text_buffer, iter, ws, wpos);
- ws[0] = '\0'; wpos = 0;
- if (italics) {
- italics--;
- if ((italics == 0) && (imhtml->format_functions & GTK_IMHTML_ITALIC) && !imhtml->wbfo)
- gtk_imhtml_toggle_italic(imhtml);
- }
- }
- break;
- case 9: /* U */
- case 10: /* UNDERLINE */
- if (!(options & GTK_IMHTML_NO_FORMATTING)) {
- gtk_text_buffer_insert(imhtml->text_buffer, iter, ws, wpos);
- ws[0] = '\0'; wpos = 0;
- if ((underline == 0) && (imhtml->format_functions & GTK_IMHTML_UNDERLINE))
- gtk_imhtml_toggle_underline(imhtml);
- underline++;
- }
- break;
- case 11: /* /U */
- case 12: /* /UNDERLINE */
- if (!(options & GTK_IMHTML_NO_FORMATTING)) {
- gtk_text_buffer_insert(imhtml->text_buffer, iter, ws, wpos);
- ws[0] = '\0'; wpos = 0;
- if (underline) {
- underline--;
- if ((underline == 0) && (imhtml->format_functions & GTK_IMHTML_UNDERLINE) && !imhtml->wbfo)
- gtk_imhtml_toggle_underline(imhtml);
- }
- }
- break;
- case 13: /* S */
- case 14: /* STRIKE */
- gtk_text_buffer_insert(imhtml->text_buffer, iter, ws, wpos);
- ws[0] = '\0'; wpos = 0;
- if ((strike == 0) && (imhtml->format_functions & GTK_IMHTML_STRIKE))
- gtk_imhtml_toggle_strike(imhtml);
- strike++;
- break;
- case 15: /* /S */
- case 16: /* /STRIKE */
- gtk_text_buffer_insert(imhtml->text_buffer, iter, ws, wpos);
- ws[0] = '\0'; wpos = 0;
- if (strike)
- strike--;
- if ((strike == 0) && (imhtml->format_functions & GTK_IMHTML_STRIKE) && !imhtml->wbfo)
- gtk_imhtml_toggle_strike(imhtml);
- break;
- case 17: /* SUB */
- /* FIXME: reimpliment this */
- sub++;
- break;
- case 18: /* /SUB */
- /* FIXME: reimpliment this */
- if (sub)
- sub--;
- break;
- case 19: /* SUP */
- /* FIXME: reimplement this */
- sup++;
- break;
- case 20: /* /SUP */
- /* FIXME: reimplement this */
- if (sup)
- sup--;
- break;
- case 21: /* PRE */
- /* FIXME: reimplement this */
- pre++;
- break;
- case 22: /* /PRE */
- /* FIXME: reimplement this */
- if (pre)
- pre--;
- break;
- case 23: /* TITLE */
- /* FIXME: what was this supposed to do anyway? */
- title++;
- break;
- case 24: /* /TITLE */
- /* FIXME: make this undo whatever 23 was supposed to do */
- if (title) {
- if (options & GTK_IMHTML_NO_TITLE) {
- wpos = 0;
- ws [wpos] = '\0';
- }
- title--;
- }
- break;
- case 25: /* BR */
- case 58: /* BR/ */
- case 61: /* BR (opt) */
- ws[wpos] = '\n';
- wpos++;
- br = TRUE;
- break;
- case 26: /* HR */
- case 42: /* HR (opt) */
- {
- int minus;
- struct scalable_data *sd = g_new(struct scalable_data, 1);
-
- ws[wpos++] = '\n';
- gtk_text_buffer_insert(imhtml->text_buffer, iter, ws, wpos);
-
- sd->scalable = scalable = gtk_imhtml_hr_new();
- sd->mark = gtk_text_buffer_create_mark(imhtml->text_buffer, NULL, iter, TRUE);
- gtk_text_view_get_visible_rect(GTK_TEXT_VIEW(imhtml), &rect);
- scalable->add_to(scalable, imhtml, iter);
- minus = gtk_text_view_get_left_margin(GTK_TEXT_VIEW(imhtml)) +
- gtk_text_view_get_right_margin(GTK_TEXT_VIEW(imhtml));
- scalable->scale(scalable, rect.width - minus, rect.height);
- imhtml->scalables = g_list_append(imhtml->scalables, sd);
- ws[0] = '\0'; wpos = 0;
- ws[wpos++] = '\n';
-
- break;
- }
- case 27: /* /FONT */
- if (fonts && !imhtml->wbfo) {
- GtkIMHtmlFontDetail *font = fonts->data;
- gtk_text_buffer_insert(imhtml->text_buffer, iter, ws, wpos);
- ws[0] = '\0'; wpos = 0;
- /* NEW_BIT (NEW_TEXT_BIT); */
-
- if (font->face && (imhtml->format_functions & GTK_IMHTML_FACE)) {
- gtk_imhtml_toggle_fontface(imhtml, NULL);
- }
- g_free (font->face);
- if (font->fore && (imhtml->format_functions & GTK_IMHTML_FORECOLOR)) {
- gtk_imhtml_toggle_forecolor(imhtml, NULL);
- }
- g_free (font->fore);
- if (font->back && (imhtml->format_functions & GTK_IMHTML_BACKCOLOR)) {
- gtk_imhtml_toggle_backcolor(imhtml, NULL);
- }
- g_free (font->back);
- g_free (font->sml);
-
- if ((font->size != 3) && (imhtml->format_functions & (GTK_IMHTML_GROW|GTK_IMHTML_SHRINK)))
- gtk_imhtml_font_set_size(imhtml, 3);
-
- fonts = g_slist_remove (fonts, font);
- g_free(font);
-
- if (fonts) {
- GtkIMHtmlFontDetail *font = fonts->data;
-
- if (font->face && (imhtml->format_functions & GTK_IMHTML_FACE))
- gtk_imhtml_toggle_fontface(imhtml, font->face);
- if (font->fore && (imhtml->format_functions & GTK_IMHTML_FORECOLOR))
- gtk_imhtml_toggle_forecolor(imhtml, font->fore);
- if (font->back && (imhtml->format_functions & GTK_IMHTML_BACKCOLOR))
- gtk_imhtml_toggle_backcolor(imhtml, font->back);
- if ((font->size != 3) && (imhtml->format_functions & (GTK_IMHTML_GROW|GTK_IMHTML_SHRINK)))
- gtk_imhtml_font_set_size(imhtml, font->size);
- }
- }
- break;
- case 28: /* /A */
- gtk_text_buffer_insert(imhtml->text_buffer, iter, ws, wpos);
- gtk_imhtml_toggle_link(imhtml, NULL);
- ws[0] = '\0'; wpos = 0;
- break;
-
- case 29: /* P */
- case 30: /* /P */
- case 31: /* H3 */
- case 32: /* /H3 */
- case 33: /* HTML */
- case 34: /* /HTML */
- case 35: /* BODY */
- break;
- case 36: /* /BODY */
- gtk_text_buffer_insert(imhtml->text_buffer, iter, ws, wpos);
- ws[0] = '\0'; wpos = 0;
- gtk_imhtml_toggle_background(imhtml, NULL);
- break;
- case 37: /* FONT */
- case 38: /* HEAD */
- case 39: /* /HEAD */
- case 40: /* BINARY */
- case 41: /* /BINARY */
- break;
- case 43: /* FONT (opt) */
- {
- gchar *color, *back, *face, *size, *sml;
- GtkIMHtmlFontDetail *font, *oldfont = NULL;
- color = gtk_imhtml_get_html_opt (tag, "COLOR=");
- back = gtk_imhtml_get_html_opt (tag, "BACK=");
- face = gtk_imhtml_get_html_opt (tag, "FACE=");
- size = gtk_imhtml_get_html_opt (tag, "SIZE=");
- sml = gtk_imhtml_get_html_opt (tag, "SML=");
- if (!(color || back || face || size || sml))
- break;
-
- gtk_text_buffer_insert(imhtml->text_buffer, iter, ws, wpos);
- ws[0] = '\0'; wpos = 0;
-
- font = g_new0 (GtkIMHtmlFontDetail, 1);
- if (fonts)
- oldfont = fonts->data;
-
- if (color && !(options & GTK_IMHTML_NO_COLOURS) && (imhtml->format_functions & GTK_IMHTML_FORECOLOR)) {
- font->fore = color;
- gtk_imhtml_toggle_forecolor(imhtml, font->fore);
- } else
- g_free(color);
-
- if (back && !(options & GTK_IMHTML_NO_COLOURS) && (imhtml->format_functions & GTK_IMHTML_BACKCOLOR)) {
- font->back = back;
- gtk_imhtml_toggle_backcolor(imhtml, font->back);
- } else
- g_free(back);
-
- if (face && !(options & GTK_IMHTML_NO_FONTS) && (imhtml->format_functions & GTK_IMHTML_FACE)) {
- font->face = face;
- gtk_imhtml_toggle_fontface(imhtml, font->face);
- } else
- g_free(face);
-
- if (sml)
- font->sml = sml;
- else {
- g_free(sml);
- if (oldfont && oldfont->sml)
- font->sml = g_strdup(oldfont->sml);
- }
-
- if (size && !(options & GTK_IMHTML_NO_SIZES) && (imhtml->format_functions & (GTK_IMHTML_GROW|GTK_IMHTML_SHRINK))) {
- if (*size == '+') {
- sscanf (size + 1, "%hd", &font->size);
- font->size += 3;
- } else if (*size == '-') {
- sscanf (size + 1, "%hd", &font->size);
- font->size = MAX (0, 3 - font->size);
- } else if (isdigit (*size)) {
- sscanf (size, "%hd", &font->size);
- }
- if (font->size > 100)
- font->size = 100;
- } else if (oldfont)
- font->size = oldfont->size;
- else
- font->size = 3;
- if ((imhtml->format_functions & (GTK_IMHTML_GROW|GTK_IMHTML_SHRINK)) && (font->size != 3 || (oldfont && oldfont->size == 3)))
- gtk_imhtml_font_set_size(imhtml, font->size);
- g_free(size);
- fonts = g_slist_prepend (fonts, font);
- }
- break;
- case 44: /* BODY (opt) */
- if (!(options & GTK_IMHTML_NO_COLOURS)) {
- char *bgcolor = gtk_imhtml_get_html_opt (tag, "BGCOLOR=");
- if (bgcolor && (imhtml->format_functions & GTK_IMHTML_BACKCOLOR)) {
- gtk_text_buffer_insert(imhtml->text_buffer, iter, ws, wpos);
- ws[0] = '\0'; wpos = 0;
- /* NEW_BIT(NEW_TEXT_BIT); */
- g_free(bg);
- bg = bgcolor;
- gtk_imhtml_toggle_background(imhtml, bg);
- } else
- g_free(bgcolor);
- }
- break;
- case 45: /* A (opt) */
- {
- gchar *href = gtk_imhtml_get_html_opt (tag, "HREF=");
- if (href && (imhtml->format_functions & GTK_IMHTML_LINK)) {
- gtk_text_buffer_insert(imhtml->text_buffer, iter, ws, wpos);
- ws[0] = '\0'; wpos = 0;
- gtk_imhtml_toggle_link(imhtml, href);
- }
- g_free(href);
- }
- break;
- case 46: /* IMG (opt) */
- case 59: /* IMG */
- {
- char *id;
-
- gtk_text_buffer_insert(imhtml->text_buffer, iter, ws, wpos);
- ws[0] = '\0'; wpos = 0;
-
- if (!(imhtml->format_functions & GTK_IMHTML_IMAGE))
- break;
-
- id = gtk_imhtml_get_html_opt(tag, "ID=");
- if (id) {
- gtk_imhtml_insert_image_at_iter(imhtml, atoi(id), iter);
- g_free(id);
- } else {
- char *src, *alt;
- src = gtk_imhtml_get_html_opt(tag, "SRC=");
- alt = gtk_imhtml_get_html_opt(tag, "ALT=");
- if (src) {
- gtk_imhtml_toggle_link(imhtml, src);
- gtk_text_buffer_insert(imhtml->text_buffer, iter, alt ? alt : src, -1);
- gtk_imhtml_toggle_link(imhtml, NULL);
- }
- g_free (src);
- g_free (alt);
- }
- break;
- }
- case 47: /* P (opt) */
- case 48: /* H3 (opt) */
- case 49: /* HTML (opt) */
- case 50: /* CITE */
- case 51: /* /CITE */
- case 56: /* SPAN (opt) */
- /* Inline CSS Support - Douglas Thrift
- *
- * color
- * background
- * font-family
- * font-size
- * text-decoration: underline
- * font-weight: bold
- * direction: rtl
- * text-align: right
- *
- * TODO:
- * background-color
- * font-style
- */
- {
- gchar *style, *color, *background, *family, *size, *direction, *alignment;
- gchar *textdec, *weight;
- GtkIMHtmlFontDetail *font, *oldfont = NULL;
- style = gtk_imhtml_get_html_opt (tag, "style=");
-
- if (!style) break;
-
- color = purple_markup_get_css_property (style, "color");
- background = purple_markup_get_css_property (style, "background");
- family = purple_markup_get_css_property (style, "font-family");
- size = purple_markup_get_css_property (style, "font-size");
- textdec = purple_markup_get_css_property (style, "text-decoration");
- weight = purple_markup_get_css_property (style, "font-weight");
- direction = purple_markup_get_css_property (style, "direction");
- alignment = purple_markup_get_css_property (style, "text-align");
-
-
- if (!(color || family || size || background || textdec || weight || direction || alignment)) {
- g_free(style);
- break;
- }
-
-
- gtk_text_buffer_insert(imhtml->text_buffer, iter, ws, wpos);
- ws[0] = '\0'; wpos = 0;
- /* NEW_BIT (NEW_TEXT_BIT); */
-
- /* Bi-Directional text support */
- if (direction && (!g_ascii_strncasecmp(direction, "RTL", 3))) {
- rtl_direction = TRUE;
- /* insert RLE character to set direction */
- ws[wpos++] = 0xE2;
- ws[wpos++] = 0x80;
- ws[wpos++] = 0xAB;
- ws[wpos] = '\0';
- gtk_text_buffer_insert(imhtml->text_buffer, iter, ws, wpos);
- ws[0] = '\0'; wpos = 0;
- }
- g_free(direction);
-
- if (alignment && (!g_ascii_strncasecmp(alignment, "RIGHT", 5))) {
- align_right = TRUE;
- align_line = gtk_text_iter_get_line(iter);
- }
- g_free(alignment);
-
- font = g_new0 (GtkIMHtmlFontDetail, 1);
- if (fonts)
- oldfont = fonts->data;
-
- if (color && !(options & GTK_IMHTML_NO_COLOURS) && (imhtml->format_functions & GTK_IMHTML_FORECOLOR)) {
- font->fore = parse_css_color(color);
- gtk_imhtml_toggle_forecolor(imhtml, font->fore);
- } else {
- if (oldfont && oldfont->fore)
- font->fore = g_strdup(oldfont->fore);
- g_free(color);
- }
-
- if (background && !(options & GTK_IMHTML_NO_COLOURS) && (imhtml->format_functions & GTK_IMHTML_BACKCOLOR)) {
- font->back = parse_css_color(background);
- gtk_imhtml_toggle_backcolor(imhtml, font->back);
- } else {
- if (oldfont && oldfont->back)
- font->back = g_strdup(oldfont->back);
- g_free(background);
- }
-
- if (family && !(options & GTK_IMHTML_NO_FONTS) && (imhtml->format_functions & GTK_IMHTML_FACE)) {
- font->face = family;
- gtk_imhtml_toggle_fontface(imhtml, font->face);
- } else {
- if (oldfont && oldfont->face)
- font->face = g_strdup(oldfont->face);
- g_free(family);
- }
- if (font->face && (atoi(font->face) > 100)) {
- /* WTF is this? */
- /* Maybe it sets a max size on the font face? I seem to
- * remember bad things happening if the font size was
- * 2 billion */
- g_free(font->face);
- font->face = g_strdup("100");
- }
-
- if (oldfont && oldfont->sml)
- font->sml = g_strdup(oldfont->sml);
-
- if (size && !(options & GTK_IMHTML_NO_SIZES) && (imhtml->format_functions & (GTK_IMHTML_SHRINK|GTK_IMHTML_GROW))) {
- if (g_ascii_strcasecmp(size, "xx-small") == 0)
- font->size = 1;
- else if (g_ascii_strcasecmp(size, "smaller") == 0
- || g_ascii_strcasecmp(size, "x-small") == 0)
- font->size = 2;
- else if (g_ascii_strcasecmp(size, "medium") == 0)
- font->size = 3;
- else if (g_ascii_strcasecmp(size, "large") == 0
- || g_ascii_strcasecmp(size, "larger") == 0)
- font->size = 4;
- else if (g_ascii_strcasecmp(size, "x-large") == 0)
- font->size = 5;
- else if (g_ascii_strcasecmp(size, "xx-large") == 0)
- font->size = 6;
-
- /*
- * TODO: Handle other values, like percentages, or
- * lengths specified as em, ex, px, in, cm, mm, pt
- * or pc. Or even better, use an actual HTML
- * renderer like webkit.
- */
- if (font->size > 0)
- gtk_imhtml_font_set_size(imhtml, font->size);
- }
- else if (oldfont)
- {
- font->size = oldfont->size;
- }
-
- if (oldfont)
- {
- font->underline = oldfont->underline;
- }
- if (textdec && font->underline != 1
- && g_ascii_strcasecmp(textdec, "underline") == 0
- && (imhtml->format_functions & GTK_IMHTML_UNDERLINE)
- && !(options & GTK_IMHTML_NO_FORMATTING))
- {
- gtk_imhtml_toggle_underline(imhtml);
- font->underline = 1;
- }
-
- if (oldfont)
- {
- font->strike = oldfont->strike;
- }
- if (textdec && font->strike != 1
- && g_ascii_strcasecmp(textdec, "line-through") == 0
- && (imhtml->format_functions & GTK_IMHTML_STRIKE)
- && !(options & GTK_IMHTML_NO_FORMATTING))
- {
- gtk_imhtml_toggle_strike(imhtml);
- font->strike = 1;
- }
- g_free(textdec);
-
- if (oldfont)
- {
- font->bold = oldfont->bold;
- }
- if (weight)
- {
- if(!g_ascii_strcasecmp(weight, "normal")) {
- font->bold = 0;
- } else if(!g_ascii_strcasecmp(weight, "bold")) {
- font->bold = 1;
- } else if(!g_ascii_strcasecmp(weight, "bolder")) {
- font->bold++;
- } else if(!g_ascii_strcasecmp(weight, "lighter")) {
- if(font->bold > 0)
- font->bold--;
- } else {
- int num = atoi(weight);
- if(num >= 700)
- font->bold = 1;
- else
- font->bold = 0;
- }
- if (((font->bold && oldfont && !oldfont->bold) || (oldfont && oldfont->bold && !font->bold) || (font->bold && !oldfont)) && !(options & GTK_IMHTML_NO_FORMATTING))
- {
- gtk_imhtml_toggle_bold(imhtml);
- }
- g_free(weight);
- }
-
- g_free(style);
- g_free(size);
- fonts = g_slist_prepend (fonts, font);
- }
- break;
- case 57: /* /SPAN */
- /* Inline CSS Support - Douglas Thrift */
- if (fonts && !imhtml->wbfo) {
- GtkIMHtmlFontDetail *oldfont = NULL;
- GtkIMHtmlFontDetail *font = fonts->data;
- gtk_text_buffer_insert(imhtml->text_buffer, iter, ws, wpos);
- ws[0] = '\0'; wpos = 0;
- /* NEW_BIT (NEW_TEXT_BIT); */
- fonts = g_slist_remove (fonts, font);
- if (fonts)
- oldfont = fonts->data;
-
- if (!oldfont) {
- gtk_imhtml_font_set_size(imhtml, 3);
- if (font->underline && !(options & GTK_IMHTML_NO_FORMATTING))
- gtk_imhtml_toggle_underline(imhtml);
- if (font->strike && !(options & GTK_IMHTML_NO_FORMATTING))
- gtk_imhtml_toggle_strike(imhtml);
- if (font->bold && !(options & GTK_IMHTML_NO_FORMATTING))
- gtk_imhtml_toggle_bold(imhtml);
- if (!(options & GTK_IMHTML_NO_FONTS))
- gtk_imhtml_toggle_fontface(imhtml, NULL);
- if (!(options & GTK_IMHTML_NO_COLOURS))
- gtk_imhtml_toggle_forecolor(imhtml, NULL);
- if (!(options & GTK_IMHTML_NO_COLOURS))
- gtk_imhtml_toggle_backcolor(imhtml, NULL);
- }
- else
- {
-
- if ((font->size != oldfont->size) && !(options & GTK_IMHTML_NO_SIZES))
- gtk_imhtml_font_set_size(imhtml, oldfont->size);
-
- if ((font->underline != oldfont->underline) && !(options & GTK_IMHTML_NO_FORMATTING))
- gtk_imhtml_toggle_underline(imhtml);
-
- if ((font->strike != oldfont->strike) && !(options & GTK_IMHTML_NO_FORMATTING))
- gtk_imhtml_toggle_strike(imhtml);
-
- if (((font->bold && !oldfont->bold) || (oldfont->bold && !font->bold)) && !(options & GTK_IMHTML_NO_FORMATTING))
- gtk_imhtml_toggle_bold(imhtml);
-
- if (font->face && (!oldfont->face || strcmp(font->face, oldfont->face) != 0) && !(options & GTK_IMHTML_NO_FONTS))
- gtk_imhtml_toggle_fontface(imhtml, oldfont->face);
-
- if (font->fore && (!oldfont->fore || strcmp(font->fore, oldfont->fore) != 0) && !(options & GTK_IMHTML_NO_COLOURS))
- gtk_imhtml_toggle_forecolor(imhtml, oldfont->fore);
-
- if (font->back && (!oldfont->back || strcmp(font->back, oldfont->back) != 0) && !(options & GTK_IMHTML_NO_COLOURS))
- gtk_imhtml_toggle_backcolor(imhtml, oldfont->back);
- }
-
- g_free (font->face);
- g_free (font->fore);
- g_free (font->back);
- g_free (font->sml);
-
- g_free (font);
- }
- break;
- case 60: /* SPAN */
- break;
- case 62: /* comment */
- /* NEW_BIT (NEW_TEXT_BIT); */
- ws[wpos] = '\0';
-
- gtk_text_buffer_insert(imhtml->text_buffer, iter, ws, wpos);
-
-#if FALSE
- wpos = g_snprintf (ws, len, "%s", tag);
- gtk_text_buffer_insert_with_tags_by_name(imhtml->text_buffer, iter, ws, wpos, "comment", NULL);
-#else
- if (imhtml->show_comments && !(options & GTK_IMHTML_NO_COMMENTS)) {
- wpos = g_snprintf (ws, len, "%s", tag);
- gtk_text_buffer_insert_with_tags_by_name(imhtml->text_buffer, iter, ws, wpos, "comment", NULL);
- }
-#endif
- ws[0] = '\0'; wpos = 0;
-
- /* NEW_BIT (NEW_COMMENT_BIT); */
- break;
- default:
- break;
- }
- c += tlen;
- pos += tlen;
- g_free(tag); /* This was allocated back in VALID_TAG() */
- } else if (imhtml->edit.link == NULL &&
- !(options & GTK_IMHTML_NO_SMILEY) &&
- gtk_imhtml_is_smiley(imhtml, fonts, c, &smilelen)) {
- GtkIMHtmlFontDetail *fd;
- gchar *sml = NULL;
-
- br = FALSE;
-
- if (fonts) {
- fd = fonts->data;
- sml = fd->sml;
- }
- if (!sml)
- sml = imhtml->protocol_name;
-
- gtk_text_buffer_insert(imhtml->text_buffer, iter, ws, wpos);
- wpos = g_snprintf (ws, smilelen + 1, "%s", c);
-
- gtk_imhtml_insert_smiley_at_iter(imhtml, sml, ws, iter);
-
- c += smilelen;
- pos += smilelen;
- wpos = 0;
- ws[0] = 0;
- } else if (*c == '&' && (amp = purple_markup_unescape_entity(c, &tlen))) {
- br = FALSE;
- while(*amp) {
- ws [wpos++] = *amp++;
- }
- c += tlen;
- pos += tlen;
- } else if (*c == '\n') {
- if (!(options & GTK_IMHTML_NO_NEWLINE)) {
- ws[wpos] = '\n';
- wpos++;
- gtk_text_buffer_insert(imhtml->text_buffer, iter, ws, wpos);
- ws[0] = '\0'; wpos = 0;
- /* NEW_BIT (NEW_TEXT_BIT); */
- } else if (!br) { /* Don't insert a space immediately after an HTML break */
- /* A newline is defined by HTML as whitespace, which means we have to replace it with a word boundary.
- * word breaks vary depending on the language used, so the correct thing to do is to use Pango to determine
- * what language this is, determine the proper word boundary to use, and insert that. I'm just going to insert
- * a space instead. What are the non-English speakers going to do? Complain in a language I'll understand?
- * Bu-wahaha! */
- ws[wpos] = ' ';
- wpos++;
- gtk_text_buffer_insert(imhtml->text_buffer, iter, ws, wpos);
- ws[0] = '\0'; wpos = 0;
- }
- c++;
- pos++;
- } else if ((pos == 0 || wpos == 0 || isspace(*(c - 1))) &&
- (len_protocol = gtk_imhtml_is_protocol(c)) > 0 &&
- c[len_protocol] && !isspace(c[len_protocol]) &&
- (c[len_protocol] != '<' || !gtk_imhtml_is_tag(c + 1, NULL, NULL, NULL))) {
- br = FALSE;
- if (wpos > 0) {
- gtk_text_buffer_insert(imhtml->text_buffer, iter, ws, wpos);
- ws[0] = '\0'; wpos = 0;
- }
- while (len_protocol--) {
- /* Skip the next len_protocol characters, but
- * make sure they're copied into the ws array.
- */
- ws [wpos++] = *c++;
- pos++;
- }
- if (!imhtml->edit.link && (imhtml->format_functions & GTK_IMHTML_LINK)) {
- while (*c && !isspace((int)*c) &&
- (*c != '<' || !gtk_imhtml_is_tag(c + 1, NULL, NULL, NULL))) {
- if (*c == '&' && (amp = purple_markup_unescape_entity(c, &tlen))) {
- while (*amp)
- ws[wpos++] = *amp++;
- c += tlen;
- pos += tlen;
- } else {
- ws [wpos++] = *c++;
- pos++;
- }
- }
- ws[wpos] = '\0';
- gtk_imhtml_toggle_link(imhtml, ws);
- gtk_text_buffer_insert(imhtml->text_buffer, iter, ws, wpos);
- ws[0] = '\0'; wpos = 0;
- gtk_imhtml_toggle_link(imhtml, NULL);
- }
- } else if (*c) {
- br = FALSE;
- ws [wpos++] = *c++;
- pos++;
- } else {
- break;
- }
- }
- gtk_text_buffer_insert(imhtml->text_buffer, iter, ws, wpos);
- ws[0] = '\0'; wpos = 0;
-
- /* NEW_BIT(NEW_TEXT_BIT); */
-
- if(align_right) {
- /* insert RLM+LRM at beginning of the line to set alignment */
- GtkTextIter line_iter;
- line_iter = *iter;
- gtk_text_iter_set_line(&line_iter, align_line);
- /* insert RLM character to set alignment */
- ws[wpos++] = 0xE2;
- ws[wpos++] = 0x80;
- ws[wpos++] = 0x8F;
-
- if (!rtl_direction)
- {
- /* insert LRM character to set direction */
- /* (alignment=right and direction=LTR) */
- ws[wpos++] = 0xE2;
- ws[wpos++] = 0x80;
- ws[wpos++] = 0x8E;
- }
-
- ws[wpos] = '\0';
- gtk_text_buffer_insert(imhtml->text_buffer, &line_iter, ws, wpos);
- gtk_text_buffer_get_end_iter(gtk_text_iter_get_buffer(&line_iter), iter);
- ws[0] = '\0'; wpos = 0;
- }
-
- while (fonts) {
- GtkIMHtmlFontDetail *font = fonts->data;
- fonts = g_slist_remove (fonts, font);
- g_free (font->face);
- g_free (font->fore);
- g_free (font->back);
- g_free (font->sml);
- g_free (font);
- }
-
- g_free(ws);
- g_free(bg);
-
- if (!imhtml->wbfo)
- gtk_imhtml_close_tags(imhtml, iter);
-
- object = g_object_ref(G_OBJECT(imhtml));
- g_signal_emit(object, signals[UPDATE_FORMAT], 0);
- g_object_unref(object);
-
- gtk_text_buffer_end_user_action(imhtml->text_buffer);
-}
-
-void gtk_imhtml_remove_smileys(GtkIMHtml *imhtml)
-{
- g_hash_table_destroy(imhtml->smiley_data);
- gtk_smiley_tree_destroy(imhtml->default_smilies);
- imhtml->smiley_data = g_hash_table_new_full(g_str_hash, g_str_equal,
- g_free, (GDestroyNotify)gtk_smiley_tree_destroy);
- imhtml->default_smilies = gtk_smiley_tree_new();
-}
-
-void gtk_imhtml_show_comments (GtkIMHtml *imhtml,
- gboolean show)
-{
-#if FALSE
- GtkTextTag *tag;
- tag = gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(imhtml->text_buffer), "comment");
- if (tag)
- g_object_set(G_OBJECT(tag), "invisible", !show, NULL);
-#endif
- imhtml->show_comments = show;
-}
-
-const char *
-gtk_imhtml_get_protocol_name(GtkIMHtml *imhtml) {
- return imhtml->protocol_name;
-}
-
-void
-gtk_imhtml_set_protocol_name(GtkIMHtml *imhtml, const gchar *protocol_name) {
- g_free(imhtml->protocol_name);
- imhtml->protocol_name = g_strdup(protocol_name);
-}
-
-void
-gtk_imhtml_delete(GtkIMHtml *imhtml, GtkTextIter *start, GtkTextIter *end) {
- GList *l;
- GSList *sl;
- GtkTextIter i, i_s, i_e;
- GObject *object = g_object_ref(G_OBJECT(imhtml));
-
- if (start == NULL) {
- gtk_text_buffer_get_start_iter(imhtml->text_buffer, &i_s);
- start = &i_s;
- }
-
- if (end == NULL) {
- gtk_text_buffer_get_end_iter(imhtml->text_buffer, &i_e);
- end = &i_e;
- }
-
- l = imhtml->scalables;
- while (l) {
- GList *next = l->next;
- struct scalable_data *sd = l->data;
- gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer,
- &i, sd->mark);
- if (gtk_text_iter_in_range(&i, start, end)) {
- GtkIMHtmlScalable *scale = GTK_IMHTML_SCALABLE(sd->scalable);
- scale->free(scale);
- g_free(sd);
- imhtml->scalables = g_list_delete_link(imhtml->scalables, l);
- }
- l = next;
- }
-
- sl = imhtml->im_images;
- while (sl) {
- GSList *next = sl->next;
- struct im_image_data *img_data = sl->data;
- gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer,
- &i, img_data->mark);
- if (gtk_text_iter_in_range(&i, start, end)) {
- if (imhtml->funcs->image_unref)
- imhtml->funcs->image_unref(img_data->id);
- imhtml->im_images = g_slist_delete_link(imhtml->im_images, sl);
- g_free(img_data);
- }
- sl = next;
- }
- gtk_text_buffer_delete(imhtml->text_buffer, start, end);
-
- g_object_set_data(G_OBJECT(imhtml), "gtkimhtml_numsmileys_total", GINT_TO_POINTER(0));
-
- g_object_unref(object);
-}
-
-void gtk_imhtml_page_up (GtkIMHtml *imhtml)
-{
- GdkRectangle rect;
- GtkTextIter iter;
-
- gtk_text_view_get_visible_rect(GTK_TEXT_VIEW(imhtml), &rect);
- gtk_text_view_get_iter_at_location(GTK_TEXT_VIEW(imhtml), &iter, rect.x,
- rect.y - rect.height);
- gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(imhtml), &iter, 0, TRUE, 0, 0);
-
-}
-void gtk_imhtml_page_down (GtkIMHtml *imhtml)
-{
- GdkRectangle rect;
- GtkTextIter iter;
-
- gtk_text_view_get_visible_rect(GTK_TEXT_VIEW(imhtml), &rect);
- gtk_text_view_get_iter_at_location(GTK_TEXT_VIEW(imhtml), &iter, rect.x,
- rect.y + rect.height);
- gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(imhtml), &iter, 0, TRUE, 0, 0);
-}
-
-/**
- * Destroys and frees a GTK+ IM/HTML scalable image.
- *
- * @param scale The GTK+ IM/HTML scalable.
- */
-static void gtk_imhtml_image_free(GtkIMHtmlScalable *scale)
-{
- GtkIMHtmlImage *image = (GtkIMHtmlImage *)scale;
-
- g_object_unref(image->pixbuf);
- g_free(image->filename);
- if (image->filesel)
- gtk_widget_destroy(image->filesel);
- g_free(scale);
-}
-
-/**
- * Destroys and frees a GTK+ IM/HTML scalable animation.
- *
- * @param scale The GTK+ IM/HTML scalable.
- */
-static void gtk_imhtml_animation_free(GtkIMHtmlScalable *scale)
-{
- GtkIMHtmlAnimation *animation = (GtkIMHtmlAnimation *)scale;
-
- if (animation->timer > 0)
- g_source_remove(animation->timer);
- if (animation->iter != NULL)
- g_object_unref(animation->iter);
- g_object_unref(animation->anim);
-
- gtk_imhtml_image_free(scale);
-}
-
-static void
-image_save_yes_cb(GtkIMHtmlImageSave *save, const char *filename)
-{
- GError *error = NULL;
- GtkIMHtmlImage *image = (GtkIMHtmlImage *)save->image;
-
- gtk_widget_destroy(image->filesel);
- image->filesel = NULL;
-
- if (save->data && save->datasize) {
- g_file_set_contents(filename, save->data, save->datasize, &error);
- } else {
- gchar *type = NULL;
- GSList *formats = gdk_pixbuf_get_formats();
- char *newfilename;
-
- while (formats) {
- GdkPixbufFormat *format = formats->data;
- gchar **extensions = gdk_pixbuf_format_get_extensions(format);
- gpointer p = extensions;
-
- while(gdk_pixbuf_format_is_writable(format) && extensions && extensions[0]){
- gchar *fmt_ext = extensions[0];
- const gchar* file_ext = filename + strlen(filename) - strlen(fmt_ext);
-
- if(!g_ascii_strcasecmp(fmt_ext, file_ext)){
- type = gdk_pixbuf_format_get_name(format);
- break;
- }
-
- extensions++;
- }
-
- g_strfreev(p);
-
- if (type)
- break;
-
- formats = formats->next;
- }
-
- g_slist_free(formats);
-
- /* If I can't find a valid type, I will just tell the user about it and then assume
- it's a png */
- if (!type){
- char *basename, *tmp;
- char *dirname;
- GtkWidget *dialog = gtk_message_dialog_new_with_markup(NULL, 0, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
- _("<span size='larger' weight='bold'>Unrecognized file type</span>\n\nDefaulting to PNG."));
-
- g_signal_connect_swapped(dialog, "response", G_CALLBACK (gtk_widget_destroy), dialog);
- gtk_widget_show(dialog);
-
- type = g_strdup("png");
- dirname = g_path_get_dirname(filename);
- basename = g_path_get_basename(filename);
- tmp = strrchr(basename, '.');
- if (tmp != NULL)
- tmp[0] = '\0';
- newfilename = g_strdup_printf("%s" G_DIR_SEPARATOR_S "%s.png", dirname, basename);
- g_free(dirname);
- g_free(basename);
- } else {
- /*
- * We're able to save the file in it's original format, so we
- * can use the original file name.
- */
- newfilename = g_strdup(filename);
- }
-
- gdk_pixbuf_save(image->pixbuf, newfilename, type, &error, NULL);
-
- g_free(newfilename);
- g_free(type);
- }
-
- if (error){
- GtkWidget *dialog = gtk_message_dialog_new_with_markup(NULL, 0, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
- _("<span size='larger' weight='bold'>Error saving image</span>\n\n%s"), error->message);
- g_signal_connect_swapped(dialog, "response", G_CALLBACK (gtk_widget_destroy), dialog);
- gtk_widget_show(dialog);
- g_error_free(error);
- }
-}
-
-static void
-image_save_check_if_exists_cb(GtkWidget *widget, gint response, GtkIMHtmlImageSave *save)
-{
- gchar *filename;
- GtkIMHtmlImage *image = (GtkIMHtmlImage *)save->image;
-
- if (response != GTK_RESPONSE_ACCEPT) {
- gtk_widget_destroy(widget);
- image->filesel = NULL;
- return;
- }
-
- filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(widget));
-
- /*
- * XXX - We should probably prompt the user to determine if they really
- * want to overwrite the file or not. However, I don't feel like doing
- * that, so we're just always going to overwrite if the file exists.
- */
- /*
- if (g_file_test(filename, G_FILE_TEST_EXISTS)) {
- } else
- image_save_yes_cb(image, filename);
- */
-
- image_save_yes_cb(save, filename);
-
- g_free(filename);
-}
-
-static void
-gtk_imhtml_image_save(GtkWidget *w, GtkIMHtmlImageSave *save)
-{
- GtkIMHtmlImage *image = (GtkIMHtmlImage *)save->image;
-
- if (image->filesel != NULL) {
- gtk_window_present(GTK_WINDOW(image->filesel));
- return;
- }
-
- image->filesel = gtk_file_chooser_dialog_new(_("Save Image"),
- NULL,
- GTK_FILE_CHOOSER_ACTION_SAVE,
- GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
- GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
- NULL);
- gtk_dialog_set_default_response(GTK_DIALOG(image->filesel), GTK_RESPONSE_ACCEPT);
- if (image->filename != NULL)
- gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(image->filesel), image->filename);
- g_signal_connect(G_OBJECT(GTK_FILE_CHOOSER(image->filesel)), "response",
- G_CALLBACK(image_save_check_if_exists_cb), save);
-
- gtk_widget_show(image->filesel);
-}
-
-static void
-gtk_imhtml_custom_smiley_save(GtkWidget *w, GtkIMHtmlImageSave *save)
-{
- GtkIMHtmlImage *image = (GtkIMHtmlImage *)save->image;
-
- /* Create an add dialog */
- PidginSmiley *editor = pidgin_smiley_edit(NULL, NULL);
- pidgin_smiley_editor_set_shortcut(editor, image->filename);
- pidgin_smiley_editor_set_image(editor, image->pixbuf);
- pidgin_smiley_editor_set_data(editor, save->data, save->datasize);
-}
-
-/*
- * So, um, AIM Direct IM lets you send any file, not just images. You can
- * just insert a sound or a file or whatever in a conversation. It's
- * basically like file transfer, except there is an icon to open the file
- * embedded in the conversation. Someone should make the Purple core handle
- * all of that.
- */
-static gboolean gtk_imhtml_image_clicked(GtkWidget *w, GdkEvent *event, GtkIMHtmlImageSave *save)
-{
- GdkEventButton *event_button = (GdkEventButton *) event;
- GtkIMHtmlImage *image = (GtkIMHtmlImage *)save->image;
-
- if (event->type == GDK_BUTTON_RELEASE) {
- if(event_button->button == 3) {
- GtkWidget *img, *item, *menu;
- menu = gtk_menu_new();
-
- /* buttons and such */
- img = gtk_image_new_from_stock(GTK_STOCK_SAVE, GTK_ICON_SIZE_MENU);
- item = gtk_image_menu_item_new_with_mnemonic(_("_Save Image..."));
- gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), img);
- g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(gtk_imhtml_image_save), save);
- gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
-
- /* Add menu item for adding custom smiley to local smileys */
- /* we only add the menu if the image is of "custom smiley size"
- <= 96x96 pixels */
- if (image->width <= 96 && image->height <= 96) {
- img = gtk_image_new_from_stock(GTK_STOCK_ADD, GTK_ICON_SIZE_MENU);
- item = gtk_image_menu_item_new_with_mnemonic(_("_Add Custom Smiley..."));
- gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), img);
- g_signal_connect(G_OBJECT(item), "activate",
- G_CALLBACK(gtk_imhtml_custom_smiley_save), save);
- gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
- }
-
- gtk_widget_show_all(menu);
- gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL,
- event_button->button, event_button->time);
-
- return TRUE;
- }
- }
- if(event->type == GDK_BUTTON_PRESS && event_button->button == 3)
- return TRUE; /* Clicking the right mouse button on a link shouldn't
- be caught by the regular GtkTextView menu */
- else
- return FALSE; /* Let clicks go through if we didn't catch anything */
-
-}
-
-/**
- * Rescales a GTK+ IM/HTML scalable image to a given size.
- *
- * @param scale The GTK+ IM/HTML scalable.
- * @param width The new width.
- * @param height The new height.
- */
-static void gtk_imhtml_image_scale(GtkIMHtmlScalable *scale, int width, int height)
-{
- GtkIMHtmlImage *im_image = (GtkIMHtmlImage *)scale;
-
- if (im_image->width > width || im_image->height > height) {
- double ratio_w, ratio_h, ratio;
- int new_h, new_w;
- GdkPixbuf *new_image = NULL;
-
- ratio_w = ((double)width - 2) / im_image->width;
- ratio_h = ((double)height - 2) / im_image->height;
-
- ratio = (ratio_w < ratio_h) ? ratio_w : ratio_h;
-
- new_w = (int)(im_image->width * ratio);
- new_h = (int)(im_image->height * ratio);
-
- new_image = gdk_pixbuf_scale_simple(im_image->pixbuf, new_w, new_h, GDK_INTERP_BILINEAR);
- gtk_image_set_from_pixbuf(im_image->image, new_image);
- g_object_unref(G_OBJECT(new_image));
- } else if (gdk_pixbuf_get_width(gtk_image_get_pixbuf(im_image->image)) != im_image->width) {
- /* Enough space to show the full-size of the image. */
- GdkPixbuf *new_image;
-
- new_image = gdk_pixbuf_scale_simple(im_image->pixbuf, im_image->width, im_image->height, GDK_INTERP_BILINEAR);
- gtk_image_set_from_pixbuf(im_image->image, new_image);
- g_object_unref(G_OBJECT(new_image));
- }
-}
-
-/**
- * Adds a GTK+ IM/HTML scalable image to a given GTK+ IM/HTML at a given iter.
- *
- * @param scale The GTK+ IM/HTML scalable.
- * @param imhtml The GTK+ IM/HTML.
- * @param iter The GtkTextIter at which to add the scalable.
- */
-static void gtk_imhtml_image_add_to(GtkIMHtmlScalable *scale, GtkIMHtml *imhtml, GtkTextIter *iter)
-{
- GtkIMHtmlImage *image = (GtkIMHtmlImage *)scale;
- GtkWidget *box = gtk_event_box_new();
- char *tag;
- GtkTextChildAnchor *anchor = gtk_text_buffer_create_child_anchor(imhtml->text_buffer, iter);
- GtkIMHtmlImageSave *save;
-
- gtk_container_add(GTK_CONTAINER(box), GTK_WIDGET(image->image));
-
- g_object_set(G_OBJECT(box), "visible-window", FALSE, NULL);
-
- gtk_widget_show(GTK_WIDGET(image->image));
- gtk_widget_show(box);
-
- tag = g_strdup_printf("<IMG ID=\"%d\">", image->id);
- g_object_set_data_full(G_OBJECT(anchor), "gtkimhtml_htmltext", tag, g_free);
- g_object_set_data(G_OBJECT(anchor), "gtkimhtml_plaintext", "[Image]");
-
- gtk_text_view_add_child_at_anchor(GTK_TEXT_VIEW(imhtml), box, anchor);
-
- save = g_new0(GtkIMHtmlImageSave, 1);
- save->image = scale;
- g_signal_connect(G_OBJECT(box), "event", G_CALLBACK(gtk_imhtml_image_clicked), save);
- g_object_set_data_full(G_OBJECT(box), "image-save-data", save, (GDestroyNotify)g_free);
-}
-
-/**
- * Creates and returns a new GTK+ IM/HTML scalable object with an image.
- *
- * @param img A GdkPixbuf of the image to add.
- * @param filename The filename to associate with the image.
- * @param id The id to associate with the image.
- *
- * @return A new IM/HTML Scalable object with an image.
- */
-static GtkIMHtmlScalable *gtk_imhtml_image_new(GdkPixbuf *img, const gchar *filename, int id)
-{
- GtkIMHtmlImage *im_image = g_malloc(sizeof(GtkIMHtmlImage));
-
- GTK_IMHTML_SCALABLE(im_image)->scale = gtk_imhtml_image_scale;
- GTK_IMHTML_SCALABLE(im_image)->add_to = gtk_imhtml_image_add_to;
- GTK_IMHTML_SCALABLE(im_image)->free = gtk_imhtml_image_free;
-
- im_image->pixbuf = img;
- im_image->image = GTK_IMAGE(gtk_image_new_from_pixbuf(im_image->pixbuf));
- im_image->width = gdk_pixbuf_get_width(img);
- im_image->height = gdk_pixbuf_get_height(img);
- im_image->mark = NULL;
- im_image->filename = g_strdup(filename);
- im_image->id = id;
- im_image->filesel = NULL;
-
- g_object_ref(img);
- return GTK_IMHTML_SCALABLE(im_image);
-}
-
-static gboolean
-animate_image_cb(gpointer data)
-{
- GtkIMHtmlImage *im_image;
- int width, height;
- int delay;
-
- im_image = data;
-
- /* Update the pointer to this GdkPixbuf frame of the animation */
- if (gdk_pixbuf_animation_iter_advance(GTK_IMHTML_ANIMATION(im_image)->iter, NULL)) {
- GdkPixbuf *pb = gdk_pixbuf_animation_iter_get_pixbuf(GTK_IMHTML_ANIMATION(im_image)->iter);
- g_object_unref(G_OBJECT(im_image->pixbuf));
- im_image->pixbuf = gdk_pixbuf_copy(pb);
-
- /* Update the displayed GtkImage */
- width = gdk_pixbuf_get_width(gtk_image_get_pixbuf(im_image->image));
- height = gdk_pixbuf_get_height(gtk_image_get_pixbuf(im_image->image));
- if (width > 0 && height > 0)
- {
- /* Need to scale the new frame to the same size as the old frame */
- GdkPixbuf *tmp;
- tmp = gdk_pixbuf_scale_simple(im_image->pixbuf, width, height, GDK_INTERP_BILINEAR);
- gtk_image_set_from_pixbuf(im_image->image, tmp);
- g_object_unref(G_OBJECT(tmp));
- } else {
- /* Display at full-size */
- gtk_image_set_from_pixbuf(im_image->image, im_image->pixbuf);
- }
- }
-
- delay = MIN(gdk_pixbuf_animation_iter_get_delay_time(GTK_IMHTML_ANIMATION(im_image)->iter), 100);
- GTK_IMHTML_ANIMATION(im_image)->timer = g_timeout_add(delay, animate_image_cb, im_image);
-
- return FALSE;
-}
-
-/**
- * Creates and returns a new GTK+ IM/HTML scalable object with an
- * animated image.
- *
- * @param img A GdkPixbufAnimation of the image to add.
- * @param filename The filename to associate with the image.
- * @param id The id to associate with the image.
- *
- * @return A new IM/HTML Scalable object with an image.
- */
-/*
- * TODO: All this animation code could be combined much better with
- * the image code. It couldn't be done when it was written
- * because it requires breaking backward compatibility. It
- * would be good to do it for 3.0.0.
- */
-static GtkIMHtmlScalable *gtk_imhtml_animation_new(GdkPixbufAnimation *anim, const gchar *filename, int id)
-{
- GtkIMHtmlImage *im_image = (GtkIMHtmlImage *) g_new0(GtkIMHtmlAnimation, 1);
-
- GTK_IMHTML_SCALABLE(im_image)->scale = gtk_imhtml_image_scale;
- GTK_IMHTML_SCALABLE(im_image)->add_to = gtk_imhtml_image_add_to;
- GTK_IMHTML_SCALABLE(im_image)->free = gtk_imhtml_animation_free;
-
- GTK_IMHTML_ANIMATION(im_image)->anim = anim;
- if (gdk_pixbuf_animation_is_static_image(anim)) {
- im_image->pixbuf = gdk_pixbuf_animation_get_static_image(anim);
- g_object_ref(im_image->pixbuf);
- } else {
- int delay;
- GdkPixbuf *pb;
- GTK_IMHTML_ANIMATION(im_image)->iter = gdk_pixbuf_animation_get_iter(anim, NULL);
- pb = gdk_pixbuf_animation_iter_get_pixbuf(GTK_IMHTML_ANIMATION(im_image)->iter);
- im_image->pixbuf = gdk_pixbuf_copy(pb);
- delay = MIN(gdk_pixbuf_animation_iter_get_delay_time(GTK_IMHTML_ANIMATION(im_image)->iter), 100);
- GTK_IMHTML_ANIMATION(im_image)->timer = g_timeout_add(delay, animate_image_cb, im_image);
- }
- im_image->image = GTK_IMAGE(gtk_image_new_from_pixbuf(im_image->pixbuf));
- im_image->width = gdk_pixbuf_animation_get_width(anim);
- im_image->height = gdk_pixbuf_animation_get_height(anim);
- im_image->filename = g_strdup(filename);
- im_image->id = id;
-
- g_object_ref(anim);
-
- return GTK_IMHTML_SCALABLE(im_image);
-}
-
-GtkIMHtmlScalable *gtk_imhtml_hr_new()
-{
- GtkIMHtmlHr *hr = g_malloc(sizeof(GtkIMHtmlHr));
-
- GTK_IMHTML_SCALABLE(hr)->scale = gtk_imhtml_hr_scale;
- GTK_IMHTML_SCALABLE(hr)->add_to = gtk_imhtml_hr_add_to;
- GTK_IMHTML_SCALABLE(hr)->free = gtk_imhtml_hr_free;
-
- hr->sep = gtk_hseparator_new();
- gtk_widget_set_size_request(hr->sep, 5000, 2);
- gtk_widget_show(hr->sep);
-
- return GTK_IMHTML_SCALABLE(hr);
-}
-
-void gtk_imhtml_hr_scale(GtkIMHtmlScalable *scale, int width, int height)
-{
- gtk_widget_set_size_request(((GtkIMHtmlHr *)scale)->sep, width - 2, 2);
-}
-
-void gtk_imhtml_hr_add_to(GtkIMHtmlScalable *scale, GtkIMHtml *imhtml, GtkTextIter *iter)
-{
- GtkIMHtmlHr *hr = (GtkIMHtmlHr *)scale;
- GtkTextChildAnchor *anchor = gtk_text_buffer_create_child_anchor(imhtml->text_buffer, iter);
- g_object_set_data(G_OBJECT(anchor), "gtkimhtml_htmltext", "<hr>");
- g_object_set_data(G_OBJECT(anchor), "gtkimhtml_plaintext", "\n---\n");
- gtk_text_view_add_child_at_anchor(GTK_TEXT_VIEW(imhtml), hr->sep, anchor);
-}
-
-void gtk_imhtml_hr_free(GtkIMHtmlScalable *scale)
-{
- g_free(scale);
-}
-
-gboolean gtk_imhtml_search_find(GtkIMHtml *imhtml, const gchar *text)
-{
- GtkTextIter iter, start, end;
- gboolean new_search = TRUE;
- GtkTextMark *start_mark;
-
- g_return_val_if_fail(imhtml != NULL, FALSE);
- g_return_val_if_fail(text != NULL, FALSE);
-
- start_mark = gtk_text_buffer_get_mark(imhtml->text_buffer, "search");
-
- if (start_mark && imhtml->search_string && !strcmp(text, imhtml->search_string))
- new_search = FALSE;
-
- if (new_search) {
- gtk_imhtml_search_clear(imhtml);
- g_free(imhtml->search_string);
- imhtml->search_string = g_strdup(text);
- gtk_text_buffer_get_end_iter(imhtml->text_buffer, &iter);
- } else {
- gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &iter,
- start_mark);
- }
-
- if (gtk_source_iter_backward_search(&iter, imhtml->search_string,
- GTK_SOURCE_SEARCH_VISIBLE_ONLY | GTK_SOURCE_SEARCH_CASE_INSENSITIVE,
- &start, &end, NULL))
- {
- gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(imhtml), &start, 0, TRUE, 0, 0);
- gtk_text_buffer_create_mark(imhtml->text_buffer, "search", &start, FALSE);
- if (new_search)
- {
- gtk_text_buffer_remove_tag_by_name(imhtml->text_buffer, "search", &iter, &end);
- do
- gtk_text_buffer_apply_tag_by_name(imhtml->text_buffer, "search", &start, &end);
- while (gtk_source_iter_backward_search(&start, imhtml->search_string,
- GTK_SOURCE_SEARCH_VISIBLE_ONLY |
- GTK_SOURCE_SEARCH_CASE_INSENSITIVE,
- &start, &end, NULL));
- }
- return TRUE;
- }
- else if (!new_search)
- {
- /* We hit the end, so start at the beginning again. */
- gtk_text_buffer_get_end_iter(imhtml->text_buffer, &iter);
-
- if (gtk_source_iter_backward_search(&iter, imhtml->search_string,
- GTK_SOURCE_SEARCH_VISIBLE_ONLY | GTK_SOURCE_SEARCH_CASE_INSENSITIVE,
- &start, &end, NULL))
- {
- gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(imhtml), &start, 0, TRUE, 0, 0);
- gtk_text_buffer_create_mark(imhtml->text_buffer, "search", &start, FALSE);
-
- return TRUE;
- }
-
- }
-
- return FALSE;
-}
-
-void gtk_imhtml_search_clear(GtkIMHtml *imhtml)
-{
- GtkTextIter start, end;
-
- g_return_if_fail(imhtml != NULL);
-
- gtk_text_buffer_get_start_iter(imhtml->text_buffer, &start);
- gtk_text_buffer_get_end_iter(imhtml->text_buffer, &end);
-
- gtk_text_buffer_remove_tag_by_name(imhtml->text_buffer, "search", &start, &end);
- g_free(imhtml->search_string);
- imhtml->search_string = NULL;
-}
-
-static GtkTextTag *find_font_forecolor_tag(GtkIMHtml *imhtml, gchar *color)
-{
- gchar str[18];
- GtkTextTag *tag;
-
- g_snprintf(str, sizeof(str), "FORECOLOR %s", color);
-
- tag = gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(imhtml->text_buffer), str);
- if (!tag) {
- GdkColor gcolor;
- if (!gdk_color_parse(color, &gcolor)) {
- gchar tmp[8];
- tmp[0] = '#';
- strncpy(&tmp[1], color, 7);
- tmp[7] = '\0';
- if (!gdk_color_parse(tmp, &gcolor))
- gdk_color_parse("black", &gcolor);
- }
- tag = gtk_text_buffer_create_tag(imhtml->text_buffer, str, "foreground-gdk", &gcolor, NULL);
- }
-
- return tag;
-}
-
-static GtkTextTag *find_font_backcolor_tag(GtkIMHtml *imhtml, gchar *color)
-{
- gchar str[18];
- GtkTextTag *tag;
-
- g_snprintf(str, sizeof(str), "BACKCOLOR %s", color);
-
- tag = gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(imhtml->text_buffer), str);
- if (!tag) {
- GdkColor gcolor;
- if (!gdk_color_parse(color, &gcolor)) {
- gchar tmp[8];
- tmp[0] = '#';
- strncpy(&tmp[1], color, 7);
- tmp[7] = '\0';
- if (!gdk_color_parse(tmp, &gcolor))
- gdk_color_parse("white", &gcolor);
- }
- tag = gtk_text_buffer_create_tag(imhtml->text_buffer, str, "background-gdk", &gcolor, NULL);
- }
-
- return tag;
-}
-
-static GtkTextTag *find_font_background_tag(GtkIMHtml *imhtml, gchar *color)
-{
- gchar str[19];
- GtkTextTag *tag;
-
- g_snprintf(str, sizeof(str), "BACKGROUND %s", color);
-
- tag = gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(imhtml->text_buffer), str);
- if (!tag)
- tag = gtk_text_buffer_create_tag(imhtml->text_buffer, str, NULL);
-
- return tag;
-}
-
-static GtkTextTag *find_font_face_tag(GtkIMHtml *imhtml, gchar *face)
-{
- gchar str[256];
- GtkTextTag *tag;
-
- g_snprintf(str, sizeof(str), "FONT FACE %s", face);
- str[255] = '\0';
-
- tag = gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(imhtml->text_buffer), str);
- if (!tag)
- tag = gtk_text_buffer_create_tag(imhtml->text_buffer, str, "family", face, NULL);
-
- return tag;
-}
-
-static GtkTextTag *find_font_size_tag(GtkIMHtml *imhtml, int size)
-{
- gchar str[24];
- GtkTextTag *tag;
-
- g_snprintf(str, sizeof(str), "FONT SIZE %d", size);
- str[23] = '\0';
-
- tag = gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(imhtml->text_buffer), str);
- if (!tag) {
- /* For reasons I don't understand, setting "scale" here scaled
- * based on some default size other than my theme's default
- * size. Our size 4 was actually smaller than our size 3 for
- * me. So this works around that oddity.
- */
- GtkTextAttributes *attr = gtk_text_view_get_default_attributes(GTK_TEXT_VIEW(imhtml));
- tag = gtk_text_buffer_create_tag(imhtml->text_buffer, str, "size",
- (gint) (pango_font_description_get_size(attr->font) *
- (double) POINT_SIZE(size)), NULL);
- gtk_text_attributes_unref(attr);
- }
-
- return tag;
-}
-
-static void remove_tag_by_prefix(GtkIMHtml *imhtml, const GtkTextIter *i, const GtkTextIter *e,
- const char *prefix, guint len, gboolean homo)
-{
- GSList *tags, *l;
- GtkTextIter iter;
-
- tags = gtk_text_iter_get_tags(i);
-
- for (l = tags; l; l = l->next) {
- GtkTextTag *tag = l->data;
- gchar *name = NULL;
-
- g_object_get(G_OBJECT(tag), "name", &name, NULL);
-
- if (name && !strncmp(name, prefix, len))
- gtk_text_buffer_remove_tag(imhtml->text_buffer, tag, i, e);
-
- g_free(name);
- }
-
- g_slist_free(tags);
-
- if (homo)
- return;
-
- iter = *i;
-
- while (gtk_text_iter_forward_char(&iter) && !gtk_text_iter_equal(&iter, e)) {
- if (gtk_text_iter_begins_tag(&iter, NULL)) {
- tags = gtk_text_iter_get_toggled_tags(&iter, TRUE);
-
- for (l = tags; l; l = l->next) {
- GtkTextTag *tag = l->data;
- gchar *name = NULL;
-
- g_object_get(G_OBJECT(tag), "name", &name, NULL);
-
- if (name && !strncmp(name, prefix, len))
- gtk_text_buffer_remove_tag(imhtml->text_buffer, tag, &iter, e);
-
- g_free(name);
- }
-
- g_slist_free(tags);
- }
- }
-}
-
-static void remove_font_size(GtkIMHtml *imhtml, GtkTextIter *i, GtkTextIter *e, gboolean homo)
-{
- remove_tag_by_prefix(imhtml, i, e, "FONT SIZE ", 10, homo);
-}
-
-static void remove_font_face(GtkIMHtml *imhtml, GtkTextIter *i, GtkTextIter *e, gboolean homo)
-{
- remove_tag_by_prefix(imhtml, i, e, "FONT FACE ", 10, homo);
-}
-
-static void remove_font_forecolor(GtkIMHtml *imhtml, GtkTextIter *i, GtkTextIter *e, gboolean homo)
-{
- remove_tag_by_prefix(imhtml, i, e, "FORECOLOR ", 10, homo);
-}
-
-static void remove_font_backcolor(GtkIMHtml *imhtml, GtkTextIter *i, GtkTextIter *e, gboolean homo)
-{
- remove_tag_by_prefix(imhtml, i, e, "BACKCOLOR ", 10, homo);
-}
-
-static void remove_font_background(GtkIMHtml *imhtml, GtkTextIter *i, GtkTextIter *e, gboolean homo)
-{
- remove_tag_by_prefix(imhtml, i, e, "BACKGROUND ", 10, homo);
-}
-
-static void remove_font_link(GtkIMHtml *imhtml, GtkTextIter *i, GtkTextIter *e, gboolean homo)
-{
- remove_tag_by_prefix(imhtml, i, e, "LINK ", 5, homo);
-}
-
-static void
-imhtml_clear_formatting(GtkIMHtml *imhtml)
-{
- GtkTextIter start, end;
-
- if (!imhtml->editable)
- return;
-
- if (!imhtml_get_iter_bounds(imhtml, &start, &end))
- return;
-
- gtk_text_buffer_remove_tag_by_name(imhtml->text_buffer, "BOLD", &start, &end);
- gtk_text_buffer_remove_tag_by_name(imhtml->text_buffer, "ITALICS", &start, &end);
- gtk_text_buffer_remove_tag_by_name(imhtml->text_buffer, "UNDERLINE", &start, &end);
- gtk_text_buffer_remove_tag_by_name(imhtml->text_buffer, "STRIKE", &start, &end);
- remove_font_size(imhtml, &start, &end, FALSE);
- remove_font_face(imhtml, &start, &end, FALSE);
- remove_font_forecolor(imhtml, &start, &end, FALSE);
- remove_font_backcolor(imhtml, &start, &end, FALSE);
- remove_font_background(imhtml, &start, &end, FALSE);
- remove_font_link(imhtml, &start, &end, FALSE);
-
- imhtml->edit.bold = 0;
- imhtml->edit.italic = 0;
- imhtml->edit.underline = 0;
- imhtml->edit.strike = 0;
- imhtml->edit.fontsize = 0;
-
- g_free(imhtml->edit.fontface);
- imhtml->edit.fontface = NULL;
-
- g_free(imhtml->edit.forecolor);
- imhtml->edit.forecolor = NULL;
-
- g_free(imhtml->edit.backcolor);
- imhtml->edit.backcolor = NULL;
-
- g_free(imhtml->edit.background);
- imhtml->edit.background = NULL;
-}
-
-/* Editable stuff */
-static void preinsert_cb(GtkTextBuffer *buffer, GtkTextIter *iter, gchar *text, gint len, GtkIMHtml *imhtml)
-{
- imhtml->insert_offset = gtk_text_iter_get_offset(iter);
-}
-
-static void insert_ca_cb(GtkTextBuffer *buffer, GtkTextIter *arg1, GtkTextChildAnchor *arg2, gpointer user_data)
-{
- GtkTextIter start;
-
- start = *arg1;
- gtk_text_iter_backward_char(&start);
-
- gtk_imhtml_apply_tags_on_insert(user_data, &start, arg1);
-}
-
-static void insert_cb(GtkTextBuffer *buffer, GtkTextIter *end, gchar *text, gint len, GtkIMHtml *imhtml)
-{
- GtkTextIter start;
-
- if (!len)
- return;
-
- start = *end;
- gtk_text_iter_set_offset(&start, imhtml->insert_offset);
-
- gtk_imhtml_apply_tags_on_insert(imhtml, &start, end);
-}
-
-static void delete_cb(GtkTextBuffer *buffer, GtkTextIter *start, GtkTextIter *end, GtkIMHtml *imhtml)
-{
- GSList *tags, *l;
-
- tags = gtk_text_iter_get_tags(start);
- for (l = tags; l != NULL; l = l->next) {
- GtkTextTag *tag = GTK_TEXT_TAG(l->data);
-
- if (tag && /* Remove the formatting only if */
- gtk_text_iter_starts_word(start) && /* beginning of a word */
- gtk_text_iter_begins_tag(start, tag) && /* the tag starts with the selection */
- (!gtk_text_iter_has_tag(end, tag) || /* the tag ends within the selection */
- gtk_text_iter_ends_tag(end, tag))) {
- gchar *name = NULL;
-
- g_object_get(G_OBJECT(tag), "name", &name, NULL);
- gtk_text_buffer_remove_tag(imhtml->text_buffer, tag, start, end);
-
- if (name && strncmp(name, "LINK ", 5) == 0 && imhtml->edit.link) {
- gtk_imhtml_toggle_link(imhtml, NULL);
- }
-
- g_free(name);
- }
- }
- g_slist_free(tags);
-}
-
-static void gtk_imhtml_apply_tags_on_insert(GtkIMHtml *imhtml, GtkTextIter *start, GtkTextIter *end)
-{
- if (imhtml->edit.bold)
- gtk_text_buffer_apply_tag_by_name(imhtml->text_buffer, "BOLD", start, end);
- else
- gtk_text_buffer_remove_tag_by_name(imhtml->text_buffer, "BOLD", start, end);
-
- if (imhtml->edit.italic)
- gtk_text_buffer_apply_tag_by_name(imhtml->text_buffer, "ITALICS", start, end);
- else
- gtk_text_buffer_remove_tag_by_name(imhtml->text_buffer, "ITALICS", start, end);
-
- if (imhtml->edit.underline)
- gtk_text_buffer_apply_tag_by_name(imhtml->text_buffer, "UNDERLINE", start, end);
- else
- gtk_text_buffer_remove_tag_by_name(imhtml->text_buffer, "UNDERLINE", start, end);
-
- if (imhtml->edit.strike)
- gtk_text_buffer_apply_tag_by_name(imhtml->text_buffer, "STRIKE", start, end);
- else
- gtk_text_buffer_remove_tag_by_name(imhtml->text_buffer, "STRIKE", start, end);
-
- if (imhtml->edit.forecolor) {
- remove_font_forecolor(imhtml, start, end, TRUE);
- gtk_text_buffer_apply_tag(imhtml->text_buffer,
- find_font_forecolor_tag(imhtml, imhtml->edit.forecolor),
- start, end);
- }
-
- if (imhtml->edit.backcolor) {
- remove_font_backcolor(imhtml, start, end, TRUE);
- gtk_text_buffer_apply_tag(imhtml->text_buffer,
- find_font_backcolor_tag(imhtml, imhtml->edit.backcolor),
- start, end);
- }
-
- if (imhtml->edit.background) {
- remove_font_background(imhtml, start, end, TRUE);
- gtk_text_buffer_apply_tag(imhtml->text_buffer,
- find_font_background_tag(imhtml, imhtml->edit.background),
- start, end);
- }
- if (imhtml->edit.fontface) {
- remove_font_face(imhtml, start, end, TRUE);
- gtk_text_buffer_apply_tag(imhtml->text_buffer,
- find_font_face_tag(imhtml, imhtml->edit.fontface),
- start, end);
- }
-
- if (imhtml->edit.fontsize) {
- remove_font_size(imhtml, start, end, TRUE);
- gtk_text_buffer_apply_tag(imhtml->text_buffer,
- find_font_size_tag(imhtml, imhtml->edit.fontsize),
- start, end);
- }
-
- if (imhtml->edit.link) {
- remove_font_link(imhtml, start, end, TRUE);
- gtk_text_buffer_apply_tag(imhtml->text_buffer,
- imhtml->edit.link,
- start, end);
- }
-}
-
-void gtk_imhtml_set_editable(GtkIMHtml *imhtml, gboolean editable)
-{
- gtk_text_view_set_editable(GTK_TEXT_VIEW(imhtml), editable);
- /*
- * We need a visible caret for accessibility, so mouseless
- * people can highlight stuff.
- */
- /* gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(imhtml), editable); */
- if (editable && !imhtml->editable) {
- g_signal_connect_after(G_OBJECT(GTK_IMHTML(imhtml)->text_buffer), "mark-set",
- G_CALLBACK(mark_set_cb), imhtml);
- g_signal_connect(G_OBJECT(imhtml), "backspace", G_CALLBACK(smart_backspace_cb), NULL);
- } else if (!editable && imhtml->editable) {
- g_signal_handlers_disconnect_by_func(G_OBJECT(GTK_IMHTML(imhtml)->text_buffer),
- mark_set_cb, imhtml);
- g_signal_handlers_disconnect_by_func(G_OBJECT(imhtml),
- smart_backspace_cb, NULL);
- }
-
- imhtml->editable = editable;
- imhtml->format_functions = GTK_IMHTML_ALL;
-}
-
-void gtk_imhtml_set_whole_buffer_formatting_only(GtkIMHtml *imhtml, gboolean wbfo)
-{
- g_return_if_fail(imhtml != NULL);
-
- imhtml->wbfo = wbfo;
-}
-
-void gtk_imhtml_set_format_functions(GtkIMHtml *imhtml, GtkIMHtmlButtons buttons)
-{
- GObject *object = g_object_ref(G_OBJECT(imhtml));
- imhtml->format_functions = buttons;
- g_signal_emit(object, signals[BUTTONS_UPDATE], 0, buttons);
- g_object_unref(object);
-}
-
-GtkIMHtmlButtons gtk_imhtml_get_format_functions(GtkIMHtml *imhtml)
-{
- return imhtml->format_functions;
-}
-
-void gtk_imhtml_get_current_format(GtkIMHtml *imhtml, gboolean *bold,
- gboolean *italic, gboolean *underline)
-{
- if (bold != NULL)
- (*bold) = imhtml->edit.bold;
- if (italic != NULL)
- (*italic) = imhtml->edit.italic;
- if (underline != NULL)
- (*underline) = imhtml->edit.underline;
-}
-
-char *
-gtk_imhtml_get_current_fontface(GtkIMHtml *imhtml)
-{
- return g_strdup(imhtml->edit.fontface);
-}
-
-char *
-gtk_imhtml_get_current_forecolor(GtkIMHtml *imhtml)
-{
- return g_strdup(imhtml->edit.forecolor);
-}
-
-char *
-gtk_imhtml_get_current_backcolor(GtkIMHtml *imhtml)
-{
- return g_strdup(imhtml->edit.backcolor);
-}
-
-char *
-gtk_imhtml_get_current_background(GtkIMHtml *imhtml)
-{
- return g_strdup(imhtml->edit.background);
-}
-
-gint
-gtk_imhtml_get_current_fontsize(GtkIMHtml *imhtml)
-{
- return imhtml->edit.fontsize;
-}
-
-gboolean gtk_imhtml_get_editable(GtkIMHtml *imhtml)
-{
- return imhtml->editable;
-}
-
-void
-gtk_imhtml_clear_formatting(GtkIMHtml *imhtml)
-{
- GObject *object;
-
- object = g_object_ref(G_OBJECT(imhtml));
- g_signal_emit(object, signals[CLEAR_FORMAT], 0);
-
- gtk_widget_grab_focus(GTK_WIDGET(imhtml));
-
- g_object_unref(object);
-}
-
-/*
- * I had this crazy idea about changing the text cursor color to reflex the foreground color
- * of the text about to be entered. This is the place you'd do it, along with the place where
- * we actually set a new foreground color.
- * I may not do this, because people will bitch about Purple overriding their gtk theme's cursor
- * colors.
- *
- * Just in case I do do this, I asked about what to set the secondary text cursor to.
- *
- * (12:45:27) ?? ???: secondary_cursor_color = (rgb(background) + rgb(primary_cursor_color) ) / 2
- * (12:45:55) ?? ???: understand?
- * (12:46:14) Tim: yeah. i didn't know there was an exact formula
- * (12:46:56) ?? ???: u might need to extract separate each color from RGB
- */
-
-static void mark_set_cb(GtkTextBuffer *buffer, GtkTextIter *arg1, GtkTextMark *mark,
- GtkIMHtml *imhtml)
-{
- GSList *tags, *l;
- GtkTextIter iter;
-
- if (mark != gtk_text_buffer_get_insert(buffer))
- return;
-
- if (!gtk_text_buffer_get_char_count(buffer))
- return;
-
- imhtml->edit.bold = imhtml->edit.italic = imhtml->edit.underline = imhtml->edit.strike = FALSE;
- g_free(imhtml->edit.forecolor);
- imhtml->edit.forecolor = NULL;
-
- g_free(imhtml->edit.backcolor);
- imhtml->edit.backcolor = NULL;
-
- g_free(imhtml->edit.fontface);
- imhtml->edit.fontface = NULL;
-
- imhtml->edit.fontsize = 0;
- imhtml->edit.link = NULL;
-
- gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &iter, mark);
-
-
- if (gtk_text_iter_is_end(&iter))
- tags = gtk_text_iter_get_toggled_tags(&iter, FALSE);
- else
- tags = gtk_text_iter_get_tags(&iter);
-
- for (l = tags; l != NULL; l = l->next) {
- GtkTextTag *tag = GTK_TEXT_TAG(l->data);
- gchar *name = NULL;
-
- g_object_get(G_OBJECT(tag), "name", &name, NULL);
-
- if (name) {
- if (strcmp(name, "BOLD") == 0)
- imhtml->edit.bold = TRUE;
- else if (strcmp(name, "ITALICS") == 0)
- imhtml->edit.italic = TRUE;
- else if (strcmp(name, "UNDERLINE") == 0)
- imhtml->edit.underline = TRUE;
- else if (strcmp(name, "STRIKE") == 0)
- imhtml->edit.strike = TRUE;
- else if (strncmp(name, "FORECOLOR ", 10) == 0)
- imhtml->edit.forecolor = g_strdup(&(name)[10]);
- else if (strncmp(name, "BACKCOLOR ", 10) == 0)
- imhtml->edit.backcolor = g_strdup(&(name)[10]);
- else if (strncmp(name, "FONT FACE ", 10) == 0)
- imhtml->edit.fontface = g_strdup(&(name)[10]);
- else if (strncmp(name, "FONT SIZE ", 10) == 0)
- imhtml->edit.fontsize = strtol(&(name)[10], NULL, 10);
- else if ((strncmp(name, "LINK ", 5) == 0) && !gtk_text_iter_is_end(&iter))
- imhtml->edit.link = tag;
- }
-
- g_free(name);
- }
-
- g_slist_free(tags);
-}
-
-static void imhtml_emit_signal_for_format(GtkIMHtml *imhtml, GtkIMHtmlButtons button)
-{
- GObject *object;
-
- g_return_if_fail(imhtml != NULL);
-
- object = g_object_ref(G_OBJECT(imhtml));
- g_signal_emit(object, signals[TOGGLE_FORMAT], 0, button);
- g_object_unref(object);
-}
-
-static void imhtml_toggle_bold(GtkIMHtml *imhtml)
-{
- GtkTextIter start, end;
-
- imhtml->edit.bold = !imhtml->edit.bold;
-
- if (!imhtml_get_iter_bounds(imhtml, &start, &end))
- return;
-
- if (imhtml->edit.bold)
- gtk_text_buffer_apply_tag_by_name(imhtml->text_buffer, "BOLD", &start, &end);
- else
- gtk_text_buffer_remove_tag_by_name(imhtml->text_buffer, "BOLD", &start, &end);
-}
-
-void gtk_imhtml_toggle_bold(GtkIMHtml *imhtml)
-{
- imhtml_emit_signal_for_format(imhtml, GTK_IMHTML_BOLD);
-}
-
-static void imhtml_toggle_italic(GtkIMHtml *imhtml)
-{
- GtkTextIter start, end;
-
- imhtml->edit.italic = !imhtml->edit.italic;
-
- if (!imhtml_get_iter_bounds(imhtml, &start, &end))
- return;
-
- if (imhtml->edit.italic)
- gtk_text_buffer_apply_tag_by_name(imhtml->text_buffer, "ITALICS", &start, &end);
- else
- gtk_text_buffer_remove_tag_by_name(imhtml->text_buffer, "ITALICS", &start, &end);
-}
-
-void gtk_imhtml_toggle_italic(GtkIMHtml *imhtml)
-{
- imhtml_emit_signal_for_format(imhtml, GTK_IMHTML_ITALIC);
-}
-
-static void imhtml_toggle_underline(GtkIMHtml *imhtml)
-{
- GtkTextIter start, end;
-
- imhtml->edit.underline = !imhtml->edit.underline;
-
- if (!imhtml_get_iter_bounds(imhtml, &start, &end))
- return;
-
- if (imhtml->edit.underline)
- gtk_text_buffer_apply_tag_by_name(imhtml->text_buffer, "UNDERLINE", &start, &end);
- else
- gtk_text_buffer_remove_tag_by_name(imhtml->text_buffer, "UNDERLINE", &start, &end);
-}
-
-void gtk_imhtml_toggle_underline(GtkIMHtml *imhtml)
-{
- imhtml_emit_signal_for_format(imhtml, GTK_IMHTML_UNDERLINE);
-}
-
-static void imhtml_toggle_strike(GtkIMHtml *imhtml)
-{
- GtkTextIter start, end;
-
- imhtml->edit.strike = !imhtml->edit.strike;
-
- if (!imhtml_get_iter_bounds(imhtml, &start, &end))
- return;
-
- if (imhtml->edit.strike)
- gtk_text_buffer_apply_tag_by_name(imhtml->text_buffer, "STRIKE", &start, &end);
- else
- gtk_text_buffer_remove_tag_by_name(imhtml->text_buffer, "STRIKE", &start, &end);
-}
-
-void gtk_imhtml_toggle_strike(GtkIMHtml *imhtml)
-{
- imhtml_emit_signal_for_format(imhtml, GTK_IMHTML_STRIKE);
-}
-
-void gtk_imhtml_font_set_size(GtkIMHtml *imhtml, gint size)
-{
- GObject *object;
- GtkTextIter start, end;
-
- imhtml->edit.fontsize = size;
-
- if (!imhtml_get_iter_bounds(imhtml, &start, &end))
- return;
-
- remove_font_size(imhtml, &start, &end, imhtml->wbfo);
- gtk_text_buffer_apply_tag(imhtml->text_buffer,
- find_font_size_tag(imhtml, imhtml->edit.fontsize), &start, &end);
-
- object = g_object_ref(G_OBJECT(imhtml));
- g_signal_emit(object, signals[TOGGLE_FORMAT], 0, GTK_IMHTML_SHRINK | GTK_IMHTML_GROW);
- g_object_unref(object);
-}
-
-static void imhtml_font_shrink(GtkIMHtml *imhtml)
-{
- GtkTextIter start, end;
-
- if (imhtml->edit.fontsize == 1)
- return;
-
- if (!imhtml->edit.fontsize)
- imhtml->edit.fontsize = 2;
- else
- imhtml->edit.fontsize--;
-
- if (!imhtml_get_iter_bounds(imhtml, &start, &end))
- return;
- remove_font_size(imhtml, &start, &end, imhtml->wbfo);
- gtk_text_buffer_apply_tag(imhtml->text_buffer,
- find_font_size_tag(imhtml, imhtml->edit.fontsize), &start, &end);
-}
-
-void gtk_imhtml_font_shrink(GtkIMHtml *imhtml)
-{
- imhtml_emit_signal_for_format(imhtml, GTK_IMHTML_SHRINK);
-}
-
-static void imhtml_font_grow(GtkIMHtml *imhtml)
-{
- GtkTextIter start, end;
-
- if (imhtml->edit.fontsize == MAX_FONT_SIZE)
- return;
-
- if (!imhtml->edit.fontsize)
- imhtml->edit.fontsize = 4;
- else
- imhtml->edit.fontsize++;
-
- if (!imhtml_get_iter_bounds(imhtml, &start, &end))
- return;
- remove_font_size(imhtml, &start, &end, imhtml->wbfo);
- gtk_text_buffer_apply_tag(imhtml->text_buffer,
- find_font_size_tag(imhtml, imhtml->edit.fontsize), &start, &end);
-}
-
-void gtk_imhtml_font_grow(GtkIMHtml *imhtml)
-{
- imhtml_emit_signal_for_format(imhtml, GTK_IMHTML_GROW);
-}
-
-static gboolean gtk_imhtml_toggle_str_tag(GtkIMHtml *imhtml, const char *value, char **edit_field,
- void (*remove_func)(GtkIMHtml *imhtml, GtkTextIter *i, GtkTextIter *e, gboolean homo),
- GtkTextTag *(find_func)(GtkIMHtml *imhtml, gchar *color), GtkIMHtmlButtons button)
-{
- GObject *object;
- GtkTextIter start;
- GtkTextIter end;
-
- g_free(*edit_field);
- *edit_field = NULL;
-
- if (value && strcmp(value, "") != 0)
- {
- *edit_field = g_strdup(value);
-
- if (imhtml_get_iter_bounds(imhtml, &start, &end)) {
- remove_func(imhtml, &start, &end, imhtml->wbfo);
- gtk_text_buffer_apply_tag(imhtml->text_buffer,
- find_func(imhtml, *edit_field), &start, &end);
- }
- }
- else
- {
- if (imhtml_get_iter_bounds(imhtml, &start, &end))
- remove_func(imhtml, &start, &end, TRUE); /* 'TRUE' or 'imhtml->wbfo'? */
- }
-
- object = g_object_ref(G_OBJECT(imhtml));
- g_signal_emit(object, signals[TOGGLE_FORMAT], 0, button);
- g_object_unref(object);
-
- return *edit_field != NULL;
-}
-
-gboolean gtk_imhtml_toggle_forecolor(GtkIMHtml *imhtml, const char *color)
-{
- return gtk_imhtml_toggle_str_tag(imhtml, color, &imhtml->edit.forecolor,
- remove_font_forecolor, find_font_forecolor_tag,
- GTK_IMHTML_FORECOLOR);
-}
-
-gboolean gtk_imhtml_toggle_backcolor(GtkIMHtml *imhtml, const char *color)
-{
- return gtk_imhtml_toggle_str_tag(imhtml, color, &imhtml->edit.backcolor,
- remove_font_backcolor, find_font_backcolor_tag,
- GTK_IMHTML_BACKCOLOR);
-}
-
-gboolean gtk_imhtml_toggle_background(GtkIMHtml *imhtml, const char *color)
-{
- return gtk_imhtml_toggle_str_tag(imhtml, color, &imhtml->edit.background,
- remove_font_background, find_font_background_tag,
- GTK_IMHTML_BACKGROUND);
-}
-
-gboolean gtk_imhtml_toggle_fontface(GtkIMHtml *imhtml, const char *face)
-{
- return gtk_imhtml_toggle_str_tag(imhtml, face, &imhtml->edit.fontface,
- remove_font_face, find_font_face_tag,
- GTK_IMHTML_FACE);
-}
-
-void gtk_imhtml_toggle_link(GtkIMHtml *imhtml, const char *url)
-{
- GObject *object;
- GtkTextIter start, end;
- GtkTextTag *linktag;
- static guint linkno = 0;
- gchar str[48];
- GdkColor *color = NULL;
-
- imhtml->edit.link = NULL;
-
- if (url) {
- g_snprintf(str, sizeof(str), "LINK %d", linkno++);
- str[47] = '\0';
-
- gtk_widget_style_get(GTK_WIDGET(imhtml), "hyperlink-color", &color, NULL);
- if (color) {
- imhtml->edit.link = linktag = gtk_text_buffer_create_tag(imhtml->text_buffer, str, "foreground-gdk", color, "underline", PANGO_UNDERLINE_SINGLE, NULL);
- gdk_color_free(color);
- } else {
- imhtml->edit.link = linktag = gtk_text_buffer_create_tag(imhtml->text_buffer, str, "foreground", "blue", "underline", PANGO_UNDERLINE_SINGLE, NULL);
- }
- g_object_set_data_full(G_OBJECT(linktag), "link_url", g_strdup(url), g_free);
- g_signal_connect(G_OBJECT(linktag), "event", G_CALLBACK(tag_event), NULL);
-
- if (imhtml->editable && gtk_text_buffer_get_selection_bounds(imhtml->text_buffer, &start, &end)) {
- remove_font_link(imhtml, &start, &end, FALSE);
- gtk_text_buffer_apply_tag(imhtml->text_buffer, linktag, &start, &end);
- }
- }
-
- object = g_object_ref(G_OBJECT(imhtml));
- g_signal_emit(object, signals[TOGGLE_FORMAT], 0, GTK_IMHTML_LINK);
- g_object_unref(object);
-}
-
-void gtk_imhtml_insert_link(GtkIMHtml *imhtml, GtkTextMark *mark, const char *url, const char *text)
-{
- GtkTextIter iter;
-
- /* Delete any currently selected text */
- gtk_text_buffer_delete_selection(imhtml->text_buffer, TRUE, TRUE);
-
- gtk_imhtml_toggle_link(imhtml, url);
- gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &iter, mark);
- gtk_text_buffer_insert(imhtml->text_buffer, &iter, text, -1);
- gtk_imhtml_toggle_link(imhtml, NULL);
-}
-
-void gtk_imhtml_insert_smiley(GtkIMHtml *imhtml, const char *sml, char *smiley)
-{
- GtkTextMark *mark;
- GtkTextIter iter;
-
- /* Delete any currently selected text */
- gtk_text_buffer_delete_selection(imhtml->text_buffer, TRUE, TRUE);
-
- mark = gtk_text_buffer_get_insert(imhtml->text_buffer);
-
- gtk_text_buffer_get_iter_at_mark(imhtml->text_buffer, &iter, mark);
- gtk_text_buffer_begin_user_action(imhtml->text_buffer);
- gtk_imhtml_insert_smiley_at_iter(imhtml, sml, smiley, &iter);
- gtk_text_buffer_end_user_action(imhtml->text_buffer);
-}
-
-/* TODO: I think this can be removed for GTK+ 3.0... */
-#if !GTK_CHECK_VERSION(3,0,0)
-static gboolean
-image_expose(GtkWidget *widget, GdkEventExpose *event, gpointer user_data)
-{
- GTK_WIDGET_CLASS(GTK_WIDGET_GET_CLASS(widget))->expose_event(widget, event);
-
- return TRUE;
-}
-#endif
-
-/* In case the smiley gets removed from the imhtml before it gets removed from the queue */
-static void animated_smiley_destroy_cb(GtkWidget *widget, GtkIMHtml *imhtml)
-{
- GList *l = imhtml->animations->head;
- while (l) {
- GList *next = l->next;
- if (l->data == widget) {
- if (l == imhtml->animations->tail)
- imhtml->animations->tail = imhtml->animations->tail->prev;
- imhtml->animations->head = g_list_delete_link(imhtml->animations->head, l);
- imhtml->num_animations--;
- }
- l = next;
- }
-}
-
-static gboolean gtk_imhtml_smiley_clicked(GtkWidget *w, GdkEvent *event, GtkIMHtmlSmiley *smiley)
-{
- GdkPixbufAnimation *anim = NULL;
- GtkIMHtmlImageSave *save = NULL;
- gboolean ret;
-
- if (event->type != GDK_BUTTON_RELEASE || ((GdkEventButton*)event)->button != 3)
- return FALSE;
-
- anim = gtk_smiley_get_image(smiley);
- if (!anim)
- return FALSE;
-
- save = g_new0(GtkIMHtmlImageSave, 1);
- save->image = (GtkIMHtmlScalable *)gtk_imhtml_animation_new(anim, smiley->smile, 0);
- save->data = smiley->data; /* Do not need to memdup here, since the smiley is not
- destroyed before this GtkIMHtmlImageSave */
- save->datasize = smiley->datasize;
- ret = gtk_imhtml_image_clicked(w, event, save);
- g_object_set_data_full(G_OBJECT(w), "image-data", save->image, (GDestroyNotify)gtk_imhtml_animation_free);
- g_object_set_data_full(G_OBJECT(w), "image-save-data", save, (GDestroyNotify)g_free);
- return ret;
-}
-
-void gtk_imhtml_insert_smiley_at_iter(GtkIMHtml *imhtml, const char *sml, char *smiley, GtkTextIter *iter)
-{
- GdkPixbuf *pixbuf = NULL;
- GdkPixbufAnimation *annipixbuf = NULL;
- GtkWidget *icon = NULL;
- GtkTextChildAnchor *anchor = NULL;
- char *unescaped;
- GtkIMHtmlSmiley *imhtml_smiley;
- GtkWidget *ebox = NULL;
- int numsmileys_thismsg, numsmileys_total;
-
- /*
- * This GtkIMHtml has the maximum number of smileys allowed, so don't
- * add any more. We do this for performance reasons, because smileys
- * are apparently pretty inefficient. Hopefully we can remove this
- * restriction when we're using a better HTML widget.
- */
- unescaped = purple_unescape_html(smiley);
- numsmileys_thismsg = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(imhtml), "gtkimhtml_numsmileys_thismsg"));
- if (numsmileys_thismsg >= 30) {
- gtk_text_buffer_insert(imhtml->text_buffer, iter, unescaped, -1);
- g_free(unescaped);
- return;
- }
- numsmileys_total = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(imhtml), "gtkimhtml_numsmileys_total"));
- if (numsmileys_total >= 300) {
- gtk_text_buffer_insert(imhtml->text_buffer, iter, unescaped, -1);
- g_free(unescaped);
- return;
- }
-
- imhtml_smiley = gtk_imhtml_smiley_get(imhtml, sml, unescaped);
-
- if (imhtml->format_functions & GTK_IMHTML_SMILEY) {
- annipixbuf = imhtml_smiley ? gtk_smiley_get_image(imhtml_smiley) : NULL;
- if (annipixbuf) {
- if (gdk_pixbuf_animation_is_static_image(annipixbuf)) {
- pixbuf = gdk_pixbuf_animation_get_static_image(annipixbuf);
- if (pixbuf)
- icon = gtk_image_new_from_pixbuf(pixbuf);
- } else {
- icon = gtk_image_new_from_animation(annipixbuf);
- if (imhtml->num_animations == 20) {
- GtkImage *image = GTK_IMAGE(g_queue_pop_head(imhtml->animations));
- GdkPixbufAnimation *anim = gtk_image_get_animation(image);
- g_signal_handlers_disconnect_matched(G_OBJECT(image), G_SIGNAL_MATCH_FUNC,
- 0, 0, NULL, G_CALLBACK(animated_smiley_destroy_cb), NULL);
- if (anim) {
- GdkPixbuf *pb = gdk_pixbuf_animation_get_static_image(anim);
- if (pb != NULL) {
- GdkPixbuf *copy = gdk_pixbuf_copy(pb);
- gtk_image_set_from_pixbuf(image, copy);
- g_object_unref(G_OBJECT(copy));
- }
- }
- } else {
- imhtml->num_animations++;
- }
- g_signal_connect(G_OBJECT(icon), "destroy", G_CALLBACK(animated_smiley_destroy_cb), imhtml);
- g_queue_push_tail(imhtml->animations, icon);
- }
- }
- }
-
- if (imhtml_smiley && imhtml_smiley->flags & GTK_IMHTML_SMILEY_CUSTOM) {
- ebox = gtk_event_box_new();
- gtk_event_box_set_visible_window(GTK_EVENT_BOX(ebox), FALSE);
- gtk_widget_show(ebox);
- }
-
- if (icon) {
- anchor = gtk_text_buffer_create_child_anchor(imhtml->text_buffer, iter);
- g_object_set_data_full(G_OBJECT(anchor), "gtkimhtml_plaintext", g_strdup(unescaped), g_free);
- g_object_set_data_full(G_OBJECT(anchor), "gtkimhtml_tiptext", g_strdup(unescaped), g_free);
- g_object_set_data_full(G_OBJECT(anchor), "gtkimhtml_htmltext", g_strdup(smiley), g_free);
-
- /* This catches the expose events generated by animated
- * images, and ensures that they are handled by the image
- * itself, without propagating to the textview and causing
- * a complete refresh */
- /* TODO: I think this should be removed for GTK+ 3.0? */
-#if !GTK_CHECK_VERSION(3,0,0)
- g_signal_connect(G_OBJECT(icon), "expose-event", G_CALLBACK(image_expose), NULL);
-#endif
-
- gtk_widget_show(icon);
- if (ebox)
- gtk_container_add(GTK_CONTAINER(ebox), icon);
- gtk_text_view_add_child_at_anchor(GTK_TEXT_VIEW(imhtml), ebox ? ebox : icon, anchor);
-
- g_object_set_data(G_OBJECT(imhtml), "gtkimhtml_numsmileys_thismsg", GINT_TO_POINTER(numsmileys_thismsg + 1));
- g_object_set_data(G_OBJECT(imhtml), "gtkimhtml_numsmileys_total", GINT_TO_POINTER(numsmileys_total + 1));
- } else if (imhtml_smiley != NULL && (imhtml->format_functions & GTK_IMHTML_SMILEY)) {
- anchor = gtk_text_buffer_create_child_anchor(imhtml->text_buffer, iter);
- imhtml_smiley->anchors = g_slist_append(imhtml_smiley->anchors, g_object_ref(anchor));
- if (ebox) {
- GtkWidget *img = gtk_image_new_from_stock(GTK_STOCK_MISSING_IMAGE, GTK_ICON_SIZE_MENU);
- gtk_container_add(GTK_CONTAINER(ebox), img);
- gtk_widget_show(img);
- g_object_set_data_full(G_OBJECT(anchor), "gtkimhtml_plaintext", g_strdup(unescaped), g_free);
- g_object_set_data_full(G_OBJECT(anchor), "gtkimhtml_tiptext", g_strdup(unescaped), g_free);
- g_object_set_data_full(G_OBJECT(anchor), "gtkimhtml_htmltext", g_strdup(smiley), g_free);
- gtk_text_view_add_child_at_anchor(GTK_TEXT_VIEW(imhtml), ebox, anchor);
- }
-
- g_object_set_data(G_OBJECT(imhtml), "gtkimhtml_numsmileys_thismsg", GINT_TO_POINTER(numsmileys_thismsg + 1));
- g_object_set_data(G_OBJECT(imhtml), "gtkimhtml_numsmileys_total", GINT_TO_POINTER(numsmileys_total + 1));
- } else {
- gtk_text_buffer_insert(imhtml->text_buffer, iter, unescaped, -1);
- }
-
- if (ebox) {
- g_signal_connect(G_OBJECT(ebox), "event", G_CALLBACK(gtk_imhtml_smiley_clicked), imhtml_smiley);
- }
-
- g_free(unescaped);
-}
-
-void gtk_imhtml_insert_image_at_iter(GtkIMHtml *imhtml, int id, GtkTextIter *iter)
-{
- GdkPixbufAnimation *anim = NULL;
- const char *filename = NULL;
- gpointer image;
- GdkRectangle rect;
- GtkIMHtmlScalable *scalable = NULL;
- struct scalable_data *sd;
- int minus;
-
- if (!imhtml->funcs || !imhtml->funcs->image_get ||
- !imhtml->funcs->image_get_size || !imhtml->funcs->image_get_data ||
- !imhtml->funcs->image_get_filename || !imhtml->funcs->image_ref ||
- !imhtml->funcs->image_unref)
- return;
-
- image = imhtml->funcs->image_get(id);
-
- if (image) {
- gpointer data;
- size_t len;
-
- data = imhtml->funcs->image_get_data(image);
- len = imhtml->funcs->image_get_size(image);
- if (data && len)
- anim = pidgin_pixbuf_anim_from_data(data, len);
-
- }
-
- if (anim) {
- struct im_image_data *t = g_new(struct im_image_data, 1);
- filename = imhtml->funcs->image_get_filename(image);
- imhtml->funcs->image_ref(id);
- t->id = id;
- t->mark = gtk_text_buffer_create_mark(imhtml->text_buffer, NULL, iter, TRUE);
- imhtml->im_images = g_slist_prepend(imhtml->im_images, t);
- scalable = gtk_imhtml_animation_new(anim, filename, id);
- g_object_unref(G_OBJECT(anim));
- } else {
- GdkPixbuf *pixbuf;
- pixbuf = gtk_widget_render_icon(GTK_WIDGET(imhtml), GTK_STOCK_MISSING_IMAGE,
- GTK_ICON_SIZE_BUTTON, "gtkimhtml-missing-image");
- scalable = gtk_imhtml_image_new(pixbuf, filename, id);
- g_object_unref(G_OBJECT(pixbuf));
- }
-
- sd = g_new(struct scalable_data, 1);
- sd->scalable = scalable;
- sd->mark = gtk_text_buffer_create_mark(imhtml->text_buffer, NULL, iter, TRUE);
- gtk_text_view_get_visible_rect(GTK_TEXT_VIEW(imhtml), &rect);
- scalable->add_to(scalable, imhtml, iter);
- minus = gtk_text_view_get_left_margin(GTK_TEXT_VIEW(imhtml)) +
- gtk_text_view_get_right_margin(GTK_TEXT_VIEW(imhtml));
- scalable->scale(scalable, rect.width - minus, rect.height);
- imhtml->scalables = g_list_append(imhtml->scalables, sd);
-}
-
-static const gchar *tag_to_html_start(GtkTextTag *tag)
-{
- static gchar buf[16384];
- gchar *name = NULL;
-
- g_object_get(G_OBJECT(tag), "name", &name, NULL);
- g_return_val_if_fail(name != NULL, "");
-
- if (strcmp(name, "BOLD") == 0) {
- g_free(name);
- return "<b>";
- } else if (strcmp(name, "ITALICS") == 0) {
- g_free(name);
- return "<i>";
- } else if (strcmp(name, "UNDERLINE") == 0) {
- g_free(name);
- return "<u>";
- } else if (strcmp(name, "STRIKE") == 0) {
- g_free(name);
- return "<s>";
- } else if (strncmp(name, "LINK ", 5) == 0) {
- char *tmp = g_object_get_data(G_OBJECT(tag), "link_url");
-
- g_free(name);
-
- if (tmp) {
- gchar *escaped = purple_markup_escape_text(tmp, -1);
- g_snprintf(buf, sizeof(buf), "<a href=\"%s\">", escaped);
- buf[sizeof(buf)-1] = '\0';
- g_free(escaped);
- return buf;
- } else {
- return "";
- }
- } else if (strncmp(name, "FORECOLOR ", 10) == 0) {
- g_snprintf(buf, sizeof(buf), "<font color=\"%s\">", &name[10]);
-
- g_free(name);
-
- return buf;
- } else if (strncmp(name, "BACKCOLOR ", 10) == 0) {
- g_snprintf(buf, sizeof(buf), "<font back=\"%s\">", &name[10]);
- g_free(name);
-
- return buf;
- } else if (strncmp(name, "BACKGROUND ", 10) == 0) {
- g_snprintf(buf, sizeof(buf), "<body bgcolor=\"%s\">", &name[11]);
- g_free(name);
-
- return buf;
- } else if (strncmp(name, "FONT FACE ", 10) == 0) {
- g_snprintf(buf, sizeof(buf), "<font face=\"%s\">", &name[10]);
- g_free(name);
-
- return buf;
- } else if (strncmp(name, "FONT SIZE ", 10) == 0) {
- g_snprintf(buf, sizeof(buf), "<font size=\"%s\">", &name[10]);
- g_free(name);
-
- return buf;
- } else {
- char *str = buf;
- gboolean isset;
- int ivalue = 0;
- GdkColor *color = NULL;
- GObject *obj = G_OBJECT(tag);
- gboolean empty = TRUE;
-
- str += g_snprintf(str, sizeof(buf) - (str - buf), "<span style='");
-
- /* Weight */
- g_object_get(obj, "weight-set", &isset, "weight", &ivalue, NULL);
- if (isset) {
- const char *weight = "";
- if (ivalue >= PANGO_WEIGHT_ULTRABOLD)
- weight = "bolder";
- else if (ivalue >= PANGO_WEIGHT_BOLD)
- weight = "bold";
- else if (ivalue >= PANGO_WEIGHT_NORMAL)
- weight = "normal";
- else
- weight = "lighter";
-
- str += g_snprintf(str, sizeof(buf) - (str - buf), "font-weight: %s;", weight);
- empty = FALSE;
- }
-
- /* Foreground color */
- g_object_get(obj, "foreground-set", &isset, "foreground-gdk", &color, NULL);
- if (isset && color) {
- str += g_snprintf(str, sizeof(buf) - (str - buf),
- "color: #%02x%02x%02x;",
- color->red >> 8, color->green >> 8, color->blue >> 8);
- empty = FALSE;
- }
- gdk_color_free(color);
-
- /* Background color */
- g_object_get(obj, "background-set", &isset, "background-gdk", &color, NULL);
- if (isset && color) {
- str += g_snprintf(str, sizeof(buf) - (str - buf),
- "background: #%02x%02x%02x;",
- color->red >> 8, color->green >> 8, color->blue >> 8);
- empty = FALSE;
- }
- gdk_color_free(color);
-
- /* Underline */
- g_object_get(obj, "underline-set", &isset, "underline", &ivalue, NULL);
- if (isset) {
- switch (ivalue) {
- case PANGO_UNDERLINE_NONE:
- case PANGO_UNDERLINE_ERROR:
- break;
- default:
- str += g_snprintf(str, sizeof(buf) - (str - buf), "text-decoration: underline;");
- empty = FALSE;
- }
- }
-
- g_snprintf(str, sizeof(buf) - (str - buf), "'>");
- g_free(name);
-
- return (empty ? "" : buf);
- }
-}
-
-static const gchar *tag_to_html_end(GtkTextTag *tag)
-{
- gchar *name;
-
- g_object_get(G_OBJECT(tag), "name", &name, NULL);
- g_return_val_if_fail(name != NULL, "");
-
- if (strcmp(name, "BOLD") == 0) {
- g_free(name);
- return "</b>";
- } else if (strcmp(name, "ITALICS") == 0) {
- g_free(name);
- return "</i>";
- } else if (strcmp(name, "UNDERLINE") == 0) {
- g_free(name);
- return "</u>";
- } else if (strcmp(name, "STRIKE") == 0) {
- g_free(name);
- return "</s>";
- } else if (strncmp(name, "LINK ", 5) == 0) {
- g_free(name);
- return "</a>";
- } else if (strncmp(name, "FORECOLOR ", 10) == 0) {
- g_free(name);
- return "</font>";
- } else if (strncmp(name, "BACKCOLOR ", 10) == 0) {
- g_free(name);
- return "</font>";
- } else if (strncmp(name, "BACKGROUND ", 10) == 0) {
- g_free(name);
- return "</body>";
- } else if (strncmp(name, "FONT FACE ", 10) == 0) {
- g_free(name);
- return "</font>";
- } else if (strncmp(name, "FONT SIZE ", 10) == 0) {
- g_free(name);
- return "</font>";
- } else {
- const char *props[] = {"weight-set", "foreground-set", "background-set",
- "size-set", "underline-set", NULL};
- int i;
- for (i = 0; props[i]; i++) {
- gboolean set = FALSE;
- g_object_get(G_OBJECT(tag), props[i], &set, NULL);
- if (set)
- return "</span>";
- }
-
- g_free(name);
-
- return "";
- }
-}
-
-typedef struct {
- GtkTextTag *tag;
- char *end;
- char *start;
-} PidginTextTagData;
-
-static PidginTextTagData *text_tag_data_new(GtkTextTag *tag)
-{
- const char *start, *end;
- PidginTextTagData *ret = NULL;
-
- start = tag_to_html_start(tag);
- if (!start || !*start)
- return NULL;
- end = tag_to_html_end(tag);
- if (!end || !*end)
- return NULL;
-
- ret = g_new0(PidginTextTagData, 1);
- ret->start = g_strdup(start);
- ret->end = g_strdup(end);
- ret->tag = tag;
- return ret;
-}
-
-static void text_tag_data_destroy(PidginTextTagData *data)
-{
- g_free(data->start);
- g_free(data->end);
- g_free(data);
-}
-
-static gboolean tag_ends_here(GtkTextTag *tag, GtkTextIter *iter, GtkTextIter *niter)
-{
- return ((gtk_text_iter_has_tag(iter, GTK_TEXT_TAG(tag)) &&
- !gtk_text_iter_has_tag(niter, GTK_TEXT_TAG(tag))) ||
- gtk_text_iter_is_end(niter));
-}
-
-/* Basic notion here: traverse through the text buffer one-by-one, non-character elements, such
- * as smileys and IM images are represented by the Unicode "unknown" character. Handle them. Else
- * check for tags that are toggled on, insert their html form, and push them on the queue. Then insert
- * the actual text. Then check for tags that are toggled off and insert them, after checking the queue.
- * Finally, replace <, >, &, and " with their HTML equivalent.
- */
-char *gtk_imhtml_get_markup_range(GtkIMHtml *imhtml, GtkTextIter *start, GtkTextIter *end)
-{
- gunichar c;
- GtkTextIter iter, next_iter, non_neutral_iter;
- gboolean is_rtl_message = FALSE;
- GString *str = g_string_new("");
- GSList *tags, *sl;
- GQueue *q;
- GtkTextTag *tag;
- PidginTextTagData *tagdata;
-
- q = g_queue_new();
-
- gtk_text_iter_order(start, end);
- non_neutral_iter = next_iter = iter = *start;
- gtk_text_iter_forward_char(&next_iter);
-
- /* Bi-directional text support */
- /* Get to the first non-neutral character */
-#ifdef HAVE_PANGO14
- while ((PANGO_DIRECTION_NEUTRAL == pango_unichar_direction(gtk_text_iter_get_char(&non_neutral_iter)))
- && gtk_text_iter_forward_char(&non_neutral_iter));
- if (PANGO_DIRECTION_RTL == pango_unichar_direction(gtk_text_iter_get_char(&non_neutral_iter))) {
- is_rtl_message = TRUE;
- g_string_append(str, "<SPAN style=\"direction:rtl;text-align:right;\">");
- }
-#endif
-
- /* First add the tags that are already in progress (we don't care about non-printing tags)*/
- tags = gtk_text_iter_get_tags(start);
-
- for (sl = tags; sl; sl = sl->next) {
- tag = sl->data;
- if (!gtk_text_iter_toggles_tag(start, GTK_TEXT_TAG(tag))) {
- PidginTextTagData *data = text_tag_data_new(tag);
- if (data) {
- g_string_append(str, data->start);
- g_queue_push_tail(q, data);
- }
- }
- }
- g_slist_free(tags);
-
- while ((c = gtk_text_iter_get_char(&iter)) != 0 && !gtk_text_iter_equal(&iter, end)) {
-
- tags = gtk_text_iter_get_tags(&iter);
-
- for (sl = tags; sl; sl = sl->next) {
- tag = sl->data;
- if (gtk_text_iter_begins_tag(&iter, GTK_TEXT_TAG(tag))) {
- PidginTextTagData *data = text_tag_data_new(tag);
- if (data) {
- g_string_append(str, data->start);
- g_queue_push_tail(q, data);
- }
- }
- }
-
- if (c == 0xFFFC) {
- GtkTextChildAnchor* anchor = gtk_text_iter_get_child_anchor(&iter);
- if (anchor) {
- char *text = g_object_get_data(G_OBJECT(anchor), "gtkimhtml_htmltext");
- if (text)
- str = g_string_append(str, text);
- }
- } else if (c == '<') {
- str = g_string_append(str, "&lt;");
- } else if (c == '>') {
- str = g_string_append(str, "&gt;");
- } else if (c == '&') {
- str = g_string_append(str, "&amp;");
- } else if (c == '"') {
- str = g_string_append(str, "&quot;");
- } else if (c == '\n') {
- str = g_string_append(str, "<br>");
- } else {
- str = g_string_append_unichar(str, c);
- }
-
- tags = g_slist_reverse(tags);
- for (sl = tags; sl; sl = sl->next) {
- tag = sl->data;
- /** don't worry about non-printing tags ending */
- if (tag_ends_here(tag, &iter, &next_iter) &&
- *tag_to_html_end(tag) &&
- *tag_to_html_start(tag))
- {
- PidginTextTagData *tmp;
- GQueue *r = g_queue_new();
-
- while ((tmp = g_queue_pop_tail(q)) && tmp->tag != tag) {
- g_string_append(str, tmp->end);
- if (!tag_ends_here(tmp->tag, &iter, &next_iter))
- g_queue_push_tail(r, tmp);
- else
- text_tag_data_destroy(tmp);
- }
-
- if (tmp != NULL) {
- g_string_append(str, tmp->end);
- text_tag_data_destroy(tmp);
- }
-#if 0 /* This can't be allowed to happen because it causes the iters to be invalidated in the debug window imhtml during text copying */
- else
- purple_debug_warning("gtkimhtml", "empty queue, more closing tags than open tags!\n");
-#endif
-
- while ((tmp = g_queue_pop_head(r))) {
- g_string_append(str, tmp->start);
- g_queue_push_tail(q, tmp);
- }
- g_queue_free(r);
- }
- }
-
- g_slist_free(tags);
- gtk_text_iter_forward_char(&iter);
- gtk_text_iter_forward_char(&next_iter);
- }
-
- while ((tagdata = g_queue_pop_tail(q))) {
- g_string_append(str, tagdata->end);
- text_tag_data_destroy(tagdata);
- }
-
- /* Bi-directional text support - close tags */
- if (is_rtl_message)
- g_string_append(str, "</SPAN>");
-
- g_queue_free(q);
- return g_string_free(str, FALSE);
-}
-
-void gtk_imhtml_close_tags(GtkIMHtml *imhtml, GtkTextIter *iter)
-{
- if (imhtml->edit.bold)
- gtk_imhtml_toggle_bold(imhtml);
-
- if (imhtml->edit.italic)
- gtk_imhtml_toggle_italic(imhtml);
-
- if (imhtml->edit.underline)
- gtk_imhtml_toggle_underline(imhtml);
-
- if (imhtml->edit.strike)
- gtk_imhtml_toggle_strike(imhtml);
-
- if (imhtml->edit.forecolor)
- gtk_imhtml_toggle_forecolor(imhtml, NULL);
-
- if (imhtml->edit.backcolor)
- gtk_imhtml_toggle_backcolor(imhtml, NULL);
-
- if (imhtml->edit.fontface)
- gtk_imhtml_toggle_fontface(imhtml, NULL);
-
- imhtml->edit.fontsize = 0;
-
- if (imhtml->edit.link)
- gtk_imhtml_toggle_link(imhtml, NULL);
-}
-
-char *gtk_imhtml_get_markup(GtkIMHtml *imhtml)
-{
- GtkTextIter start, end;
-
- gtk_text_buffer_get_start_iter(imhtml->text_buffer, &start);
- gtk_text_buffer_get_end_iter(imhtml->text_buffer, &end);
- return gtk_imhtml_get_markup_range(imhtml, &start, &end);
-}
-
-char **gtk_imhtml_get_markup_lines(GtkIMHtml *imhtml)
-{
- int i, j, lines;
- GtkTextIter start, end;
- char **ret;
-
- lines = gtk_text_buffer_get_line_count(imhtml->text_buffer);
- ret = g_new0(char *, lines + 1);
- gtk_text_buffer_get_start_iter(imhtml->text_buffer, &start);
- end = start;
- gtk_text_iter_forward_to_line_end(&end);
-
- for (i = 0, j = 0; i < lines; i++) {
- if (gtk_text_iter_get_char(&start) != '\n') {
- ret[j] = gtk_imhtml_get_markup_range(imhtml, &start, &end);
- if (ret[j] != NULL)
- j++;
- }
-
- gtk_text_iter_forward_line(&start);
- end = start;
- gtk_text_iter_forward_to_line_end(&end);
- }
-
- return ret;
-}
-
-char *gtk_imhtml_get_text(GtkIMHtml *imhtml, GtkTextIter *start, GtkTextIter *stop)
-{
- GString *str = g_string_new("");
- GtkTextIter iter, end;
- gunichar c;
-
- if (start == NULL)
- gtk_text_buffer_get_start_iter(imhtml->text_buffer, &iter);
- else
- iter = *start;
-
- if (stop == NULL)
- gtk_text_buffer_get_end_iter(imhtml->text_buffer, &end);
- else
- end = *stop;
-
- gtk_text_iter_order(&iter, &end);
-
- while ((c = gtk_text_iter_get_char(&iter)) != 0 && !gtk_text_iter_equal(&iter, &end)) {
- if (c == 0xFFFC) {
- GtkTextChildAnchor* anchor;
- char *text = NULL;
-
- anchor = gtk_text_iter_get_child_anchor(&iter);
- if (anchor)
- text = g_object_get_data(G_OBJECT(anchor), "gtkimhtml_plaintext");
- if (text)
- str = g_string_append(str, text);
- } else {
- g_string_append_unichar(str, c);
- }
- gtk_text_iter_forward_char(&iter);
- }
-
- return g_string_free(str, FALSE);
-}
-
-void gtk_imhtml_set_funcs(GtkIMHtml *imhtml, GtkIMHtmlFuncs *f)
-{
- g_return_if_fail(imhtml != NULL);
- imhtml->funcs = f;
-}
-
-void gtk_imhtml_setup_entry(GtkIMHtml *imhtml, PurpleConnectionFlags flags)
-{
- GtkIMHtmlButtons buttons;
-
- if (flags & PURPLE_CONNECTION_FLAG_HTML) {
- char color[8];
- GdkColor fg_color, bg_color;
-
- buttons = GTK_IMHTML_ALL;
-
- if (flags & PURPLE_CONNECTION_FLAG_NO_BGCOLOR)
- buttons &= ~GTK_IMHTML_BACKCOLOR;
- if (flags & PURPLE_CONNECTION_FLAG_NO_FONTSIZE)
- {
- buttons &= ~GTK_IMHTML_GROW;
- buttons &= ~GTK_IMHTML_SHRINK;
- }
- if (flags & PURPLE_CONNECTION_FLAG_NO_URLDESC)
- buttons &= ~GTK_IMHTML_LINKDESC;
-
- gtk_imhtml_set_format_functions(imhtml, GTK_IMHTML_ALL);
- if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/send_bold") != imhtml->edit.bold)
- gtk_imhtml_toggle_bold(imhtml);
-
- if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/send_italic") != imhtml->edit.italic)
- gtk_imhtml_toggle_italic(imhtml);
-
- if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/send_underline") != imhtml->edit.underline)
- gtk_imhtml_toggle_underline(imhtml);
-
- gtk_imhtml_toggle_fontface(imhtml,
- purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/font_face"));
-
- if (!(flags & PURPLE_CONNECTION_FLAG_NO_FONTSIZE))
- {
- int size = purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/font_size");
-
- /* 3 is the default. */
- if (size != 3)
- gtk_imhtml_font_set_size(imhtml, size);
- }
-
- if(strcmp(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/fgcolor"), "") != 0)
- {
- gdk_color_parse(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/fgcolor"),
- &fg_color);
- g_snprintf(color, sizeof(color), "#%02x%02x%02x",
- fg_color.red / 256,
- fg_color.green / 256,
- fg_color.blue / 256);
- } else
- strcpy(color, "");
-
- gtk_imhtml_toggle_forecolor(imhtml, color);
-
- if(!(flags & PURPLE_CONNECTION_FLAG_NO_BGCOLOR) &&
- strcmp(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/bgcolor"), "") != 0)
- {
- gdk_color_parse(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/bgcolor"),
- &bg_color);
- g_snprintf(color, sizeof(color), "#%02x%02x%02x",
- bg_color.red / 256,
- bg_color.green / 256,
- bg_color.blue / 256);
- } else
- strcpy(color, "");
-
- gtk_imhtml_toggle_background(imhtml, color);
-
- if (flags & PURPLE_CONNECTION_FLAG_FORMATTING_WBFO)
- gtk_imhtml_set_whole_buffer_formatting_only(imhtml, TRUE);
- else
- gtk_imhtml_set_whole_buffer_formatting_only(imhtml, FALSE);
- } else {
- buttons = GTK_IMHTML_SMILEY | GTK_IMHTML_IMAGE;
- imhtml_clear_formatting(imhtml);
- }
-
- if (flags & PURPLE_CONNECTION_FLAG_NO_IMAGES)
- buttons &= ~GTK_IMHTML_IMAGE;
-
- if (flags & PURPLE_CONNECTION_FLAG_ALLOW_CUSTOM_SMILEY)
- buttons |= GTK_IMHTML_CUSTOM_SMILEY;
- else
- buttons &= ~GTK_IMHTML_CUSTOM_SMILEY;
-
- gtk_imhtml_set_format_functions(imhtml, buttons);
-}
-
-/*******
- * GtkIMHtmlSmiley functions
- *******/
-static void gtk_custom_smiley_allocated(GdkPixbufLoader *loader, gpointer user_data)
-{
- GtkIMHtmlSmiley *smiley;
-
- smiley = (GtkIMHtmlSmiley *)user_data;
- smiley->icon = gdk_pixbuf_loader_get_animation(loader);
-
- if (smiley->icon)
- g_object_ref(G_OBJECT(smiley->icon));
-#ifdef DEBUG_CUSTOM_SMILEY
- purple_debug_info("custom-smiley", "gtk_custom_smiley_allocated(): got GdkPixbufAnimation %p for smiley '%s'\n", smiley->icon, smiley->smile);
-#endif
-}
-
-static void gtk_custom_smiley_closed(GdkPixbufLoader *loader, gpointer user_data)
-{
- GtkIMHtmlSmiley *smiley;
- GtkWidget *icon = NULL;
- GtkTextChildAnchor *anchor = NULL;
- GSList *current = NULL;
-
- smiley = (GtkIMHtmlSmiley *)user_data;
- if (!smiley->imhtml) {
-#ifdef DEBUG_CUSTOM_SMILEY
- purple_debug_error("custom-smiley", "gtk_custom_smiley_closed(): orphan smiley found: %p\n", smiley);
-#endif
- g_object_unref(G_OBJECT(loader));
- smiley->loader = NULL;
- return;
- }
-
- for (current = smiley->anchors; current; current = g_slist_next(current)) {
- anchor = GTK_TEXT_CHILD_ANCHOR(current->data);
- if (gtk_text_child_anchor_get_deleted(anchor))
- icon = NULL;
- else
- icon = gtk_image_new_from_animation(smiley->icon);
-
-#ifdef DEBUG_CUSTOM_SMILEY
- purple_debug_info("custom-smiley", "gtk_custom_smiley_closed(): got GtkImage %p from GtkPixbufAnimation %p for smiley '%s'\n",
- icon, smiley->icon, smiley->smile);
-#endif
- if (icon) {
- GList *wids;
- gtk_widget_show(icon);
-
- wids = gtk_text_child_anchor_get_widgets(anchor);
-
- g_object_set_data_full(G_OBJECT(anchor), "gtkimhtml_plaintext", purple_unescape_html(smiley->smile), g_free);
- g_object_set_data_full(G_OBJECT(anchor), "gtkimhtml_htmltext", g_strdup(smiley->smile), g_free);
-
- if (smiley->imhtml) {
- if (wids) {
- GList *children = gtk_container_get_children(GTK_CONTAINER(wids->data));
- g_list_foreach(children, (GFunc)gtk_widget_destroy, NULL);
- g_list_free(children);
- gtk_container_add(GTK_CONTAINER(wids->data), icon);
- } else
- gtk_text_view_add_child_at_anchor(GTK_TEXT_VIEW(smiley->imhtml), icon, anchor);
- }
- g_list_free(wids);
- }
- g_object_unref(anchor);
- }
-
- g_slist_free(smiley->anchors);
- smiley->anchors = NULL;
-
- g_object_unref(G_OBJECT(loader));
- smiley->loader = NULL;
-}
-
-static void
-gtk_custom_smiley_size_prepared(GdkPixbufLoader *loader, gint width, gint height, gpointer data)
-{
- if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/resize_custom_smileys")) {
- int custom_smileys_size = purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/custom_smileys_size");
- if (width <= custom_smileys_size && height <= custom_smileys_size)
- return;
-
- if (width >= height) {
- height = height * custom_smileys_size / width;
- width = custom_smileys_size;
- } else {
- width = width * custom_smileys_size / height;
- height = custom_smileys_size;
- }
- }
- gdk_pixbuf_loader_set_size(loader, width, height);
-}
-
-void
-gtk_imhtml_smiley_reload(GtkIMHtmlSmiley *smiley)
-{
- if (smiley->icon)
- g_object_unref(smiley->icon);
- if (smiley->loader)
- g_object_unref(smiley->loader); /* XXX: does this crash? */
-
- smiley->icon = NULL;
- smiley->loader = NULL;
-
- if (smiley->file) {
- /* We do not use the pixbuf loader for a smiley that can be loaded
- * from a file. (e.g., local custom smileys)
- */
- return;
- }
-
- smiley->loader = gdk_pixbuf_loader_new();
-
- g_signal_connect(smiley->loader, "area_prepared", G_CALLBACK(gtk_custom_smiley_allocated), smiley);
- g_signal_connect(smiley->loader, "closed", G_CALLBACK(gtk_custom_smiley_closed), smiley);
- g_signal_connect(smiley->loader, "size_prepared", G_CALLBACK(gtk_custom_smiley_size_prepared), smiley);
-}
-
-GtkIMHtmlSmiley *gtk_imhtml_smiley_create(const char *file, const char *shortcut, gboolean hide,
- GtkIMHtmlSmileyFlags flags)
-{
- GtkIMHtmlSmiley *smiley = g_new0(GtkIMHtmlSmiley, 1);
- smiley->file = g_strdup(file);
- smiley->smile = g_strdup(shortcut);
- smiley->hidden = hide;
- smiley->flags = flags;
- smiley->imhtml = NULL;
- gtk_imhtml_smiley_reload(smiley);
- return smiley;
-}
-
-void gtk_imhtml_smiley_destroy(GtkIMHtmlSmiley *smiley)
-{
- gtk_imhtml_disassociate_smiley(smiley);
- g_free(smiley->smile);
- g_free(smiley->file);
- if (smiley->icon)
- g_object_unref(smiley->icon);
- if (smiley->loader)
- g_object_unref(smiley->loader);
- g_free(smiley->data);
- g_free(smiley);
-}
-
-gboolean gtk_imhtml_class_register_protocol(const char *name,
- gboolean (*activate)(GtkIMHtml *imhtml, GtkIMHtmlLink *link),
- gboolean (*context_menu)(GtkIMHtml *imhtml, GtkIMHtmlLink *link, GtkWidget *menu))
-{
- GtkIMHtmlClass *klass;
- GtkIMHtmlProtocol *proto;
-
- g_return_val_if_fail(name, FALSE);
-
- klass = g_type_class_ref(GTK_TYPE_IMHTML);
- g_return_val_if_fail(klass, FALSE);
-
- if ((proto = imhtml_find_protocol(name, TRUE))) {
- if (activate) {
- return FALSE;
- }
- klass->protocols = g_list_remove(klass->protocols, proto);
- g_free(proto->name);
- g_free(proto);
- return TRUE;
- } else if (!activate) {
- return FALSE;
- }
-
- proto = g_new0(GtkIMHtmlProtocol, 1);
- proto->name = g_strdup(name);
- proto->length = strlen(name);
- proto->activate = activate;
- proto->context_menu = context_menu;
- klass->protocols = g_list_prepend(klass->protocols, proto);
-
- return TRUE;
-}
-
-static void
-gtk_imhtml_activate_tag(GtkIMHtml *imhtml, GtkTextTag *tag)
-{
- /* A link was clicked--we emit the "url_clicked" signal
- * with the URL as the argument */
- g_object_ref(G_OBJECT(tag));
- g_signal_emit(imhtml, signals[URL_CLICKED], 0, g_object_get_data(G_OBJECT(tag), "link_url"));
- g_object_unref(G_OBJECT(tag));
- g_object_set_data(G_OBJECT(tag), "visited", GINT_TO_POINTER(TRUE));
- gtk_imhtml_set_link_color(GTK_IMHTML(imhtml), tag);
-}
-
-gboolean gtk_imhtml_link_activate(GtkIMHtmlLink *link)
-{
- g_return_val_if_fail(link, FALSE);
-
- if (link->tag) {
- gtk_imhtml_activate_tag(link->imhtml, link->tag);
- } else if (link->url) {
- g_signal_emit(link->imhtml, signals[URL_CLICKED], 0, link->url);
- } else
- return FALSE;
- return TRUE;
-}
-
-const char *gtk_imhtml_link_get_url(GtkIMHtmlLink *link)
-{
- return link->url;
-}
-
-const GtkTextTag * gtk_imhtml_link_get_text_tag(GtkIMHtmlLink *link)
-{
- return link->tag;
-}
-
-static gboolean return_add_newline_cb(GtkWidget *widget, gpointer data)
-{
- GtkTextBuffer *buffer;
- GtkTextMark *mark;
- GtkTextIter iter;
-
- buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(widget));
-
- /* Delete any currently selected text */
- gtk_text_buffer_delete_selection(buffer, TRUE, TRUE);
-
- /* Insert a newline at the current cursor position */
- mark = gtk_text_buffer_get_insert(buffer);
- gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
- gtk_imhtml_insert_html_at_iter(GTK_IMHTML(widget), "\n", 0, &iter);
-
- /*
- * If we just newlined ourselves past the end of the visible area
- * then scroll down so the cursor is in view.
- */
- gtk_text_view_scroll_to_mark(GTK_TEXT_VIEW(widget),
- gtk_text_buffer_get_insert(buffer),
- 0, FALSE, 0.0, 0.0);
-
- return TRUE;
-}
-
-/*
- * It's kind of a pain that we need this function and the above just
- * to reinstate the default GtkTextView behavior. It might be better
- * if GtkIMHtml didn't intercept the enter key and just required the
- * application to deal with it--it's really not much more work than it
- * is to connect to the current "message_send" signal.
- */
-void gtk_imhtml_set_return_inserts_newline(GtkIMHtml *imhtml)
-{
- g_signal_connect(G_OBJECT(imhtml), "message_send",
- G_CALLBACK(return_add_newline_cb), NULL);
-}
-
-void gtk_imhtml_set_populate_primary_clipboard(GtkIMHtml *imhtml, gboolean populate)
-{
- gulong signal_id;
- signal_id = g_signal_handler_find(imhtml->text_buffer,
- G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_UNBLOCKED, 0, 0, NULL,
- mark_set_so_update_selection_cb, NULL);
- if (populate) {
- if (!signal_id) {
- /* We didn't find an unblocked signal handler, which means there
- is a blocked handler. Now unblock it.
- This is necessary to avoid a mutex-lock when the debug message
- saying 'no handler is blocked' is printed in the debug window.
- -- sad
- */
- g_signal_handlers_unblock_matched(imhtml->text_buffer,
- G_SIGNAL_MATCH_FUNC, 0, 0, NULL,
- mark_set_so_update_selection_cb, NULL);
- }
- } else {
- /* Block only if we found an unblocked handler */
- if (signal_id)
- g_signal_handler_block(imhtml->text_buffer, signal_id);
- }
-}
--- a/pidgin/gtkimhtml.h Sun Feb 02 02:00:31 2014 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,831 +0,0 @@
-/**
- * @file gtkimhtml.h GTK+ IM/HTML rendering component
- * @ingroup pidgin
- * @see @ref gtkimhtml-signals
- */
-
-/* Pidgin is the legal property of its developers, whose names are too numerous
- * to list here. Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
- */
-#ifndef _PIDGINIMHTML_H_
-#define _PIDGINIMHTML_H_
-
-#include <gdk/gdk.h>
-#include <gtk/gtk.h>
-#include "gtksourceundomanager.h"
-
-#include "connection.h"
-
-G_BEGIN_DECLS
-
-/**************************************************************************
- * @name Structures
- **************************************************************************/
-/*@{*/
-
-#define GTK_TYPE_IMHTML (gtk_imhtml_get_type())
-#define GTK_IMHTML(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GTK_TYPE_IMHTML, GtkIMHtml))
-#define GTK_IMHTML_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GTK_TYPE_IMHTML, GtkIMHtmlClass))
-#define GTK_IS_IMHTML(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GTK_TYPE_IMHTML))
-#define GTK_IS_IMHTML_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GTK_TYPE_IMHTML))
-#define GTK_IMHTML_SCALABLE(obj) ((GtkIMHtmlScalable *)obj)
-#define GTK_IMHTML_ANIMATION(obj) ((GtkIMHtmlAnimation *)obj)
-
-typedef struct _GtkIMHtml GtkIMHtml;
-typedef struct _GtkIMHtmlClass GtkIMHtmlClass;
-typedef struct _GtkSmileyTree GtkSmileyTree;
-typedef struct _GtkIMHtmlSmiley GtkIMHtmlSmiley;
-typedef struct _GtkIMHtmlScalable GtkIMHtmlScalable;
-typedef struct _GtkIMHtmlImage GtkIMHtmlImage;
-typedef struct _GtkIMHtmlAnimation GtkIMHtmlAnimation;
-typedef struct _GtkIMHtmlHr GtkIMHtmlHr;
-typedef struct _GtkIMHtmlFuncs GtkIMHtmlFuncs;
-
-typedef struct _GtkIMHtmlLink GtkIMHtmlLink;
-
-typedef enum {
- GTK_IMHTML_BOLD = 1 << 0,
- GTK_IMHTML_ITALIC = 1 << 1,
- GTK_IMHTML_UNDERLINE = 1 << 2,
- GTK_IMHTML_GROW = 1 << 3,
- GTK_IMHTML_SHRINK = 1 << 4,
- GTK_IMHTML_FACE = 1 << 5,
- GTK_IMHTML_FORECOLOR = 1 << 6,
- GTK_IMHTML_BACKCOLOR = 1 << 7,
- GTK_IMHTML_BACKGROUND = 1 << 8,
- GTK_IMHTML_LINK = 1 << 9,
- GTK_IMHTML_IMAGE = 1 << 10,
- GTK_IMHTML_SMILEY = 1 << 11,
- GTK_IMHTML_LINKDESC = 1 << 12,
- GTK_IMHTML_STRIKE = 1 << 13,
- /** Show custom smileys when appropriate. */
- GTK_IMHTML_CUSTOM_SMILEY = 1 << 14,
- GTK_IMHTML_ALL = -1
-} GtkIMHtmlButtons;
-
-typedef enum {
- GTK_IMHTML_SMILEY_CUSTOM = 1 << 0
-} GtkIMHtmlSmileyFlags;
-
-struct _GtkIMHtml {
- GtkTextView text_view;
- GtkTextBuffer *text_buffer;
- GdkCursor *hand_cursor;
- GdkCursor *arrow_cursor;
- GdkCursor *text_cursor;
- GHashTable *smiley_data;
- GtkSmileyTree *default_smilies;
- char *protocol_name;
- guint scroll_src;
- GTimer *scroll_time;
- GQueue *animations;
- int num_animations;
-
- gboolean show_comments;
-
- GtkWidget *tip_window;
- char *tip;
- guint tip_timer;
- GtkTextTag *prelit_tag;
-
- GList *scalables;
- GdkRectangle old_rect;
-
- gchar *search_string;
-
- gboolean editable;
- GtkIMHtmlButtons format_functions;
- gboolean wbfo; /* Whole buffer formatting only. */
-
- gint insert_offset;
-
- struct {
- gboolean bold:1;
- gboolean italic:1;
- gboolean underline:1;
- gboolean strike:1;
- gchar *forecolor;
- gchar *backcolor;
- gchar *background;
- gchar *fontface;
- int fontsize;
- GtkTextTag *link;
- } edit;
-
- GSList *im_images;
- GtkIMHtmlFuncs *funcs;
- GtkSourceUndoManager *undo_manager;
-};
-
-struct _GtkIMHtmlClass {
- GtkTextViewClass parent_class;
-
- void (*url_clicked)(GtkIMHtml *, const gchar *);
- void (*buttons_update)(GtkIMHtml *, GtkIMHtmlButtons);
- void (*toggle_format)(GtkIMHtml *, GtkIMHtmlButtons);
- void (*clear_format)(GtkIMHtml *);
- void (*update_format)(GtkIMHtml *);
- gboolean (*message_send)(GtkIMHtml *);
- void (*undo)(GtkIMHtml *);
- void (*redo)(GtkIMHtml *);
- GList *protocols; /* List of GtkIMHtmlProtocol's */
-};
-
-struct _GtkIMHtmlSmiley {
- gchar *smile;
- gchar *file;
- GdkPixbufAnimation *icon;
- gboolean hidden;
- GdkPixbufLoader *loader;
- GSList *anchors;
- GtkIMHtmlSmileyFlags flags;
- GtkIMHtml *imhtml;
- gpointer data;
- gsize datasize;
-};
-
-typedef enum {
- GTK_IMHTML_NO_COLOURS = 1 << 0,
- GTK_IMHTML_NO_FONTS = 1 << 1,
- GTK_IMHTML_NO_COMMENTS = 1 << 2, /* Remove */
- 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,
- GTK_IMHTML_USE_POINTSIZE = 1 << 8,
- GTK_IMHTML_NO_FORMATTING = 1 << 9,
- GTK_IMHTML_USE_SMOOTHSCROLLING = 1 << 10,
- GTK_IMHTML_NO_SMILEY = 1 << 11
-} GtkIMHtmlOptions;
-
-enum {
- GTK_IMHTML_DRAG_HTML = 0,
- GTK_IMHTML_DRAG_TEXT,
- GTK_IMHTML_DRAG_UTF8_STRING,
- GTK_IMHTML_DRAG_URL,
- GTK_IMHTML_DRAG_COMPOUND_TEXT,
- GTK_IMHTML_DRAG_STRING,
- GTK_IMHTML_DRAG_NUM
-};
-
-#define GTK_IMHTML_DND_TARGETS \
- { "text/uri-list", 0, GTK_IMHTML_DRAG_URL }, \
- { "_NETSCAPE_URL", 0, GTK_IMHTML_DRAG_URL }, \
- { "text/html", 0, GTK_IMHTML_DRAG_HTML }, \
- { "x-url/ftp", 0, GTK_IMHTML_DRAG_URL }, \
- { "x-url/http", 0, GTK_IMHTML_DRAG_URL }, \
- { "UTF8_STRING", 0, GTK_IMHTML_DRAG_UTF8_STRING }, \
- { "COMPOUND_TEXT", 0, GTK_IMHTML_DRAG_COMPOUND_TEXT }, \
- { "STRING", 0, GTK_IMHTML_DRAG_STRING }, \
- { "text/plain", 0, GTK_IMHTML_DRAG_TEXT }, \
- { "TEXT", 0, GTK_IMHTML_DRAG_TEXT }
-
-typedef gpointer (*GtkIMHtmlGetImageFunc) (int id);
-typedef gpointer (*GtkIMHtmlGetImageDataFunc) (gpointer i);
-typedef size_t (*GtkIMHtmlGetImageSizeFunc) (gpointer i);
-typedef const char *(*GtkIMHtmlGetImageFilenameFunc)(gpointer i);
-typedef void (*GtkIMHtmlImageRefFunc) (int id);
-typedef void (*GtkIMHtmlImageUnrefFunc) (int id);
-
-struct _GtkIMHtmlFuncs {
- GtkIMHtmlGetImageFunc image_get;
- GtkIMHtmlGetImageDataFunc image_get_data;
- GtkIMHtmlGetImageSizeFunc image_get_size;
- GtkIMHtmlGetImageFilenameFunc image_get_filename;
- GtkIMHtmlImageRefFunc image_ref;
- GtkIMHtmlImageUnrefFunc image_unref;
-};
-
-/*@}*/
-
-/**************************************************************************
- * @name GTK+ IM/HTML rendering component API
- **************************************************************************/
-/*@{*/
-
-/**
- * Returns the GType object for an IM/HTML widget.
- *
- * @return The GType for an IM/HTML widget.
- */
-GType gtk_imhtml_get_type(void);
-
-/**
- * Creates and returns a new GTK+ IM/HTML widget.
- *
- * @return The GTK+ IM/HTML widget created.
- */
-GtkWidget *gtk_imhtml_new(void *, void *);
-
-/**
- * Returns the smiley object associated with the text.
- *
- * @param imhtml The GTK+ IM/HTML.
- * @param sml The name of the smiley category.
- * @param text The text associated with the smiley.
- */
-
-GtkIMHtmlSmiley *gtk_imhtml_smiley_get(GtkIMHtml * imhtml,
- const gchar * sml, const gchar * text);
-
-
-/**
- * Associates a smiley with a GTK+ IM/HTML.
- *
- * @param imhtml The GTK+ IM/HTML.
- * @param sml The name of the smiley category.
- * @param smiley The GtkIMSmiley to associate.
- */
-void gtk_imhtml_associate_smiley(GtkIMHtml *imhtml, const gchar *sml, GtkIMHtmlSmiley *smiley);
-
-/**
- * Removes all smileys associated with a GTK+ IM/HTML.
- *
- * @param imhtml The GTK+ IM/HTML.
- */
-void gtk_imhtml_remove_smileys(GtkIMHtml *imhtml);
-
-/**
- * Sets the function callbacks to use with a GTK+ IM/HTML instance.
- *
- * @param imhtml The GTK+ IM/HTML.
- * @param f The GtkIMHTMLFuncs struct containing the functions to use.
- */
-void gtk_imhtml_set_funcs(GtkIMHtml *imhtml, GtkIMHtmlFuncs *f);
-
-/**
- * Enables or disables showing the contents of HTML comments in a GTK+ IM/HTML.
- *
- * @param imhtml The GTK+ IM/HTML.
- * @param show @c TRUE if comments should be shown, or @c FALSE otherwise.
- */
-void gtk_imhtml_show_comments(GtkIMHtml *imhtml, gboolean show);
-
-/**
- * Gets the protocol name associated with this GTK+ IM/HTML.
- *
- * @param imhtml The GTK+ IM/HTML
- */
-const char *gtk_imhtml_get_protocol_name(GtkIMHtml *imhtml);
-
-/**
- * Associates a protocol name with a GTK+ IM/HTML.
- *
- * @param imhtml The GTK+ IM/HTML.
- * @param protocol_name The protocol name to associate with the IM/HTML.
- */
-void gtk_imhtml_set_protocol_name(GtkIMHtml *imhtml, const gchar *protocol_name);
-
-/**
- * Appends HTML formatted text to a GTK+ IM/HTML.
- *
- * @param imhtml The GTK+ IM/HTML.
- * @param text The formatted text to append.
- * @param options A GtkIMHtmlOptions object indicating insert behavior.
- */
-#define gtk_imhtml_append_text(imhtml, text, options) \
- gtk_imhtml_append_text_with_images(imhtml, text, options, NULL)
-
-/**
- * Appends HTML formatted text to a GTK+ IM/HTML.
- *
- * @param imhtml The GTK+ IM/HTML.
- * @param text The formatted text to append.
- * @param options A GtkIMHtmlOptions object indicating insert behavior.
- * @param unused Use @c NULL value.
- */
-void gtk_imhtml_append_text_with_images(GtkIMHtml *imhtml,
- const gchar *text,
- GtkIMHtmlOptions options,
- GSList *unused);
-
-/**
- * Inserts HTML formatted text to a GTK+ IM/HTML at a given iter.
- *
- * @param imhtml The GTK+ IM/HTML.
- * @param text The formatted text to append.
- * @param options A GtkIMHtmlOptions object indicating insert behavior.
- * @param iter A GtkTextIter in the GTK+ IM/HTML at which to insert text.
- */
-void gtk_imhtml_insert_html_at_iter(GtkIMHtml *imhtml,
- const gchar *text,
- GtkIMHtmlOptions options,
- GtkTextIter *iter);
-
-/**
- * Scrolls a GTK+ IM/HTML to the end of its contents.
- *
- * @param imhtml The GTK+ IM/HTML.
- * @param smooth A boolean indicating if smooth scrolling should be used.
- */
-void gtk_imhtml_scroll_to_end(GtkIMHtml *imhtml, gboolean smooth);
-
-/**
- * Delete the contents of a GTK+ IM/HTML between start and end.
- *
- * @param imhtml The GTK+ IM/HTML.
- * @param start a postition in the imhtml's buffer
- * @param end another postition in the imhtml's buffer
- */
-void gtk_imhtml_delete(GtkIMHtml *imhtml, GtkTextIter *start, GtkTextIter *end);
-
-/**
- * Purges the contents from a GTK+ IM/HTML and resets formatting.
- *
- * @param imhtml The GTK+ IM/HTML.
- */
-#define gtk_imhtml_clear(imhtml) \
- gtk_imhtml_delete(imhtml, NULL, NULL)
-
-/**
- * Scrolls a GTK+ IM/HTML up by one page.
- *
- * @param imhtml The GTK+ IM/HTML.
- */
-void gtk_imhtml_page_up(GtkIMHtml *imhtml);
-
-/**
- * Scrolls a GTK+ IM/HTML down by one page.
- *
- * @param imhtml The GTK+ IM/HTML.
- */
-void gtk_imhtml_page_down(GtkIMHtml *imhtml);
-
-/**
- * Creates and returns an new GTK+ IM/HTML scalable object.
- *
- * @return A new IM/HTML Scalable object.
- */
-GtkIMHtmlScalable *gtk_imhtml_scalable_new(void);
-
-/**
- * Creates and returns an new GTK+ IM/HTML scalable with a horizontal rule.
- *
- * @return A new IM/HTML Scalable object with an image.
- */
-GtkIMHtmlScalable *gtk_imhtml_hr_new(void);
-
-/**
- * Destroys and frees a GTK+ IM/HTML scalable horizontal rule.
- *
- * @param scale The GTK+ IM/HTML scalable.
- */
-void gtk_imhtml_hr_free(GtkIMHtmlScalable *scale);
-
-/**
- * Rescales a GTK+ IM/HTML scalable horizontal rule to a given size.
- *
- * @param scale The GTK+ IM/HTML scalable.
- * @param width The new width.
- * @param height The new height.
- */
-void gtk_imhtml_hr_scale(GtkIMHtmlScalable *scale, int width, int height);
-
-/**
- * Adds a GTK+ IM/HTML scalable horizontal rule to a given GTK+ IM/HTML at
- * a given iter.
- *
- * @param scale The GTK+ IM/HTML scalable.
- * @param imhtml The GTK+ IM/HTML.
- * @param iter The GtkTextIter at which to add the scalable.
- */
-void gtk_imhtml_hr_add_to(GtkIMHtmlScalable *scale, GtkIMHtml *imhtml, GtkTextIter *iter);
-
-/**
- * Finds and highlights a given string in a GTK+ IM/HTML.
- *
- * @param imhtml The GTK+ IM/HTML.
- * @param text The string to search for.
- *
- * @return @c TRUE if a search was performed, or @c FALSE if not.
- */
-gboolean gtk_imhtml_search_find(GtkIMHtml *imhtml, const gchar *text);
-
-/**
- * Clears the highlighting from a prior search in a GTK+ IM/HTML.
- *
- * @param imhtml The GTK+ IM/HTML.
- */
-void gtk_imhtml_search_clear(GtkIMHtml *imhtml);
-
-/**
- * Enables or disables editing in a GTK+ IM/HTML.
- *
- * @param imhtml The GTK+ IM/HTML.
- * @param editable @c TRUE to make the widget editable, or @c FALSE otherwise.
- */
-void gtk_imhtml_set_editable(GtkIMHtml *imhtml, gboolean editable);
-
-/**
- * Enables or disables whole buffer formatting only (wbfo) in a GTK+ IM/HTML.
- * In this mode formatting options to the buffer take effect for the entire
- * buffer instead of specific text.
- *
- * @param imhtml The GTK+ IM/HTML.
- * @param wbfo @c TRUE to enable the mode, or @c FALSE otherwise.
- */
-void gtk_imhtml_set_whole_buffer_formatting_only(GtkIMHtml *imhtml, gboolean wbfo);
-
-/**
- * Indicates which formatting functions to enable and disable in a GTK+ IM/HTML.
- *
- * @param imhtml The GTK+ IM/HTML.
- * @param buttons A GtkIMHtmlButtons bitmask indicating which functions to use.
- */
-void gtk_imhtml_set_format_functions(GtkIMHtml *imhtml, GtkIMHtmlButtons buttons);
-
-/**
- * Returns which formatting functions are enabled in a GTK+ IM/HTML.
- *
- * @param imhtml The GTK+ IM/HTML.
- *
- * @return A GtkIMHtmlButtons bitmask indicating which functions to are enabled.
- */
-GtkIMHtmlButtons gtk_imhtml_get_format_functions(GtkIMHtml *imhtml);
-
-/**
- * Sets each boolean to @c TRUE or @c FALSE to indicate if that formatting option
- * is enabled at the current position in a GTK+ IM/HTML.
- *
- * @param imhtml The GTK+ IM/HTML.
- * @param bold The boolean to set for bold or @c NULL.
- * @param italic The boolean to set for italic or @c NULL.
- * @param underline The boolean to set for underline or @c NULL.
- */
-void gtk_imhtml_get_current_format(GtkIMHtml *imhtml, gboolean *bold, gboolean *italic, gboolean *underline);
-
-/**
- * Returns a string containing the selected font face at the current position
- * in a GTK+ IM/HTML.
- *
- * @param imhtml The GTK+ IM/HTML.
- *
- * @return A string containg the font face or @c NULL if none is set.
- */
-char *gtk_imhtml_get_current_fontface(GtkIMHtml *imhtml);
-
-/**
- * Returns a string containing the selected foreground color at the current
- * position in a GTK+ IM/HTML.
- *
- * @param imhtml The GTK+ IM/HTML.
- *
- * @return A string containg the foreground color or @c NULL if none is set.
- */
-char *gtk_imhtml_get_current_forecolor(GtkIMHtml *imhtml);
-
-/**
- * Returns a string containing the selected font background color at the current
- * position in a GTK+ IM/HTML.
- *
- * @param imhtml The GTK+ IM/HTML.
- *
- * @return A string containg the font background color or @c NULL if none is set.
- */
-char *gtk_imhtml_get_current_backcolor(GtkIMHtml *imhtml);
-
-/**
- * Returns a string containing the selected background color at the current
- * position in a GTK+ IM/HTML.
- *
- * @param imhtml The GTK+ IM/HTML.
- *
- * @return A string containg the background color or @c NULL if none is set.
- */
-char *gtk_imhtml_get_current_background(GtkIMHtml *imhtml);
-
-/**
- * Returns a integer containing the selected HTML font size at the current
- * position in a GTK+ IM/HTML.
- *
- * @param imhtml The GTK+ IM/HTML.
- *
- * @return The HTML font size.
- */
-gint gtk_imhtml_get_current_fontsize(GtkIMHtml *imhtml);
-
-/**
- * Checks whether a GTK+ IM/HTML is marked as editable.
- *
- * @param imhtml The GTK+ IM/HTML.
- *
- * @return @c TRUE if the IM/HTML is editable, or @c FALSE otherwise.
- */
-gboolean gtk_imhtml_get_editable(GtkIMHtml *imhtml);
-
-/**
- * Clear all the formatting on a GTK+ IM/HTML.
- *
- * @param imhtml The GTK+ IM/HTML.
- */
-void gtk_imhtml_clear_formatting(GtkIMHtml *imhtml);
-
-/**
- * Toggles bold at the cursor location or selection in a GTK+ IM/HTML.
- *
- * @param imhtml The GTK+ IM/HTML.
- */
-void gtk_imhtml_toggle_bold(GtkIMHtml *imhtml);
-
-/**
- * Toggles italic at the cursor location or selection in a GTK+ IM/HTML.
- *
- * @param imhtml The GTK+ IM/HTML.
- */
-void gtk_imhtml_toggle_italic(GtkIMHtml *imhtml);
-
-/**
- * Toggles underline at the cursor location or selection in a GTK+ IM/HTML.
- *
- * @param imhtml The GTK+ IM/HTML.
- */
-void gtk_imhtml_toggle_underline(GtkIMHtml *imhtml);
-
-/**
- * Toggles strikethrough at the cursor location or selection in a GTK+ IM/HTML.
- *
- * @param imhtml The GTK+ IM/HTML.
- */
-void gtk_imhtml_toggle_strike(GtkIMHtml *imhtml);
-
-/**
- * Toggles a foreground color at the current location or selection in a GTK
- * IM/HTML.
- *
- * @param imhtml The GTK+ IM/HTML.
- * @param color The HTML-style color, or @c NULL or "" to clear the color.
- *
- * @return @c TRUE if a color was set, or @c FALSE if it was cleared.
- */
-gboolean gtk_imhtml_toggle_forecolor(GtkIMHtml *imhtml, const char *color);
-
-/**
- * Toggles a background color at the current location or selection in a GTK
- * IM/HTML.
- *
- * @param imhtml The GTK+ IM/HTML.
- * @param color The HTML-style color, or @c NULL or "" to clear the color.
- *
- * @return @c TRUE if a color was set, or @c FALSE if it was cleared.
- */
-gboolean gtk_imhtml_toggle_backcolor(GtkIMHtml *imhtml, const char *color);
-
-/**
- * Toggles a background color at the current location or selection in a GTK
- * IM/HTML.
- *
- * @param imhtml The GTK+ IM/HTML.
- * @param color The HTML-style color, or @c NULL or "" to clear the color.
- *
- * @return @c TRUE if a color was set, or @c FALSE if it was cleared.
- */
-gboolean gtk_imhtml_toggle_background(GtkIMHtml *imhtml, const char *color);
-
-/**
- * Toggles a font face at the current location or selection in a GTK+ IM/HTML.
- *
- * @param imhtml The GTK+ IM/HTML.
- * @param face The font face name, or @c NULL or "" to clear the font.
- *
- * @return @c TRUE if a font name was set, or @c FALSE if it was cleared.
- */
-gboolean gtk_imhtml_toggle_fontface(GtkIMHtml *imhtml, const char *face);
-
-/**
- * Toggles a link tag with the given URL at the current location or selection
- * in a GTK+ IM/HTML.
- *
- * @param imhtml The GTK+ IM/HTML.
- * @param url The URL for the link or @c NULL to terminate the link.
- */
-void gtk_imhtml_toggle_link(GtkIMHtml *imhtml, const char *url);
-
-/**
- * Inserts a link to the given url at the given GtkTextMark in a GTK+ IM/HTML.
- *
- * @param imhtml The GTK+ IM/HTML.
- * @param mark The GtkTextMark to insert the link at.
- * @param url The URL for the link.
- * @param text The string to use for the link description.
- */
-void gtk_imhtml_insert_link(GtkIMHtml *imhtml, GtkTextMark *mark, const char *url, const char *text);
-
-/**
- * Inserts a smiley at the current location or selection in a GTK+ IM/HTML.
- *
- * @param imhtml The GTK+ IM/HTML.
- * @param sml The category of the smiley.
- * @param smiley The text of the smiley to insert.
- */
-void gtk_imhtml_insert_smiley(GtkIMHtml *imhtml, const char *sml, char *smiley);
-/**
- * Inserts a smiley at the given iter in a GTK+ IM/HTML.
- *
- * @param imhtml The GTK+ IM/HTML.
- * @param sml The category of the smiley.
- * @param smiley The text of the smiley to insert.
- * @param iter The GtkTextIter in the IM/HTML to insert the smiley at.
- */
-void gtk_imhtml_insert_smiley_at_iter(GtkIMHtml *imhtml, const char *sml, char *smiley, GtkTextIter *iter);
-
-/**
- * Inserts the IM/HTML scalable image with the given id at the given iter in a
- * GTK+ IM/HTML.
- *
- * @param imhtml The GTK+ IM/HTML.
- * @param id The id of the IM/HTML scalable.
- * @param iter The GtkTextIter in the IM/HTML to insert the image at.
- */
-void gtk_imhtml_insert_image_at_iter(GtkIMHtml *imhtml, int id, GtkTextIter *iter);
-
-/**
- * Sets the font size at the current location or selection in a GTK+ IM/HTML.
- *
- * @param imhtml The GTK+ IM/HTML.
- * @param size The HTML font size to use.
- */
-void gtk_imhtml_font_set_size(GtkIMHtml *imhtml, gint size);
-
-/**
- * Decreases the font size by 1 at the current location or selection in a GTK
- * IM/HTML.
- *
- * @param imhtml The GTK+ IM/HTML.
- */
-void gtk_imhtml_font_shrink(GtkIMHtml *imhtml);
-
-/**
- * Increases the font size by 1 at the current location or selection in a GTK
- * IM/HTML.
- *
- * @param imhtml The GTK+ IM/HTML.
- */
-void gtk_imhtml_font_grow(GtkIMHtml *imhtml);
-
-/**
- * Returns the HTML formatted contents between two iters in a GTK+ IM/HTML.
- *
- * @param imhtml The GTK+ IM/HTML.
- * @param start The GtkTextIter indicating the start point in the IM/HTML.
- * @param end The GtkTextIter indicating the end point in the IM/HTML.
- *
- * @return A string containing the HTML formatted text.
- */
-char *gtk_imhtml_get_markup_range(GtkIMHtml *imhtml, GtkTextIter *start, GtkTextIter *end);
-
-/**
- * Returns the entire HTML formatted contents of a GTK+ IM/HTML.
- *
- * @param imhtml The GTK+ IM/HTML.
- *
- * @return A string containing the HTML formatted text.
- */
-char *gtk_imhtml_get_markup(GtkIMHtml *imhtml);
-
-/**
- * Returns a null terminated array of pointers to null terminated strings, each
- * string for each line. g_strfreev() should be called to free it when done.
- *
- * @param imhtml The GTK+ IM/HTML.
- *
- * @return A null terminated array of null terminated HTML formatted strings.
- */
-char **gtk_imhtml_get_markup_lines(GtkIMHtml *imhtml);
-
-/**
- * Returns the entire unformatted (plain text) contents of a GTK+ IM/HTML
- * between two iters in a GTK+ IM/HTML.
- *
- * @param imhtml The GTK+ IM/HTML.
- * @param start The GtkTextIter indicating the start point in the IM/HTML.
- * @param stop The GtkTextIter indicating the end point in the IM/HTML.
- *
- * @return A string containing the unformatted text.
- */
-char *gtk_imhtml_get_text(GtkIMHtml *imhtml, GtkTextIter *start, GtkTextIter *stop);
-
-/**
- * Setup formatting for an imhtml depending on the flags specified.
- *
- * @param imhtml The GTK+ IM/HTML.
- * @param flags The connection flag which describes the allowed types of formatting.
- */
-void gtk_imhtml_setup_entry(GtkIMHtml *imhtml, PurpleConnectionFlags flags);
-
-/**
- * Create a new GtkIMHtmlSmiley.
- *
- * @param file The image file for the smiley
- * @param shortcut The key shortcut for the smiley
- * @param hide @c TRUE if the smiley should be hidden in the smiley dialog, @c FALSE otherwise
- * @param flags The smiley flags
- *
- * @return The newly created smiley
- */
-GtkIMHtmlSmiley *gtk_imhtml_smiley_create(const char *file, const char *shortcut, gboolean hide,
- GtkIMHtmlSmileyFlags flags);
-
-/**
- * Reload the image data for the smiley.
- *
- * @param smiley The smiley to reload
- */
-void gtk_imhtml_smiley_reload(GtkIMHtmlSmiley *smiley);
-
-/**
- * Destroy a GtkIMHtmlSmiley.
- *
- * @param smiley The smiley to destroy
- */
-void gtk_imhtml_smiley_destroy(GtkIMHtmlSmiley *smiley);
-
-/**
- * Register a protocol with the GtkIMHtml widget. Registering a protocol would
- * allow certain text to be clickable.
- *
- * @param name The name of the protocol (e.g. http://)
- * @param activate The callback to trigger when the protocol text is clicked.
- * Removes any current protocol definition if @c NULL. The
- * callback should return @c TRUE if the link was activated
- * properly, @c FALSE otherwise.
- * @param context_menu The callback to trigger when the context menu is popped
- * up on the protocol text. The callback should return
- * @c TRUE if the request for context menu was processed
- * successfully, @c FALSE otherwise.
- *
- * @return @c TRUE if the protocol was successfully registered (or unregistered, when \a activate is @c NULL)
- */
-gboolean gtk_imhtml_class_register_protocol(const char *name,
- gboolean (*activate)(GtkIMHtml *imhtml, GtkIMHtmlLink *link),
- gboolean (*context_menu)(GtkIMHtml *imhtml, GtkIMHtmlLink *link, GtkWidget *menu));
-
-/**
- * Get the URL associated with a link. This should be used by the IMHtml protocol-callbacks.
- *
- * @param link The GtkIMHtmlLink object sent to the callback functions
- *
- * @return The URL
- */
-const char *gtk_imhtml_link_get_url(GtkIMHtmlLink *link);
-
-/**
- * Get the GtkTextTag object (if any) associated with a particular link.
- *
- * @param link The GtkIMHtmlLink object sent to the callback functions
- *
- * @return The GtkTextTag object, or @c NULL
- */
-const GtkTextTag *gtk_imhtml_link_get_text_tag(GtkIMHtmlLink *link);
-
-/**
- * Activates a GtkIMHtmlLink object. This triggers the 'url-clicked' signal, marks the
- * link as visited (when possible).
- *
- * @param link The GtkIMHtmlLink object sent to the callback functions
- *
- * @return @c TRUE if 'url-clicked' signal was emitted, @c FALSE otherwise.
- */
-gboolean gtk_imhtml_link_activate(GtkIMHtmlLink *link);
-
-/**
- * By default this widget intercepts presses of the "return" key and
- * emits the "message_send" signal instead. If you don't want this
- * behavior, and you want the standard GtkTextView behavior of
- * inserting a newline into the buffer, then call this function.
- *
- * @param imhtml The GtkIMHtml where you want the "return" key to add
- * newline and not emit the "message_send" signal.
- */
-void gtk_imhtml_set_return_inserts_newline(GtkIMHtml *imhtml);
-
-/**
- * By default this widget populates the PRIMARY clipboard with any selected
- * text (as you would expect). For scenarios (e.g. select-on-focus) where this
- * would be problematic, this function can disable the PRIMARY population.
- *
- * @param imhtml The GtkIMHtml to enable/disable populating PRIMARY
- * @param populate enable/disable PRIMARY population
- */
-void gtk_imhtml_set_populate_primary_clipboard(GtkIMHtml *imhtml, gboolean populate);
-
-/*@}*/
-
-G_END_DECLS
-
-#endif /* _PIDGINIMHTML_H_ */
--- a/pidgin/gtkimhtmltoolbar.c Sun Feb 02 02:00:31 2014 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,1606 +0,0 @@
-/*
- * @file gtkimhtmltoolbar.c GTK+ IMHtml Toolbar
- * @ingroup pidgin
- */
-
-/* pidgin
- *
- * Pidgin is the legal property of its developers, whose names are too numerous
- * to list here. Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
- *
- */
-#include "internal.h"
-#include "pidgin.h"
-
-#include "imgstore.h"
-#include "notify.h"
-#include "prefs.h"
-#include "request.h"
-#include "pidginstock.h"
-#include "util.h"
-#include "debug.h"
-
-#include "gtkdialogs.h"
-#include "gtkimhtmltoolbar.h"
-#include "gtksmiley.h"
-#include "gtkthemes.h"
-#include "gtkutils.h"
-
-#include <gdk/gdkkeysyms.h>
-
-#define GTK_TOOLTIPS_VAR toolbar->tooltips
-#include "gtk3compat.h"
-
-static GtkHBoxClass *parent_class = NULL;
-
-static void toggle_button_set_active_block(GtkToggleButton *button,
- gboolean is_active,
- GtkIMHtmlToolbar *toolbar);
-
-static gboolean
-gtk_imhtmltoolbar_popup_menu(GtkWidget *widget,
- GdkEventButton *event, GtkIMHtmlToolbar *toolbar);
-
-static void do_bold(GtkWidget *bold, GtkIMHtmlToolbar *toolbar)
-{
- g_return_if_fail(toolbar != NULL);
- gtk_imhtml_toggle_bold(GTK_IMHTML(toolbar->imhtml));
- gtk_widget_grab_focus(toolbar->imhtml);
-}
-
-static void
-do_italic(GtkWidget *italic, GtkIMHtmlToolbar *toolbar)
-{
- g_return_if_fail(toolbar != NULL);
- gtk_imhtml_toggle_italic(GTK_IMHTML(toolbar->imhtml));
- gtk_widget_grab_focus(toolbar->imhtml);
-}
-
-static void
-do_underline(GtkWidget *underline, GtkIMHtmlToolbar *toolbar)
-{
- g_return_if_fail(toolbar != NULL);
- gtk_imhtml_toggle_underline(GTK_IMHTML(toolbar->imhtml));
- gtk_widget_grab_focus(toolbar->imhtml);
-}
-
-static void
-do_strikethrough(GtkWidget *strikethrough, GtkIMHtmlToolbar *toolbar)
-{
- g_return_if_fail(toolbar != NULL);
- gtk_imhtml_toggle_strike(GTK_IMHTML(toolbar->imhtml));
- gtk_widget_grab_focus(toolbar->imhtml);
-}
-
-static void
-do_small(GtkWidget *smalltb, GtkIMHtmlToolbar *toolbar)
-{
- g_return_if_fail(toolbar != NULL);
- /* Only shrink the font on activation, not deactivation as well */
- if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(smalltb)))
- gtk_imhtml_font_shrink(GTK_IMHTML(toolbar->imhtml));
- gtk_widget_grab_focus(toolbar->imhtml);
-}
-
-static void
-do_big(GtkWidget *large, GtkIMHtmlToolbar *toolbar)
-{
- g_return_if_fail(toolbar);
- /* Only grow the font on activation, not deactivation as well */
- if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(large)))
- gtk_imhtml_font_grow(GTK_IMHTML(toolbar->imhtml));
- gtk_widget_grab_focus(toolbar->imhtml);
-}
-
-static gboolean
-destroy_toolbar_font(GtkWidget *widget, GdkEvent *event,
- GtkIMHtmlToolbar *toolbar)
-{
- if (widget != NULL)
- gtk_imhtml_toggle_fontface(GTK_IMHTML(toolbar->imhtml), "");
-
- if (toolbar->font_dialog != NULL)
- {
- gtk_widget_destroy(toolbar->font_dialog);
- toolbar->font_dialog = NULL;
- }
- return FALSE;
-}
-
-static void
-realize_toolbar_font(GtkWidget *widget, GtkIMHtmlToolbar *toolbar)
-{
- GtkFontSelection *sel;
-
- sel = GTK_FONT_SELECTION(
- gtk_font_selection_dialog_get_font_selection(GTK_FONT_SELECTION_DIALOG(toolbar->font_dialog)));
- gtk_widget_hide(gtk_widget_get_parent(
- gtk_font_selection_get_size_entry(sel)));
- gtk_widget_show_all(gtk_font_selection_get_family_list(sel));
- gtk_widget_show(gtk_widget_get_parent(
- gtk_font_selection_get_family_list(sel)));
- gtk_widget_show(gtk_widget_get_parent(gtk_widget_get_parent(
- gtk_font_selection_get_family_list(sel))));
-}
-
-static void
-cancel_toolbar_font(GtkWidget *widget, GtkIMHtmlToolbar *toolbar)
-{
- destroy_toolbar_font(widget, NULL, toolbar);
-}
-
-static void
-apply_font(GtkWidget *widget, GtkFontSelectionDialog *fontsel)
-{
- /* this could be expanded to include font size, weight, etc.
- but for now only works with font face */
- gchar *fontname = gtk_font_selection_dialog_get_font_name(fontsel);
- GtkIMHtmlToolbar *toolbar = g_object_get_data(G_OBJECT(fontsel),
- "purple_toolbar");
-
- if (fontname) {
- const gchar *family_name = NULL;
- PangoFontDescription *desc = NULL;
-
- desc = pango_font_description_from_string(fontname);
- family_name = pango_font_description_get_family(desc);
-
- if (family_name) {
- gtk_imhtml_toggle_fontface(GTK_IMHTML(toolbar->imhtml),
- family_name);
- }
-
- pango_font_description_free(desc);
- g_free(fontname);
- }
-
- cancel_toolbar_font(NULL, toolbar);
-}
-
-static void
-toggle_font(GtkWidget *font, GtkIMHtmlToolbar *toolbar)
-{
- g_return_if_fail(toolbar);
-
- if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(font))) {
- char *fontname = gtk_imhtml_get_current_fontface(GTK_IMHTML(toolbar->imhtml));
-
- if (!toolbar->font_dialog) {
- toolbar->font_dialog = gtk_font_selection_dialog_new(_("Select Font"));
-
- g_object_set_data(G_OBJECT(toolbar->font_dialog), "purple_toolbar", toolbar);
-
- if(fontname) {
- char *fonttif = g_strdup_printf("%s 12", fontname);
- g_free(fontname);
- gtk_font_selection_dialog_set_font_name(GTK_FONT_SELECTION_DIALOG(toolbar->font_dialog),
- fonttif);
- g_free(fonttif);
- } else {
- gtk_font_selection_dialog_set_font_name(GTK_FONT_SELECTION_DIALOG(toolbar->font_dialog),
- DEFAULT_FONT_FACE);
- }
-
- g_signal_connect(G_OBJECT(toolbar->font_dialog), "delete_event",
- G_CALLBACK(destroy_toolbar_font), toolbar);
- g_signal_connect(G_OBJECT(
- gtk_font_selection_dialog_get_ok_button(GTK_FONT_SELECTION_DIALOG(toolbar->font_dialog))),
- "clicked", G_CALLBACK(apply_font), toolbar->font_dialog);
- g_signal_connect(G_OBJECT(
- gtk_font_selection_dialog_get_cancel_button(GTK_FONT_SELECTION_DIALOG(toolbar->font_dialog))),
- "clicked", G_CALLBACK(cancel_toolbar_font), toolbar);
- g_signal_connect_after(G_OBJECT(toolbar->font_dialog), "realize",
- G_CALLBACK(realize_toolbar_font), toolbar);
- }
- gtk_window_present(GTK_WINDOW(toolbar->font_dialog));
- } else {
- cancel_toolbar_font(font, toolbar);
- }
- gtk_widget_grab_focus(toolbar->imhtml);
-}
-
-static gboolean
-destroy_toolbar_fgcolor(GtkWidget *widget, GdkEvent *event,
- GtkIMHtmlToolbar *toolbar)
-{
- if (widget != NULL)
- gtk_imhtml_toggle_forecolor(GTK_IMHTML(toolbar->imhtml), "");
-
- if (toolbar->fgcolor_dialog != NULL)
- {
- gtk_widget_destroy(toolbar->fgcolor_dialog);
- toolbar->fgcolor_dialog = NULL;
- }
- return FALSE;
-}
-
-static void cancel_toolbar_fgcolor(GtkWidget *widget,
- GtkIMHtmlToolbar *toolbar)
-{
- destroy_toolbar_fgcolor(widget, NULL, toolbar);
-}
-
-static void do_fgcolor(GtkWidget *widget, GtkColorSelection *colorsel)
-{
- GdkColor text_color;
- GtkIMHtmlToolbar *toolbar = g_object_get_data(G_OBJECT(colorsel), "purple_toolbar");
- char *open_tag;
-
- open_tag = g_malloc(30);
- gtk_color_selection_get_current_color(colorsel, &text_color);
- g_snprintf(open_tag, 23, "#%02X%02X%02X",
- text_color.red / 256,
- text_color.green / 256,
- text_color.blue / 256);
- gtk_imhtml_toggle_forecolor(GTK_IMHTML(toolbar->imhtml), open_tag);
- g_free(open_tag);
-
- cancel_toolbar_fgcolor(NULL, toolbar);
-}
-
-static void
-toggle_fg_color(GtkWidget *color, GtkIMHtmlToolbar *toolbar)
-{
- if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(color))) {
- GtkWidget *colorsel;
- GdkColor fgcolor;
- char *color = gtk_imhtml_get_current_forecolor(GTK_IMHTML(toolbar->imhtml));
-
- if (!toolbar->fgcolor_dialog) {
- GtkWidget *ok_button;
- GtkWidget *cancel_button;
-
- toolbar->fgcolor_dialog = gtk_color_selection_dialog_new(_("Select Text Color"));
- colorsel =
- gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(toolbar->fgcolor_dialog));
- if (color) {
- gdk_color_parse(color, &fgcolor);
- gtk_color_selection_set_current_color(GTK_COLOR_SELECTION(colorsel), &fgcolor);
- g_free(color);
- }
-
- g_object_set_data(G_OBJECT(colorsel), "purple_toolbar", toolbar);
-
- g_object_get(G_OBJECT(toolbar->fgcolor_dialog), "ok-button", &ok_button, NULL);
- g_object_get(G_OBJECT(toolbar->fgcolor_dialog), "cancel-button", &cancel_button, NULL);
- g_signal_connect(G_OBJECT(toolbar->fgcolor_dialog), "delete_event",
- G_CALLBACK(destroy_toolbar_fgcolor), toolbar);
- g_signal_connect(G_OBJECT(ok_button), "clicked", G_CALLBACK(do_fgcolor), colorsel);
- g_signal_connect(G_OBJECT(cancel_button), "clicked",
- G_CALLBACK(cancel_toolbar_fgcolor), toolbar);
- }
- gtk_window_present(GTK_WINDOW(toolbar->fgcolor_dialog));
- } else {
- cancel_toolbar_fgcolor(color, toolbar);
- }
- gtk_widget_grab_focus(toolbar->imhtml);
-}
-
-static gboolean
-destroy_toolbar_bgcolor(GtkWidget *widget, GdkEvent *event,
- GtkIMHtmlToolbar *toolbar)
-{
- if (widget != NULL) {
- if (gtk_text_buffer_get_selection_bounds(GTK_IMHTML(toolbar->imhtml)->text_buffer, NULL, NULL))
- gtk_imhtml_toggle_backcolor(GTK_IMHTML(toolbar->imhtml), "");
- else
- gtk_imhtml_toggle_background(GTK_IMHTML(toolbar->imhtml), "");
- }
-
- if (toolbar->bgcolor_dialog != NULL)
- {
- gtk_widget_destroy(toolbar->bgcolor_dialog);
- toolbar->bgcolor_dialog = NULL;
- }
- return FALSE;
-}
-
-static void
-cancel_toolbar_bgcolor(GtkWidget *widget, GtkIMHtmlToolbar *toolbar)
-{
- destroy_toolbar_bgcolor(widget, NULL, toolbar);
-}
-
-static void do_bgcolor(GtkWidget *widget, GtkColorSelection *colorsel)
-{
- GdkColor text_color;
- GtkIMHtmlToolbar *toolbar = g_object_get_data(G_OBJECT(colorsel), "purple_toolbar");
- char *open_tag;
-
- open_tag = g_malloc(30);
- gtk_color_selection_get_current_color(colorsel, &text_color);
- g_snprintf(open_tag, 23, "#%02X%02X%02X",
- text_color.red / 256,
- text_color.green / 256,
- text_color.blue / 256);
- if (gtk_text_buffer_get_selection_bounds(GTK_IMHTML(toolbar->imhtml)->text_buffer, NULL, NULL))
- gtk_imhtml_toggle_backcolor(GTK_IMHTML(toolbar->imhtml), open_tag);
- else
- gtk_imhtml_toggle_background(GTK_IMHTML(toolbar->imhtml), open_tag);
- g_free(open_tag);
-
- cancel_toolbar_bgcolor(NULL, toolbar);
-}
-
-static void
-toggle_bg_color(GtkWidget *color, GtkIMHtmlToolbar *toolbar)
-{
- if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(color))) {
- GtkWidget *colorsel;
- GdkColor bgcolor;
- char *color = gtk_imhtml_get_current_backcolor(GTK_IMHTML(toolbar->imhtml));
-
- if (!toolbar->bgcolor_dialog) {
- GtkWidget *ok_button;
- GtkWidget *cancel_button;
-
- toolbar->bgcolor_dialog = gtk_color_selection_dialog_new(_("Select Background Color"));
- colorsel =
- gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(toolbar->bgcolor_dialog));
-
- if (color) {
- gdk_color_parse(color, &bgcolor);
- gtk_color_selection_set_current_color(GTK_COLOR_SELECTION(colorsel), &bgcolor);
- g_free(color);
- }
-
- g_object_set_data(G_OBJECT(colorsel), "purple_toolbar", toolbar);
-
- g_object_get(G_OBJECT(toolbar->bgcolor_dialog), "ok-button", &ok_button, NULL);
- g_object_get(G_OBJECT(toolbar->bgcolor_dialog), "cancel-button",
- &cancel_button, NULL);
- g_signal_connect(G_OBJECT(toolbar->bgcolor_dialog), "delete_event",
- G_CALLBACK(destroy_toolbar_bgcolor), toolbar);
- g_signal_connect(G_OBJECT(ok_button), "clicked",
- G_CALLBACK(do_bgcolor), colorsel);
- g_signal_connect(G_OBJECT(cancel_button), "clicked",
- G_CALLBACK(cancel_toolbar_bgcolor), toolbar);
-
- }
- gtk_window_present(GTK_WINDOW(toolbar->bgcolor_dialog));
- } else {
- cancel_toolbar_bgcolor(color, toolbar);
- }
- gtk_widget_grab_focus(toolbar->imhtml);
-}
-
-static void
-clear_formatting_cb(GtkWidget *clear, GtkIMHtmlToolbar *toolbar)
-{
- toggle_button_set_active_block(GTK_TOGGLE_BUTTON(toolbar->clear), FALSE, toolbar);
- gtk_imhtml_clear_formatting(GTK_IMHTML(toolbar->imhtml));
-}
-
-static void
-cancel_link_cb(GtkIMHtmlToolbar *toolbar, PurpleRequestFields *fields)
-{
- gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toolbar->link), FALSE);
-
- toolbar->link_dialog = NULL;
-}
-
-static void
-close_link_dialog(GtkIMHtmlToolbar *toolbar)
-{
- if (toolbar->link_dialog != NULL)
- {
- purple_request_close(PURPLE_REQUEST_FIELDS, toolbar->link_dialog);
- toolbar->link_dialog = NULL;
- }
-}
-
-static void
-do_insert_link_cb(GtkIMHtmlToolbar *toolbar, PurpleRequestFields *fields)
-{
- const char *url, *description;
-
- url = purple_request_fields_get_string(fields, "url");
- if (GTK_IMHTML(toolbar->imhtml)->format_functions & GTK_IMHTML_LINKDESC)
- description = purple_request_fields_get_string(fields, "description");
- else
- description = NULL;
-
- if (description == NULL)
- description = url;
-
- gtk_imhtml_insert_link(GTK_IMHTML(toolbar->imhtml),
- gtk_text_buffer_get_insert(GTK_IMHTML(toolbar->imhtml)->text_buffer),
- url, description);
-
- gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toolbar->link), FALSE);
-
- toolbar->link_dialog = NULL;
-}
-
-static void
-insert_link_cb(GtkWidget *w, GtkIMHtmlToolbar *toolbar)
-{
- if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(toolbar->link))) {
- PurpleRequestFields *fields;
- PurpleRequestFieldGroup *group;
- PurpleRequestField *field;
- GtkTextIter start, end;
- char *msg;
- char *desc = NULL;
-
- fields = purple_request_fields_new();
-
- group = purple_request_field_group_new(NULL);
- purple_request_fields_add_group(fields, group);
-
- field = purple_request_field_string_new("url", _("_URL"), NULL, FALSE);
- purple_request_field_set_required(field, TRUE);
- purple_request_field_group_add_field(group, field);
-
- if(GTK_IMHTML(toolbar->imhtml)->format_functions & GTK_IMHTML_LINKDESC) {
- if (gtk_text_buffer_get_selection_bounds(GTK_IMHTML(toolbar->imhtml)->text_buffer, &start, &end)) {
- desc = gtk_imhtml_get_text(GTK_IMHTML(toolbar->imhtml), &start, &end);
- }
- field = purple_request_field_string_new("description", _("_Description"),
- desc, FALSE);
- purple_request_field_group_add_field(group, field);
- msg = g_strdup(_("Please enter the URL and description of the "
- "link that you want to insert. The description "
- "is optional."));
- } else {
- msg = g_strdup(_("Please enter the URL of the "
- "link that you want to insert."));
- }
-
- toolbar->link_dialog =
- purple_request_fields(toolbar, _("Insert Link"),
- NULL,
- msg,
- fields,
- _("_Insert"), G_CALLBACK(do_insert_link_cb),
- _("Cancel"), G_CALLBACK(cancel_link_cb),
- NULL,
- toolbar);
- g_free(msg);
- g_free(desc);
- } else {
- close_link_dialog(toolbar);
- }
- gtk_widget_grab_focus(toolbar->imhtml);
-}
-
-static void insert_hr_cb(GtkWidget *widget, GtkIMHtmlToolbar *toolbar)
-{
- GtkTextIter iter;
- GtkTextMark *ins;
- GtkIMHtmlScalable *hr;
-
- ins = gtk_text_buffer_get_insert(gtk_text_view_get_buffer(GTK_TEXT_VIEW(toolbar->imhtml)));
- gtk_text_buffer_get_iter_at_mark(gtk_text_view_get_buffer(GTK_TEXT_VIEW(toolbar->imhtml)), &iter, ins);
- hr = gtk_imhtml_hr_new();
- gtk_imhtml_hr_add_to(hr, GTK_IMHTML(toolbar->imhtml), &iter);
-}
-
-static void
-do_insert_image_cb(GtkWidget *widget, int response, GtkIMHtmlToolbar *toolbar)
-{
- gchar *filename, *name, *buf;
- char *filedata;
- size_t size;
- GError *error = NULL;
- int id;
- GtkTextIter iter;
- GtkTextMark *ins;
-
- if (response != GTK_RESPONSE_ACCEPT)
- {
- gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toolbar->image), FALSE);
- return;
- }
-
- filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(widget));
-
- if (filename == NULL) {
- gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toolbar->image), FALSE);
- return;
- }
-
- /* The following triggers a callback that closes the widget */
- gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toolbar->image), FALSE);
-
- if (!g_file_get_contents(filename, &filedata, &size, &error)) {
- purple_notify_error(NULL, NULL, error->message, NULL, NULL);
-
- g_error_free(error);
- g_free(filename);
-
- return;
- }
-
- name = strrchr(filename, G_DIR_SEPARATOR) + 1;
-
- id = purple_imgstore_new_with_id(filedata, size, name);
-
- if (id == 0) {
- buf = g_strdup_printf(_("Failed to store image: %s\n"), filename);
- purple_notify_error(NULL, NULL, buf, NULL, NULL);
-
- g_free(buf);
- g_free(filename);
-
- return;
- }
-
- g_free(filename);
-
- ins = gtk_text_buffer_get_insert(gtk_text_view_get_buffer(GTK_TEXT_VIEW(toolbar->imhtml)));
- gtk_text_buffer_get_iter_at_mark(gtk_text_view_get_buffer(GTK_TEXT_VIEW(toolbar->imhtml)),
- &iter, ins);
- gtk_imhtml_insert_image_at_iter(GTK_IMHTML(toolbar->imhtml), id, &iter);
- purple_imgstore_unref_by_id(id);
-}
-
-
-static void
-insert_image_cb(GtkWidget *save, GtkIMHtmlToolbar *toolbar)
-{
- GtkWidget *window;
-
- if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(toolbar->image))) {
- window = gtk_file_chooser_dialog_new(_("Insert Image"),
- NULL,
- GTK_FILE_CHOOSER_ACTION_OPEN,
- GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
- GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
- NULL);
- gtk_dialog_set_default_response(GTK_DIALOG(window), GTK_RESPONSE_ACCEPT);
- g_signal_connect(G_OBJECT(GTK_FILE_CHOOSER(window)),
- "response", G_CALLBACK(do_insert_image_cb), toolbar);
-
- gtk_widget_show(window);
- toolbar->image_dialog = window;
- } else {
- gtk_widget_destroy(toolbar->image_dialog);
- toolbar->image_dialog = NULL;
- }
-
- gtk_widget_grab_focus(toolbar->imhtml);
-}
-
-
-static void
-destroy_smiley_dialog(GtkIMHtmlToolbar *toolbar)
-{
- if (toolbar->smiley_dialog != NULL)
- {
- gtk_widget_destroy(toolbar->smiley_dialog);
- toolbar->smiley_dialog = NULL;
- }
-}
-
-static gboolean
-close_smiley_dialog(GtkIMHtmlToolbar *toolbar)
-{
- gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toolbar->smiley), FALSE);
- return FALSE;
-}
-
-
-static void
-insert_smiley_text(GtkWidget *widget, GtkIMHtmlToolbar *toolbar)
-{
- char *smiley_text, *escaped_smiley;
-
- smiley_text = g_object_get_data(G_OBJECT(widget), "smiley_text");
- escaped_smiley = g_markup_escape_text(smiley_text, -1);
-
- gtk_imhtml_insert_smiley(GTK_IMHTML(toolbar->imhtml),
- GTK_IMHTML(toolbar->imhtml)->protocol_name,
- escaped_smiley);
-
- g_free(escaped_smiley);
-
- close_smiley_dialog(toolbar);
-}
-
-/* smiley buttons list */
-struct smiley_button_list {
- int width, height;
- GtkWidget *button;
- const GtkIMHtmlSmiley *smiley;
- struct smiley_button_list *next;
-};
-
-static struct smiley_button_list *
-sort_smileys(struct smiley_button_list *ls, GtkIMHtmlToolbar *toolbar,
- int *width, const GtkIMHtmlSmiley *smiley)
-{
- GtkWidget *image;
- GtkWidget *button;
- GtkRequisition size;
- struct smiley_button_list *cur;
- struct smiley_button_list *it, *it_last;
- const gchar *filename = smiley->file;
- gchar *face = smiley->smile;
- PurpleSmiley *psmiley = NULL;
- gboolean supports_custom = (gtk_imhtml_get_format_functions(GTK_IMHTML(toolbar->imhtml)) & GTK_IMHTML_CUSTOM_SMILEY);
-
- cur = g_new0(struct smiley_button_list, 1);
- it = ls;
- it_last = ls; /* list iterators*/
- image = gtk_image_new_from_file(filename);
-
- gtk_widget_size_request(image, &size);
-
- if (size.width > 24 &&
- smiley->flags & GTK_IMHTML_SMILEY_CUSTOM) { /* This is a custom smiley, let's scale it */
- GdkPixbuf *pixbuf = NULL;
- GtkImageType type;
-
- type = gtk_image_get_storage_type(GTK_IMAGE(image));
-
- if (type == GTK_IMAGE_PIXBUF) {
- pixbuf = gtk_image_get_pixbuf(GTK_IMAGE(image));
- } else if (type == GTK_IMAGE_ANIMATION) {
- GdkPixbufAnimation *animation;
-
- animation = gtk_image_get_animation(GTK_IMAGE(image));
-
- pixbuf = gdk_pixbuf_animation_get_static_image(animation);
- }
-
- if (pixbuf != NULL) {
- GdkPixbuf *resized;
- resized = gdk_pixbuf_scale_simple(pixbuf, 24, 24,
- GDK_INTERP_HYPER);
-
- gtk_image_set_from_pixbuf(GTK_IMAGE(image), resized); /* This unrefs pixbuf */
- gtk_widget_size_request(image, &size);
- g_object_unref(G_OBJECT(resized));
- }
- }
-
- (*width) += size.width;
-
- button = gtk_button_new();
- gtk_container_add(GTK_CONTAINER(button), image);
-
- g_object_set_data(G_OBJECT(button), "smiley_text", face);
- g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(insert_smiley_text), toolbar);
-
- gtk_widget_set_tooltip_text(button, face);
-
- /* these look really weird with borders */
- gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE);
-
- psmiley = purple_smileys_find_by_shortcut(smiley->smile);
- /* If this is a "non-custom" smiley, check to see if its shortcut is
- "shadowed" by any custom smiley. This can only happen if the connection
- is custom smiley-enabled */
- if (supports_custom && psmiley && !(smiley->flags & GTK_IMHTML_SMILEY_CUSTOM)) {
- gchar tip[128];
- g_snprintf(tip, sizeof(tip),
- _("This smiley is disabled because a custom smiley exists for this shortcut:\n %s"),
- face);
- gtk_widget_set_tooltip_text(button, tip);
- gtk_widget_set_sensitive(button, FALSE);
- } else if (psmiley) {
- /* Remove the button if the smiley is destroyed */
- g_signal_connect_object(G_OBJECT(psmiley), "destroy", G_CALLBACK(gtk_widget_destroy),
- button, G_CONNECT_SWAPPED);
- }
-
- /* set current element to add */
- cur->height = size.height;
- cur->width = size.width;
- cur->button = button;
- cur->smiley = smiley;
- cur->next = ls;
-
- /* check where to insert by height */
- if (ls == NULL)
- return cur;
- while (it != NULL) {
- it_last = it;
- it = it->next;
- }
- cur->next = it;
- it_last->next = cur;
- return ls;
-}
-
-static gboolean
-smiley_is_unique(GSList *list, GtkIMHtmlSmiley *smiley)
-{
- while (list) {
- GtkIMHtmlSmiley *cur = (GtkIMHtmlSmiley *) list->data;
- if (!strcmp(cur->file, smiley->file))
- return FALSE;
- list = list->next;
- }
- return TRUE;
-}
-
-static gboolean
-smiley_dialog_input_cb(GtkWidget *dialog, GdkEvent *event, GtkIMHtmlToolbar *toolbar)
-{
- if ((event->type == GDK_KEY_PRESS && event->key.keyval == GDK_KEY_Escape) ||
- (event->type == GDK_BUTTON_PRESS && event->button.button == 1))
- {
- close_smiley_dialog(toolbar);
- return TRUE;
- }
-
- return FALSE;
-}
-
-static void
-add_smiley_list(GtkWidget *container, struct smiley_button_list *list,
- int max_width, gboolean custom)
-{
- GtkWidget *line;
- int line_width = 0;
-
- if (!list)
- return;
-
- line = gtk_hbox_new(FALSE, 0);
- gtk_box_pack_start(GTK_BOX(container), line, FALSE, FALSE, 0);
- for (; list; list = list->next) {
- if (custom != !!(list->smiley->flags & GTK_IMHTML_SMILEY_CUSTOM))
- continue;
- gtk_box_pack_start(GTK_BOX(line), list->button, FALSE, FALSE, 0);
- gtk_widget_show(list->button);
- line_width += list->width;
- if (line_width >= max_width) {
- if (list->next) {
- line = gtk_hbox_new(FALSE, 0);
- gtk_box_pack_start(GTK_BOX(container), line, FALSE, FALSE, 0);
- }
- line_width = 0;
- }
- }
-}
-
-static void
-insert_smiley_cb(GtkWidget *smiley, GtkIMHtmlToolbar *toolbar)
-{
- GtkWidget *dialog, *vbox;
- GtkWidget *smiley_table = NULL;
- GSList *smileys, *unique_smileys = NULL;
- const GSList *custom_smileys = NULL;
- gboolean supports_custom = FALSE;
- GtkRequisition req;
- GtkWidget *scrolled, *viewport;
-
- if (!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(smiley))) {
- destroy_smiley_dialog(toolbar);
- gtk_widget_grab_focus(toolbar->imhtml);
- return;
- }
-
- if (toolbar->sml)
- smileys = pidgin_themes_get_proto_smileys(toolbar->sml);
- else
- smileys = pidgin_themes_get_proto_smileys(NULL);
-
- /* Note: prepend smileys to list to avoid O(n^2) overhead when there is
- a large number of smileys... need to revers the list after for the dialog
- work... */
- while(smileys) {
- GtkIMHtmlSmiley *smiley = (GtkIMHtmlSmiley *) smileys->data;
- if(!smiley->hidden) {
- if(smiley_is_unique(unique_smileys, smiley)) {
- unique_smileys = g_slist_prepend(unique_smileys, smiley);
- }
- }
- smileys = smileys->next;
- }
- supports_custom = (gtk_imhtml_get_format_functions(GTK_IMHTML(toolbar->imhtml)) & GTK_IMHTML_CUSTOM_SMILEY);
- if (toolbar->imhtml && supports_custom) {
- const GSList *iterator = NULL;
- custom_smileys = pidgin_smileys_get_all();
-
- for (iterator = custom_smileys ; iterator ;
- iterator = g_slist_next(iterator)) {
- GtkIMHtmlSmiley *smiley = (GtkIMHtmlSmiley *) iterator->data;
- unique_smileys = g_slist_prepend(unique_smileys, smiley);
- }
- }
-
- /* we need to reverse the list to get the smileys in the correct order */
- unique_smileys = g_slist_reverse(unique_smileys);
-
- dialog = pidgin_create_dialog(_("Smile!"), 0, "smiley_dialog", FALSE);
- gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_MOUSE);
- vbox = pidgin_dialog_get_vbox_with_properties(GTK_DIALOG(dialog), FALSE, 0);
-
- if (unique_smileys != NULL) {
- struct smiley_button_list *ls;
- int max_line_width, num_lines, button_width = 0;
-
- /* We use hboxes packed in a vbox */
- ls = NULL;
- max_line_width = 0;
- num_lines = floor(sqrt(g_slist_length(unique_smileys)));
- smiley_table = gtk_vbox_new(FALSE, 0);
-
- if (supports_custom) {
- GtkWidget *manage = gtk_button_new_with_mnemonic(_("_Manage custom smileys"));
- GtkRequisition req;
- g_signal_connect(G_OBJECT(manage), "clicked",
- G_CALLBACK(pidgin_smiley_manager_show), NULL);
- g_signal_connect_swapped(G_OBJECT(manage), "clicked",
- G_CALLBACK(gtk_widget_destroy), dialog);
- gtk_box_pack_end(GTK_BOX(vbox), manage, FALSE, TRUE, 0);
- gtk_widget_size_request(manage, &req);
- button_width = req.width;
- }
-
- /* create list of smileys sorted by height */
- while (unique_smileys) {
- GtkIMHtmlSmiley *smiley = (GtkIMHtmlSmiley *) unique_smileys->data;
- if (!smiley->hidden) {
- ls = sort_smileys(ls, toolbar, &max_line_width, smiley);
- }
- unique_smileys = g_slist_delete_link(unique_smileys, unique_smileys);
- }
- /* The window will be at least as wide as the 'Manage ..' button */
- max_line_width = MAX(button_width, max_line_width / num_lines);
-
- /* pack buttons of the list */
- add_smiley_list(smiley_table, ls, max_line_width, FALSE);
- if (supports_custom) {
- gtk_box_pack_start(GTK_BOX(smiley_table), gtk_hseparator_new(), TRUE, FALSE, 0);
- add_smiley_list(smiley_table, ls, max_line_width, TRUE);
- }
- while (ls) {
- struct smiley_button_list *tmp = ls->next;
- g_free(ls);
- ls = tmp;
- }
-
- gtk_widget_add_events(dialog, GDK_KEY_PRESS_MASK);
- }
- else {
- smiley_table = gtk_label_new(_("This theme has no available smileys."));
- gtk_widget_add_events(dialog, GDK_KEY_PRESS_MASK | GDK_BUTTON_PRESS_MASK);
- g_signal_connect(G_OBJECT(dialog), "button-press-event", (GCallback)smiley_dialog_input_cb, toolbar);
- }
-
-
- scrolled = pidgin_make_scrollable(smiley_table, GTK_POLICY_NEVER, GTK_POLICY_NEVER, GTK_SHADOW_NONE, -1, -1);
- gtk_box_pack_start(GTK_BOX(vbox), scrolled, TRUE, TRUE, 0);
- gtk_widget_show(smiley_table);
-
- viewport = gtk_widget_get_parent(smiley_table);
- gtk_viewport_set_shadow_type(GTK_VIEWPORT(viewport), GTK_SHADOW_NONE);
-
- /* connect signals */
- g_signal_connect_swapped(G_OBJECT(dialog), "destroy", G_CALLBACK(close_smiley_dialog), toolbar);
- g_signal_connect(G_OBJECT(dialog), "key-press-event", G_CALLBACK(smiley_dialog_input_cb), toolbar);
-
- gtk_window_set_transient_for(GTK_WINDOW(dialog),
- GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(toolbar))));
-
- /* show everything */
- gtk_widget_show_all(dialog);
-
- gtk_widget_size_request(viewport, &req);
- gtk_widget_set_size_request(scrolled, MIN(300, req.width), MIN(290, req.height));
-
- /* The window has to be made resizable, and the scrollbars in the scrolled window
- * enabled only after setting the desired size of the window. If we do either of
- * these tasks before now, GTK+ miscalculates the required size, and erronously
- * makes one or both scrollbars visible (sometimes).
- * I too think this hack is gross. But I couldn't find a better way -- sadrul */
- gtk_window_set_resizable(GTK_WINDOW(dialog), TRUE);
- g_object_set(G_OBJECT(scrolled),
- "hscrollbar-policy", GTK_POLICY_AUTOMATIC,
- "vscrollbar-policy", GTK_POLICY_AUTOMATIC,
- NULL);
-
-#ifdef _WIN32
- winpidgin_ensure_onscreen(dialog);
-#endif
-
- toolbar->smiley_dialog = dialog;
-
- gtk_widget_grab_focus(toolbar->imhtml);
-}
-
-static void send_attention_cb(GtkWidget *attention, GtkIMHtmlToolbar *toolbar)
-{
- PurpleConversation *conv =
- g_object_get_data(G_OBJECT(toolbar), "active_conv");
- const gchar *who = purple_conversation_get_name(conv);
- PurpleConnection *gc = purple_conversation_get_connection(conv);
-
- toggle_button_set_active_block(GTK_TOGGLE_BUTTON(attention), FALSE, toolbar);
- purple_prpl_send_attention(gc, who, 0);
- gtk_widget_grab_focus(toolbar->imhtml);
-}
-
-static void update_buttons_cb(GtkIMHtml *imhtml, GtkIMHtmlButtons buttons, GtkIMHtmlToolbar *toolbar)
-{
- gtk_widget_set_sensitive(GTK_WIDGET(toolbar->bold), buttons & GTK_IMHTML_BOLD);
- gtk_widget_set_sensitive(GTK_WIDGET(toolbar->italic), buttons & GTK_IMHTML_ITALIC);
- gtk_widget_set_sensitive(GTK_WIDGET(toolbar->underline), buttons & GTK_IMHTML_UNDERLINE);
- gtk_widget_set_sensitive(GTK_WIDGET(toolbar->strikethrough), buttons & GTK_IMHTML_STRIKE);
-
- gtk_widget_set_sensitive(GTK_WIDGET(toolbar->larger_size), buttons & GTK_IMHTML_GROW);
- gtk_widget_set_sensitive(GTK_WIDGET(toolbar->smaller_size), buttons & GTK_IMHTML_SHRINK);
-
- gtk_widget_set_sensitive(GTK_WIDGET(toolbar->font), buttons & GTK_IMHTML_FACE);
- gtk_widget_set_sensitive(GTK_WIDGET(toolbar->fgcolor), buttons & GTK_IMHTML_FORECOLOR);
- gtk_widget_set_sensitive(GTK_WIDGET(toolbar->bgcolor), buttons & GTK_IMHTML_BACKCOLOR);
-
- gtk_widget_set_sensitive(GTK_WIDGET(toolbar->clear),
- (buttons & GTK_IMHTML_BOLD ||
- buttons & GTK_IMHTML_ITALIC ||
- buttons & GTK_IMHTML_UNDERLINE ||
- buttons & GTK_IMHTML_STRIKE ||
- buttons & GTK_IMHTML_GROW ||
- buttons & GTK_IMHTML_SHRINK ||
- buttons & GTK_IMHTML_FACE ||
- buttons & GTK_IMHTML_FORECOLOR ||
- buttons & GTK_IMHTML_BACKCOLOR));
-
- gtk_widget_set_sensitive(GTK_WIDGET(toolbar->image), buttons & GTK_IMHTML_IMAGE);
- gtk_widget_set_sensitive(GTK_WIDGET(toolbar->link), buttons & GTK_IMHTML_LINK);
- gtk_widget_set_sensitive(GTK_WIDGET(toolbar->smiley), buttons & GTK_IMHTML_SMILEY);
-}
-
-/* we call this when we want to _set_active the toggle button, it'll
- * block the callback thats connected to the button so we don't have to
- * do the double toggling hack
- */
-static void toggle_button_set_active_block(GtkToggleButton *button,
- gboolean is_active,
- GtkIMHtmlToolbar *toolbar)
-{
- GObject *object;
- g_return_if_fail(toolbar);
-
- object = g_object_ref(button);
- g_signal_handlers_block_matched(object, G_SIGNAL_MATCH_DATA,
- 0, 0, NULL, NULL, toolbar);
- gtk_toggle_button_set_active(button, is_active);
- g_signal_handlers_unblock_matched(object, G_SIGNAL_MATCH_DATA,
- 0, 0, NULL, NULL, toolbar);
- g_object_unref(object);
-}
-
-static void update_buttons(GtkIMHtmlToolbar *toolbar)
-{
- gboolean bold, italic, underline, strike;
- char *tmp;
- char *tmp2;
- GtkLabel *label = g_object_get_data(G_OBJECT(toolbar), "font_label");
-
- gtk_label_set_label(label, _("_Font"));
-
- gtk_imhtml_get_current_format(GTK_IMHTML(toolbar->imhtml),
- &bold, &italic, &underline);
- strike = GTK_IMHTML(toolbar->imhtml)->edit.strike;
-
- if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(toolbar->bold)) != bold)
- toggle_button_set_active_block(GTK_TOGGLE_BUTTON(toolbar->bold), bold,
- toolbar);
- if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(toolbar->italic)) != italic)
- toggle_button_set_active_block(GTK_TOGGLE_BUTTON(toolbar->italic), italic,
- toolbar);
- if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(toolbar->underline)) != underline)
- toggle_button_set_active_block(GTK_TOGGLE_BUTTON(toolbar->underline),
- underline, toolbar);
- if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(toolbar->strikethrough)) != strike)
- toggle_button_set_active_block(GTK_TOGGLE_BUTTON(toolbar->strikethrough),
- strike, toolbar);
-
- /* These buttons aren't ever "active". */
- gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toolbar->smaller_size), FALSE);
- gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toolbar->larger_size), FALSE);
-
- if (bold) {
- gchar *markup = g_strdup_printf("<b>%s</b>",
- gtk_label_get_label(label));
- gtk_label_set_markup_with_mnemonic(label, markup);
- g_free(markup);
- }
- if (italic) {
- gchar *markup = g_strdup_printf("<i>%s</i>",
- gtk_label_get_label(label));
- gtk_label_set_markup_with_mnemonic(label, markup);
- g_free(markup);
- }
- if (underline) {
- gchar *markup = g_strdup_printf("<u>%s</u>",
- gtk_label_get_label(label));
- gtk_label_set_markup_with_mnemonic(label, markup);
- g_free(markup);
- }
- if (strike) {
- gchar *markup = g_strdup_printf("<s>%s</s>",
- gtk_label_get_label(label));
- gtk_label_set_markup_with_mnemonic(label, markup);
- g_free(markup);
- }
-
- tmp = gtk_imhtml_get_current_fontface(GTK_IMHTML(toolbar->imhtml));
- toggle_button_set_active_block(GTK_TOGGLE_BUTTON(toolbar->font),
- (tmp != NULL), toolbar);
- if (tmp != NULL) {
- gchar *markup = g_strdup_printf("<span face=\"%s\">%s</span>",
- tmp, gtk_label_get_label(label));
- gtk_label_set_markup_with_mnemonic(label, markup);
- g_free(markup);
- }
- g_free(tmp);
-
- tmp = gtk_imhtml_get_current_forecolor(GTK_IMHTML(toolbar->imhtml));
- toggle_button_set_active_block(GTK_TOGGLE_BUTTON(toolbar->fgcolor),
- (tmp != NULL), toolbar);
- if (tmp != NULL) {
- gchar *markup = g_strdup_printf("<span foreground=\"%s\">%s</span>",
- tmp, gtk_label_get_label(label));
- gtk_label_set_markup_with_mnemonic(label, markup);
- g_free(markup);
- }
- g_free(tmp);
-
- tmp = gtk_imhtml_get_current_backcolor(GTK_IMHTML(toolbar->imhtml));
- tmp2 = gtk_imhtml_get_current_background(GTK_IMHTML(toolbar->imhtml));
- toggle_button_set_active_block(GTK_TOGGLE_BUTTON(toolbar->bgcolor),
- (tmp != NULL || tmp2 != NULL), toolbar);
- if (tmp != NULL) {
- gchar *markup = g_strdup_printf("<span background=\"%s\">%s</span>",
- tmp, gtk_label_get_label(label));
- gtk_label_set_markup_with_mnemonic(label, markup);
- g_free(markup);
- }
- g_free(tmp);
- g_free(tmp2);
-}
-
-static void toggle_button_cb(GtkIMHtml *imhtml, GtkIMHtmlButtons buttons, GtkIMHtmlToolbar *toolbar)
-{
- update_buttons(toolbar);
-}
-
-static void update_format_cb(GtkIMHtml *imhtml, GtkIMHtmlToolbar *toolbar) {
- update_buttons(toolbar);
-}
-
-static void mark_set_cb(GtkTextBuffer *buffer, GtkTextIter *location,
- GtkTextMark *mark, GtkIMHtmlToolbar *toolbar)
-{
- if(mark != gtk_text_buffer_get_insert(buffer))
- return;
-
- update_buttons(toolbar);
-}
-
-
-/* This comes from gtkmenutoolbutton.c from gtk+
- * Copyright (C) 2003 Ricardo Fernandez Pascual
- * Copyright (C) 2004 Paolo Borelli
- */
-static void
-menu_position_func (GtkMenu *menu,
- int *x,
- int *y,
- gboolean *push_in,
- gpointer data)
-{
- GtkWidget *widget = GTK_WIDGET(data);
- GtkRequisition menu_req;
- GtkAllocation allocation;
- gint ythickness = gtk_widget_get_style(widget)->ythickness;
- int savy;
-
- gtk_widget_get_allocation(widget, &allocation);
- gtk_widget_size_request(GTK_WIDGET (menu), &menu_req);
- gdk_window_get_origin(gtk_widget_get_window(widget), x, y);
- *x += allocation.x;
- *y += allocation.y + allocation.height;
- savy = *y;
-
- pidgin_menu_position_func_helper(menu, x, y, push_in, data);
-
- if (savy > *y + ythickness + 1)
- *y -= allocation.height;
-}
-
-static gboolean
-button_activate_on_click(GtkWidget *button, GdkEventButton *event, GtkIMHtmlToolbar *toolbar)
-{
- if (event->button == 1 && GTK_IS_TOGGLE_BUTTON(button))
- gtk_widget_activate(button);
- else if (event->button == 3)
- return gtk_imhtmltoolbar_popup_menu(button, event, toolbar);
- return FALSE;
-}
-
-static void pidgin_menu_clicked(GtkWidget *button, GtkMenu *menu)
-{
- gtk_widget_show_all(GTK_WIDGET(menu));
- gtk_menu_popup(menu, NULL, NULL, menu_position_func, button, 0, gtk_get_current_event_time());
-}
-
-static void pidgin_menu_deactivate(GtkWidget *menu, GtkToggleButton *button)
-{
- gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), FALSE);
-}
-
-enum {
- LAST_SIGNAL
-};
-/* static guint signals [LAST_SIGNAL] = { 0 }; */
-
-static void
-gtk_imhtmltoolbar_finalize (GObject *object)
-{
- GtkIMHtmlToolbar *toolbar = GTK_IMHTMLTOOLBAR(object);
- GtkWidget *menu;
-
- if (toolbar->image_dialog != NULL)
- {
- gtk_widget_destroy(toolbar->image_dialog);
- toolbar->image_dialog = NULL;
- }
-
- destroy_toolbar_font(NULL, NULL, toolbar);
- if (toolbar->smiley_dialog != NULL) {
- g_signal_handlers_disconnect_by_func(G_OBJECT(toolbar->smiley_dialog), close_smiley_dialog, toolbar);
- destroy_smiley_dialog(toolbar);
- }
- destroy_toolbar_bgcolor(NULL, NULL, toolbar);
- destroy_toolbar_fgcolor(NULL, NULL, toolbar);
- close_link_dialog(toolbar);
- if (toolbar->imhtml) {
- g_signal_handlers_disconnect_matched(toolbar->imhtml,
- G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL,
- toolbar);
- g_signal_handlers_disconnect_matched(GTK_IMHTML(toolbar->imhtml)->text_buffer,
- G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL,
- toolbar);
- }
-
- g_free(toolbar->sml);
-
- menu = g_object_get_data(object, "font_menu");
- if (menu)
- gtk_widget_destroy(menu);
- menu = g_object_get_data(object, "insert_menu");
- if (menu)
- gtk_widget_destroy(menu);
-
- purple_prefs_disconnect_by_handle(object);
-
- G_OBJECT_CLASS(parent_class)->finalize (object);
-}
-
-static void
-switch_toolbar_view(GtkWidget *item, GtkIMHtmlToolbar *toolbar)
-{
- purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/conversations/toolbar/wide",
- !purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/toolbar/wide"));
-}
-
-static gboolean
-gtk_imhtmltoolbar_popup_menu(GtkWidget *widget, GdkEventButton *event, GtkIMHtmlToolbar *toolbar)
-{
- GtkWidget *menu;
- GtkWidget *item;
- gboolean wide;
-
- if (event->button != 3)
- return FALSE;
-
- wide = gtk_widget_get_visible(toolbar->bold);
-
- menu = gtk_menu_new();
- item = gtk_menu_item_new_with_mnemonic(wide ? _("Group Items") : _("Ungroup Items"));
- g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(switch_toolbar_view), toolbar);
- gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
- gtk_widget_show(item);
-
- gtk_menu_popup(GTK_MENU(menu), NULL, NULL, pidgin_menu_position_func_helper,
- widget, event->button, event->time);
- return TRUE;
-}
-
-/* Boring GTK+ stuff */
-static void gtk_imhtmltoolbar_class_init (GtkIMHtmlToolbarClass *class)
-{
- GObjectClass *gobject_class;
- gobject_class = (GObjectClass*) class;
- parent_class = g_type_class_ref(GTK_TYPE_HBOX);
- gobject_class->finalize = gtk_imhtmltoolbar_finalize;
-
- purple_prefs_add_none(PIDGIN_PREFS_ROOT "/conversations/toolbar");
- purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/conversations/toolbar/wide", FALSE);
-}
-
-static void gtk_imhtmltoolbar_create_old_buttons(GtkIMHtmlToolbar *toolbar)
-{
- GtkWidget *hbox;
- GtkWidget *button;
- struct {
- char *stock;
- gpointer callback;
- GtkWidget **button;
- const char *tooltip;
- } buttons[] = {
- {GTK_STOCK_BOLD, G_CALLBACK(do_bold), &toolbar->bold, _("Bold")},
- {GTK_STOCK_ITALIC, do_italic, &toolbar->italic, _("Italic")},
- {GTK_STOCK_UNDERLINE, do_underline, &toolbar->underline, _("Underline")},
- {GTK_STOCK_STRIKETHROUGH, do_strikethrough, &toolbar->strikethrough, _("Strikethrough")},
- {"", NULL, NULL, NULL},
- {PIDGIN_STOCK_TOOLBAR_TEXT_LARGER, do_big, &toolbar->larger_size, _("Increase Font Size")},
- {PIDGIN_STOCK_TOOLBAR_TEXT_SMALLER, do_small, &toolbar->smaller_size, _("Decrease Font Size")},
- {"", NULL, NULL, NULL},
- {PIDGIN_STOCK_TOOLBAR_FONT_FACE, toggle_font, &toolbar->font, _("Font Face")},
- {PIDGIN_STOCK_TOOLBAR_FGCOLOR, toggle_fg_color, &toolbar->fgcolor, _("Foreground Color")},
- {PIDGIN_STOCK_TOOLBAR_BGCOLOR, toggle_bg_color, &toolbar->bgcolor, _("Background Color")},
- {"", NULL, NULL, NULL},
- {PIDGIN_STOCK_CLEAR, clear_formatting_cb, &toolbar->clear, _("Reset Formatting")},
- {"", NULL, NULL, NULL},
- {PIDGIN_STOCK_TOOLBAR_INSERT_IMAGE, insert_image_cb, &toolbar->image, _("Insert IM Image")},
- {PIDGIN_STOCK_TOOLBAR_INSERT_LINK, insert_link_cb, &toolbar->link, _("Insert Link")},
- {"", NULL, NULL, NULL},
- {PIDGIN_STOCK_TOOLBAR_SMILEY, insert_smiley_cb, &toolbar->smiley, _("Insert Smiley")},
- {PIDGIN_STOCK_TOOLBAR_SEND_ATTENTION, send_attention_cb, &toolbar->attention, _("Send Attention")},
- {NULL, NULL, NULL, NULL}
- };
- int iter;
-
- hbox = gtk_hbox_new(FALSE, 0);
-
- for (iter = 0; buttons[iter].stock; iter++) {
- if (buttons[iter].stock[0]) {
- button = pidgin_pixbuf_toolbar_button_from_stock(buttons[iter].stock);
- g_signal_connect(G_OBJECT(button), "button-press-event", G_CALLBACK(gtk_imhtmltoolbar_popup_menu), toolbar);
- g_signal_connect(G_OBJECT(button), "clicked",
- G_CALLBACK(buttons[iter].callback), toolbar);
- *(buttons[iter].button) = button;
- gtk_widget_set_tooltip_text(button, buttons[iter].tooltip);
- } else
- button = gtk_vseparator_new();
- gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
- }
-
- gtk_box_pack_start(GTK_BOX(toolbar), hbox, FALSE, FALSE, 0);
- g_object_set_data(G_OBJECT(toolbar), "wide-view", hbox);
-}
-
-static void
-button_visibility_changed(GtkWidget *button, gpointer dontcare, GtkWidget *item)
-{
- if (gtk_widget_get_visible(button))
- gtk_widget_hide(item);
- else
- gtk_widget_show(item);
-}
-
-static void
-button_sensitiveness_changed(GtkWidget *button, gpointer dontcare, GtkWidget *item)
-{
- gtk_widget_set_sensitive(item, gtk_widget_get_sensitive(button));
-}
-
-static void
-update_menuitem(GtkToggleButton *button, GtkCheckMenuItem *item)
-{
- g_signal_handlers_block_by_func(G_OBJECT(item), G_CALLBACK(gtk_button_clicked), button);
- gtk_check_menu_item_set_active(item, gtk_toggle_button_get_active(button));
- g_signal_handlers_unblock_by_func(G_OBJECT(item), G_CALLBACK(gtk_button_clicked), button);
-}
-
-static void
-enable_markup(GtkWidget *widget, gpointer null)
-{
- if (GTK_IS_LABEL(widget))
- g_object_set(G_OBJECT(widget), "use-markup", TRUE, NULL);
-}
-
-static void
-imhtmltoolbar_view_pref_changed(const char *name, PurplePrefType type,
- gconstpointer value, gpointer toolbar)
-{
- if (value) {
- gtk_widget_hide(g_object_get_data(G_OBJECT(toolbar), "lean-view"));
- gtk_widget_show_all(g_object_get_data(G_OBJECT(toolbar), "wide-view"));
- } else {
- gtk_widget_hide(g_object_get_data(G_OBJECT(toolbar), "wide-view"));
- gtk_widget_show_all(g_object_get_data(G_OBJECT(toolbar), "lean-view"));
- }
-}
-
-static void gtk_imhtmltoolbar_init (GtkIMHtmlToolbar *toolbar)
-{
- GtkWidget *hbox = GTK_WIDGET(toolbar), *event = gtk_event_box_new();
- GtkWidget *bbox, *box = gtk_hbox_new(FALSE, 0);
- GtkWidget *image;
- GtkWidget *label;
- GtkWidget *insert_button;
- GtkWidget *font_button;
- GtkWidget *smiley_button;
- GtkWidget *attention_button;
- GtkWidget *font_menu;
- GtkWidget *insert_menu;
- GtkWidget *menuitem;
- GtkWidget *sep;
- int i;
- struct {
- const char *label;
- GtkWidget **button;
- gboolean check;
- } buttons[] = {
- {_("<b>_Bold</b>"), &toolbar->bold, TRUE},
- {_("<i>_Italic</i>"), &toolbar->italic, TRUE},
- {_("<u>_Underline</u>"), &toolbar->underline, TRUE},
- {_("<span strikethrough='true'>Strikethrough</span>"), &toolbar->strikethrough, TRUE},
- {_("<span size='larger'>_Larger</span>"), &toolbar->larger_size, TRUE},
-#if 0
- {_("_Normal"), &toolbar->normal_size, TRUE},
-#endif
- {_("<span size='smaller'>_Smaller</span>"), &toolbar->smaller_size, TRUE},
- /* If we want to show the formatting for the following items, we would
- * need to update them when formatting changes. The above items don't need
- * no updating nor nothin' */
- {_("_Font face"), &toolbar->font, TRUE},
- {_("Foreground _color"), &toolbar->fgcolor, TRUE},
- {_("Bac_kground color"), &toolbar->bgcolor, TRUE},
- {_("_Reset formatting"), &toolbar->clear, FALSE},
- {NULL, NULL, FALSE}
- };
-
- toolbar->imhtml = NULL;
- toolbar->font_dialog = NULL;
- toolbar->fgcolor_dialog = NULL;
- toolbar->bgcolor_dialog = NULL;
- toolbar->link_dialog = NULL;
- toolbar->smiley_dialog = NULL;
- toolbar->image_dialog = NULL;
-
- gtk_box_set_spacing(GTK_BOX(toolbar), 3);
-
- gtk_imhtmltoolbar_create_old_buttons(toolbar);
-
- /* Fonts */
- font_button = gtk_toggle_button_new();
- gtk_button_set_relief(GTK_BUTTON(font_button), GTK_RELIEF_NONE);
- bbox = gtk_hbox_new(FALSE, 3);
- gtk_container_add(GTK_CONTAINER(font_button), bbox);
- image = gtk_image_new_from_stock(GTK_STOCK_BOLD, gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL));
- gtk_box_pack_start(GTK_BOX(bbox), image, FALSE, FALSE, 0);
- label = gtk_label_new_with_mnemonic(_("_Font"));
- gtk_label_set_use_markup(GTK_LABEL(label), TRUE);
- g_object_set_data(G_OBJECT(hbox), "font_label", label);
- gtk_box_pack_start(GTK_BOX(bbox), label, FALSE, FALSE, 0);
- gtk_box_pack_start(GTK_BOX(box), font_button, FALSE, FALSE, 0);
- gtk_widget_show_all(font_button);
-
- font_menu = gtk_menu_new();
- g_object_set_data(G_OBJECT(toolbar), "font_menu", font_menu);
-
- for (i = 0; buttons[i].label; i++) {
- GtkWidget *old = *buttons[i].button;
- if (buttons[i].check) {
- menuitem = gtk_check_menu_item_new_with_mnemonic(buttons[i].label);
- g_signal_connect_after(G_OBJECT(old), "toggled",
- G_CALLBACK(update_menuitem), menuitem);
- } else {
- menuitem = gtk_menu_item_new_with_mnemonic(buttons[i].label);
- }
- g_signal_connect_swapped(G_OBJECT(menuitem), "activate",
- G_CALLBACK(gtk_button_clicked), old);
- gtk_menu_shell_append(GTK_MENU_SHELL(font_menu), menuitem);
- g_signal_connect(G_OBJECT(old), "notify::sensitive",
- G_CALLBACK(button_sensitiveness_changed), menuitem);
- g_signal_connect(G_OBJECT(old), "notify::visible",
- G_CALLBACK(button_visibility_changed), menuitem);
- gtk_container_foreach(GTK_CONTAINER(menuitem), (GtkCallback)enable_markup, NULL);
- }
-
- g_signal_connect(G_OBJECT(font_button), "button-press-event", G_CALLBACK(button_activate_on_click), toolbar);
- g_signal_connect(G_OBJECT(font_button), "activate", G_CALLBACK(pidgin_menu_clicked), font_menu);
- g_signal_connect_data(G_OBJECT(font_menu), "deactivate", G_CALLBACK(pidgin_menu_deactivate),
- g_object_ref(font_button), (GClosureNotify)g_object_unref, 0);
-
- /* Sep */
- sep = gtk_vseparator_new();
- gtk_box_pack_start(GTK_BOX(box), sep, FALSE, FALSE, 0);
- gtk_widget_show_all(sep);
-
- /* Insert */
- insert_button = gtk_toggle_button_new();
- gtk_button_set_relief(GTK_BUTTON(insert_button), GTK_RELIEF_NONE);
- bbox = gtk_hbox_new(FALSE, 3);
- gtk_container_add(GTK_CONTAINER(insert_button), bbox);
- image = gtk_image_new_from_stock(PIDGIN_STOCK_TOOLBAR_INSERT, gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL));
- gtk_box_pack_start(GTK_BOX(bbox), image, FALSE, FALSE, 0);
- label = gtk_label_new_with_mnemonic(_("_Insert"));
- gtk_box_pack_start(GTK_BOX(bbox), label, FALSE, FALSE, 0);
- gtk_box_pack_start(GTK_BOX(box), insert_button, FALSE, FALSE, 0);
- gtk_widget_show_all(insert_button);
-
- insert_menu = gtk_menu_new();
- g_object_set_data(G_OBJECT(toolbar), "insert_menu", insert_menu);
-
- menuitem = gtk_menu_item_new_with_mnemonic(_("_Image"));
- g_signal_connect_swapped(G_OBJECT(menuitem), "activate", G_CALLBACK(gtk_button_clicked), toolbar->image);
- gtk_menu_shell_append(GTK_MENU_SHELL(insert_menu), menuitem);
- g_signal_connect(G_OBJECT(toolbar->image), "notify::sensitive",
- G_CALLBACK(button_sensitiveness_changed), menuitem);
- g_signal_connect(G_OBJECT(toolbar->image), "notify::visible",
- G_CALLBACK(button_visibility_changed), menuitem);
-
- menuitem = gtk_menu_item_new_with_mnemonic(_("_Link"));
- g_signal_connect_swapped(G_OBJECT(menuitem), "activate", G_CALLBACK(gtk_button_clicked), toolbar->link);
- gtk_menu_shell_append(GTK_MENU_SHELL(insert_menu), menuitem);
- g_signal_connect(G_OBJECT(toolbar->link), "notify::sensitive",
- G_CALLBACK(button_sensitiveness_changed), menuitem);
- g_signal_connect(G_OBJECT(toolbar->link), "notify::visible",
- G_CALLBACK(button_visibility_changed), menuitem);
-
- menuitem = gtk_menu_item_new_with_mnemonic(_("_Horizontal rule"));
- g_signal_connect(G_OBJECT(menuitem), "activate" , G_CALLBACK(insert_hr_cb), toolbar);
- gtk_menu_shell_append(GTK_MENU_SHELL(insert_menu), menuitem);
- toolbar->insert_hr = menuitem;
-
- g_signal_connect(G_OBJECT(insert_button), "button-press-event", G_CALLBACK(button_activate_on_click), toolbar);
- g_signal_connect(G_OBJECT(insert_button), "activate", G_CALLBACK(pidgin_menu_clicked), insert_menu);
- g_signal_connect_data(G_OBJECT(insert_menu), "deactivate", G_CALLBACK(pidgin_menu_deactivate),
- g_object_ref(insert_button), (GClosureNotify)g_object_unref, 0);
- toolbar->sml = NULL;
-
- /* Sep */
- sep = gtk_vseparator_new();
- gtk_box_pack_start(GTK_BOX(box), sep, FALSE, FALSE, 0);
- gtk_widget_show_all(sep);
-
- /* Smiley */
- smiley_button = gtk_button_new();
- gtk_button_set_relief(GTK_BUTTON(smiley_button), GTK_RELIEF_NONE);
- bbox = gtk_hbox_new(FALSE, 3);
- gtk_container_add(GTK_CONTAINER(smiley_button), bbox);
- image = gtk_image_new_from_stock(PIDGIN_STOCK_TOOLBAR_SMILEY, gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL));
- gtk_box_pack_start(GTK_BOX(bbox), image, FALSE, FALSE, 0);
- label = gtk_label_new_with_mnemonic(_("_Smile!"));
- gtk_box_pack_start(GTK_BOX(bbox), label, FALSE, FALSE, 0);
- gtk_box_pack_start(GTK_BOX(box), smiley_button, FALSE, FALSE, 0);
- g_signal_connect(G_OBJECT(smiley_button), "button-press-event", G_CALLBACK(gtk_imhtmltoolbar_popup_menu), toolbar);
- g_signal_connect_swapped(G_OBJECT(smiley_button), "clicked", G_CALLBACK(gtk_button_clicked), toolbar->smiley);
- gtk_widget_show_all(smiley_button);
-
- /* Sep */
- sep = gtk_vseparator_new();
- gtk_box_pack_start(GTK_BOX(box), sep, FALSE, FALSE, 0);
- gtk_widget_show_all(sep);
-
- /* Attention */
- attention_button = gtk_button_new();
- gtk_button_set_relief(GTK_BUTTON(attention_button), GTK_RELIEF_NONE);
- bbox = gtk_hbox_new(FALSE, 3);
- gtk_container_add(GTK_CONTAINER(attention_button), bbox);
- image = gtk_image_new_from_stock(PIDGIN_STOCK_TOOLBAR_SEND_ATTENTION,
- gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL));
- gtk_box_pack_start(GTK_BOX(bbox), image, FALSE, FALSE, 0);
- label = gtk_label_new_with_mnemonic(_("_Attention!"));
- gtk_box_pack_start(GTK_BOX(bbox), label, FALSE, FALSE, 0);
- gtk_box_pack_start(GTK_BOX(box), attention_button, FALSE, FALSE, 0);
- g_signal_connect_swapped(G_OBJECT(attention_button), "clicked",
- G_CALLBACK(gtk_button_clicked), toolbar->attention);
- gtk_widget_show_all(attention_button);
-
- g_signal_connect(G_OBJECT(toolbar->attention), "notify::sensitive",
- G_CALLBACK(button_sensitiveness_changed), attention_button);
- g_signal_connect(G_OBJECT(toolbar->attention), "notify::visible",
- G_CALLBACK(button_visibility_changed), attention_button);
-
- /* set attention button to be greyed out until we get a conversation */
- gtk_widget_set_sensitive(toolbar->attention, FALSE);
-
- gtk_box_pack_start(GTK_BOX(hbox), box, FALSE, FALSE, 0);
- g_object_set_data(G_OBJECT(hbox), "lean-view", box);
- gtk_widget_show(box);
-
- purple_prefs_connect_callback(toolbar, PIDGIN_PREFS_ROOT "/conversations/toolbar/wide",
- imhtmltoolbar_view_pref_changed, toolbar);
- g_signal_connect_data(G_OBJECT(toolbar), "realize",
- G_CALLBACK(purple_prefs_trigger_callback), PIDGIN_PREFS_ROOT "/conversations/toolbar/wide",
- NULL, G_CONNECT_AFTER | G_CONNECT_SWAPPED);
-
- gtk_event_box_set_visible_window(GTK_EVENT_BOX(event), FALSE);
-
- gtk_widget_add_events(event, GDK_BUTTON_PRESS_MASK);
- gtk_box_pack_start(GTK_BOX(hbox), event, TRUE, TRUE, 0);
- g_signal_connect(G_OBJECT(event), "button-press-event", G_CALLBACK(gtk_imhtmltoolbar_popup_menu), toolbar);
- gtk_widget_show(event);
-}
-
-GtkWidget *gtk_imhtmltoolbar_new()
-{
- return GTK_WIDGET(g_object_new(gtk_imhtmltoolbar_get_type(), NULL));
-}
-
-GType gtk_imhtmltoolbar_get_type()
-{
- static GType imhtmltoolbar_type = 0;
-
- if (!imhtmltoolbar_type) {
- static const GTypeInfo imhtmltoolbar_info = {
- sizeof(GtkIMHtmlToolbarClass),
- NULL,
- NULL,
- (GClassInitFunc) gtk_imhtmltoolbar_class_init,
- NULL,
- NULL,
- sizeof (GtkIMHtmlToolbar),
- 0,
- (GInstanceInitFunc) gtk_imhtmltoolbar_init,
- NULL
- };
-
- imhtmltoolbar_type = g_type_register_static(GTK_TYPE_HBOX,
- "GtkIMHtmlToolbar", &imhtmltoolbar_info, 0);
- }
-
- return imhtmltoolbar_type;
-}
-
-
-void gtk_imhtmltoolbar_attach(GtkIMHtmlToolbar *toolbar, GtkWidget *imhtml)
-{
- GtkIMHtmlButtons buttons;
-
- g_return_if_fail(toolbar != NULL);
- g_return_if_fail(GTK_IS_IMHTMLTOOLBAR(toolbar));
- g_return_if_fail(imhtml != NULL);
- g_return_if_fail(GTK_IS_IMHTML(imhtml));
-
- toolbar->imhtml = imhtml;
- g_signal_connect(G_OBJECT(imhtml), "format_buttons_update", G_CALLBACK(update_buttons_cb), toolbar);
- g_signal_connect_after(G_OBJECT(imhtml), "format_function_toggle", G_CALLBACK(toggle_button_cb), toolbar);
- g_signal_connect_after(G_OBJECT(imhtml), "format_function_clear", G_CALLBACK(update_format_cb), toolbar);
- g_signal_connect(G_OBJECT(imhtml), "format_function_update", G_CALLBACK(update_format_cb), toolbar);
- g_signal_connect_after(G_OBJECT(GTK_IMHTML(imhtml)->text_buffer), "mark-set", G_CALLBACK(mark_set_cb), toolbar);
-
- buttons = gtk_imhtml_get_format_functions(GTK_IMHTML(imhtml));
- update_buttons_cb(GTK_IMHTML(imhtml), buttons, toolbar);
- update_buttons(toolbar);
-}
-
-void gtk_imhtmltoolbar_associate_smileys(GtkIMHtmlToolbar *toolbar, const char *proto_id)
-{
- g_free(toolbar->sml);
- toolbar->sml = g_strdup(proto_id);
-}
-
-void gtk_imhtmltoolbar_switch_active_conversation(GtkIMHtmlToolbar *toolbar,
- PurpleConversation *conv)
-{
- PurpleConnection *gc = purple_conversation_get_connection(conv);
- PurplePlugin *prpl = purple_connection_get_prpl(gc);
-
- g_object_set_data(G_OBJECT(toolbar), "active_conv", conv);
-
- /* gray out attention button on protocols that don't support it
- for the time being it is always disabled for chats */
- gtk_widget_set_sensitive(toolbar->attention,
- conv && prpl && PURPLE_IS_IM_CONVERSATION(conv) &&
- PURPLE_PLUGIN_PROTOCOL_INFO(prpl)->send_attention != NULL);
-}
-
--- a/pidgin/gtkimhtmltoolbar.h Sun Feb 02 02:00:31 2014 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,97 +0,0 @@
-/*
- * GtkIMHtmlToolbar
- *
- * Pidgin is the legal property of its developers, whose names are too numerous
- * to list here. Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
- *
- */
-#ifndef _PIDGINIMHTMLTOOLBAR_H_
-#define _PIDGINIMHTMLTOOLBAR_H_
-
-#include <gtk/gtk.h>
-#include "gtkimhtml.h"
-
-G_BEGIN_DECLS
-
-#define DEFAULT_FONT_FACE "Helvetica 12"
-
-#define GTK_TYPE_IMHTMLTOOLBAR (gtk_imhtmltoolbar_get_type())
-#define GTK_IMHTMLTOOLBAR(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GTK_TYPE_IMHTMLTOOLBAR, GtkIMHtmlToolbar))
-#define GTK_IMHTMLTOOLBAR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GTK_TYPE_IMHTMLTOOLBAR, GtkIMHtmlToolbarClass))
-#define GTK_IS_IMHTMLTOOLBAR(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GTK_TYPE_IMHTMLTOOLBAR))
-#define GTK_IS_IMHTMLTOOLBAR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GTK_TYPE_IMHTMLTOOLBAR))
-
-typedef struct _GtkIMHtmlToolbar GtkIMHtmlToolbar;
-typedef struct _GtkIMHtmlToolbarClass GtkIMHtmlToolbarClass;
-
-struct _GtkIMHtmlToolbar {
- GtkHBox box;
-
- GtkWidget *imhtml;
-
- gpointer depr1;
-
- GtkWidget *bold;
- GtkWidget *italic;
- GtkWidget *underline;
-
- GtkWidget *larger_size;
- GtkWidget *normal_size;
- GtkWidget *smaller_size;
-
- GtkWidget *font;
- GtkWidget *fgcolor;
- GtkWidget *bgcolor;
-
- GtkWidget *clear;
-
- GtkWidget *image;
- GtkWidget *link;
- GtkWidget *smiley;
- GtkWidget *attention;
-
- GtkWidget *font_dialog;
- GtkWidget *fgcolor_dialog;
- GtkWidget *bgcolor_dialog;
- GtkWidget *link_dialog;
- GtkWidget *smiley_dialog;
- GtkWidget *image_dialog;
-
- char *sml;
- GtkWidget *strikethrough;
- GtkWidget *insert_hr;
- GtkWidget *call;
-};
-
-struct _GtkIMHtmlToolbarClass {
- GtkHBoxClass parent_class;
-
-};
-
-GType gtk_imhtmltoolbar_get_type (void);
-GtkWidget* gtk_imhtmltoolbar_new (void);
-
-void gtk_imhtmltoolbar_attach (GtkIMHtmlToolbar *toolbar, GtkWidget *imhtml);
-void gtk_imhtmltoolbar_associate_smileys (GtkIMHtmlToolbar *toolbar, const char *proto_id);
-
-void gtk_imhtmltoolbar_switch_active_conversation(GtkIMHtmlToolbar *toolbar,
- PurpleConversation *conv);
-
-G_END_DECLS
-
-#endif /* _PIDGINIMHTMLTOOLBAR_H_ */