diff --git a/README b/README index f965819f23eff77358b7d8dcd09ba472da9f2903..d9fb16853536fea26dccd652855e172db1d5cfeb 100644 --- a/README +++ b/README @@ -5,8 +5,8 @@ Xfce4 Dictionary - A client program to query different dictionaries This program allows you to search different kinds of dictionary services for words or phrases and shows you the result. Currently you can query a "Dict" server(RFC 2229), any online dictionary -service by opening a web browser or search for words using the -aspell/ispell program. +service by opening a web browser or search for words using a spell check +program like aspell, ispell or enchant. xfce4-dict contains a stand-alone application called "xfce4-dict" and a panel plugin for the Xfce panel. @@ -100,11 +100,12 @@ other known browsers are tried. But it's better to set a default browser using "Preferred Applications" in the Xfce settings manager. -Aspell/ispell based search +Spell checking based search -------------------------- -It is also possible to verify the spelling of word using aspell (or its -predecessor ispell). To get this working you need to have the aspell or +It is also possible to verify the spelling of word using spell checking +programs like enchant or aspell (or its predecessor ispell). +To get this working you need to have the enchant, aspell or ispell binary in your binary search path and at least one dictionary -working. If you have multiple aspell dictionaries installed, you can +working. If you have multiple dictionaries installed, you can select the one to use in the preferences dialog. diff --git a/lib/Makefile.am b/lib/Makefile.am index 32fa87980666bdab10416a9a7cf136e83bc55e80..a60adc187964b64e437922ef910338aaf91bb7e2 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -4,21 +4,21 @@ noinst_LTLIBRARIES = \ libdict.la libdict_la_SOURCES = \ - libdict.h \ - aspell.c \ - aspell.h \ common.c \ common.h \ dictd.c \ dictd.h \ gui.c \ gui.h \ + libdict.h \ + prefs.c \ + prefs.h \ sexy-icon-entry.c \ sexy-icon-entry.h \ + spell.c \ + spell.h \ wraplabel.c \ - wraplabel.h \ - prefs.c \ - prefs.h + wraplabel.h libdict_la_CFLAGS = \ -I$(top_srcdir) \ diff --git a/lib/common.c b/lib/common.c index 46730f437792a9de85abfe04e53f1c53b67a6f8b..98504557978124781432761d5e52cf3f1f4d2c99 100644 --- a/lib/common.c +++ b/lib/common.c @@ -20,7 +20,7 @@ /* All files in lib/ form a collection of common used code by the xfce4-dict stand-alone * application and the xfce4-dict-plugin. - * This file contains very common code and has some glue to combine the aspell query code, + * This file contains very common code and has some glue to combine the spell query code, * the dictd server query code and the web dictionary code together. */ @@ -38,7 +38,7 @@ #include "common.h" -#include "aspell.h" +#include "spell.h" #include "dictd.h" #include "gui.h" @@ -234,7 +234,7 @@ void dict_search_word(DictData *dd, const gchar *word) } case DICTMODE_SPELL: { - dict_aspell_start_query(dd, dd->searched_word); + dict_spell_start_query(dd, dd->searched_word); break; } default: @@ -308,6 +308,22 @@ static gchar *get_hex_from_color(GdkColor *color) } +static gchar *get_spell_program(void) +{ + gchar *path; + + path = g_find_program_in_path("enchant"); + if (path != NULL) + return path; + + path = g_find_program_in_path("aspell"); + if (path != NULL) + return path; + + return g_strdup(""); +} + + void dict_read_rc_file(DictData *dd) { XfceRc *rc; @@ -316,11 +332,12 @@ void dict_read_rc_file(DictData *dd) gint port = 2628; gint panel_entry_size = 150; gboolean show_panel_entry = FALSE; + gchar *spell_bin_default = get_spell_program(); const gchar *server = "dict.org"; const gchar *dict = "*"; const gchar *weburl = NULL; - const gchar *spell_bin = "aspell"; - const gchar *spell_dictionary = "en"; + const gchar *spell_bin = NULL; + const gchar *spell_dictionary = "en"; /// TODO find by LANG env const gchar *geo = "-1;0;0;0;0;"; const gchar *link_color_str = "#0000ff"; const gchar *phon_color_str = "#006300"; @@ -335,7 +352,7 @@ void dict_read_rc_file(DictData *dd) port = xfce_rc_read_int_entry(rc, "port", port); server = xfce_rc_read_entry(rc, "server", server); dict = xfce_rc_read_entry(rc, "dict", dict); - spell_bin = xfce_rc_read_entry(rc, "spell_bin", spell_bin); + spell_bin = xfce_rc_read_entry(rc, "spell_bin", spell_bin_default); link_color_str = xfce_rc_read_entry(rc, "link_color", link_color_str); phon_color_str = xfce_rc_read_entry(rc, "phonetic_color", phon_color_str); @@ -356,8 +373,14 @@ void dict_read_rc_file(DictData *dd) dd->port = port; dd->server = g_strdup(server); dd->dictionary = g_strdup(dict); - dd->spell_bin = g_strdup(spell_bin); dd->spell_dictionary = g_strdup(spell_dictionary); + if (spell_bin != NULL) + { + dd->spell_bin = g_strdup(spell_bin); + g_free(spell_bin_default); + } + else + dd->spell_bin = spell_bin_default; dd->link_color = g_new0(GdkColor, 1); gdk_color_parse(link_color_str, dd->link_color); diff --git a/lib/dictd.c b/lib/dictd.c index 1cf0fd487e4a4d6c1121e80ff6a43ee305c964f4..81344bdc30eb4ec5cc3a1a1bc86bd0005be7bfae 100644 --- a/lib/dictd.c +++ b/lib/dictd.c @@ -48,7 +48,7 @@ #include "common.h" #include "dictd.h" #include "gui.h" -#include "aspell.h" +#include "spell.h" #include "prefs.h" @@ -381,7 +381,7 @@ static gboolean process_server_response(DictData *dd) g_free(dd->query_buffer); /* if we had no luck searching a word, maybe we have a typo so try searching with - * aspell and offer a Web search*/ + * spell check and offer a Web search*/ if (NZV(dd->web_url)) { gchar *text = g_strdup_printf( @@ -397,7 +397,7 @@ static gboolean process_server_response(DictData *dd) if (NZV(dd->spell_bin)) { gtk_text_buffer_insert(dd->main_textbuffer, &dd->textiter, "\n", 1); - dict_aspell_start_query(dd, dd->searched_word); + dict_spell_start_query(dd, dd->searched_word); } return FALSE; diff --git a/lib/libdict.h b/lib/libdict.h index 73c5e7b314ec15e687fc2819f27f45d2d9944aa8..427901b0faebc2cbed1cdaad10c655715a8f859a 100644 --- a/lib/libdict.h +++ b/lib/libdict.h @@ -22,7 +22,7 @@ #define LIBDICT_H 1 #include "common.h" -#include "aspell.h" +#include "spell.h" #include "dictd.h" #include "prefs.h" #include "gui.h" diff --git a/lib/prefs.c b/lib/prefs.c index 92ed1dac0c4ed938cb1ae60412d8a13bc7b51e2c..9dbbcfd542dda4037a828f2e773c48219fc781ef 100644 --- a/lib/prefs.c +++ b/lib/prefs.c @@ -32,6 +32,7 @@ #include "common.h" #include "prefs.h" #include "dictd.h" +#include "spell.h" #include "wraplabel.h" @@ -46,7 +47,7 @@ enum NOTEBOOK_PAGE_GENERAL = 0, NOTEBOOK_PAGE_DICTD, NOTEBOOK_PAGE_WEB, - NOTEBOOK_PAGE_ASPELL + NOTEBOOK_PAGE_SPELL }; static const web_dict_t web_dicts[] = @@ -87,39 +88,6 @@ static void search_method_changed(GtkRadioButton *radiobutton, DictData *dd) } -static void get_spell_dictionaries(GtkWidget *spell_combo, DictData *dd) -{ - if (NZV(dd->spell_bin)) - { - gchar *tmp = NULL, *cmd, *locale_cmd; - - cmd = g_strconcat(dd->spell_bin, " dump dicts", NULL); - locale_cmd = g_locale_from_utf8(cmd, -1, NULL, NULL, NULL); - if (locale_cmd == NULL) - locale_cmd = g_strdup(cmd); - g_spawn_command_line_sync(locale_cmd, &tmp, NULL, NULL, NULL); - if (NZV(tmp)) - { - gchar **list = g_strsplit_set(tmp, "\n\r", -1); - gchar *item; - guint i, len = g_strv_length(list); - for (i = 0; i < len; i++) - { - item = g_strstrip(list[i]); - gtk_combo_box_append_text(GTK_COMBO_BOX(spell_combo), item); - if (strcmp(dd->spell_dictionary, item) == 0) - gtk_combo_box_set_active(GTK_COMBO_BOX(spell_combo), i); - } - g_strfreev(list); - } - - g_free(cmd); - g_free(locale_cmd); - g_free(tmp); - } -} - - void dict_prefs_dialog_response(GtkWidget *dlg, gint response, DictData *dd) { gchar *dictionary, *search_url; @@ -592,9 +560,9 @@ GtkWidget *dict_prefs_dialog_show(GtkWidget *parent, DictData *dd) } /* - * Page: ASPELL + * Page: SPELL */ -#define PAGE_ASPELL /* only for navigation in Geany's symbol list ;-) */ +#define PAGE_SPELL /* only for navigation in Geany's symbol list ;-) */ { GtkWidget *table, *spell_entry, *spell_combo; @@ -604,9 +572,9 @@ GtkWidget *dict_prefs_dialog_show(GtkWidget *parent, DictData *dd) gtk_container_set_border_width(GTK_CONTAINER(inner_vbox), 5); gtk_widget_show(inner_vbox); gtk_notebook_insert_page(GTK_NOTEBOOK(notebook), - notebook_vbox, gtk_label_new(_("Spell Check")), NOTEBOOK_PAGE_ASPELL); + notebook_vbox, gtk_label_new(_("Spell Check")), NOTEBOOK_PAGE_SPELL); - label1 = gtk_label_new_with_mnemonic(_("Aspell Program:")); + label1 = gtk_label_new_with_mnemonic(_("Spell Check Program:")); gtk_widget_show(label1); spell_entry = gtk_entry_new(); @@ -621,7 +589,7 @@ GtkWidget *dict_prefs_dialog_show(GtkWidget *parent, DictData *dd) gtk_widget_show(label2); spell_combo = gtk_combo_box_new_text(); - get_spell_dictionaries(spell_combo, dd); + dict_spell_get_dictionaries(dd, spell_combo); gtk_widget_show(spell_combo); g_object_set_data(G_OBJECT(dialog), "spell_combo", spell_combo); diff --git a/lib/spell.c b/lib/spell.c new file mode 100644 index 0000000000000000000000000000000000000000..b68ba81285e6638b2e6982fb940545a5ac9b31ce --- /dev/null +++ b/lib/spell.c @@ -0,0 +1,345 @@ +/* $Id$ + * + * Copyright 2006-2008 Enrico Tröger <enrico(dot)troeger(at)uvena(dot)de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +/* Code to execute the aspell or enchant (or ispell or anything command line compatible) + * binary with a given search term and reads it output. */ + + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdlib.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <string.h> + +#include <gtk/gtk.h> + +#include <libxfcegui4/libxfcegui4.h> + +#include "common.h" +#include "spell.h" +#include "gui.h" + + +typedef struct +{ + DictData *dd; + gchar *word; +} iodata; + + +static GIOChannel *set_up_io_channel(gint fd, GIOCondition cond, GIOFunc func, gpointer data) +{ + GIOChannel *ioc; + + ioc = g_io_channel_unix_new(fd); + + g_io_channel_set_flags(ioc, G_IO_FLAG_NONBLOCK, NULL); + g_io_channel_set_encoding(ioc, NULL, NULL); + /* "auto-close" */ + g_io_channel_set_close_on_unref(ioc, TRUE); + + g_io_add_watch(ioc, cond, func, (gpointer) data); + g_io_channel_unref(ioc); + + return ioc; +} + + +static gboolean iofunc_read(GIOChannel *ioc, GIOCondition cond, gpointer data) +{ + iodata *iod = data; + if (cond & (G_IO_IN | G_IO_PRI)) + { + gchar *msg, *tmp; + DictData *dd = iod->dd; + + while (g_io_channel_read_line(ioc, &msg, NULL, NULL, NULL) && msg != NULL) + { + if (msg[0] == '&') + { /* & cmd 17 7: ... */ + gint count; + tmp = strchr(msg + 2, ' ') + 1; + count = atoi(tmp); + dict_gui_status_add(dd, ngettext("%d suggestion found.", + "%d suggestions found.", + count), count); + + gtk_text_buffer_insert(dd->main_textbuffer, &dd->textiter, "\n", 1); + tmp = g_strdup_printf(_("Suggestions for \"%s\":"), iod->word); + gtk_text_buffer_insert_with_tags_by_name( + dd->main_textbuffer, &dd->textiter, tmp, -1, "bold", NULL); + g_free(tmp); + gtk_text_buffer_insert(dd->main_textbuffer, &dd->textiter, "\n", 1); + + tmp = strchr(msg, ':') + 2; + gtk_text_buffer_insert(dd->main_textbuffer, &dd->textiter, g_strchomp(tmp), -1); + gtk_text_buffer_insert(dd->main_textbuffer, &dd->textiter, "\n\n", 2); + } + else if (msg[0] == '*') + { + gtk_text_buffer_insert(dd->main_textbuffer, &dd->textiter, "\n", 1); + tmp = g_strdup_printf(_("\"%s\" is spelled correctly."), iod->word); + gtk_text_buffer_insert(dd->main_textbuffer, &dd->textiter, tmp, -1); + gtk_text_buffer_insert(dd->main_textbuffer, &dd->textiter, "\n\n", 2); + g_free(tmp); + } + else if (msg[0] == '#') + { + gtk_text_buffer_insert(dd->main_textbuffer, &dd->textiter, "\n", 1); + tmp = g_strdup_printf(_("No suggestions could be found for \"%s\"."), + iod->word); + gtk_text_buffer_insert(dd->main_textbuffer, &dd->textiter, tmp, -1); + gtk_text_buffer_insert(dd->main_textbuffer, &dd->textiter, "\n\n", 2); + g_free(tmp); + } + g_free(msg); + } + return TRUE; + } + + g_free(iod->word); + g_free(iod); + + return FALSE; +} + + +static gboolean iofunc_read_err(GIOChannel *ioc, GIOCondition cond, gpointer data) +{ + if (cond & (G_IO_IN | G_IO_PRI)) + { + gchar *msg; + DictData *dd = data; + + while (g_io_channel_read_line(ioc, &msg, NULL, NULL, NULL) && msg != NULL) + { + /* translation hint: + * Error while executing <spell command, e.g. "aspell"> (<error message>) */ + dict_gui_status_add(dd, _("Error while executing \"%s\" (%s)."), + dd->spell_bin, g_strstrip(msg)); + g_free(msg); + } + return TRUE; + } + + return FALSE; +} + + +static gboolean iofunc_write(GIOChannel *ioc, GIOCondition cond, gpointer data) +{ + if (NZV((gchar *) data)) + g_io_channel_write_chars(ioc, (gchar *) data, -1, NULL, NULL); + + g_free(data); + + return FALSE; +} + + +void dict_spell_start_query(DictData *dd, const gchar *word) +{ + GError *error = NULL; + gchar **argv; + gchar *locale_cmd; + gint stdout_fd; + gint stderr_fd; + gint stdin_fd; + guint i; + gsize tts_len; + gchar **tts; /* text to search */ + iodata *iod; + + if (! NZV(dd->spell_bin)) + { + dict_gui_status_add(dd, _("Please set the spell check command in the preferences dialog.")); + return; + } + + if (! NZV(word)) + { + dict_gui_status_add(dd, _("Invalid input.")); + return; + } + + tts = g_strsplit_set(word, " -_,.", 0); + tts_len = g_strv_length(tts); + + for (i = 0; i < tts_len; i++) + { + locale_cmd = g_locale_from_utf8(dd->spell_bin, -1, NULL, NULL, NULL); + if (locale_cmd == NULL) + locale_cmd = g_strdup(dd->spell_bin); + + argv = g_new0(gchar*, 5); + argv[0] = locale_cmd; + argv[1] = g_strdup("-a"); + argv[2] = g_strdup("-d"); + argv[3] = g_strdup(dd->spell_dictionary); + argv[4] = NULL; + + if (g_spawn_async_with_pipes(NULL, argv, NULL, + G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD, NULL, NULL, NULL, + &stdin_fd, &stdout_fd, &stderr_fd, &error)) + { + iod = g_new(iodata, 1); + iod->dd = dd; + iod->word = g_strdup(tts[i]); + + set_up_io_channel(stdin_fd, G_IO_OUT, iofunc_write, g_strdup(tts[i])); + set_up_io_channel(stdout_fd, G_IO_IN|G_IO_PRI|G_IO_HUP|G_IO_ERR|G_IO_NVAL, iofunc_read, iod); + set_up_io_channel(stderr_fd, G_IO_IN|G_IO_PRI|G_IO_HUP|G_IO_ERR|G_IO_NVAL, iofunc_read_err, dd); + dict_gui_status_add(dd, _("Ready.")); + } + else + { + dict_gui_status_add(dd, _("Process failed (%s)"), error->message); + g_error_free(error); + error = NULL; + } + + g_strfreev(argv); + } + + g_strfreev(tts); +} + + +static gchar *get_enchant_dict_string(GPtrArray *dicts, const gchar *str) +{ + guint i; + gchar *result = g_strstrip(g_strdup(str)); + gchar *e; + + /* dictionaries are in the form 'en_GB (aspell)', we search the first space and change + * it to the end of the string to ignore the rest (provider name) */ + e = strchr(result, ' '); + if (e != NULL) + *e = '\0'; + + /* sometimes dictionaries are named lang-LOCALE instead of lang_LOCALE, so replace the + * hyphen by a dash, enchant seems to not care about it. */ + for (i = 0; i < strlen(result); i++) + { + if (result[i] == '-') + result[i] = '_'; + } + + /* find duplicates and skip them */ + for (i = 0; i < dicts->len; i++) + { + if (strcmp(g_ptr_array_index(dicts, i), result) == 0) + { + g_free(result); + return NULL; + } + } + return result; +} + + +static gchar **get_enchant_dicts(const gchar *str) +{ + gchar **list = g_strsplit_set(str, "\r\n", -1); + gchar *item; + guint i, len = g_strv_length(list); + GPtrArray *dicts = g_ptr_array_new(); + + for (i = 0; i < len; i++) + { + item = get_enchant_dict_string(dicts, list[i]); + if (item != NULL) + g_ptr_array_add(dicts, item); + } + + g_strfreev(list); + + list = g_new0(gchar *, dicts->len); + for (i = 0; i < dicts->len; i++) + { + list[i] = g_ptr_array_index(dicts, i); + } + g_ptr_array_free(dicts, TRUE); + + return list; +} + + +static gchar **get_aspell_dicts(const gchar *str) +{ + gchar **list = g_strsplit_set(str, "\r\n", -1); + guint i, len = g_strv_length(list); + + for (i = 0; i < len; i++) + { + g_strstrip(list[i]); + } + return list; +} + + +void dict_spell_get_dictionaries(DictData *dd, GtkWidget *spell_combo) +{ + if (NZV(dd->spell_bin)) + { + gchar *tmp = NULL, *cmd, *locale_cmd; + gboolean use_enchant = FALSE; + + if (strstr(dd->spell_bin, "enchant") != NULL) + { + cmd = g_strdup("enchant-lsmod -list-dicts"); + use_enchant = TRUE; + } + else + cmd = g_strconcat(dd->spell_bin, " dump dicts", NULL); + + locale_cmd = g_locale_from_utf8(cmd, -1, NULL, NULL, NULL); + if (locale_cmd == NULL) + locale_cmd = g_strdup(cmd); + + g_spawn_command_line_sync(locale_cmd, &tmp, NULL, NULL, NULL); + + if (NZV(tmp)) + { + gchar **list; + guint i, len; + + list = (use_enchant) ? get_enchant_dicts(tmp) : get_aspell_dicts(tmp); + len = g_strv_length(list); + for (i = 0; i < len; i++) + { + if (NZV(list[i])) + { + gtk_combo_box_append_text(GTK_COMBO_BOX(spell_combo), list[i]); + if (strcmp(dd->spell_dictionary, list[i]) == 0) + gtk_combo_box_set_active(GTK_COMBO_BOX(spell_combo), i); + } + } + g_strfreev(list); + } + + g_free(cmd); + g_free(locale_cmd); + g_free(tmp); + } +} diff --git a/lib/spell.h b/lib/spell.h new file mode 100644 index 0000000000000000000000000000000000000000..d88b37ff50a0d7bc57e8806104ad30459ad2ea5a --- /dev/null +++ b/lib/spell.h @@ -0,0 +1,30 @@ +/* $Id$ + * + * Copyright 2006-2008 Enrico Tröger <enrico(dot)troeger(at)uvena(dot)de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#ifndef SPELL_H +#define SPELL_H 1 + + +void dict_spell_start_query(DictData *dd, const gchar *word); + +void dict_spell_get_dictionaries(DictData *dd, GtkWidget *spell_combo); + + +#endif diff --git a/po/POTFILES.in b/po/POTFILES.in index 723fc82d64584fa06237c7c1b92b8af3e0ad3943..35012e0e47387b85c71801ee34427bb444576909 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -3,7 +3,7 @@ panel-plugin/xfce4-dict-plugin.c src/popup_plugin.c src/xfce4-dict.c src/xfce4-dict.desktop.in -lib/aspell.c +lib/spell.c lib/common.c lib/dictd.c lib/gui.c diff --git a/xfce4-dict.1 b/xfce4-dict.1 index cd579cb3df128289eb08ff47d72336a441d540ac..c414b28c11e6430adc44ccca22434770f177fb2d 100644 --- a/xfce4-dict.1 +++ b/xfce4-dict.1 @@ -9,8 +9,8 @@ xfce4-dict \(em a client program to query different dictionaries xfce4-dict allows you to search different kinds of dictionary services for words or phrases and shows you the result. Currently you can query a "Dict" server(RFC 2229), any online dictionary -service by opening a web browser or search for words using the -aspell/ispell program. +service by opening a web browser or search for words using a spell check +program like aspell, ispell or enchant. .PP Homepage: http://goodies.xfce.org/projects/applications/xfce4-dict .SH "OPTIONS"