diff --git a/configure.ac b/configure.ac index 01162dac..976bba37 100644 --- a/configure.ac +++ b/configure.ac @@ -150,6 +150,10 @@ AC_ARG_ENABLE(libproxy, [AS_HELP_STRING([--disable-libproxy],[disable libproxy support (default: auto)])], libproxy=$enableval, libproxy=auto) +AC_ARG_ENABLE(isocodes, + [AS_HELP_STRING([--disable-isocodes],[disable iso-codes with spell-check])], + isocodes=$enableval, isocodes=yes) + AC_ARG_ENABLE(minimal-flags, [AS_HELP_STRING([--enable-minimal-flags],[only add those CFLAGS that are really needed or not intrusive (default: no)])], minimalflags=$enableval, minimalflags=no) @@ -534,12 +538,17 @@ dnl ********************************************************************* dnl ** SPELL ************************************************************ dnl ********************************************************************* -if test "$gtkfe" = "xyes" ; then - iso_codes_prefix=`$PKG_CONFIG --variable=prefix iso-codes 2>/dev/null || echo /usr` - AC_MSG_NOTICE([iso-codes prefix: $iso_codes_prefix]) - AC_DEFINE_UNQUOTED([ISO_CODES_PREFIX], ["$iso_codes_prefix"], [ISO codes prefix]) - AC_DEFINE_UNQUOTED([ISO_CODES_LOCALEDIR], ["$iso_codes_prefix/share/locale"], [ISO codes locale dir]) - AC_DEFINE([HAVE_ISO_CODES], [1], [iso-codes available]) +if test "x$isocodes" = "xyes" ; then + PKG_CHECK_MODULES(ISOCODES, "iso-codes", [ + iso_codes_prefix=`$PKG_CONFIG --variable=prefix iso-codes 2>/dev/null || echo /usr` + AC_MSG_NOTICE([iso-codes prefix: $iso_codes_prefix]) + AC_DEFINE_UNQUOTED([ISO_CODES_PREFIX], ["$iso_codes_prefix"], [ISO codes prefix]) + AC_DEFINE_UNQUOTED([ISO_CODES_LOCALEDIR], ["$iso_codes_prefix/share/locale"], [ISO codes locale dir]) + AC_DEFINE([HAVE_ISO_CODES], [1], [iso-codes available]) + ], [ + isocodes=no + AC_MSG_WARN(iso-codes not found!) + ]) fi dnl ********************************************************************* @@ -559,6 +568,7 @@ AM_CONDITIONAL(DO_DOAT, test "x$doat" = "xyes") AM_CONDITIONAL(DO_FISHLIM, test "x$fishlim" = "xyes") AM_CONDITIONAL(DO_SYSINFO, test "x$sysinfo" = "xyes") AM_CONDITIONAL(USE_DBUS, test "x$dbus" = "xyes") +AM_CONDITIONAL(HAVE_ISO_CODES, test "x$isocodes" = "xyes") AM_CONDITIONAL(WITH_TM, test "x$theme_manager" != "xno") dnl ********************************************************************* diff --git a/src/common/cfgfiles.c b/src/common/cfgfiles.c index 93f1d147..63745869 100644 --- a/src/common/cfgfiles.c +++ b/src/common/cfgfiles.c @@ -425,6 +425,7 @@ const struct prefs vars[] = {"gui_dialog_width", P_OFFINT (hex_gui_dialog_width), TYPE_INT}, {"gui_focus_omitalerts", P_OFFINT (hex_gui_focus_omitalerts), TYPE_BOOL}, {"gui_hide_menu", P_OFFINT (hex_gui_hide_menu), TYPE_BOOL}, + {"gui_input_attr", P_OFFINT (hex_gui_input_attr), TYPE_BOOL}, {"gui_input_icon", P_OFFINT (hex_gui_input_icon), TYPE_BOOL}, {"gui_input_nick", P_OFFINT (hex_gui_input_nick), TYPE_BOOL}, {"gui_input_spell", P_OFFINT (hex_gui_input_spell), TYPE_BOOL}, @@ -668,6 +669,53 @@ get_default_language (void) return lang_no >= 0 ? lang_no : find_language_number ("en"); } +static char * +get_default_spell_languages (void) +{ + const gchar* const *langs = g_get_language_names (); + char *last = NULL; + char *p; + char lang_list[64]; + char *ret = lang_list; + int i; + + if (langs != NULL) + { + memset (lang_list, 0, sizeof(lang_list)); + + for (i = 0; langs[i]; i++) + { + if (g_ascii_strncasecmp (langs[i], "C", 1) != 0 && strlen (langs[i]) >= 2) + { + /* Avoid duplicates */ + if (!last || !g_str_has_prefix (langs[i], last)) + { + if (last != NULL) + { + g_free(last); + g_strlcat (lang_list, ",", sizeof(lang_list)); + } + + /* ignore .utf8 */ + if ((p = strchr (langs[i], '.'))) + *p='\0'; + + last = g_strndup (langs[i], 2); + + g_strlcat (lang_list, langs[i], sizeof(lang_list)); + } + } + } + if (last != NULL) + g_free(last); + + if (lang_list[0]) + return ret; + } + + return "en"; +} + void load_default_config(void) { @@ -705,6 +753,7 @@ load_default_config(void) prefs.hex_gui_autoopen_dialog = 1; prefs.hex_gui_autoopen_recv = 1; prefs.hex_gui_autoopen_send = 1; + prefs.hex_gui_input_attr = 1; prefs.hex_gui_input_icon = 1; prefs.hex_gui_input_nick = 1; prefs.hex_gui_input_spell = 1; @@ -844,7 +893,8 @@ load_default_config(void) strcpy (prefs.hex_text_font_main, DEF_FONT); #endif strcpy (prefs.hex_text_font_alternative, DEF_FONT_ALTER); - strcpy (prefs.hex_text_spell_langs, g_getenv ("LC_ALL") ? g_getenv ("LC_ALL") : "en_US"); + strcpy (prefs.hex_text_spell_langs, get_default_spell_languages ()); + /* private variables */ prefs.local_ip = 0xffffffff; diff --git a/src/common/hexchat.h b/src/common/hexchat.h index 82ebd28d..e1e402c2 100644 --- a/src/common/hexchat.h +++ b/src/common/hexchat.h @@ -151,6 +151,7 @@ struct hexchatprefs unsigned int hex_gui_compact; unsigned int hex_gui_focus_omitalerts; unsigned int hex_gui_hide_menu; + unsigned int hex_gui_input_attr; unsigned int hex_gui_input_icon; unsigned int hex_gui_input_nick; unsigned int hex_gui_input_spell; diff --git a/src/fe-gtk/Makefile.am b/src/fe-gtk/Makefile.am index 259d2c89..2e95232e 100644 --- a/src/fe-gtk/Makefile.am +++ b/src/fe-gtk/Makefile.am @@ -18,10 +18,14 @@ if DO_PLUGIN plugingui_c = plugingui.c endif +if HAVE_ISO_CODES +iso_codes_c = sexy-iso-codes.c +endif + hexchat_SOURCES = ascii.c banlist.c chanlist.c chanview.c custom-list.c \ dccgui.c editlist.c fe-gtk.c fkeys.c gtkutil.c ignoregui.c joind.c menu.c \ maingui.c notifygui.c palette.c pixmaps.c plugin-tray.c $(plugingui_c) \ - rawlog.c resources.c servlistgui.c setup.c sexy-iso-codes.c sexy-marshal.c \ + rawlog.c resources.c servlistgui.c setup.c $(iso_codes_c) sexy-marshal.c \ sexy-spell-entry.c textgui.c urlgrab.c userlistgui.c xtext.c resources.c: ../../data/hexchat.gresource.xml $(shell $(GLIB_COMPILE_RESOURCES) --sourcedir=../../data --generate-dependencies ../../data/hexchat.gresource.xml) diff --git a/src/fe-gtk/maingui.c b/src/fe-gtk/maingui.c index 6b7bf61c..4177514e 100644 --- a/src/fe-gtk/maingui.c +++ b/src/fe-gtk/maingui.c @@ -2971,6 +2971,7 @@ mg_create_entry (session *sess, GtkWidget *box) gui->input_box = entry = sexy_spell_entry_new (); sexy_spell_entry_set_checked ((SexySpellEntry *)entry, prefs.hex_gui_input_spell); + sexy_spell_entry_set_parse_attributes ((SexySpellEntry *)entry, prefs.hex_gui_input_attr); gtk_entry_set_max_length (GTK_ENTRY (gui->input_box), 0); g_signal_connect (G_OBJECT (entry), "activate", diff --git a/src/fe-gtk/setup.c b/src/fe-gtk/setup.c index 99fb9434..72c3bb35 100644 --- a/src/fe-gtk/setup.c +++ b/src/fe-gtk/setup.c @@ -187,6 +187,7 @@ static const setting inputbox_settings[] = { {ST_HEADER, N_("Input Box"),0,0,0}, {ST_TOGGLE, N_("Use the Text box font and colors"), P_OFFINTNL(hex_gui_input_style),0,0,0}, + {ST_TOGGLE, N_("Show attributes"), P_OFFINTNL (hex_gui_input_attr),0,0,0}, {ST_TOGGLE, N_("Show nick box"), P_OFFINTNL(hex_gui_input_nick),0,0,1}, {ST_TOGGLE, N_("Show user mode icon in nick box"), P_OFFINTNL(hex_gui_input_icon),0,0,0}, {ST_TOGGLE, N_("Spell checking"), P_OFFINTNL(hex_gui_input_spell),0,0,1}, @@ -2024,6 +2025,7 @@ setup_apply_to_sess (session_gui *gui) sexy_spell_entry_activate_default_languages((SexySpellEntry *)gui->input_box); sexy_spell_entry_set_checked ((SexySpellEntry *)gui->input_box, prefs.hex_gui_input_spell); + sexy_spell_entry_set_parse_attributes ((SexySpellEntry *)gui->input_box, prefs.hex_gui_input_attr); } static void diff --git a/src/fe-gtk/sexy-spell-entry.c b/src/fe-gtk/sexy-spell-entry.c index 044a6610..24424c17 100644 --- a/src/fe-gtk/sexy-spell-entry.c +++ b/src/fe-gtk/sexy-spell-entry.c @@ -19,7 +19,7 @@ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ #ifdef HAVE_CONFIG_H -# include "config.h" +#include "config.h" #endif #include @@ -86,6 +86,7 @@ struct _SexySpellEntryPriv gint *word_starts; gint *word_ends; gboolean checked; + gboolean parseattr; }; static void sexy_spell_entry_class_init(SexySpellEntryClass *klass); @@ -122,6 +123,10 @@ static void entry_strsplit_utf8 (GtkEntry static GtkEntryClass *parent_class = NULL; +#ifdef HAVE_ISO_CODES +static int codetable_ref = 0; +#endif + G_DEFINE_TYPE_EXTENDED(SexySpellEntry, sexy_spell_entry, GTK_TYPE_ENTRY, 0, G_IMPLEMENT_INTERFACE(GTK_TYPE_EDITABLE, sexy_spell_entry_editable_init)); enum @@ -352,12 +357,19 @@ static void insert_color (SexySpellEntry *entry, guint start, int fgcolor, int bgcolor) { PangoAttribute *fgattr; + PangoAttribute *ulattr; PangoAttribute *bgattr; if (fgcolor < 0 || fgcolor > MAX_COL) + { fgattr = pango_attr_foreground_new (colors[COL_FG].red, colors[COL_FG].green, colors[COL_FG].blue); + ulattr = pango_attr_underline_color_new (colors[COL_FG].red, colors[COL_FG].green, colors[COL_FG].blue); + } else + { fgattr = pango_attr_foreground_new (colors[fgcolor].red, colors[fgcolor].green, colors[fgcolor].blue); + ulattr = pango_attr_underline_color_new (colors[fgcolor].red, colors[fgcolor].green, colors[fgcolor].blue); + } if (bgcolor < 0 || bgcolor > MAX_COL) bgattr = pango_attr_background_new (colors[COL_BG].red, colors[COL_BG].green, colors[COL_BG].blue); @@ -367,6 +379,9 @@ insert_color (SexySpellEntry *entry, guint start, int fgcolor, int bgcolor) fgattr->start_index = start; fgattr->end_index = PANGO_ATTR_INDEX_TO_TEXT_END; pango_attr_list_change (entry->priv->attr_list, fgattr); + ulattr->start_index = start; + ulattr->end_index = PANGO_ATTR_INDEX_TO_TEXT_END; + pango_attr_list_change (entry->priv->attr_list, ulattr); bgattr->start_index = start; bgattr->end_index = PANGO_ATTR_INDEX_TO_TEXT_END; pango_attr_list_change (entry->priv->attr_list, bgattr); @@ -431,7 +446,7 @@ add_to_dictionary(GtkWidget *menuitem, SexySpellEntry *entry) g_free(entry->priv->word_ends); } entry_strsplit_utf8(GTK_ENTRY(entry), &entry->priv->words, &entry->priv->word_starts, &entry->priv->word_ends); - sexy_spell_entry_recheck_all(entry); + sexy_spell_entry_recheck_all (entry); } static void @@ -703,15 +718,19 @@ sexy_spell_entry_init(SexySpellEntry *entry) entry->priv->dict_hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); if (have_enchant) - sexy_spell_entry_activate_default_languages(entry); - + { #ifdef HAVE_ISO_CODES - codetable_init (); + if (codetable_ref == 0) + codetable_init (); + codetable_ref++; #endif + sexy_spell_entry_activate_default_languages(entry); + } entry->priv->attr_list = pango_attr_list_new(); entry->priv->checked = TRUE; + entry->priv->parseattr = TRUE; g_signal_connect(G_OBJECT(entry), "popup-menu", G_CALLBACK(sexy_spell_entry_popup_menu), entry); g_signal_connect(G_OBJECT(entry), "populate-popup", G_CALLBACK(sexy_spell_entry_populate_popup), NULL); @@ -754,7 +773,9 @@ sexy_spell_entry_finalize(GObject *obj) g_free(entry->priv); #ifdef HAVE_ISO_CODES - codetable_free (); + codetable_ref--; + if (codetable_ref == 0) + codetable_free (); #endif if (G_OBJECT_CLASS(parent_class)->finalize) @@ -859,17 +880,16 @@ check_word(SexySpellEntry *entry, int start, int end) } static void -check_attributes (SexySpellEntry *entry, char *text, int len) +check_attributes (SexySpellEntry *entry, const char *text, int len) { - PangoAttribute *attr; - PangoAttrList *attr_list = entry->priv->attr_list; gboolean bold = FALSE; gboolean italic = FALSE; gboolean underline = FALSE; int parsing_color = 0; char fg_color[3]; char bg_color[3]; - int i, bg_offset, fg_offset = 0; + int i, bg_offset = 0; + int fg_offset = 0; memset (bg_color, 0, sizeof(bg_color)); memset (fg_color, 0, sizeof(fg_color)); @@ -882,28 +902,40 @@ check_attributes (SexySpellEntry *entry, char *text, int len) insert_hiddenchar (entry, i, i + 1); insert_bold (entry, i, bold); bold = !bold; - break; + goto check_color; case ATTR_ITALICS: + insert_hiddenchar (entry, i, i + 1); insert_italic (entry, i, italic); italic = !italic; - break; + goto check_color; case ATTR_UNDERLINE: + insert_hiddenchar (entry, i, i + 1); insert_underline (entry, i, underline); underline = !underline; - break; + goto check_color; case ATTR_RESET: insert_hiddenchar (entry, i, i + 1); insert_reset (entry, i); - break; + goto check_color; + + case ATTR_HIDDEN: + insert_hiddenchar (entry, i, i + 1); + goto check_color; + + case ATTR_REVERSE: + insert_hiddenchar (entry, i, i + 1); + insert_color (entry, i, COL_BG, COL_FG); + goto check_color; case ATTR_COLOR: parsing_color = 1; break; default: +check_color: if (!parsing_color) continue; @@ -974,15 +1006,24 @@ static void sexy_spell_entry_recheck_all(SexySpellEntry *entry) { GdkRectangle rect; + GtkAllocation allocation; GtkWidget *widget = GTK_WIDGET(entry); PangoLayout *layout; int length, i, text_len; - char *text; + const char *text; /* Remove all existing pango attributes. These will get readded as we check */ pango_attr_list_unref(entry->priv->attr_list); entry->priv->attr_list = pango_attr_list_new(); + if (entry->priv->parseattr) + { + /* Check for attributes */ + text = gtk_entry_get_text (GTK_ENTRY (entry)); + text_len = gtk_entry_get_text_length (GTK_ENTRY (entry)); + check_attributes (entry, text, text_len); + } + if (have_enchant && entry->priv->checked && g_slist_length (entry->priv->dict_list) != 0) { @@ -996,18 +1037,16 @@ sexy_spell_entry_recheck_all(SexySpellEntry *entry) } } - /* Check for attributes */ - text = gtk_entry_get_text (GTK_ENTRY (entry)); - text_len = gtk_entry_get_text_length (GTK_ENTRY (entry)); - check_attributes (entry, text, text_len); - layout = gtk_entry_get_layout(GTK_ENTRY(entry)); pango_layout_set_attributes(layout, entry->priv->attr_list); - if (GTK_WIDGET_REALIZED(GTK_WIDGET(entry))) { + if (gtk_widget_get_realized (GTK_WIDGET(entry))) + { + gtk_widget_get_allocation (GTK_WIDGET(entry), &allocation); + rect.x = 0; rect.y = 0; - rect.width = widget->allocation.width; - rect.height = widget->allocation.height; + rect.width = allocation.width; + rect.height = allocation.height; gdk_window_invalidate_rect(gtk_widget_get_window (widget), &rect, TRUE); } } @@ -1052,7 +1091,7 @@ static void entry_strsplit_utf8(GtkEntry *entry, gchar ***set, gint **starts, gint **ends) { PangoLayout *layout; - PangoLogAttr *log_attrs; + const PangoLogAttr *log_attrs; const gchar *text; gint n_attrs, n_strings, i, j; @@ -1110,35 +1149,33 @@ sexy_spell_entry_changed(GtkEditable *editable, gpointer data) sexy_spell_entry_recheck_all(entry); } -#if 0 static gboolean enchant_has_lang(const gchar *lang, GSList *langs) { GSList *i; - for (i = langs; i; i = g_slist_next(i)) { - if (strcmp(lang, i->data) == 0) { + for (i = langs; i; i = g_slist_next(i)) + { + if (strcmp(lang, i->data) == 0) + { return TRUE; } } return FALSE; } -#endif /** * sexy_spell_entry_activate_default_languages: * @entry: A #SexySpellEntry. * - * Activate spell checking for languages specified in the $LANG - * or $LANGUAGE environment variables. These languages are + * Activate spell checking for languages specified in the + * text_spell_langs setting. These languages are * activated by default, so this function need only be called * if they were previously deactivated. */ void sexy_spell_entry_activate_default_languages(SexySpellEntry *entry) { - /*const gchar* const *langs; - int i; - gchar *lastprefix = NULL;*/ - GSList *enchant_langs, *i; + GSList *enchant_langs; + char *lang, *langs; if (!have_enchant) return; @@ -1146,43 +1183,29 @@ sexy_spell_entry_activate_default_languages(SexySpellEntry *entry) if (!entry->priv->broker) entry->priv->broker = enchant_broker_init(); - - /*langs = g_get_language_names (); - - if (langs == NULL) - return;*/ - enchant_langs = sexy_spell_entry_get_languages(entry); - /*for (i = 0; langs[i]; i++) { - if ((g_ascii_strncasecmp(langs[i], "C", 1) != 0) && - (strlen(langs[i]) >= 2) && - enchant_has_lang(langs[i], enchant_langs)) { - if ((lastprefix == NULL) || (g_str_has_prefix(langs[i], lastprefix) == FALSE)) - sexy_spell_entry_activate_language_internal(entry, langs[i], NULL); - if (lastprefix != NULL) - g_free(lastprefix); - lastprefix = g_strndup(langs[i], 2); - } - } - if (lastprefix != NULL) - g_free(lastprefix);*/ + langs = g_strdup (prefs.hex_text_spell_langs); - for (i = enchant_langs; i; i = g_slist_next (i)) + lang = strtok (langs, ","); + while (lang != NULL) { - if (strstr (prefs.hex_text_spell_langs, i->data) != NULL) + if (enchant_has_lang (lang, enchant_langs)) { - sexy_spell_entry_activate_language_internal (entry, i->data, NULL); + sexy_spell_entry_activate_language_internal (entry, lang, NULL); } + lang = strtok (NULL, ","); } g_slist_foreach(enchant_langs, (GFunc) g_free, NULL); g_slist_free(enchant_langs); - g_slist_free (i); + g_free (langs); /* If we don't have any languages activated, use "en" */ if (entry->priv->dict_list == NULL) sexy_spell_entry_activate_language_internal(entry, "en", NULL); + + sexy_spell_entry_recheck_all (entry); } static void @@ -1292,8 +1315,14 @@ sexy_spell_entry_get_language_name(const SexySpellEntry *entry, g_return_val_if_fail (have_enchant, NULL); + if (codetable_ref == 0) + codetable_init (); + codetable_lookup (lang, &lang_name, &country_name); + if (codetable_ref == 0) + codetable_free (); + if (strlen (country_name) != 0) return g_strdup_printf ("%s (%s)", lang_name, country_name); else @@ -1518,19 +1547,15 @@ sexy_spell_entry_set_checked(SexySpellEntry *entry, gboolean checked) entry->priv->checked = checked; widget = GTK_WIDGET(entry); - if (checked == FALSE && GTK_WIDGET_REALIZED(widget)) { - PangoLayout *layout; - GdkRectangle rect; - + if (checked == FALSE && gtk_widget_get_realized (widget)) + { /* This will unmark any existing */ sexy_spell_entry_recheck_all (entry); - - rect.x = 0; rect.y = 0; - rect.width = widget->allocation.width; - rect.height = widget->allocation.height; - gdk_window_invalidate_rect(gtk_widget_get_window (widget), &rect, TRUE); - } else { - if (entry->priv->words) { + } + else + { + if (entry->priv->words) + { g_strfreev(entry->priv->words); g_free(entry->priv->word_starts); g_free(entry->priv->word_ends); @@ -1539,3 +1564,39 @@ sexy_spell_entry_set_checked(SexySpellEntry *entry, gboolean checked) sexy_spell_entry_recheck_all(entry); } } + +/** +* sexy_spell_entry_set_parse_attributes: +* @entry: A #SexySpellEntry. +* @parse: Whether to enable showing attributes +* +* Sets whether to enable showing attributes is enabled. +*/ +void +sexy_spell_entry_set_parse_attributes (SexySpellEntry *entry, gboolean parse) +{ + GtkWidget *widget; + + if (entry->priv->parseattr == parse) + return; + + entry->priv->parseattr = parse; + widget = GTK_WIDGET (entry); + + if (parse == FALSE && gtk_widget_get_realized (widget)) + { + /* This will remove current attrs */ + sexy_spell_entry_recheck_all (entry); + } + else + { + if (entry->priv->words) + { + g_strfreev (entry->priv->words); + g_free (entry->priv->word_starts); + g_free (entry->priv->word_ends); + } + entry_strsplit_utf8 (GTK_ENTRY (entry), &entry->priv->words, &entry->priv->word_starts, &entry->priv->word_ends); + sexy_spell_entry_recheck_all (entry); + } +} \ No newline at end of file diff --git a/src/fe-gtk/sexy-spell-entry.h b/src/fe-gtk/sexy-spell-entry.h index 1c761235..1e6fd1f2 100644 --- a/src/fe-gtk/sexy-spell-entry.h +++ b/src/fe-gtk/sexy-spell-entry.h @@ -77,6 +77,7 @@ gboolean sexy_spell_entry_set_active_languages(SexySpellEntry *entry, GSList * GSList *sexy_spell_entry_get_active_languages(SexySpellEntry *entry); gboolean sexy_spell_entry_is_checked(SexySpellEntry *entry); void sexy_spell_entry_set_checked(SexySpellEntry *entry, gboolean checked); +void sexy_spell_entry_set_parse_attributes (SexySpellEntry *entry, gboolean parse); void sexy_spell_entry_activate_default_languages(SexySpellEntry *entry); G_END_DECLS