gee
oldstatus
2005-09-19, Nathan Walp
* Gaim is the legal property of its developers, whose names are too numerous * to list here. Please refer to the COPYRIGHT file distributed with this * This program is free software; you can redistribute it and/or modify * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "gtksourceiter.h" #include <gdk/gdkkeysyms.h> #ifdef HAVE_LANGINFO_CODESET #include <gdk/gdkwin32.h> # define N_(String) gettext_noop (String) # define N_(String) (String) # define N_(String) (String) #include <pango/pango-font.h> /* GTK+ < 2.4.x hack, see gtkgaim.h for details. */ #if (!GTK_CHECK_VERSION(2,4,0)) #define GTK_WRAP_WORD_CHAR GTK_WRAP_WORD #define TOOLTIP_TIMEOUT 500 #if (!GTK_CHECK_VERSION(2,2,0)) #define gtk_widget_get_clipboard(x, y) gtk_clipboard_get(y) gtk_text_view_drag_motion ( GtkWidget * widget , 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 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 ); static gboolean gtk_imhtml_is_amp_escape ( const gchar * string , gchar ** replace , gint * length ); 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 gtk_imhtml_mapped_scroll_to_end ( GtkWidget * imhtml , gpointer data ); /* POINT_SIZE converts from AIM font sizes to a point size scale factor. */ #define POINT_SIZE(x) (_point_sizes [MIN ((x > 0 ? x : 1), MAX_FONT_SIZE) - 1]) static gdouble _point_sizes [] = { .69444444 , .8333333 , 1 , 1.2 , 1.44 , 1.728 , 2.0736 }; static guint signals [ LAST_SIGNAL ] = { 0 }; GtkTargetEntry selection_targets [] = { { "text/html" , 0 , TARGET_HTML }, { "UTF8_STRING" , 0 , TARGET_UTF8_STRING }, { "COMPOUND_TEXT" , 0 , TARGET_COMPOUND_TEXT }, { "STRING" , 0 , TARGET_STRING }, { "TEXT" , 0 , TARGET_TEXT }}; GtkTargetEntry link_drag_drop_targets [] = { /* Win32 clipboard format value, and functions to convert back and * forth between HTML and the clipboard format. static UINT win_html_fmt ; clipboard_win32_to_html ( char * clipboard ) { int clipboard_length = 0 ; #if 0 /* Debugging for Windows clipboard */ gaim_debug_info("imhtml clipboard", "from clipboard: %s\n", clipboard); fd = g_fopen("e:\\gaimcb.txt", "wb"); fprintf(fd, "%s", clipboard); clipboard_length = strlen ( clipboard ); if ( ! ( header = strstr ( clipboard , "StartFragment:" )) || ( header - clipboard ) >= clipboard_length ) sscanf ( header , "StartFragment:%d" , & start ); if ( ! ( header = strstr ( clipboard , "EndFragment:" )) || ( header - clipboard ) >= clipboard_length ) sscanf ( header , "EndFragment:%d" , & finish ); if ( finish > clipboard_length ) finish = clipboard_length ; 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 ); html = g_strjoinv ( " \n " , split ); #if 0 /* Debugging for Windows clipboard */ gaim_debug_info("imhtml clipboard", "HTML fragment: '%s'\n", html); clipboard_html_to_win32 ( char * html ) { clipboard = g_string_new ( "Version:1.0 \r\n " ); g_string_append ( clipboard , "StartHTML:0000000105 \r\n " ); tmp = g_strdup_printf ( "EndHTML:%010d \r\n " , 147 + length ); g_string_append ( clipboard , tmp ); g_string_append ( clipboard , "StartFragment:0000000127 \r\n " ); tmp = g_strdup_printf ( "EndFragment:%010d \r\n " , 127 + length ); g_string_append ( clipboard , tmp ); g_string_append ( clipboard , "<!--StartFragment--> \r\n " ); g_string_append ( clipboard , html ); g_string_append ( clipboard , " \r\n <!--EndFragment-->" ); g_string_free ( clipboard , FALSE ); #if 0 /* Debugging for Windows clipboard */ gaim_debug_info("imhtml clipboard", "from gaim: %s\n", ret); static void clipboard_copy_html_win32 ( GtkIMHtml * imhtml ) { gchar * clipboard = clipboard_html_to_win32 ( imhtml -> clipboard_html_string ); HWND hwnd = GDK_WINDOW_HWND ( GTK_WIDGET ( imhtml ) -> window ); if ( OpenClipboard ( hwnd )) { gint length = strlen ( clipboard ); HGLOBAL hdata = GlobalAlloc ( GMEM_MOVEABLE , length ); gchar * buffer = GlobalLock ( hdata ); memcpy ( buffer , clipboard , length ); if ( SetClipboardData ( win_html_fmt , hdata ) == NULL ) { g_win32_error_message ( GetLastError ()); gaim_debug_info ( "html clipboard" , "Unable to set clipboard data: %s \n " , err_msg ? err_msg : "Unknown Error" ); static gboolean clipboard_paste_html_win32 ( GtkIMHtml * imhtml ) { 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 ); error_reading_clipboard = TRUE ; char * buffer = GlobalLock ( hdata ); error_reading_clipboard = TRUE ; char * text = clipboard_win32_to_html ( imhtml_paste_insert ( imhtml , text , error_reading_clipboard = TRUE ; if ( error_reading_clipboard ) { gchar * err_msg = g_win32_error_message ( GetLastError ()); gaim_debug_info ( "html clipboard" , "Unable to read clipboard data: %s \n " , err_msg ? err_msg : "Unknown Error" ); return g_new0 ( GtkSmileyTree , 1 ); gtk_smiley_tree_insert ( GtkSmileyTree * tree , const gchar * x = smiley -> smile ; t -> values = g_string_new ( "" ); pos = strchr ( t -> values -> str , * x ); 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 ); index = GPOINTER_TO_INT ( pos ) - GPOINTER_TO_INT ( t -> values -> str ); void gtk_smiley_tree_destroy ( GtkSmileyTree * tree ) GSList * list = g_slist_append ( NULL , tree ); GtkSmileyTree * t = list -> data ; list = g_slist_remove ( list , t ); for ( i = 0 ; i < t -> values -> len ; i ++ ) list = g_slist_append ( list , t -> children [ i ]); g_string_free ( t -> values , TRUE ); static gboolean gtk_size_allocate_cb ( GtkIMHtml * widget , GtkAllocation * alloc , gpointer user_data ) gtk_text_view_get_visible_rect ( GTK_TEXT_VIEW ( widget ), & rect ); if ( widget -> old_rect . width != rect . width || widget -> 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 )); GtkIMHtmlScalable * scale = GTK_IMHTML_SCALABLE ( iter -> data ); scale -> scale ( scale , rect . width - xminus , rect . height ); gtk_imhtml_tip_paint ( GtkIMHtml * imhtml ) g_return_val_if_fail ( GTK_IS_IMHTML ( imhtml ), FALSE ); layout = gtk_widget_create_pango_layout ( imhtml -> tip_window , imhtml -> tip ); gtk_paint_flat_box ( imhtml -> tip_window -> style , imhtml -> tip_window -> window , GTK_STATE_NORMAL , GTK_SHADOW_OUT , NULL , imhtml -> tip_window , "tooltip" , 0 , 0 , -1 , -1 ); gtk_paint_layout ( imhtml -> tip_window -> style , imhtml -> tip_window -> window , GTK_STATE_NORMAL , FALSE , NULL , imhtml -> tip_window , NULL , 4 , 4 , layout ); gtk_imhtml_tip ( gpointer data ) GtkIMHtml * imhtml = data ; PangoFontMetrics * font_metrics ; gint gap , x , y , h , w , scr_w , baseline_skip ; g_return_val_if_fail ( GTK_IS_IMHTML ( imhtml ), FALSE ); if ( ! imhtml -> tip || ! GTK_WIDGET_DRAWABLE ( GTK_WIDGET ( imhtml ))) { gtk_widget_destroy ( imhtml -> tip_window ); imhtml -> tip_window = NULL ; imhtml -> tip_window = gtk_window_new ( GTK_WINDOW_POPUP ); gtk_widget_set_app_paintable ( imhtml -> tip_window , TRUE ); gtk_window_set_resizable ( GTK_WINDOW ( imhtml -> tip_window ), FALSE ); gtk_widget_set_name ( imhtml -> tip_window , "gtk-tooltips" ); g_signal_connect_swapped ( G_OBJECT ( imhtml -> tip_window ), "expose_event" , G_CALLBACK ( gtk_imhtml_tip_paint ), imhtml ); gtk_widget_ensure_style ( imhtml -> tip_window ); layout = gtk_widget_create_pango_layout ( imhtml -> tip_window , imhtml -> tip ); font = pango_context_load_font ( pango_layout_get_context ( layout ), imhtml -> tip_window -> style -> font_desc ); char * tmp = pango_font_description_to_string ( imhtml -> tip_window -> style -> font_desc ); gaim_debug ( GAIM_DEBUG_ERROR , "gtk_imhtml_tip" , "pango_context_load_font() couldn't load font: '%s' \n " , 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 ); baseline_skip = PANGO_PIXELS ( pango_font_metrics_get_ascent ( font_metrics ) + pango_font_metrics_get_descent ( font_metrics )); gdk_window_get_pointer ( NULL , & x , & y , NULL ); if ( GTK_WIDGET_NO_WINDOW ( GTK_WIDGET ( imhtml ))) y += GTK_WIDGET ( imhtml ) -> allocation . y ; scr_w = gdk_screen_width (); 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_widget_show ( imhtml -> tip_window ); gtk_window_move ( GTK_WINDOW ( imhtml -> tip_window ), x , y ); pango_font_metrics_unref ( font_metrics ); gboolean gtk_motion_event_notify ( GtkWidget * imhtml , GdkEventMotion * event , gpointer data ) GdkWindow * win = event -> window ; GSList * tags = NULL , * templist = NULL ; gdk_window_get_pointer ( GTK_WIDGET ( imhtml ) -> window , 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 ); GtkTextTag * tag = templist -> data ; tip = g_object_get_data ( G_OBJECT ( tag ), "link_url" ); templist = templist -> next ; if ( GTK_IMHTML ( imhtml ) -> tip ) { if (( tip == GTK_IMHTML ( imhtml ) -> tip )) { /* 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 ) gdk_window_set_cursor ( win , GTK_IMHTML ( imhtml ) -> text_cursor ); gdk_window_set_cursor ( win , GTK_IMHTML ( imhtml ) -> arrow_cursor ); if ( GTK_IMHTML ( imhtml ) -> tip_timer ) g_source_remove ( GTK_IMHTML ( imhtml ) -> tip_timer ); GTK_IMHTML ( imhtml ) -> tip_timer = 0 ; if ( ! GTK_IMHTML ( imhtml ) -> editable ) gdk_window_set_cursor ( win , GTK_IMHTML ( imhtml ) -> hand_cursor ); GTK_IMHTML ( imhtml ) -> tip_timer = g_timeout_add ( TOOLTIP_TIMEOUT , GTK_IMHTML ( imhtml ) -> tip = tip ; gboolean gtk_enter_event_notify ( GtkWidget * imhtml , GdkEventCrossing * event , gpointer data ) if ( GTK_IMHTML ( imhtml ) -> editable ) gtk_text_view_get_window ( GTK_TEXT_VIEW ( imhtml ), GTK_IMHTML ( imhtml ) -> text_cursor ); gtk_text_view_get_window ( GTK_TEXT_VIEW ( imhtml ), GTK_IMHTML ( imhtml ) -> arrow_cursor ); /* propagate the event normally */ 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 ) -> 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 ; gtk_text_view_get_window ( GTK_TEXT_VIEW ( imhtml ), GTK_TEXT_WINDOW_TEXT ), NULL ); /* propagate the event normally */ * XXX - This should be removed eventually. * This function exists to work around a gross bug in GtkTextView. * Basically, we short circuit ctrl+a and ctrl+end because they make * It's supposed to be fixed in gtk2.2. You can view the bug report at * http://bugzilla.gnome.org/show_bug.cgi?id=107939 * I'm adding some keyboard shortcuts too. gboolean gtk_key_pressed_cb ( GtkIMHtml * imhtml , GdkEventKey * event , gpointer data ) if ( event -> state & GDK_CONTROL_MASK ) #if (!GTK_CHECK_VERSION(2,2,0)) #endif /* !(Gtk+ >= 2.2.0) */ case 'b' : /* ctrl-b is GDK_Left, which moves backwards. */ if ( imhtml -> format_functions & GTK_IMHTML_BOLD ) { if ( imhtml -> html_shortcuts ) { gtk_imhtml_toggle_bold ( imhtml ); if ( imhtml -> format_functions & GTK_IMHTML_ITALIC ) { if ( imhtml -> html_shortcuts ) { gtk_imhtml_toggle_italic ( imhtml ); case 'u' : /* ctrl-u is GDK_Clear, which clears the line. */ if ( imhtml -> format_functions & GTK_IMHTML_UNDERLINE ) { if ( imhtml -> html_shortcuts ) { gtk_imhtml_toggle_underline ( imhtml ); if ( imhtml -> format_functions & GTK_IMHTML_SHRINK ) gtk_imhtml_font_shrink ( imhtml ); if ( imhtml -> format_functions & GTK_IMHTML_GROW ) gtk_imhtml_font_grow ( imhtml ); case '1' : strcpy ( buf , ":-)" ); break ; case '2' : strcpy ( buf , ":-(" ); break ; case '3' : strcpy ( buf , ";-)" ); break ; case '4' : strcpy ( buf , ":-P" ); break ; case '5' : strcpy ( buf , "=-O" ); break ; case '6' : strcpy ( buf , ":-*" ); break ; case '7' : strcpy ( buf , ">:o" ); break ; case '8' : strcpy ( buf , "8-)" ); break ; case '!' : strcpy ( buf , ":-$" ); break ; case '@' : strcpy ( buf , ":-!" ); break ; case '#' : strcpy ( buf , ":-[" ); break ; case '$' : strcpy ( buf , "O:-)" ); break ; case '%' : strcpy ( buf , ":-/" ); break ; case '^' : strcpy ( buf , ":'(" ); break ; case '&' : strcpy ( buf , ":-X" ); break ; case '*' : strcpy ( buf , ":-D" ); break ; if ( * buf && imhtml -> smiley_shortcuts ) { gtk_imhtml_insert_smiley ( imhtml , imhtml -> protocol_name , buf ); 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 hijack_menu_cb ( GtkIMHtml * imhtml , GtkMenu * menu , gpointer data ) menuitem = gtk_menu_item_new_with_mnemonic ( _ ( "Pa_ste As Text" )); gtk_widget_show ( menuitem ); gtk_widget_set_sensitive ( menuitem , 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 ); static void gtk_imhtml_clipboard_get ( GtkClipboard * clipboard , GtkSelectionData * selection_data , guint info , GtkIMHtml * imhtml ) { GtkTextMark * sel = gtk_text_buffer_get_selection_bound ( imhtml -> text_buffer ); GtkTextMark * ins = gtk_text_buffer_get_insert ( 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 ); primary = gtk_widget_get_clipboard ( GTK_WIDGET ( imhtml ), GDK_SELECTION_PRIMARY ) == clipboard ; if ( info == TARGET_HTML ) { GString * str = g_string_new ( NULL ); text = gtk_imhtml_get_markup_range ( imhtml , & start , & end ); text = imhtml -> clipboard_html_string ; /* Mozilla asks that we start our text/html with the Unicode byte order mark */ str = g_string_append_unichar ( str , 0xfeff ); str = g_string_append ( str , text ); str = g_string_append_unichar ( str , 0x0000 ); selection = g_convert ( str -> str , str -> len , "UCS-2" , "UTF-8" , NULL , & len , NULL ); gtk_selection_data_set ( selection_data , gdk_atom_intern ( "text/html" , FALSE ), 16 , selection , len ); g_string_free ( str , TRUE ); text = gtk_imhtml_get_text ( imhtml , & start , & end ); text = imhtml -> clipboard_text_string ; gtk_selection_data_set_text ( selection_data , text , strlen ( text )); if ( primary ) /* This was allocated here */ static void gtk_imhtml_primary_clipboard_clear ( GtkClipboard * clipboard , GtkIMHtml * imhtml ) 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" ), static void copy_clipboard_cb ( GtkIMHtml * imhtml , gpointer unused ) GtkTextMark * sel = gtk_text_buffer_get_selection_bound ( imhtml -> text_buffer ); GtkTextMark * ins = gtk_text_buffer_get_insert ( 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 ); gtk_clipboard_set_with_owner ( gtk_widget_get_clipboard ( GTK_WIDGET ( imhtml ), GDK_SELECTION_CLIPBOARD ), selection_targets , sizeof ( selection_targets ) / sizeof ( GtkTargetEntry ), ( GtkClipboardGetFunc ) gtk_imhtml_clipboard_get , ( GtkClipboardClearFunc ) NULL , G_OBJECT ( imhtml )); if ( imhtml -> clipboard_html_string ) { g_free ( imhtml -> clipboard_html_string ); g_free ( imhtml -> clipboard_text_string ); imhtml -> clipboard_html_string = gtk_imhtml_get_markup_range ( imhtml , & start , & end ); imhtml -> clipboard_text_string = gtk_imhtml_get_text ( imhtml , & start , & end ); /* We're going to still copy plain text, but let's toss the "HTML Format" we need into the windows clipboard now as well. */ clipboard_copy_html_win32 ( imhtml ); g_signal_stop_emission_by_name ( imhtml , "copy-clipboard" ); static void cut_clipboard_cb ( GtkIMHtml * imhtml , gpointer unused ) GtkTextMark * sel = gtk_text_buffer_get_selection_bound ( imhtml -> text_buffer ); GtkTextMark * ins = gtk_text_buffer_get_insert ( 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 ); gtk_clipboard_set_with_owner ( gtk_widget_get_clipboard ( GTK_WIDGET ( imhtml ), GDK_SELECTION_CLIPBOARD ), selection_targets , sizeof ( selection_targets ) / sizeof ( GtkTargetEntry ), ( GtkClipboardGetFunc ) gtk_imhtml_clipboard_get , ( GtkClipboardClearFunc ) NULL , G_OBJECT ( imhtml )); if ( imhtml -> clipboard_html_string ) { g_free ( imhtml -> clipboard_html_string ); g_free ( imhtml -> clipboard_text_string ); imhtml -> clipboard_html_string = gtk_imhtml_get_markup_range ( imhtml , & start , & end ); imhtml -> clipboard_text_string = gtk_imhtml_get_text ( imhtml , & start , & end ); /* We're going to still copy plain text, but let's toss the "HTML Format" we need into the windows clipboard now as well. */ clipboard_copy_html_win32 ( imhtml ); 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 ) GtkIMHtmlOptions flags = plaintext ? 0 : ( GTK_IMHTML_NO_NEWLINE | GTK_IMHTML_NO_COMMENTS ); if ( gtk_text_buffer_get_selection_bounds ( imhtml -> text_buffer , NULL , NULL )) 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 ), static void paste_plaintext_received_cb ( GtkClipboard * clipboard , const gchar * text , gpointer data ) tmp = gaim_escape_html ( text ); imhtml_paste_insert ( data , tmp , TRUE ); static void paste_received_cb ( GtkClipboard * clipboard , GtkSelectionData * selection_data , gpointer data ) GtkIMHtml * imhtml = data ; if ( ! gtk_text_view_get_editable ( GTK_TEXT_VIEW ( imhtml ))) if ( selection_data -> length < 0 ) { gtk_clipboard_request_text ( clipboard , paste_plaintext_received_cb , imhtml ); /* Here's some debug code, for figuring out what sent to us over the clipboard. */ gaim_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 (selection_data->data[i] == '\0') printf("%c", selection_data->data[i]); text = g_malloc ( selection_data -> length ); memcpy ( text , selection_data -> data , selection_data -> length ); if ( selection_data -> length >= 2 && ( * ( guint16 * ) text == 0xfeff || * ( guint16 * ) text == 0xfffe )) { char * utf8 = g_convert ( text , selection_data -> length , "UTF-8" , "UCS-2" , NULL , NULL , NULL ); gaim_debug_warning ( "gtkimhtml" , "g_convert from UCS-2 failed in paste_received_cb \n " ); tmp = g_utf8_next_char ( text ); memmove ( text , tmp , strlen ( tmp ) + 1 ); if ( ! ( * text ) || ! g_utf8_validate ( text , -1 , NULL )) { gaim_debug_warning ( "gtkimhtml" , "empty string or invalid UTF-8 in paste_received_cb \n " ); imhtml_paste_insert ( imhtml , text , FALSE ); static void paste_clipboard_cb ( GtkIMHtml * imhtml , gpointer blah ) /* 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 )) { GtkClipboard * clipboard = gtk_widget_get_clipboard ( GTK_WIDGET ( imhtml ), GDK_SELECTION_CLIPBOARD ); gtk_clipboard_request_contents ( clipboard , gdk_atom_intern ( "text/html" , FALSE ), paste_received_cb , imhtml ); g_signal_stop_emission_by_name ( imhtml , "paste-clipboard" ); static 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 ) { GtkClipboard * clipboard = gtk_widget_get_clipboard ( GTK_WIDGET ( imhtml ), GDK_SELECTION_PRIMARY ); gtk_text_view_window_to_buffer_coords ( GTK_TEXT_VIEW ( imhtml ), 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 ); static GtkTextViewClass * parent_class = NULL ; gtk_imhtml_finalize ( GObject * object ) GtkIMHtml * imhtml = GTK_IMHTML ( object ); g_source_remove ( imhtml -> scroll_src ); 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 ); gtk_widget_destroy ( imhtml -> tip_window ); gtk_timeout_remove ( imhtml -> tip_timer ); for ( scalables = imhtml -> scalables ; scalables ; scalables = scalables -> next ) { GtkIMHtmlScalable * scale = GTK_IMHTML_SCALABLE ( scalables -> data ); for ( l = imhtml -> im_images ; l ; l = l -> next ) { id = GPOINTER_TO_INT ( l -> data ); if ( imhtml -> funcs -> image_unref ) imhtml -> funcs -> image_unref ( id ); if ( imhtml -> clipboard_text_string ) { g_free ( imhtml -> clipboard_text_string ); g_free ( imhtml -> clipboard_html_string ); g_list_free ( imhtml -> scalables ); g_slist_free ( imhtml -> im_images ); if ( imhtml -> protocol_name ) g_free ( imhtml -> protocol_name ); if ( imhtml -> search_string ) g_free ( imhtml -> search_string ); G_OBJECT_CLASS ( parent_class ) -> finalize ( object ); static void gtk_imhtml_class_init ( GtkIMHtmlClass * klass ) GtkWidgetClass * widget_class = ( GtkWidgetClass * ) klass ; GtkObjectClass * object_class ; GObjectClass * gobject_class ; object_class = ( GtkObjectClass * ) klass ; gobject_class = ( GObjectClass * ) klass ; parent_class = gtk_type_class ( GTK_TYPE_TEXT_VIEW ); signals [ URL_CLICKED ] = g_signal_new ( "url_clicked" , G_TYPE_FROM_CLASS ( gobject_class ), G_STRUCT_OFFSET ( GtkIMHtmlClass , url_clicked ), g_cclosure_marshal_VOID__POINTER , signals [ BUTTONS_UPDATE ] = g_signal_new ( "format_buttons_update" , G_TYPE_FROM_CLASS ( gobject_class ), G_STRUCT_OFFSET ( GtkIMHtmlClass , buttons_update ), g_cclosure_marshal_VOID__INT , signals [ TOGGLE_FORMAT ] = g_signal_new ( "format_function_toggle" , G_TYPE_FROM_CLASS ( gobject_class ), G_STRUCT_OFFSET ( GtkIMHtmlClass , toggle_format ), g_cclosure_marshal_VOID__INT , signals [ CLEAR_FORMAT ] = g_signal_new ( "format_function_clear" , G_TYPE_FROM_CLASS ( gobject_class ), G_STRUCT_OFFSET ( GtkIMHtmlClass , clear_format ), g_cclosure_marshal_VOID__VOID , signals [ UPDATE_FORMAT ] = g_signal_new ( "format_function_update" , G_TYPE_FROM_CLASS ( gobject_class ), G_STRUCT_OFFSET ( GtkIMHtmlClass , update_format ), g_cclosure_marshal_VOID__VOID , gobject_class -> finalize = gtk_imhtml_finalize ; widget_class -> drag_motion = gtk_text_view_drag_motion ; gtk_widget_class_install_style_property ( widget_class , g_param_spec_boxed ( "hyperlink-color" , _ ( "Color to draw hyperlinks." ), GDK_TYPE_COLOR , G_PARAM_READABLE )); static void gtk_imhtml_init ( GtkIMHtml * imhtml ) imhtml -> text_buffer = gtk_text_buffer_new ( NULL ); gtk_text_buffer_get_end_iter ( imhtml -> text_buffer , & iter ); imhtml -> scrollpoint = gtk_text_buffer_create_mark ( imhtml -> text_buffer , NULL , & iter , FALSE ); gtk_text_view_set_buffer ( GTK_TEXT_VIEW ( imhtml ), imhtml -> text_buffer ); gtk_text_view_set_wrap_mode ( GTK_TEXT_VIEW ( imhtml ), GTK_WRAP_WORD_CHAR ); gtk_text_view_set_pixels_below_lines ( GTK_TEXT_VIEW ( imhtml ), 5 ); 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 ); /* 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 -> original_fsize = 0 ; 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_after ( G_OBJECT ( imhtml ), "map" , G_CALLBACK ( gtk_imhtml_mapped_scroll_to_end ), imhtml ); g_signal_connect ( G_OBJECT ( imhtml ), "size-allocate" , G_CALLBACK ( gtk_size_allocate_cb ), NULL ); 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 ), "key_press_event" , G_CALLBACK ( gtk_key_pressed_cb ), 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_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 ), 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_after ( G_OBJECT ( GTK_IMHTML ( imhtml ) -> text_buffer ), "mark-set" , G_CALLBACK ( mark_set_so_update_selection_cb ), imhtml ); gtk_widget_add_events ( GTK_WIDGET ( imhtml ), GDK_LEAVE_NOTIFY_MASK | GDK_ENTER_NOTIFY_MASK ); imhtml -> clipboard_text_string = NULL ; imhtml -> clipboard_html_string = NULL ; 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 ; gtk_imhtml_set_editable ( imhtml , FALSE ); g_signal_connect ( G_OBJECT ( imhtml ), "populate-popup" , G_CALLBACK ( hijack_menu_cb ), NULL ); /* Register HTML Format as desired clipboard format */ win_html_fmt = RegisterClipboardFormat ( "HTML Format" ); 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 ; static const GTypeInfo imhtml_info = { ( GClassInitFunc ) gtk_imhtml_class_init , ( GInstanceInitFunc ) gtk_imhtml_init imhtml_type = g_type_register_static ( gtk_text_view_get_type (), "GtkIMHtml" , & imhtml_info , 0 ); static void url_data_destroy ( gpointer mydata ) struct url_data * data = mydata ; g_object_unref ( data -> object ); static void url_open ( GtkWidget * w , struct url_data * data ) { g_signal_emit ( data -> object , signals [ URL_CLICKED ], 0 , data -> url ); static void url_copy ( GtkWidget * w , gchar * url ) { clipboard = gtk_widget_get_clipboard ( w , GDK_SELECTION_PRIMARY ); gtk_clipboard_set_text ( clipboard , url , -1 ); clipboard = gtk_widget_get_clipboard ( w , GDK_SELECTION_CLIPBOARD ); gtk_clipboard_set_text ( clipboard , url , -1 ); /* The callback for an event on a link tag. */ gboolean tag_event ( GtkTextTag * tag , GObject * imhtml , GdkEvent * event , GtkTextIter * arg2 , gpointer unused ) { GdkEventButton * event_button = ( GdkEventButton * ) event ; if ( GTK_IMHTML ( imhtml ) -> editable ) if ( event -> type == GDK_BUTTON_RELEASE ) { if (( event_button -> button == 1 ) || ( event_button -> button == 2 )) { /* 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 )) /* 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 )); } else if ( event_button -> button == 3 ) { GtkWidget * img , * item , * menu ; struct url_data * tempdata = g_new ( struct url_data , 1 ); tempdata -> object = g_object_ref ( imhtml ); tempdata -> url = g_strdup ( g_object_get_data ( G_OBJECT ( tag ), "link_url" )); /* 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 ); gdk_window_set_cursor ( event_button -> window , GTK_IMHTML ( imhtml ) -> arrow_cursor ); g_object_set_data_full ( G_OBJECT ( menu ), "x-imhtml-url-data" , tempdata , url_data_destroy ); if ( ! strncmp ( tempdata -> url , "mailto:" , 7 )) /* Copy E-Mail Address */ img = gtk_image_new_from_stock ( GTK_STOCK_COPY , item = gtk_image_menu_item_new_with_mnemonic ( _ ( "_Copy E-Mail Address" )); gtk_image_menu_item_set_image ( GTK_IMAGE_MENU_ITEM ( item ), img ); g_signal_connect ( G_OBJECT ( item ), "activate" , G_CALLBACK ( url_copy ), tempdata -> url + 7 ); gtk_menu_shell_append ( GTK_MENU_SHELL ( menu ), item ); img = gtk_image_new_from_stock ( GTK_STOCK_COPY , item = gtk_image_menu_item_new_with_mnemonic ( _ ( "_Copy Link Location" )); gtk_image_menu_item_set_image ( GTK_IMAGE_MENU_ITEM ( item ), img ); g_signal_connect ( G_OBJECT ( item ), "activate" , G_CALLBACK ( url_copy ), tempdata -> url ); gtk_menu_shell_append ( GTK_MENU_SHELL ( menu ), item ); /* Open Link in Browser */ img = gtk_image_new_from_stock ( GTK_STOCK_JUMP_TO , item = gtk_image_menu_item_new_with_mnemonic ( _ ( "_Open Link in Browser" )); gtk_image_menu_item_set_image ( GTK_IMAGE_MENU_ITEM ( item ), img ); g_signal_connect ( G_OBJECT ( item ), "activate" , G_CALLBACK ( url_open ), tempdata ); 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 ); 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 */ return FALSE ; /* Let clicks go through if we didn't catch anything */ gtk_text_view_drag_motion ( GtkWidget * widget , GdkDragAction suggested_action = 0 ; if ( gtk_drag_dest_find_target ( widget , context , NULL ) == GDK_NONE ) { /* can't accept any of the offered targets */ GtkWidget * source_widget ; suggested_action = context -> suggested_action ; 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 (( context -> actions & 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. 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 ); gtk_drag_get_data ( widget , context , target , time ); gtk_drag_finish ( context , FALSE , FALSE , time ); gtk_imhtml_link_drag_rcv_cb ( GtkWidget * widget , GdkDragContext * dc , guint x , guint y , GtkSelectionData * sd , guint info , guint t , GtkIMHtml * imhtml ) GtkTextMark * mark = gtk_text_buffer_get_insert ( imhtml -> text_buffer ); gtk_text_buffer_get_iter_at_mark ( imhtml -> text_buffer , & iter , mark ); if ( gtk_imhtml_get_editable ( imhtml ) && sd -> data ){ case GTK_IMHTML_DRAG_URL : gaim_str_strip_cr ( sd -> data ); links = g_strsplit ( sd -> data , " \n " , 0 ); while (( link = links [ i ]) != NULL ){ if ( gaim_str_has_prefix ( link , "http://" ) || gaim_str_has_prefix ( link , "https://" ) || gaim_str_has_prefix ( link , "ftp://" )) gtk_imhtml_insert_link ( imhtml , mark , link , label ); /* 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 ); case GTK_IMHTML_DRAG_HTML : /* 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 ( sd -> length >= 2 && ! g_utf8_validate ( text , sd -> length - 1 , NULL )) { utf8 = g_convert ( text , sd -> length , "UTF-8" , "UCS-2" , NULL , NULL , NULL ); gaim_debug_warning ( "gtkimhtml" , "g_convert from UCS-2 failed in drag_rcv_cb \n " ); if ( * ( guint16 * ) text == 0xfeff || * ( guint16 * ) text == 0xfffe || TRUE ) { tmp = g_utf8_next_char ( utf8 ); memmove ( utf8 , tmp , strlen ( tmp ) + 1 ); } else if ( ! ( * text ) || ! g_utf8_validate ( text , -1 , NULL )) { gaim_debug_warning ( "gtkimhtml" , "empty string or invalid UTF-8 in drag_rcv_cb \n " ); gtk_imhtml_insert_html_at_iter ( imhtml , utf8 ? utf8 : text , 0 , & iter ); case GTK_IMHTML_DRAG_TEXT : if ( ! ( * text ) || ! g_utf8_validate ( text , -1 , NULL )) { gaim_debug_warning ( "gtkimhtml" , "empty string or invalid UTF-8 in drag_rcv_cb \n " ); char * tmp = gaim_escape_html ( text ); gtk_imhtml_insert_html_at_iter ( imhtml , tmp , 0 , & iter ); gtk_drag_finish ( dc , FALSE , FALSE , t ); gtk_drag_finish ( dc , TRUE , ( dc -> action == GDK_ACTION_MOVE ), t ); gtk_drag_finish ( dc , FALSE , FALSE , t ); static void gtk_smiley_tree_remove (GtkSmileyTree *tree, const gchar *x = smiley->smile; pos = strchr (t->values->str, *x); t = t->children [(int) pos - (int) t->values->str]; gtk_smiley_tree_lookup ( GtkSmileyTree * tree , if ( * x == '&' && gtk_imhtml_is_amp_escape ( x , & amp , & alen )) { /* Make sure all chars of the unescaped value match */ pos = strchr ( t -> values -> str , * amp ); t = t -> children [ GPOINTER_TO_INT ( pos ) - GPOINTER_TO_INT ( t -> values -> str )]; 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 */ pos = strchr ( t -> values -> str , * x ); t = t -> children [ GPOINTER_TO_INT ( pos ) - GPOINTER_TO_INT ( t -> values -> str )]; gtk_imhtml_associate_smiley ( GtkIMHtml * imhtml , g_return_if_fail ( imhtml != NULL ); g_return_if_fail ( GTK_IS_IMHTML ( imhtml )); 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 ); gtk_smiley_tree_insert ( tree , smiley ); gtk_imhtml_is_smiley ( GtkIMHtml * imhtml , GtkIMHtmlFontDetail * font ; sml = imhtml -> protocol_name ; if ( ! sml || ! ( tree = g_hash_table_lookup ( imhtml -> smiley_data , sml ))) tree = imhtml -> default_smilies ; * len = gtk_smiley_tree_lookup ( tree , text ); gtk_smiley_tree_image ( GtkIMHtml * imhtml , t = imhtml -> default_smilies ; t = g_hash_table_lookup ( imhtml -> smiley_data , sml ); return sml ? gtk_smiley_tree_image ( imhtml , NULL , text ) : NULL ; return sml ? gtk_smiley_tree_image ( imhtml , NULL , text ) : NULL ; pos = strchr ( t -> values -> str , * x ); t = t -> children [ GPOINTER_TO_INT ( pos ) - GPOINTER_TO_INT ( t -> values -> str )]; return sml ? gtk_smiley_tree_image ( imhtml , NULL , text ) : NULL ; t -> image -> icon = gdk_pixbuf_animation_new_from_file ( t -> image -> file , NULL ); #define VALID_TAG(x) if (!g_ascii_strncasecmp (string, x ">", strlen (x ">"))) { \ *tag = g_strndup (string, strlen (x)); \ #define VALID_OPT_TAG(x) if (!g_ascii_strncasecmp (string, x " ", strlen (x " "))) { \ const gchar *c = string + strlen (x " "); \ gboolean quote = FALSE; \ if (*c == '"' || *c == '\'') { \ if (quote && (*c == e)) \ } else if (!quote && (*c == '>')) \ *tag = g_strndup (string, c - string); \ gtk_imhtml_is_amp_escape ( const gchar * string , g_return_val_if_fail ( string != NULL , FALSE ); g_return_val_if_fail ( replace != NULL , FALSE ); g_return_val_if_fail ( length != NULL , FALSE ); if ( ! g_ascii_strncasecmp ( string , "&" , 5 )) { } else if ( ! g_ascii_strncasecmp ( string , "<" , 4 )) { } else if ( ! g_ascii_strncasecmp ( string , ">" , 4 )) { } else if ( ! g_ascii_strncasecmp ( string , " " , 6 )) { } else if ( ! g_ascii_strncasecmp ( string , "©" , 6 )) { } else if ( ! g_ascii_strncasecmp ( string , """ , 6 )) { } else if ( ! g_ascii_strncasecmp ( string , "®" , 5 )) { } else if ( ! g_ascii_strncasecmp ( string , "'" , 6 )) { } else if ( * ( string + 1 ) == '#' ) { if (( sscanf ( string , "&#%u;" , & pound ) == 1 ) && pound != 0 ) { if ( * ( string + 3 + ( gint ) log10 ( pound )) != ';' ) buflen = g_unichar_to_utf8 (( gunichar ) pound , buf ); while ( isdigit (( gint ) string [ * length ])) ( * length ) ++ ; if ( string [ * length ] == ';' ) ( * length ) ++ ; gtk_imhtml_is_tag ( const gchar * string , if ( ! ( close = strchr ( string , '>' ))) VALID_TAG ( "/UNDERLINE" ); VALID_TAG ( "BR/" ); /* hack until gtkimhtml handles things better */ if ( ! g_ascii_strncasecmp ( string , "!--" , strlen ( "!--" ))) { gchar * e = strstr ( string + strlen ( "!--" ), "-->" ); * len = e - string + strlen ( "-->" ); * tag = g_strndup ( string + strlen ( "!--" ), * len - strlen ( "!---->" )); * len = close - string + 1 ; * tag = g_strndup ( string , * len - 1 ); gtk_imhtml_get_html_opt ( gchar * tag , while ( g_ascii_strncasecmp ( t , opt , strlen ( opt ))) { while ( * t && ! (( * t == ' ' ) && ! quote )) { while ( * t && ( * t == ' ' )) t ++ ; if ( ! g_ascii_strncasecmp ( t , opt , strlen ( opt ))) { if (( * t == '\"' ) || ( * t == '\'' )) { while ( * e && ( * e != * ( t - 1 ))) e ++ ; val = g_strndup ( a , e - a ); while ( * e && ! isspace (( gint ) * e )) e ++ ; val = g_strndup ( a , e - a ); if ( gtk_imhtml_is_amp_escape ( e , & c , & len )) { ret = g_string_append ( ret , c ); ret = g_string_append_c ( ret , * e ); return g_string_free ( ret , FALSE ); /* Inline CSS Support - Douglas Thrift */ gtk_imhtml_get_css_opt ( gchar * style , while ( g_ascii_strncasecmp ( t , opt , strlen ( opt ))) { /* gboolean quote = FALSE; */ while ( * t && ! (( * t == ' ' ) /*&& !quote*/ )) { while ( * t && ( * t == ' ' )) t ++ ; if ( ! g_ascii_strncasecmp ( t , opt , strlen ( opt ))) { /* if ((*t == '\"') || (*t == '\'')) { while (*e && (*e != *(t - 1))) e++; val = g_strndup(a, e - a); while (*e && !isspace ((gint) *e)) e++; val = g_strndup(a, e - a); while ( * e && * e != ';' ) e ++ ; val = g_strndup ( a , e - a ); if ( gtk_imhtml_is_amp_escape ( e , & c , & len )) { ret = g_string_append ( ret , c ); ret = g_string_append_c ( ret , * e ); g_string_free ( ret , FALSE ); static const char * accepted_protocols [] = { static const int accepted_protocols_size = 3 ; /* 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. */ int gtk_imhtml_is_protocol ( const char * text ) for ( i = 0 ; i < accepted_protocols_size ; i ++ ){ if ( strncasecmp ( text , accepted_protocols [ i ], strlen ( accepted_protocols [ i ])) == 0 ){ return strlen ( accepted_protocols [ i ]); <KingAnt> marv: The two IM image functions in oscar are gaim_odc_send_im and gaim_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 gaim_imgstore_add [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 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 gaim 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 , GtkIMHtmlOptions options , GtkTextIter iter , ins , sel ; int y , height , 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 )) { 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 )) { sel_offset = gtk_text_iter_get_offset ( & sel ); 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 )){ options |= GTK_IMHTML_NO_SCROLL ; gtk_imhtml_insert_html_at_iter ( imhtml , text , options , & iter ); 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 ); 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 ); #define SCROLL_TO_END_PENDING "gtk_imhtml_scroll_to_end_pending" gboolean scroll_idle_cb ( gpointer data ) GtkTextView * imhtml = data ; adj = GTK_TEXT_VIEW ( imhtml ) -> vadjustment ; gtk_adjustment_set_value ( adj , adj -> upper - adj -> page_size ); GTK_IMHTML ( imhtml ) -> scroll_src = 0 ; static void gtk_imhtml_mapped_scroll_to_end ( GtkWidget * imhtml , gpointer data ) if ( g_object_get_data ( G_OBJECT ( imhtml ), SCROLL_TO_END_PENDING )) { g_object_set_data ( G_OBJECT ( imhtml ), SCROLL_TO_END_PENDING , GINT_TO_POINTER ( 0 )); if ( GTK_IMHTML ( imhtml ) -> scroll_src ) g_source_remove ( GTK_IMHTML ( imhtml ) -> scroll_src ); GTK_IMHTML ( imhtml ) -> scroll_src = g_idle_add_full ( GTK_TEXT_VIEW_PRIORITY_VALIDATE + 10 , scroll_idle_cb , imhtml , NULL ); void gtk_imhtml_scroll_to_end ( GtkIMHtml * imhtml ) if ( imhtml -> scroll_src ) { g_source_remove ( imhtml -> scroll_src ); if ( ! GTK_WIDGET ( imhtml ) -> window || ! gdk_window_is_visible ( GTK_WIDGET ( imhtml ) -> window )) g_object_set_data ( G_OBJECT ( imhtml ), SCROLL_TO_END_PENDING , GINT_TO_POINTER ( 1 )); imhtml -> scroll_src = g_idle_add_full ( GTK_TEXT_VIEW_PRIORITY_VALIDATE + 10 , scroll_idle_cb , imhtml , NULL ); void gtk_imhtml_insert_html_at_iter ( GtkIMHtml * imhtml , GtkIMHtmlOptions options , gint tlen , smilelen , wpos = 0 ; GtkIMHtmlScalable * scalable = NULL ; g_return_if_fail ( imhtml != NULL ); g_return_if_fail ( GTK_IS_IMHTML ( imhtml )); g_return_if_fail ( text != NULL ); if ( * c == '<' && gtk_imhtml_is_tag ( c + 1 , & tag , & tlen , & type )) { gtk_text_buffer_insert ( imhtml -> text_buffer , iter , ws , wpos ); if (( bold == 0 ) && ( imhtml -> format_functions & GTK_IMHTML_BOLD )) gtk_imhtml_toggle_bold ( imhtml ); gtk_text_buffer_insert ( imhtml -> text_buffer , iter , ws , wpos ); if (( bold == 0 ) && ( imhtml -> format_functions & GTK_IMHTML_BOLD ) && ! imhtml -> wbfo ) gtk_imhtml_toggle_bold ( imhtml ); gtk_text_buffer_insert ( imhtml -> text_buffer , iter , ws , wpos ); if (( italics == 0 ) && ( imhtml -> format_functions & GTK_IMHTML_ITALIC )) gtk_imhtml_toggle_italic ( imhtml ); gtk_text_buffer_insert ( imhtml -> text_buffer , iter , ws , wpos ); if (( italics == 0 ) && ( imhtml -> format_functions & GTK_IMHTML_ITALIC ) && ! imhtml -> wbfo ) gtk_imhtml_toggle_italic ( imhtml ); gtk_text_buffer_insert ( imhtml -> text_buffer , iter , ws , wpos ); if (( underline == 0 ) && ( imhtml -> format_functions & GTK_IMHTML_UNDERLINE )) gtk_imhtml_toggle_underline ( imhtml ); case 12 : /* /UNDERLINE */ gtk_text_buffer_insert ( imhtml -> text_buffer , iter , ws , wpos ); if (( underline == 0 ) && ( imhtml -> format_functions & GTK_IMHTML_UNDERLINE ) && ! imhtml -> wbfo ) gtk_imhtml_toggle_underline ( imhtml ); gtk_text_buffer_insert ( imhtml -> text_buffer , iter , ws , wpos ); if (( strike == 0 ) && ( imhtml -> format_functions & GTK_IMHTML_STRIKE )) gtk_imhtml_toggle_strike ( imhtml ); gtk_text_buffer_insert ( imhtml -> text_buffer , iter , ws , wpos ); if (( strike == 0 ) && ( imhtml -> format_functions & GTK_IMHTML_STRIKE ) && ! imhtml -> wbfo ) gtk_imhtml_toggle_strike ( imhtml ); /* FIXME: reimpliment this */ /* FIXME: reimpliment this */ /* FIXME: reimplement this */ /* FIXME: reimplement this */ /* FIXME: reimplement this */ /* FIXME: reimplement this */ /* FIXME: what was this supposed to do anyway? */ /* FIXME: make this undo whatever 23 was supposed to do */ if ( options & GTK_IMHTML_NO_TITLE ) { gtk_text_buffer_insert ( imhtml -> text_buffer , iter , ws , wpos ); scalable = gtk_imhtml_hr_new (); 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 , scalable ); if ( fonts && ! imhtml -> wbfo ) { GtkIMHtmlFontDetail * font = fonts -> data ; gtk_text_buffer_insert ( imhtml -> text_buffer , iter , ws , wpos ); /* NEW_BIT (NEW_TEXT_BIT); */ if ( font -> face && ( imhtml -> format_functions & GTK_IMHTML_FACE )) { gtk_imhtml_toggle_fontface ( imhtml , NULL ); if ( font -> fore && ( imhtml -> format_functions & GTK_IMHTML_FORECOLOR )) { gtk_imhtml_toggle_forecolor ( imhtml , NULL ); if ( font -> back && ( imhtml -> format_functions & GTK_IMHTML_BACKCOLOR )) { gtk_imhtml_toggle_backcolor ( imhtml , NULL ); if (( font -> size != 3 ) && ( imhtml -> format_functions & ( GTK_IMHTML_GROW | GTK_IMHTML_SHRINK ))) gtk_imhtml_font_set_size ( imhtml , 3 ); 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 ); gtk_text_buffer_insert ( imhtml -> text_buffer , iter , ws , wpos ); gtk_imhtml_toggle_link ( imhtml , NULL ); 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 )) gtk_text_buffer_insert ( imhtml -> text_buffer , iter , ws , wpos ); font = g_new0 ( GtkIMHtmlFontDetail , 1 ); if ( color && ! ( options & GTK_IMHTML_NO_COLOURS ) && ( imhtml -> format_functions & GTK_IMHTML_FORECOLOR )) { gtk_imhtml_toggle_forecolor ( imhtml , font -> fore ); //else if (oldfont && oldfont->fore) // font->fore = g_strdup(oldfont->fore); if ( back && ! ( options & GTK_IMHTML_NO_COLOURS ) && ( imhtml -> format_functions & GTK_IMHTML_BACKCOLOR )) { gtk_imhtml_toggle_backcolor ( imhtml , font -> back ); //else if (oldfont && oldfont->back) // font->back = g_strdup(oldfont->back); if ( face && ! ( options & GTK_IMHTML_NO_FONTS ) && ( imhtml -> format_functions & GTK_IMHTML_FACE )) { gtk_imhtml_toggle_fontface ( imhtml , font -> face ); //else if (oldfont && oldfont->face) // font->face = g_strdup(oldfont->face); else 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 ))) { sscanf ( size + 1 , "%hd" , & font -> size ); } 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 ); font -> size = oldfont -> size ; if (( imhtml -> format_functions & ( GTK_IMHTML_GROW | GTK_IMHTML_SHRINK ))) gtk_imhtml_font_set_size ( imhtml , font -> size ); fonts = g_slist_prepend ( fonts , font ); 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 ); /* NEW_BIT(NEW_TEXT_BIT); */ gtk_imhtml_toggle_backcolor ( imhtml , bg ); 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 ); gtk_imhtml_toggle_link ( imhtml , href ); gtk_text_buffer_insert ( imhtml -> text_buffer , iter , ws , wpos ); if ( ! ( imhtml -> format_functions & GTK_IMHTML_IMAGE )) id = gtk_imhtml_get_html_opt ( tag , "ID=" ); gtk_imhtml_insert_image_at_iter ( imhtml , atoi ( id ), iter ); case 49 : /* HTML (opt) */ case 56 : /* SPAN (opt) */ /* Inline CSS Support - Douglas Thrift * text-decoration: underline gchar * style , * color , * background , * family , * size ; GtkIMHtmlFontDetail * font , * oldfont = NULL ; style = gtk_imhtml_get_html_opt ( tag , "style=" ); color = gtk_imhtml_get_css_opt ( style , "color: " ); background = gtk_imhtml_get_css_opt ( style , "background: " ); family = gtk_imhtml_get_css_opt ( style , size = gtk_imhtml_get_css_opt ( style , "font-size: " ); textdec = gtk_imhtml_get_css_opt ( style , "text-decoration: " ); if ( ! ( color || family || size || background || textdec )) { gtk_text_buffer_insert ( imhtml -> text_buffer , iter , ws , wpos ); /* NEW_BIT (NEW_TEXT_BIT); */ font = g_new0 ( GtkIMHtmlFontDetail , 1 ); if ( color && ! ( options & GTK_IMHTML_NO_COLOURS ) && ( imhtml -> format_functions & GTK_IMHTML_FORECOLOR )) gtk_imhtml_toggle_forecolor ( imhtml , font -> fore ); else if ( oldfont && oldfont -> fore ) font -> fore = g_strdup ( oldfont -> fore ); if ( background && ! ( options & GTK_IMHTML_NO_COLOURS ) && ( imhtml -> format_functions & GTK_IMHTML_BACKCOLOR )) gtk_imhtml_toggle_backcolor ( imhtml , font -> back ); else if ( oldfont && oldfont -> back ) font -> back = g_strdup ( oldfont -> back ); if ( family && ! ( options & GTK_IMHTML_NO_FONTS ) && ( imhtml -> format_functions & GTK_IMHTML_FACE )) gtk_imhtml_toggle_fontface ( imhtml , font -> face ); else if ( oldfont && oldfont -> face ) font -> face = g_strdup ( oldfont -> face ); if ( font -> face && ( atoi ( font -> face ) > 100 )) { /* Maybe it sets a max size on the font face? I seem to * remember bad things happening if the font size was 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 ) else if ( g_ascii_strcasecmp ( size , "smaller" ) == 0 || g_ascii_strcasecmp ( size , "x-small" ) == 0 ) else if ( g_ascii_strcasecmp ( size , "larger" ) == 0 || g_ascii_strcasecmp ( size , "medium" ) == 0 ) else if ( g_ascii_strcasecmp ( size , "large" ) == 0 ) else if ( g_ascii_strcasecmp ( size , "x-large" ) == 0 ) else if ( g_ascii_strcasecmp ( size , "xx-large" ) == 0 ) gtk_imhtml_font_set_size ( imhtml , font -> size ); font -> size = oldfont -> size ; font -> underline = oldfont -> underline ; if ( textdec && font -> underline != 1 && g_ascii_strcasecmp ( textdec , "underline" ) == 0 && ( imhtml -> format_functions & GTK_IMHTML_UNDERLINE )) gtk_imhtml_toggle_underline ( imhtml ); fonts = g_slist_prepend ( fonts , font ); /* 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 ); /* NEW_BIT (NEW_TEXT_BIT); */ fonts = g_slist_remove ( fonts , font ); gtk_imhtml_font_set_size ( imhtml , 3 ); gtk_imhtml_toggle_underline ( imhtml ); gtk_imhtml_toggle_fontface ( imhtml , NULL ); gtk_imhtml_toggle_forecolor ( imhtml , NULL ); gtk_imhtml_toggle_backcolor ( imhtml , NULL ); if ( font -> size != oldfont -> size ) gtk_imhtml_font_set_size ( imhtml , oldfont -> size ); if ( font -> underline != oldfont -> underline ) gtk_imhtml_toggle_underline ( imhtml ); if ( font -> face && ( ! oldfont -> face || strcmp ( font -> face , oldfont -> face ) != 0 )) gtk_imhtml_toggle_fontface ( imhtml , oldfont -> face ); if ( font -> fore && ( ! oldfont -> fore || strcmp ( font -> fore , oldfont -> fore ) != 0 )) gtk_imhtml_toggle_forecolor ( imhtml , oldfont -> fore ); if ( font -> back && ( ! oldfont -> back || strcmp ( font -> back , oldfont -> back ) != 0 )) gtk_imhtml_toggle_backcolor ( imhtml , oldfont -> back ); /* NEW_BIT (NEW_TEXT_BIT); */ gtk_text_buffer_insert ( imhtml -> text_buffer , iter , ws , wpos ); if ( imhtml -> show_comments && ! ( options & GTK_IMHTML_NO_COMMENTS )) { wpos = g_snprintf ( ws , len , "%s" , tag ); gtk_text_buffer_insert ( imhtml -> text_buffer , iter , ws , wpos ); /* NEW_BIT (NEW_COMMENT_BIT); */ g_free ( tag ); /* This was allocated back in VALID_TAG() */ } else if ( gtk_imhtml_is_smiley ( imhtml , fonts , c , & smilelen )) { 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 ); } else if ( * c == '&' && gtk_imhtml_is_amp_escape ( c , & amp , & tlen )) { if ( ! ( options & GTK_IMHTML_NO_NEWLINE )) { gtk_text_buffer_insert ( imhtml -> text_buffer , iter , ws , wpos ); /* 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? gtk_text_buffer_insert ( imhtml -> text_buffer , iter , ws , wpos ); } else if (( len_protocol = gtk_imhtml_is_protocol ( c )) > 0 ){ /* Skip the next len_protocol characters, but make sure they're copied into the ws array. gtk_text_buffer_insert ( imhtml -> text_buffer , iter , ws , wpos ); /* NEW_BIT(NEW_TEXT_BIT); */ GtkIMHtmlFontDetail * font = fonts -> data ; fonts = g_slist_remove ( fonts , font ); gtk_imhtml_close_tags ( imhtml , iter ); object = g_object_ref ( G_OBJECT ( imhtml )); g_signal_emit ( object , signals [ UPDATE_FORMAT ], 0 ); 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 , imhtml -> show_comments = show ; void gtk_imhtml_html_shortcuts ( GtkIMHtml * imhtml , imhtml -> html_shortcuts = allow ; void gtk_imhtml_smiley_shortcuts ( GtkIMHtml * imhtml , imhtml -> smiley_shortcuts = allow ; gtk_imhtml_set_protocol_name ( GtkIMHtml * imhtml , const gchar * protocol_name ) { if ( imhtml -> protocol_name ) g_free ( imhtml -> protocol_name ); imhtml -> protocol_name = protocol_name ? g_strdup ( protocol_name ) : NULL ; gtk_imhtml_clear ( GtkIMHtml * imhtml ) GObject * object = g_object_ref ( G_OBJECT ( imhtml )); gtk_text_buffer_get_start_iter ( imhtml -> text_buffer , & start ); gtk_text_buffer_get_end_iter ( imhtml -> text_buffer , & end ); gtk_text_buffer_delete ( imhtml -> text_buffer , & start , & end ); for ( del = imhtml -> scalables ; del ; del = del -> next ) { GtkIMHtmlScalable * scale = del -> data ; g_list_free ( imhtml -> scalables ); imhtml -> scalables = NULL ; gtk_imhtml_close_tags ( imhtml , & start ); g_signal_emit ( object , signals [ CLEAR_FORMAT ], 0 ); void gtk_imhtml_page_up ( GtkIMHtml * imhtml ) 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 , gtk_text_view_scroll_to_iter ( GTK_TEXT_VIEW ( imhtml ), & iter , 0 , TRUE , 0 , 0 ); void gtk_imhtml_page_down ( GtkIMHtml * imhtml ) 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 , gtk_text_view_scroll_to_iter ( GTK_TEXT_VIEW ( imhtml ), & iter , 0 , TRUE , 0 , 0 ); /* GtkIMHtmlScalable, gtk_imhtml_image, gtk_imhtml_hr */ GtkIMHtmlScalable * gtk_imhtml_image_new ( GdkPixbuf * img , const gchar * filename , int id ) GtkIMHtmlImage * im_image = g_malloc ( sizeof ( GtkIMHtmlImage )); GtkImage * image = GTK_IMAGE ( gtk_image_new_from_pixbuf ( img )); 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 -> width = gdk_pixbuf_get_width ( img ); im_image -> height = gdk_pixbuf_get_height ( img ); im_image -> filename = filename ? g_strdup ( filename ) : NULL ; im_image -> filesel = NULL ; return GTK_IMHTML_SCALABLE ( im_image ); void gtk_imhtml_image_scale ( GtkIMHtmlScalable * scale , int width , int height ) GtkIMHtmlImage * image = ( GtkIMHtmlImage * ) scale ; if ( image -> width > width || image -> height > height ){ GdkPixbuf * new_image = NULL ; int new_width = image -> width , new_height = image -> height ; if ( image -> width > ( width - 2 )){ factor = ( float )( width ) / image -> width ; new_height = image -> height * factor ; if ( new_height >= ( height - 2 )){ factor = ( float )( height ) / new_height ; new_width = new_width * factor ; new_image = gdk_pixbuf_scale_simple ( image -> pixbuf , new_width , new_height , GDK_INTERP_BILINEAR ); gtk_image_set_from_pixbuf ( image -> image , new_image ); g_object_unref ( G_OBJECT ( new_image )); image_save_yes_cb ( GtkIMHtmlImage * image , const char * filename ) #if GTK_CHECK_VERSION(2,2,0) GSList * formats = gdk_pixbuf_get_formats (); char * basename = g_path_get_basename ( filename ); char * ext = strrchr ( basename , '.' ); gtk_widget_destroy ( image -> filesel ); #if GTK_CHECK_VERSION(2,2,0) GdkPixbufFormat * format = formats -> data ; gchar ** extensions = gdk_pixbuf_format_get_extensions ( format ); 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 ( ! strcmp ( fmt_ext , file_ext )){ type = gdk_pixbuf_format_get_name ( format ); /* this is really ugly code, but I think it will work */ if ( ! g_ascii_strcasecmp ( ext , "jpeg" ) || ! g_ascii_strcasecmp ( ext , "jpg" )) else if ( ! g_ascii_strcasecmp ( ext , "png" )) /* If I can't find a valid type, I will just tell the user about it and then assume #if GTK_CHECK_VERSION(2,4,0) 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\n Defaulting to PNG." )); GtkWidget * dialog = gtk_message_dialog_new ( NULL , 0 , GTK_MESSAGE_ERROR , GTK_BUTTONS_OK , _ ( "Unrecognized file type \n\n Defaulting to PNG." )); g_signal_connect_swapped ( dialog , "response" , G_CALLBACK ( gtk_widget_destroy ), dialog ); gdk_pixbuf_save ( image -> pixbuf , filename , type , & error , NULL ); #if GTK_CHECK_VERSION(2,4,0) 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 ); GtkWidget * dialog = gtk_message_dialog_new ( NULL , 0 , GTK_MESSAGE_ERROR , GTK_BUTTONS_OK , _ ( "Error saving image \n\n %s" ), error -> message ); g_signal_connect_swapped ( dialog , "response" , G_CALLBACK ( gtk_widget_destroy ), dialog ); #if GTK_CHECK_VERSION(2,4,0) /* FILECHOOSER */ image_save_check_if_exists_cb ( GtkWidget * widget , gint response , GtkIMHtmlImage * image ) if ( response != GTK_RESPONSE_ACCEPT ) { gtk_widget_destroy ( widget ); filename = gtk_file_chooser_get_filename ( GTK_FILE_CHOOSER ( widget )); image_save_check_if_exists_cb ( GtkWidget * button , GtkIMHtmlImage * image ) filename = g_strdup ( gtk_file_selection_get_filename ( GTK_FILE_SELECTION ( image -> filesel ))); if ( g_file_test ( filename , G_FILE_TEST_IS_DIR )) { /* append a / is needed */ if ( filename [ strlen ( filename ) - 1 ] != G_DIR_SEPARATOR ) { dirname = g_strconcat ( filename , G_DIR_SEPARATOR_S , NULL ); dirname = g_strdup ( filename ); gtk_file_selection_set_filename ( GTK_FILE_SELECTION ( image -> filesel ), dirname ); * 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)) { image_save_yes_cb(image, filename); image_save_yes_cb ( image , filename ); #if !GTK_CHECK_VERSION(2,4,0) /* FILECHOOSER */ image_save_cancel_cb ( GtkIMHtmlImage * image ) gtk_widget_destroy ( image -> filesel ); gtk_imhtml_image_save ( GtkWidget * w , GtkIMHtmlImage * image ) if ( image -> filesel != NULL ) { gtk_window_present ( GTK_WINDOW ( image -> filesel )); #if GTK_CHECK_VERSION(2,4,0) /* FILECHOOSER */ image -> filesel = gtk_file_chooser_dialog_new ( _ ( "Save Image" ), GTK_FILE_CHOOSER_ACTION_SAVE , GTK_STOCK_CANCEL , GTK_RESPONSE_CANCEL , GTK_STOCK_SAVE , GTK_RESPONSE_ACCEPT , 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 ), image ); image -> filesel = gtk_file_selection_new ( _ ( "Save Image" )); if ( image -> filename != NULL ) gtk_file_selection_set_filename ( GTK_FILE_SELECTION ( image -> filesel ), image -> filename ); g_signal_connect_swapped ( G_OBJECT ( GTK_FILE_SELECTION ( image -> filesel )), "delete_event" , G_CALLBACK ( image_save_cancel_cb ), image ); g_signal_connect_swapped ( G_OBJECT ( GTK_FILE_SELECTION ( image -> filesel ) -> cancel_button ), "clicked" , G_CALLBACK ( image_save_cancel_cb ), image ); g_signal_connect ( G_OBJECT ( GTK_FILE_SELECTION ( image -> filesel ) -> ok_button ), "clicked" , G_CALLBACK ( image_save_check_if_exists_cb ), image ); gtk_widget_show ( image -> filesel ); * 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 Gaim core handle static gboolean gtk_imhtml_image_clicked ( GtkWidget * w , GdkEvent * event , GtkIMHtmlImage * image ) GdkEventButton * event_button = ( GdkEventButton * ) event ; if ( event -> type == GDK_BUTTON_RELEASE ) { if ( event_button -> button == 3 ) { GtkWidget * img , * item , * menu ; gchar * text = g_strdup_printf ( _ ( "_Save Image..." )); img = gtk_image_new_from_stock ( GTK_STOCK_SAVE , GTK_ICON_SIZE_MENU ); item = gtk_image_menu_item_new_with_mnemonic ( text ); 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 ), image ); 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 ); 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 */ return FALSE ; /* Let clicks go through if we didn't catch anything */ void gtk_imhtml_image_free ( GtkIMHtmlScalable * scale ) GtkIMHtmlImage * image = ( GtkIMHtmlImage * ) scale ; g_object_unref ( image -> pixbuf ); gtk_widget_destroy ( image -> filesel ); void gtk_imhtml_image_add_to ( GtkIMHtmlScalable * scale , GtkIMHtml * imhtml , GtkTextIter * iter ) GtkIMHtmlImage * image = ( GtkIMHtmlImage * ) scale ; GtkWidget * box = gtk_event_box_new (); GtkTextChildAnchor * anchor = gtk_text_buffer_create_child_anchor ( imhtml -> text_buffer , iter ); gtk_container_add ( GTK_CONTAINER ( box ), GTK_WIDGET ( image -> image )); if ( ! gtk_check_version ( 2 , 4 , 0 )) g_object_set ( G_OBJECT ( box ), "visible-window" , FALSE , NULL ); gtk_widget_show ( GTK_WIDGET ( image -> image )); 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 ); g_signal_connect ( G_OBJECT ( box ), "event" , G_CALLBACK ( gtk_imhtml_image_clicked ), 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 ) gboolean gtk_imhtml_search_find ( GtkIMHtml * imhtml , const gchar * text ) GtkTextIter iter , start , end ; gboolean new_search = TRUE ; g_return_val_if_fail ( imhtml != NULL , FALSE ); g_return_val_if_fail ( text != NULL , FALSE ); if ( imhtml -> search_string && ! strcmp ( text , imhtml -> search_string )) gtk_imhtml_search_clear ( imhtml ); gtk_text_buffer_get_start_iter ( imhtml -> text_buffer , & iter ); gtk_text_buffer_get_iter_at_mark ( imhtml -> text_buffer , & iter , gtk_text_buffer_get_mark ( imhtml -> text_buffer , "search" )); g_free ( imhtml -> search_string ); imhtml -> search_string = g_strdup ( text ); if ( gtk_source_iter_forward_search ( & iter , imhtml -> search_string , GTK_SOURCE_SEARCH_VISIBLE_ONLY | GTK_SOURCE_SEARCH_CASE_INSENSITIVE , gtk_text_view_scroll_to_iter ( GTK_TEXT_VIEW ( imhtml ), & start , 0 , TRUE , 0 , 0 ); gtk_text_buffer_create_mark ( imhtml -> text_buffer , "search" , & end , FALSE ); gtk_text_buffer_remove_tag_by_name ( imhtml -> text_buffer , "search" , & iter , & end ); gtk_text_buffer_apply_tag_by_name ( imhtml -> text_buffer , "search" , & start , & end ); while ( gtk_source_iter_forward_search ( & end , imhtml -> search_string , GTK_SOURCE_SEARCH_VISIBLE_ONLY | GTK_SOURCE_SEARCH_CASE_INSENSITIVE , gtk_imhtml_search_clear ( imhtml ); void gtk_imhtml_search_clear ( GtkIMHtml * imhtml ) 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 ); if ( imhtml -> search_string ) g_free ( imhtml -> search_string ); imhtml -> search_string = NULL ; static GtkTextTag * find_font_forecolor_tag ( GtkIMHtml * imhtml , gchar * color ) g_snprintf ( str , sizeof ( str ), "FORECOLOR %s" , color ); tag = gtk_text_tag_table_lookup ( gtk_text_buffer_get_tag_table ( imhtml -> text_buffer ), str ); tag = gtk_text_buffer_create_tag ( imhtml -> text_buffer , str , "foreground" , color , NULL ); static GtkTextTag * find_font_backcolor_tag ( GtkIMHtml * imhtml , gchar * color ) g_snprintf ( str , sizeof ( str ), "BACKCOLOR %s" , color ); tag = gtk_text_tag_table_lookup ( gtk_text_buffer_get_tag_table ( imhtml -> text_buffer ), str ); tag = gtk_text_buffer_create_tag ( imhtml -> text_buffer , str , "background" , color , NULL ); static GtkTextTag * find_font_face_tag ( GtkIMHtml * imhtml , gchar * face ) g_snprintf ( str , sizeof ( str ), "FONT FACE %s" , face ); tag = gtk_text_tag_table_lookup ( gtk_text_buffer_get_tag_table ( imhtml -> text_buffer ), str ); tag = gtk_text_buffer_create_tag ( imhtml -> text_buffer , str , "family" , face , NULL ); static void _init_original_fsize ( GtkIMHtml * imhtml ) attr = gtk_text_view_get_default_attributes ( GTK_TEXT_VIEW ( imhtml )); imhtml -> original_fsize = pango_font_description_get_size ( attr -> font ); gtk_text_attributes_unref ( attr ); static void _recalculate_font_sizes ( GtkTextTag * tag , gpointer imhtml ) if ( strncmp ( tag -> name , "FONT SIZE " , 10 ) == 0 ) { size = strtol ( tag -> name + 10 , NULL , 10 ); g_object_set ( G_OBJECT ( tag ), "size" , ( gint ) ( GTK_IMHTML ( imhtml ) -> original_fsize * (( double ) POINT_SIZE ( size ) * GTK_IMHTML ( imhtml ) -> zoom )), NULL ); void gtk_imhtml_font_zoom ( GtkIMHtml * imhtml , double zoom ) PangoFontDescription * font_desc = pango_font_description_new (); if ( ! imhtml -> original_fsize ) _init_original_fsize ( imhtml ); gtk_text_tag_table_foreach ( gtk_text_buffer_get_tag_table ( imhtml -> text_buffer ), _recalculate_font_sizes , imhtml ); pango_font_description_set_size ( font_desc , ( gint )(( double ) imhtml -> original_fsize * zoom )); s = gtk_widget_get_modifier_style ( GTK_WIDGET ( imhtml )); s -> font_desc = font_desc ; gtk_widget_modify_style ( GTK_WIDGET ( imhtml ), s ); static GtkTextTag * find_font_size_tag ( GtkIMHtml * imhtml , int size ) if ( ! imhtml -> original_fsize ) _init_original_fsize ( imhtml ); g_snprintf ( str , sizeof ( str ), "FONT SIZE %d" , size ); tag = gtk_text_tag_table_lookup ( gtk_text_buffer_get_tag_table ( imhtml -> text_buffer ), str ); /* 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. tag = gtk_text_buffer_create_tag ( imhtml -> text_buffer , str , "size" , ( gint ) ( imhtml -> original_fsize * (( double ) POINT_SIZE ( size ) * imhtml -> zoom )), NULL ); static void remove_tag_by_prefix ( GtkIMHtml * imhtml , const GtkTextIter * i , const GtkTextIter * e , const char * prefix , guint len , gboolean homo ) tags = gtk_text_iter_get_tags ( i ); for ( l = tags ; l ; l = l -> next ) { GtkTextTag * tag = l -> data ; if ( tag -> name && ! strncmp ( tag -> name , prefix , len )) gtk_text_buffer_remove_tag ( imhtml -> text_buffer , tag , i , e ); 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 ; if ( tag -> name && ! strncmp ( tag -> name , prefix , len )) gtk_text_buffer_remove_tag ( imhtml -> text_buffer , tag , & iter , e ); 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_link ( GtkIMHtml * imhtml , GtkTextIter * i , GtkTextIter * e , gboolean homo ) remove_tag_by_prefix ( imhtml , i , e , "LINK " , 5 , homo ); 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 ) 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 ) gtk_text_iter_set_offset ( & start , imhtml -> insert_offset ); gtk_imhtml_apply_tags_on_insert ( imhtml , & start , end ); static void gtk_imhtml_apply_tags_on_insert ( GtkIMHtml * imhtml , GtkTextIter * start , GtkTextIter * end ) gtk_text_buffer_apply_tag_by_name ( imhtml -> text_buffer , "BOLD" , start , end ); gtk_text_buffer_remove_tag_by_name ( imhtml -> text_buffer , "BOLD" , start , end ); gtk_text_buffer_apply_tag_by_name ( imhtml -> text_buffer , "ITALICS" , start , end ); 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 ); gtk_text_buffer_remove_tag_by_name ( imhtml -> text_buffer , "UNDERLINE" , start , end ); gtk_text_buffer_apply_tag_by_name ( imhtml -> text_buffer , "STRIKE" , start , end ); 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 ), 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 ), 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 ), 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 ), remove_font_link ( imhtml , start , end , TRUE ); gtk_text_buffer_apply_tag ( imhtml -> text_buffer , 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); */ imhtml -> editable = editable ; imhtml -> format_functions = GTK_IMHTML_ALL ; g_signal_connect_after ( G_OBJECT ( GTK_IMHTML ( imhtml ) -> text_buffer ), "mark-set" , G_CALLBACK ( mark_set_cb ), imhtml ); void gtk_imhtml_set_whole_buffer_formatting_only ( GtkIMHtml * imhtml , gboolean wbfo ) g_return_if_fail ( imhtml != NULL ); 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 ); 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 ( imhtml -> edit . underline ) gtk_imhtml_get_current_fontface ( GtkIMHtml * imhtml ) if ( imhtml -> edit . fontface ) return g_strdup ( imhtml -> edit . fontface ); gtk_imhtml_get_current_forecolor ( GtkIMHtml * imhtml ) if ( imhtml -> edit . forecolor ) return g_strdup ( imhtml -> edit . forecolor ); gtk_imhtml_get_current_backcolor ( GtkIMHtml * imhtml ) if ( imhtml -> edit . backcolor ) return g_strdup ( imhtml -> edit . backcolor ); gtk_imhtml_get_current_fontsize ( GtkIMHtml * imhtml ) return imhtml -> edit . fontsize ; gboolean gtk_imhtml_get_editable ( GtkIMHtml * imhtml ) * 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 Gaim overriding their gtk theme's cursor * 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 , if ( mark != gtk_text_buffer_get_insert ( buffer )) if ( ! gtk_text_buffer_get_char_count ( buffer )) imhtml -> edit . bold = imhtml -> edit . italic = imhtml -> edit . underline = imhtml -> edit . strike = FALSE ; if ( imhtml -> edit . forecolor ) g_free ( imhtml -> edit . forecolor ); imhtml -> edit . forecolor = NULL ; if ( imhtml -> edit . backcolor ) g_free ( imhtml -> edit . backcolor ); imhtml -> edit . backcolor = NULL ; if ( imhtml -> edit . fontface ) 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 ); tags = gtk_text_iter_get_tags ( & iter ); for ( l = tags ; l != NULL ; l = l -> next ) { GtkTextTag * tag = GTK_TEXT_TAG ( l -> data ); if ( strcmp ( tag -> name , "BOLD" ) == 0 ) imhtml -> edit . bold = TRUE ; if ( strcmp ( tag -> name , "ITALICS" ) == 0 ) imhtml -> edit . italic = TRUE ; if ( strcmp ( tag -> name , "UNDERLINE" ) == 0 ) imhtml -> edit . underline = TRUE ; if ( strcmp ( tag -> name , "STRIKE" ) == 0 ) imhtml -> edit . strike = TRUE ; if ( strncmp ( tag -> name , "FORECOLOR " , 10 ) == 0 ) imhtml -> edit . forecolor = g_strdup ( & ( tag -> name )[ 10 ]); if ( strncmp ( tag -> name , "BACKCOLOR " , 10 ) == 0 ) imhtml -> edit . backcolor = g_strdup ( & ( tag -> name )[ 10 ]); if ( strncmp ( tag -> name , "FONT FACE " , 10 ) == 0 ) imhtml -> edit . fontface = g_strdup ( & ( tag -> name )[ 10 ]); if ( strncmp ( tag -> name , "FONT SIZE " , 10 ) == 0 ) imhtml -> edit . fontsize = strtol ( & ( tag -> name )[ 10 ], NULL , 10 ); if (( strncmp ( tag -> name , "LINK " , 5 ) == 0 ) && ! gtk_text_iter_is_end ( & iter )) gboolean gtk_imhtml_toggle_bold ( GtkIMHtml * imhtml ) imhtml -> edit . bold = ! imhtml -> edit . bold ; gtk_text_buffer_get_bounds ( imhtml -> text_buffer , & start , & end ); gtk_text_buffer_apply_tag_by_name ( imhtml -> text_buffer , "BOLD" , & start , & end ); gtk_text_buffer_remove_tag_by_name ( imhtml -> text_buffer , "BOLD" , & start , & end ); } else if ( imhtml -> editable && gtk_text_buffer_get_selection_bounds ( imhtml -> text_buffer , & start , & end )) { gtk_text_buffer_apply_tag_by_name ( imhtml -> text_buffer , "BOLD" , & start , & end ); gtk_text_buffer_remove_tag_by_name ( imhtml -> text_buffer , "BOLD" , & start , & end ); object = g_object_ref ( G_OBJECT ( imhtml )); g_signal_emit ( object , signals [ TOGGLE_FORMAT ], 0 , GTK_IMHTML_BOLD ); return ( imhtml -> edit . bold != FALSE ); gboolean gtk_imhtml_toggle_italic ( GtkIMHtml * imhtml ) imhtml -> edit . italic = ! imhtml -> edit . italic ; gtk_text_buffer_get_bounds ( imhtml -> text_buffer , & start , & end ); gtk_text_buffer_apply_tag_by_name ( imhtml -> text_buffer , "ITALICS" , & start , & end ); gtk_text_buffer_remove_tag_by_name ( imhtml -> text_buffer , "ITALICS" , & start , & end ); } else if ( imhtml -> editable && gtk_text_buffer_get_selection_bounds ( imhtml -> text_buffer , & start , & end )) { gtk_text_buffer_apply_tag_by_name ( imhtml -> text_buffer , "ITALICS" , & start , & end ); gtk_text_buffer_remove_tag_by_name ( imhtml -> text_buffer , "ITALICS" , & start , & end ); object = g_object_ref ( G_OBJECT ( imhtml )); g_signal_emit ( object , signals [ TOGGLE_FORMAT ], 0 , GTK_IMHTML_ITALIC ); return imhtml -> edit . italic != FALSE ; gboolean gtk_imhtml_toggle_underline ( GtkIMHtml * imhtml ) imhtml -> edit . underline = ! imhtml -> edit . underline ; gtk_text_buffer_get_bounds ( imhtml -> text_buffer , & start , & end ); if ( imhtml -> edit . underline ) gtk_text_buffer_apply_tag_by_name ( imhtml -> text_buffer , "UNDERLINE" , & start , & end ); gtk_text_buffer_remove_tag_by_name ( imhtml -> text_buffer , "UNDERLINE" , & start , & end ); } else if ( imhtml -> editable && gtk_text_buffer_get_selection_bounds ( imhtml -> text_buffer , & start , & end )) { if ( imhtml -> edit . underline ) gtk_text_buffer_apply_tag_by_name ( imhtml -> text_buffer , "UNDERLINE" , & start , & end ); gtk_text_buffer_remove_tag_by_name ( imhtml -> text_buffer , "UNDERLINE" , & start , & end ); object = g_object_ref ( G_OBJECT ( imhtml )); g_signal_emit ( object , signals [ TOGGLE_FORMAT ], 0 , GTK_IMHTML_UNDERLINE ); return imhtml -> edit . underline != FALSE ; gboolean gtk_imhtml_toggle_strike ( GtkIMHtml * imhtml ) imhtml -> edit . strike = ! imhtml -> edit . strike ; gtk_text_buffer_get_bounds ( imhtml -> text_buffer , & start , & end ); gtk_text_buffer_apply_tag_by_name ( imhtml -> text_buffer , "STRIKE" , & start , & end ); gtk_text_buffer_remove_tag_by_name ( imhtml -> text_buffer , "STRIKE" , & start , & end ); } else if ( imhtml -> editable && gtk_text_buffer_get_selection_bounds ( imhtml -> text_buffer , & start , & end )) { gtk_text_buffer_apply_tag_by_name ( imhtml -> text_buffer , "STRIKE" , & start , & end ); gtk_text_buffer_remove_tag_by_name ( imhtml -> text_buffer , "STRIKE" , & start , & end ); object = g_object_ref ( G_OBJECT ( imhtml )); g_signal_emit ( object , signals [ TOGGLE_FORMAT ], 0 , GTK_IMHTML_STRIKE ); return imhtml -> edit . strike != FALSE ; void gtk_imhtml_font_set_size ( GtkIMHtml * imhtml , gint size ) imhtml -> edit . fontsize = size ; gtk_text_buffer_get_bounds ( imhtml -> text_buffer , & start , & end ); 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 ); } else if ( imhtml -> editable && gtk_text_buffer_get_selection_bounds ( imhtml -> text_buffer , & start , & end )) { remove_font_size ( imhtml , & start , & end , FALSE ); 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 , b ); void gtk_imhtml_font_shrink ( GtkIMHtml * imhtml ) if ( imhtml -> edit . fontsize == 1 ) if ( ! imhtml -> edit . fontsize ) imhtml -> edit . fontsize = 2 ; gtk_text_buffer_get_bounds ( imhtml -> text_buffer , & start , & end ); 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 ); } else if ( imhtml -> editable && gtk_text_buffer_get_selection_bounds ( imhtml -> text_buffer , & start , & end )) { remove_font_size ( imhtml , & start , & end , FALSE ); 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 ); void gtk_imhtml_font_grow ( GtkIMHtml * imhtml ) if ( imhtml -> edit . fontsize == MAX_FONT_SIZE ) if ( ! imhtml -> edit . fontsize ) imhtml -> edit . fontsize = 4 ; gtk_text_buffer_get_bounds ( imhtml -> text_buffer , & start , & end ); 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 ); } else if ( imhtml -> editable && gtk_text_buffer_get_selection_bounds ( imhtml -> text_buffer , & start , & end )) { remove_font_size ( imhtml , & start , & end , FALSE ); 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_GROW ); gboolean gtk_imhtml_toggle_forecolor ( GtkIMHtml * imhtml , const char * color ) if ( imhtml -> edit . forecolor != NULL ) g_free ( imhtml -> edit . forecolor ); if ( color && strcmp ( color , "" ) != 0 ) { imhtml -> edit . forecolor = g_strdup ( color ); gtk_text_buffer_get_bounds ( imhtml -> text_buffer , & start , & end ); 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 ); } else if ( imhtml -> editable && gtk_text_buffer_get_selection_bounds ( imhtml -> text_buffer , & start , & end )) { remove_font_forecolor ( imhtml , & start , & end , FALSE ); gtk_text_buffer_apply_tag ( imhtml -> text_buffer , find_font_forecolor_tag ( imhtml , imhtml -> edit . forecolor ), imhtml -> edit . forecolor = NULL ; gtk_text_buffer_get_bounds ( imhtml -> text_buffer , & start , & end ); remove_font_forecolor ( imhtml , & start , & end , TRUE ); object = g_object_ref ( G_OBJECT ( imhtml )); g_signal_emit ( object , signals [ TOGGLE_FORMAT ], 0 , GTK_IMHTML_FORECOLOR ); return imhtml -> edit . forecolor != NULL ; gboolean gtk_imhtml_toggle_backcolor ( GtkIMHtml * imhtml , const char * color ) if ( imhtml -> edit . backcolor != NULL ) g_free ( imhtml -> edit . backcolor ); if ( color && strcmp ( color , "" ) != 0 ) { imhtml -> edit . backcolor = g_strdup ( color ); gtk_text_buffer_get_bounds ( imhtml -> text_buffer , & start , & end ); 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 ); } else if ( imhtml -> editable && gtk_text_buffer_get_selection_bounds ( imhtml -> text_buffer , & start , & end )) { remove_font_backcolor ( imhtml , & start , & end , FALSE ); gtk_text_buffer_apply_tag ( imhtml -> text_buffer , find_font_backcolor_tag ( imhtml , imhtml -> edit . backcolor ), & start , & end ); imhtml -> edit . backcolor = NULL ; gtk_text_buffer_get_bounds ( imhtml -> text_buffer , & start , & end ); remove_font_backcolor ( imhtml , & start , & end , TRUE ); object = g_object_ref ( G_OBJECT ( imhtml )); g_signal_emit ( object , signals [ TOGGLE_FORMAT ], 0 , GTK_IMHTML_BACKCOLOR ); return imhtml -> edit . backcolor != NULL ; gboolean gtk_imhtml_toggle_fontface ( GtkIMHtml * imhtml , const char * face ) if ( imhtml -> edit . fontface != NULL ) g_free ( imhtml -> edit . fontface ); if ( face && strcmp ( face , "" ) != 0 ) { imhtml -> edit . fontface = g_strdup ( face ); gtk_text_buffer_get_bounds ( imhtml -> text_buffer , & start , & end ); 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 ); } else if ( imhtml -> editable && gtk_text_buffer_get_selection_bounds ( imhtml -> text_buffer , & start , & end )) { remove_font_face ( imhtml , & start , & end , FALSE ); gtk_text_buffer_apply_tag ( imhtml -> text_buffer , find_font_face_tag ( imhtml , imhtml -> edit . fontface ), imhtml -> edit . fontface = NULL ; gtk_text_buffer_get_bounds ( imhtml -> text_buffer , & start , & end ); remove_font_face ( imhtml , & start , & end , TRUE ); object = g_object_ref ( G_OBJECT ( imhtml )); g_signal_emit ( object , signals [ TOGGLE_FORMAT ], 0 , GTK_IMHTML_FACE ); return imhtml -> edit . fontface != NULL ; void gtk_imhtml_toggle_link ( GtkIMHtml * imhtml , const char * url ) imhtml -> edit . link = NULL ; g_snprintf ( str , sizeof ( str ), "LINK %d" , linkno ++ ); gtk_widget_style_get ( GTK_WIDGET ( imhtml ), "hyperlink-color" , & color , NULL ); imhtml -> edit . link = linktag = gtk_text_buffer_create_tag ( imhtml -> text_buffer , str , "foreground-gdk" , color , "underline" , PANGO_UNDERLINE_SINGLE , NULL ); 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 ); void gtk_imhtml_insert_link ( GtkIMHtml * imhtml , GtkTextMark * mark , const char * url , const char * text ) if ( gtk_text_buffer_get_selection_bounds ( imhtml -> text_buffer , NULL , NULL )) 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 ) mark = gtk_text_buffer_get_insert ( imhtml -> text_buffer ); gtk_text_buffer_get_iter_at_mark ( imhtml -> text_buffer , & iter , mark ); gtk_imhtml_insert_smiley_at_iter ( imhtml , sml , smiley , & iter ); void gtk_imhtml_insert_smiley_at_iter ( GtkIMHtml * imhtml , const char * sml , char * smiley , GtkTextIter * iter ) GdkPixbuf * pixbuf = NULL ; GdkPixbufAnimation * annipixbuf = NULL ; GtkTextChildAnchor * anchor ; char * unescaped = gaim_unescape_html ( smiley ); if ( imhtml -> format_functions & GTK_IMHTML_SMILEY ) annipixbuf = gtk_smiley_tree_image ( imhtml , sml , unescaped ); if ( gdk_pixbuf_animation_is_static_image ( annipixbuf )) { pixbuf = gdk_pixbuf_animation_get_static_image ( annipixbuf ); icon = gtk_image_new_from_pixbuf ( pixbuf ); icon = gtk_image_new_from_animation ( annipixbuf ); 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_htmltext" , g_strdup ( smiley ), g_free ); gtk_text_view_add_child_at_anchor ( GTK_TEXT_VIEW ( imhtml ), icon , anchor ); gtk_text_buffer_insert ( imhtml -> text_buffer , iter , smiley , -1 ); void gtk_imhtml_insert_image_at_iter ( GtkIMHtml * imhtml , int id , GtkTextIter * iter ) GdkPixbuf * pixbuf = NULL ; const char * filename = NULL ; GtkIMHtmlScalable * scalable = NULL ; 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 ) image = imhtml -> funcs -> image_get ( id ); data = imhtml -> funcs -> image_get_data ( image ); len = imhtml -> funcs -> image_get_size ( image ); GdkPixbufLoader * loader = gdk_pixbuf_loader_new (); gdk_pixbuf_loader_write ( loader , data , len , NULL ); pixbuf = gdk_pixbuf_loader_get_pixbuf ( loader ); g_object_ref ( G_OBJECT ( pixbuf )); gdk_pixbuf_loader_close ( loader , NULL ); g_object_unref ( G_OBJECT ( loader )); filename = imhtml -> funcs -> image_get_filename ( image ); imhtml -> funcs -> image_ref ( id ); imhtml -> im_images = g_slist_prepend ( imhtml -> im_images , GINT_TO_POINTER ( id )); 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 ); 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 , scalable ); g_object_unref ( G_OBJECT ( pixbuf )); static const gchar * tag_to_html_start ( GtkTextTag * tag ) g_return_val_if_fail ( name != NULL , "" ); if ( strcmp ( name , "BOLD" ) == 0 ) { } else if ( strcmp ( name , "ITALICS" ) == 0 ) { } else if ( strcmp ( name , "UNDERLINE" ) == 0 ) { } else if ( strcmp ( name , "STRIKE" ) == 0 ) { } else if ( strncmp ( name , "LINK " , 5 ) == 0 ) { char * tmp = g_object_get_data ( G_OBJECT ( tag ), "link_url" ); g_snprintf ( buf , sizeof ( buf ), "<a href= \" %s \" >" , tmp ); buf [ sizeof ( buf ) -1 ] = '\0' ; } else if ( strncmp ( name , "FORECOLOR " , 10 ) == 0 ) { g_snprintf ( buf , sizeof ( buf ), "<font color= \" %s \" >" , & name [ 10 ]); } else if ( strncmp ( name , "BACKCOLOR " , 10 ) == 0 ) { g_snprintf ( buf , sizeof ( buf ), "<font back= \" %s \" >" , & name [ 10 ]); } else if ( strncmp ( name , "FONT FACE " , 10 ) == 0 ) { g_snprintf ( buf , sizeof ( buf ), "<font face= \" %s \" >" , & name [ 10 ]); } else if ( strncmp ( name , "FONT SIZE " , 10 ) == 0 ) { g_snprintf ( buf , sizeof ( buf ), "<font size= \" %s \" >" , & name [ 10 ]); static const gchar * tag_to_html_end ( GtkTextTag * tag ) g_return_val_if_fail ( name != NULL , "" ); if ( strcmp ( name , "BOLD" ) == 0 ) { } else if ( strcmp ( name , "ITALICS" ) == 0 ) { } else if ( strcmp ( name , "UNDERLINE" ) == 0 ) { } else if ( strcmp ( name , "STRIKE" ) == 0 ) { } else if ( strncmp ( name , "LINK " , 5 ) == 0 ) { } else if ( strncmp ( name , "FORECOLOR " , 10 ) == 0 ) { } else if ( strncmp ( name , "BACKCOLOR " , 10 ) == 0 ) { } else if ( strncmp ( name , "FONT FACE " , 10 ) == 0 ) { } else if ( strncmp ( name , "FONT SIZE " , 10 ) == 0 ) { 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 ) GtkTextIter iter , nextiter ; GString * str = g_string_new ( "" ); gtk_text_iter_order ( start , end ); nextiter = iter = * start ; gtk_text_iter_forward_char ( & nextiter ); /* 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 ) { if ( ! gtk_text_iter_toggles_tag ( start , GTK_TEXT_TAG ( tag ))) { if ( strlen ( tag_to_html_end ( tag )) > 0 ) g_string_append ( str , tag_to_html_start ( tag )); g_queue_push_tail ( q , tag ); 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 ) { if ( gtk_text_iter_begins_tag ( & iter , GTK_TEXT_TAG ( tag ))) { if ( strlen ( tag_to_html_end ( tag )) > 0 ) g_string_append ( str , tag_to_html_start ( tag )); g_queue_push_tail ( q , tag ); GtkTextChildAnchor * anchor = gtk_text_iter_get_child_anchor ( & iter ); char * text = g_object_get_data ( G_OBJECT ( anchor ), "gtkimhtml_htmltext" ); str = g_string_append ( str , text ); str = g_string_append ( str , "<" ); str = g_string_append ( str , ">" ); str = g_string_append ( str , "&" ); str = g_string_append ( str , """ ); str = g_string_append ( str , "<br>" ); str = g_string_append_unichar ( str , c ); tags = g_slist_reverse ( tags ); for ( sl = tags ; sl ; sl = sl -> next ) { /** don't worry about non-printing tags ending */ if ( tag_ends_here ( tag , & iter , & nextiter ) && strlen ( tag_to_html_end ( tag )) > 0 ) { while (( tmp = g_queue_pop_tail ( q )) != tag ) { if ( ! tag_ends_here ( tmp , & iter , & nextiter ) && strlen ( tag_to_html_end ( tmp )) > 0 ) g_queue_push_tail ( r , tmp ); g_string_append ( str , tag_to_html_end ( GTK_TEXT_TAG ( tmp ))); gaim_debug_warning ( "gtkimhtml" , "empty queue, more closing tags than open tags! \n " ); g_string_append ( str , tag_to_html_end ( GTK_TEXT_TAG ( tag ))); while (( tmp = g_queue_pop_head ( r ))) { g_string_append ( str , tag_to_html_start ( GTK_TEXT_TAG ( tmp ))); g_queue_push_tail ( q , tmp ); gtk_text_iter_forward_char ( & iter ); gtk_text_iter_forward_char ( & nextiter ); while (( tag = g_queue_pop_tail ( q ))) g_string_append ( str , tag_to_html_end ( GTK_TEXT_TAG ( tag ))); return g_string_free ( str , FALSE ); void gtk_imhtml_close_tags ( GtkIMHtml * imhtml , GtkTextIter * iter ) gtk_imhtml_toggle_bold ( imhtml ); gtk_imhtml_toggle_italic ( imhtml ); if ( imhtml -> edit . underline ) gtk_imhtml_toggle_underline ( imhtml ); 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 ; gtk_imhtml_toggle_link ( imhtml , NULL ); gtk_text_buffer_remove_all_tags ( imhtml -> text_buffer , iter , iter ); char * gtk_imhtml_get_markup ( GtkIMHtml * imhtml ) 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 ) 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 ); 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 ); gtk_text_iter_forward_line ( & start ); gtk_text_iter_forward_to_line_end ( & end ); char * gtk_imhtml_get_text ( GtkIMHtml * imhtml , GtkTextIter * start , GtkTextIter * stop ) GString * str = g_string_new ( "" ); gtk_text_buffer_get_start_iter ( imhtml -> text_buffer , & iter ); gtk_text_buffer_get_end_iter ( imhtml -> text_buffer , & end ); gtk_text_iter_order ( & iter , & end ); while (( c = gtk_text_iter_get_char ( & iter )) != 0 && ! gtk_text_iter_equal ( & iter , & end )) { GtkTextChildAnchor * anchor ; anchor = gtk_text_iter_get_child_anchor ( & iter ); text = g_object_get_data ( G_OBJECT ( anchor ), "gtkimhtml_plaintext" ); str = g_string_append ( str , text ); 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 );