SF.net SVN: geany-plugins: [74] trunk/spellcheck

eht16 at users.sourceforge.net eht16 at xxxxx
Thu Jun 19 21:34:54 UTC 2008


Revision: 74
          http://geany-plugins.svn.sourceforge.net/geany-plugins/?rev=74&view=rev
Author:   eht16
Date:     2008-06-19 14:34:52 -0700 (Thu, 19 Jun 2008)

Log Message:
-----------
Use enchant as spell check engine instead of Aspell.
Highlight only misspelled instead of whole the line.
Add Tools->Spellcheck sub menu to easily change the language to use for spell checking.
Add "check as you type" feature.
Change doc pointer usage.
Update Readme.                     

Modified Paths:
--------------
    trunk/spellcheck/ChangeLog
    trunk/spellcheck/README
    trunk/spellcheck/configure.in
    trunk/spellcheck/src/Makefile.am
    trunk/spellcheck/src/spellcheck.c

Modified: trunk/spellcheck/ChangeLog
===================================================================
--- trunk/spellcheck/ChangeLog	2008-06-19 21:20:36 UTC (rev 73)
+++ trunk/spellcheck/ChangeLog	2008-06-19 21:34:52 UTC (rev 74)
@@ -1,3 +1,15 @@
+2008-06-19  Enrico Tröger  <enrico(dot)troeger(at)uvena(dot)de>
+
+ * README, configure.in, src/Makefile.am, src/spellcheck.c:
+   Use enchant as spell check engine instead of Aspell.
+   Highlight only misspelled instead of whole the line.
+   Add Tools->Spellcheck sub menu to easily change the language
+   to use for spell checking.
+   Add "check as you type" feature.
+   Change doc pointer usage.
+   Update Readme.
+
+
 2008-06-03  Enrico Tröger  <enrico(dot)troeger(at)uvena(dot)de>
 
  * src/spellcheck.c:

Modified: trunk/spellcheck/README
===================================================================
--- trunk/spellcheck/README	2008-06-19 21:20:36 UTC (rev 73)
+++ trunk/spellcheck/README	2008-06-19 21:34:52 UTC (rev 74)
@@ -1,34 +1,63 @@
 Spell Check Plugin
-------------------
+==================
 
+.. contents::
 
+
 About
 -----
 This plugin checks the content of the current document in Geany with the spell
-check program Aspell. You can also select a certain text passage, then the
+check library Enchant. You can also select a certain text passage, then the
 plugin will only check the selected text. All lines with misspelled words
 are highlighted with a red squiggly underline and the wrong words are printed
 in the messages window at the bottom of Geany together with available
-suggestions. For the plugin to work at all, you need to have the Aspell
-library installed together with at least one dictionary.
+suggestions. For the plugin to work at all, you need to have the Enchant
+library installed together with at least one backend (Aspell, Myspell,
+Hunspell, ...).
 The plugin's configure dialog lists all available languages/dictionaries
 which can be used for the spell check.
 
 
+Downloads
+---------
+
+Spellcheck 0.1
+^^^^^^^^^^^^^^
+
+http://geany-plugins.sourceforge.net/spellcheck/spellcheck-0.1.tar.bz2
+
+http://geany-plugins.sourceforge.net/spellcheck/spellcheck-0.1.tar.bz2.sig (GPG-Signature)
+
+or
+
+http://geany-plugins.sourceforge.net/spellcheck/spellcheck-0.1.tar.gz
+
+http://geany-plugins.sourceforge.net/spellcheck/spellcheck-0.1.tar.gz.sig (GPG-Signature)
+
+
+Development Code
+^^^^^^^^^^^^^^^^
+
+Get the code from:
+
+	svn checkout http://geany-plugins.svn.sourceforge.net/svnroot/geany-plugins/trunk/spellcheck
+
+
 Requirements
 ------------
 For compiling the plugin yourself, you will need the GTK (>= 2.6.0) libraries
 and header files. You will also need its dependency libraries and header
 files, such as Pango, Glib and ATK. All these files are available at
 http://www.gtk.org.
-Furthermore, you need to have installed libaspell with its development files.
+Furthermore, you need to have installed enchant with its development files
+(libenchant-dev or enchant-devel).
 
 And obviously, you will need have Geany installed. If you have Geany installed
 from the sources, you should be ready to go.
 If you used a preprared package e.g. from your distribution you probably need
 to install an additional package, this might be called geany-dev or geany-devel.
 Please note that in order to compile and use this plugin, you need a current SVN
-version of Geany, at least SVN >= r2590.
+version of Geany, at least SVN >= 2715.
 
 Furthermore you need, of course, a C compiler and the Make tool.
 The GNU versions of these tools are recommended.
@@ -68,3 +97,4 @@
 Ideas, questions, patches and bug reports
 -----------------------------------------
 Send it to me at enrico(dot)troeger(at)uvena(dot)de
+or report them at http://sourceforge.net/tracker/?group_id=222729.

Modified: trunk/spellcheck/configure.in
===================================================================
--- trunk/spellcheck/configure.in	2008-06-19 21:20:36 UTC (rev 73)
+++ trunk/spellcheck/configure.in	2008-06-19 21:34:52 UTC (rev 74)
@@ -16,20 +16,18 @@
 
 # checking for Geany
 PKG_CHECK_MODULES(GEANY, [geany >= 0.15])
-AC_SUBST(GTK_CFLAGS)
-AC_SUBST(GTK_LIBS)
+AC_SUBST(GEANY_CFLAGS)
+AC_SUBST(GEANY_LIBS)
 
+# checking for libenchant
+PKG_CHECK_MODULES(ENCHANT, [enchant >= 1.3])
+AC_SUBST(ENCHANT_CFLAGS)
+AC_SUBST(ENCHANT_LIBS)
+
 GEANY_VERSION=`$PKG_CONFIG --modversion geany`
 GTK_VERSION=`$PKG_CONFIG --modversion gtk+-2.0`
+ENCHANT_VERSION=`$PKG_CONFIG --modversion enchant`
 
-# checking for aspell
-AC_CHECK_HEADER(aspell.h, [AC_DEFINE(HAVE_ASPELL_H, 1,
-   [Define to 1 if you have the <aspell.h> header file.] )],
-      [AC_MSG_ERROR([You must have the aspell dev library installed to build the spellcheck plugin.]) ] )
-AC_CHECK_LIB(aspell, new_aspell_speller, SPELLER_LIB="-laspell",
-      [AC_MSG_ERROR([You must have the aspell dev library installed to build the spellcheck plugin.]) ] )
-AC_SUBST(SPELLER_LIB)
-
 # i18n
 GETTEXT_PACKAGE=spellcheck
 AC_SUBST(GETTEXT_PACKAGE)
@@ -65,6 +63,7 @@
 echo "Install Spell Check files in         : ${prefix}"
 echo "Using Geany version                  : ${GEANY_VERSION}"
 echo "Using GTK version                    : ${GTK_VERSION}"
+echo "Using libenchant version             : ${ENCHANT_VERSION}"
 echo ""
 echo "Configuration is done OK."
 echo ""

Modified: trunk/spellcheck/src/Makefile.am
===================================================================
--- trunk/spellcheck/src/Makefile.am	2008-06-19 21:20:36 UTC (rev 73)
+++ trunk/spellcheck/src/Makefile.am	2008-06-19 21:34:52 UTC (rev 74)
@@ -2,6 +2,6 @@
 lib_LTLIBRARIES = spellcheck.la
 spellcheck_la_SOURCES = spellcheck.c
 spellcheck_la_LDFLAGS = -module -avoid-version
-spellcheck_la_LIBADD  = @GEANY_LIBS@  $(INTLLIBS) @SPELLER_LIB@
+spellcheck_la_LIBADD  = @GEANY_LIBS@  $(INTLLIBS) @ENCHANT_LIBS@
 
-AM_CFLAGS = @GEANY_CFLAGS@ -DLOCALEDIR=\""$(localedir)"\"
+AM_CFLAGS = @GEANY_CFLAGS@ @ENCHANT_CFLAGS@ -DLOCALEDIR=\""$(localedir)"\"

Modified: trunk/spellcheck/src/spellcheck.c
===================================================================
--- trunk/spellcheck/src/spellcheck.c	2008-06-19 21:20:36 UTC (rev 73)
+++ trunk/spellcheck/src/spellcheck.c	2008-06-19 21:34:52 UTC (rev 74)
@@ -35,7 +35,7 @@
 #endif
 
 #include <string.h>
-#include <aspell.h>
+#include <enchant.h>
 
 #include "plugindata.h"
 
@@ -54,11 +54,24 @@
 GeanyFunctions	*geany_functions;
 
 
-PLUGIN_VERSION_CHECK(67)
+PLUGIN_VERSION_CHECK(71)
 PLUGIN_SET_INFO(_("Spell Check"), _("Checks the spelling of the current document."), "0.2",
 			_("The Geany developer team"))
 
 
+typedef struct
+{
+	gchar *config_file;
+	gchar *default_language;
+	gboolean check_while_typing;
+	gulong signal_id;
+	GPtrArray *dicts;
+	EnchantBroker *broker;
+	EnchantDict *dict;
+} SpellCheck;
+static SpellCheck *sc;
+
+
 /* Keybinding(s) */
 enum
 {
@@ -67,183 +80,260 @@
 };
 PLUGIN_KEY_GROUP(spellcheck, KB_COUNT)
 
-static gchar *config_file;
-static gchar *language;
 
 
+/* currently unused */
+#ifdef G_OS_WIN32
+#warning TODO check Windows support
 /* On Windows we need to find the Aspell installation prefix via the Windows Registry
  * and then set the prefix in the Aspell config object. */
 static void set_up_aspell_prefix(AspellConfig *config)
 {
-#ifdef G_OS_WIN32
 	char sTemp[1024];
 	HKEY hkey;
 	DWORD len = sizeof(sTemp);
-	
+
 	if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT("SOFTWARE\\Aspell"), 0,
 			KEY_QUERY_VALUE, &hkey) != ERROR_SUCCESS)
 		return;
-	
+
 	if (RegQueryValueEx(hkey, NULL, 0, NULL, (LPBYTE)sTemp, &len) == ERROR_SUCCESS)
 		aspell_config_replace(config, "prefix", sTemp);
 
 	RegCloseKey(hkey);
+}
 #endif
+
+
+static void dict_describe(const gchar* const lang, const gchar* const name,
+						  const gchar* const desc, const gchar* const file, void *target)
+{
+	gchar **result = (gchar**) target;
+	*result = g_strdup_printf("\"%s\" (%s)", lang, name);
 }
 
 
-static void print_word_list(AspellSpeller *speller, GString *str, const AspellWordList *wl)
+static gint check_word(GeanyDocument *doc, gint line_number, GString *str, gint end_pos)
 {
-	if (wl == NULL)
+	gsize j;
+	gsize n_suggs = 0;
+	gchar **suggs;
+	gchar *word;
+
+	/* early out if the word is spelled correctly */
+	if (enchant_dict_check(sc->dict, str->str, -1) == 0)
+		return 0;
+
+	word = g_strdup(str->str);
+	g_string_erase(str, 0, str->len);
+	suggs = enchant_dict_suggest(sc->dict, word, -1, &n_suggs);
+
+	if (suggs != NULL)
 	{
-		g_string_append_c(str, '?');
-	}
-	else
-	{
-		AspellStringEnumeration *els = aspell_word_list_elements(wl);
-		const char *word;
+		g_string_append_printf(str, "line %d: %s | ",  line_number + 1, word);
 
-		while ((word = aspell_string_enumeration_next(els)) != 0)
+		g_string_append(str, _("Try: "));
+
+		/* Now find the misspellings in the line, limit suggestions to a maximum of 15 (for now) */
+		for (j = 0; j < MIN(n_suggs, 15); j++)
 		{
-			g_string_append(str, word);
+			g_string_append(str, suggs[j]);
 			g_string_append_c(str, ' ');
 		}
-		delete_aspell_string_enumeration(els);
+
+		p_editor->set_indicator(doc, end_pos - strlen(word), end_pos);
+		p_msgwindow->msg_add(COLOR_RED, line_number + 1, doc, "%s", str->str);
+
+		if (suggs != NULL && n_suggs)
+			enchant_dict_free_string_list(sc->dict, suggs);
 	}
+	g_free(word);
+
+	return n_suggs;
 }
 
 
-static gint check_document(AspellSpeller *speller, gint idx)
+static gint process_line(GeanyDocument *doc, gint line_number, const gchar *line)
 {
-	AspellCanHaveError *ret;
-	AspellDocumentChecker *checker;
-	AspellToken token;
-	gchar *word_begin;
+	gint end_pos, char_len;
+	gunichar c;
+	GString *str = g_string_sized_new(256);
+	gint suggestions_found = 0;
+
+	end_pos = p_sci->get_position_from_line(doc->sci, line_number);
+	/* split line into words */
+	while ((c = g_utf8_get_char_validated(line, -1)) != (gunichar) -1 && c != 0)
+	{
+		if (g_unichar_isalpha(c) || c == '\'')
+		{
+			/* part of a word */
+			g_string_append_unichar(str, c);
+		}
+		else if (str->len > 0)
+		{
+			g_string_append_c(str, '\0');
+			suggestions_found += check_word(doc, line_number, str, end_pos);
+			g_string_erase(str, 0, str->len);
+		}
+
+		/* calculate byte len of c and add skip these in line */
+		char_len = g_unichar_to_utf8(c, NULL);
+		line += char_len;
+		end_pos += char_len;
+	}
+	g_string_free(str, TRUE);
+
+	return suggestions_found;
+}
+
+
+static void check_document(GeanyDocument *doc)
+{
 	gchar *line;
-	gint linewidth;
 	gint i;
 	gint first_line, last_line;
-	gboolean suggestions_found = FALSE;
-	GString *str = g_string_sized_new(1024);
+	gchar *dict_string = NULL;
+	gint suggestions_found = 0;
 
-	if (! DOC_IDX_VALID(idx))
-	return 1;
+	enchant_dict_describe(sc->dict, dict_describe, &dict_string);
 
-	str = g_string_sized_new(1024);
-	if (p_sci->can_copy(documents[idx]->sci))
+	if (p_sci->can_copy(doc->sci))
 	{
-		first_line = p_sci->get_line_from_position(
-			documents[idx]->sci, p_sci->get_selection_start(documents[idx]->sci));
-		last_line = p_sci->get_line_from_position(
-			documents[idx]->sci, p_sci->get_selection_end(documents[idx]->sci));
+		first_line = p_sci->get_line_from_position(doc->sci, p_sci->get_selection_start(doc->sci));
+		last_line = p_sci->get_line_from_position(doc->sci, p_sci->get_selection_end(doc->sci));
 
-		p_msgwindow->msg_add(COLOR_BLUE, -1, -1,
-			_("Checking file \"%s\" (lines %d to %d):"),
-			DOC_FILENAME(idx), first_line + 1, last_line + 1);
+		p_msgwindow->msg_add(COLOR_BLUE, -1, NULL,
+			_("Checking file \"%s\" (lines %d to %d using %s):"),
+			DOC_FILENAME(doc), first_line + 1, last_line + 1, dict_string);
 	}
 	else
 	{
 		first_line = 0;
-		last_line = p_sci->get_line_count(documents[idx]->sci);
-		p_msgwindow->msg_add(COLOR_BLUE, -1, -1, _("Checking file \"%s\":"),
-			DOC_FILENAME(idx));
+		last_line = p_sci->get_line_count(doc->sci);
+		p_msgwindow->msg_add(COLOR_BLUE, -1, NULL, _("Checking file \"%s\" (using %s):"),
+			DOC_FILENAME(doc), dict_string);
 	}
+	g_free(dict_string);
 
-	/* Set up the document checker */
-	ret = new_aspell_document_checker(speller);
-	if (aspell_error(ret) != 0)
+	for (i = first_line; i < last_line; i++)
 	{
-		g_warning("spellcheck: %s", aspell_error_message(ret));
-		return 4;
-	}
+		line = p_sci->get_line(doc->sci, i);
 
-	checker = to_aspell_document_checker(ret);
+		suggestions_found += process_line(doc, i, line);
 
-	for (i = first_line; i < last_line; i++)
-	{
-		line = p_sci->get_line(documents[idx]->sci, i);
-		linewidth = strlen(line);
+		g_free(line);
+	}
 
-		/* First process the line */
-		aspell_document_checker_process(checker, line, -1);
+	if (suggestions_found == 0)
+		p_msgwindow->msg_add(COLOR_BLUE, -1, NULL, _("The checked text is spelled correctly."));
+}
 
-		/* Now find the misspellings in the line */
-		while (token = aspell_document_checker_next_misspelling(checker), token.len != 0)
-		{
-			p_editor->set_indicator_on_line(idx, i);
-			/* Print out the misspelling and possible replacements */
-			word_begin = line + token.offset;
 
-			g_string_append_printf (str, "line %d: ",  i + 1);
-			g_string_append_len(str, word_begin, token.len);
+static void broker_init_failed()
+{
+	const gchar *err = enchant_broker_get_error(sc->broker);
+	p_dialogs->show_msgbox(GTK_MESSAGE_ERROR,
+		_("The Enchant library couldn't be initialized (%s)."),
+		(err != NULL) ? err : _("unknown error"));
+}
 
-			g_string_append(str, " | ");
-			g_string_append(str, _("Try: "));
-			print_word_list(speller, str, aspell_speller_suggest(speller, word_begin, token.len));
 
-			p_msgwindow->msg_add(COLOR_RED, i + 1, idx, "%s", str->str);
-			g_string_erase(str, 0, str->len);
-			suggestions_found = TRUE;
-		}
+static void perform_check(GeanyDocument *doc)
+{
+	p_editor->clear_indicators(doc);
+	p_msgwindow->clear_tab(MSG_MESSAGE);
+	p_msgwindow->switch_tab(MSG_MESSAGE, FALSE);
 
-		g_free(line);
-	}
+	check_document(doc);
+}
 
-	delete_aspell_document_checker(checker);
 
-	g_string_free(str, TRUE);
+static void clear_indicators_on_line(GeanyDocument *doc, gint line_number)
+{
+	glong start_pos, length;
 
-	if (! suggestions_found)
-		p_msgwindow->msg_add(COLOR_BLUE, -1, -1, _("The checked text is spelled correctly."));
+	g_return_if_fail(doc != NULL);
 
-	return 0;
+	start_pos = p_sci->get_position_from_line(doc->sci, line_number);
+	length = p_sci->get_line_length(doc->sci, line_number);
+	if (length > 0)
+	{
+		p_sci->send_message(doc->sci, SCI_STARTSTYLING, start_pos, INDIC2_MASK);
+		p_sci->send_message(doc->sci, SCI_SETSTYLING, length, 0);
+	}
 }
 
 
-static void perform_check(gint idx)
+/* Checks only the last word before the current cursor position -> check as you type. */
+static gboolean on_key_release(GtkWidget *widget, GdkEventKey *ev, gpointer user_data)
 {
-	AspellCanHaveError *ret;
-	AspellSpeller *speller;
-	AspellConfig *config;
+	gint line_number;
+	GString *str = g_string_sized_new(256);
+	gchar *line;
+	GeanyDocument *doc;
+	static time_t time_prev = 0;
+	time_t time_now = time(NULL);
 
-	if (! DOC_IDX_VALID(idx))
-		return;
+	if (! sc->check_while_typing)
+		return FALSE;
+	/* check only once a second */
+	if (time_now == time_prev)
+		return FALSE;
+	/* set current time for the next key press */
+	time_prev = time_now;
 
-	p_editor->clear_indicators(idx);
-	p_msgwindow->clear_tab(MSG_MESSAGE);
-	p_msgwindow->switch_tab(MSG_MESSAGE, FALSE);
+	doc = p_document->get_current();
+	if (doc == NULL)
+		return FALSE;
 
-	config = new_aspell_config();
-	aspell_config_replace(config, "lang", language);
-	aspell_config_replace(config, "encoding", "utf-8");
-	set_up_aspell_prefix(config);
+	if (ev->keyval == '\r' &&
+		p_sci->send_message(doc->sci, SCI_GETEOLMODE, 0, 0) == SC_EOL_CRLF)
+	{	/* prevent double line checking */
+		return FALSE;
+	}
 
-	ret = new_aspell_speller(config);
-	delete_aspell_config(config);
-	if (aspell_error(ret) != 0)
+	line_number = p_sci->get_current_line(doc->sci);
+	if (ev->keyval == '\n' || ev->keyval == '\r')
+		line_number--; /* check previous line if we start a new one */
+	line = p_sci->get_line(doc->sci, line_number);
+
+	clear_indicators_on_line(doc, line_number);
+	if (process_line(doc, line_number, line) != 0)
 	{
-		delete_aspell_can_have_error(ret);
-		return;
+		p_msgwindow->switch_tab(MSG_MESSAGE, FALSE);
 	}
 
-	speller = to_aspell_speller(ret);
-	check_document(speller, idx);
-	delete_aspell_speller(speller);
+	g_string_free(str, TRUE);
+	g_free(line);
+
+	return FALSE;
 }
 
 
-static void
-item_activate(GtkMenuItem *menuitem, gpointer gdata)
+static void on_menu_item_activate(GtkMenuItem *menuitem, gpointer gdata)
 {
-	gint idx = p_document->get_cur_idx();
+	GeanyDocument *doc = p_document->get_current();
 
-	perform_check(idx);
+	/* Another language was chosen from the menu item, so make it default for this session. */
+    if (gdata != NULL)
+		setptr(sc->default_language, g_strdup(gdata));
+
+	/* Request new dict object */
+	enchant_broker_free_dict(sc->broker, sc->dict);
+	sc->dict = enchant_broker_request_dict(sc->broker, sc->default_language);
+	if (sc->dict == NULL)
+	{
+		broker_init_failed();
+		return;
+	}
+
+	perform_check(doc);
 }
 
 
 static void kb_activate(guint key_id)
 {
-	item_activate(NULL, NULL);
+	on_menu_item_activate(NULL, NULL);
 }
 
 
@@ -259,7 +349,7 @@
 #ifdef G_OS_WIN32
 	gchar *install_dir = g_win32_get_package_installation_directory("geany", NULL);
 	/* e.g. C:\Program Files\geany\lib\locale */
-	locale_dir = g_strconcat(install_dir, "\\lib\\locale", NULL);
+	locale_dir = g_strconcat(install_dir, "\\share\\locale", NULL);
 	g_free(install_dir);
 #else
 	locale_dir = g_strdup(LOCALEDIR);
@@ -285,34 +375,34 @@
 	}
 	else
 		lang = "en";
-	
+
 	return lang;
 }
 
 
-static void fill_dicts_combo(GtkComboBox *combo)
+static void add_dict_array(const gchar* const lang_tag, const gchar* const provider_name,
+						   const gchar* const provider_desc, const gchar* const provider_file,
+						   gpointer user_data)
 {
-	AspellConfig *config;
-	AspellDictInfoList *dlist;
-	AspellDictInfoEnumeration *dels;
-	const AspellDictInfo *entry;
-	guint i = 0;
+	guint i;
+	gchar *result = g_strdup(lang_tag);
 
-	config = new_aspell_config();
-	set_up_aspell_prefix(config);
-	dlist = get_aspell_dict_info_list(config);
-	delete_aspell_config(config);
+	/* 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] = '_';
+	}
 
-	dels = aspell_dict_info_list_elements(dlist);
-	while ((entry = aspell_dict_info_enumeration_next(dels)) != 0)
+	/* find duplicates and skip them */
+	for (i = 0; i < sc->dicts->len; i++)
 	{
-		gtk_combo_box_append_text(combo, entry->name);
+		if (p_utils->str_equal(g_ptr_array_index(sc->dicts, i), result))
+			return;
+	}
 
-		if (p_utils->str_equal(entry->name, language))
-			gtk_combo_box_set_active(GTK_COMBO_BOX(combo), i);
-		i++;
-	}
-	delete_aspell_dict_info_enumeration(dels);
+	g_ptr_array_add(sc->dicts, result);
 }
 
 
@@ -322,14 +412,18 @@
 	{
 		GKeyFile *config = g_key_file_new();
 		gchar *data;
-		gchar *config_dir = g_path_get_dirname(config_file);
+		gchar *config_dir = g_path_get_dirname(sc->config_file);
 
-		setptr(language, gtk_combo_box_get_active_text(GTK_COMBO_BOX(
+		setptr(sc->default_language, gtk_combo_box_get_active_text(GTK_COMBO_BOX(
 			g_object_get_data(G_OBJECT(dialog), "combo"))));
 
-		g_key_file_load_from_file(config, config_file, G_KEY_FILE_NONE, NULL);
-		g_key_file_set_string(config, "spellcheck", "language", language);
+		sc->check_while_typing = (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(
+			g_object_get_data(G_OBJECT(dialog), "check"))));
 
+		g_key_file_load_from_file(config, sc->config_file, G_KEY_FILE_NONE, NULL);
+		g_key_file_set_string(config, "spellcheck", "language", sc->default_language);
+		g_key_file_set_boolean(config, "spellcheck", "check_while_typing", sc->check_while_typing);
+
 		if (! g_file_test(config_dir, G_FILE_TEST_IS_DIR) && p_utils->mkdir(config_dir, TRUE) != 0)
 		{
 			p_dialogs->show_msgbox(GTK_MESSAGE_ERROR,
@@ -339,7 +433,7 @@
 		{
 			/* write config to file */
 			data = g_key_file_to_data(config, NULL, NULL);
-			p_utils->write_file(config_file, data);
+			p_utils->write_file(sc->config_file, data);
 			g_free(data);
 		}
 		g_free(config_dir);
@@ -348,28 +442,92 @@
 }
 
 
+static gint sort_dicts(gconstpointer a, gconstpointer b)
+{	/* casting mania ;-) */
+	return strcmp((gchar*)((GPtrArray*)a)->pdata, (gchar*)((GPtrArray*)b)->pdata);
+}
+
+
+static void create_dicts_array()
+{
+	sc->dicts = g_ptr_array_new();
+
+	enchant_broker_list_dicts(sc->broker, add_dict_array, sc->dicts);
+
+	g_ptr_array_sort(sc->dicts, sort_dicts);
+}
+
+
+static GtkWidget *create_menu()
+{
+	GtkWidget *sp_item, *menu, *subitem;
+	guint i;
+
+	sp_item = gtk_menu_item_new_with_mnemonic(_("_Spell Check"));
+	gtk_container_add(GTK_CONTAINER(main_widgets->tools_menu), sp_item);
+
+	menu = gtk_menu_new();
+	gtk_menu_item_set_submenu(GTK_MENU_ITEM(sp_item), menu);
+
+	subitem = gtk_menu_item_new_with_mnemonic(_("Default"));
+	gtk_container_add(GTK_CONTAINER(menu), subitem);
+	g_signal_connect((gpointer) subitem, "activate", G_CALLBACK(on_menu_item_activate), NULL);
+
+	subitem = gtk_separator_menu_item_new();
+	gtk_container_add(GTK_CONTAINER(menu), subitem);
+
+	for (i = 0; i < sc->dicts->len; i++)
+	{
+		GtkWidget *menu_item;
+
+		menu_item = gtk_menu_item_new_with_label(g_ptr_array_index(sc->dicts, i));
+		gtk_container_add(GTK_CONTAINER(menu), menu_item);
+		g_signal_connect((gpointer) menu_item, "activate",
+			G_CALLBACK(on_menu_item_activate), g_ptr_array_index(sc->dicts, i));
+	}
+
+	return sp_item;
+}
+
+
 void plugin_init(GeanyData *data)
 {
 	GtkWidget *sp_item;
 	GKeyFile *config = g_key_file_new();
 
-	config_file = g_strconcat(app->configdir, G_DIR_SEPARATOR_S, "plugins", G_DIR_SEPARATOR_S,
+	sc = g_new0(SpellCheck, 1);
+
+	sc->config_file = g_strconcat(app->configdir, G_DIR_SEPARATOR_S, "plugins", G_DIR_SEPARATOR_S,
 		"spellcheck", G_DIR_SEPARATOR_S, "spellcheck.conf", NULL);
 
-	g_key_file_load_from_file(config, config_file, G_KEY_FILE_NONE, NULL);
-	language = p_utils->get_setting_string(config, "spellcheck", "language", get_default_lang());
+	g_key_file_load_from_file(config, sc->config_file, G_KEY_FILE_NONE, NULL);
+	sc->default_language = p_utils->get_setting_string(config,
+		"spellcheck", "language", get_default_lang());
+	sc->check_while_typing = p_utils->get_setting_boolean(config,
+		"spellcheck", "check_while_typing", FALSE);
 	g_key_file_free(config);
 
 	locale_init();
 
-	sp_item = gtk_menu_item_new_with_mnemonic(_("_Spell Check"));
-	gtk_widget_show(sp_item);
-	gtk_container_add(GTK_CONTAINER(main_widgets->tools_menu), sp_item);
-	g_signal_connect(G_OBJECT(sp_item), "activate", G_CALLBACK(item_activate), NULL);
+	sc->broker = enchant_broker_init();
+	sc->dict = enchant_broker_request_dict(sc->broker, sc->default_language);
+	if (sc->dict == NULL)
+	{
+		broker_init_failed();
+		return;
+	}
 
+	create_dicts_array();
+
+	sp_item = create_menu();
+	gtk_widget_show_all(sp_item);
+
 	plugin_fields->menu_item = sp_item;
 	plugin_fields->flags = PLUGIN_IS_DOCUMENT_SENSITIVE;
 
+	sc->signal_id = g_signal_connect(main_widgets->window,
+		"key-release-event", G_CALLBACK(on_key_release), NULL);
+
 	/* setup keybindings */
 	p_keybindings->set_item(plugin_key_group, KB_SPELL_CHECK, kb_activate,
 		0, 0, "spell_check", _("Run Spell Check"), NULL);
@@ -378,7 +536,8 @@
 
 GtkWidget *plugin_configure(GtkDialog *dialog)
 {
-	GtkWidget *label, *vbox, *combo;
+	GtkWidget *label, *vbox, *combo, *check;
+	guint i;
 
 	vbox = gtk_vbox_new(FALSE, 6);
 
@@ -387,12 +546,27 @@
 	gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
 
 	combo = gtk_combo_box_new_text();
-	fill_dicts_combo(GTK_COMBO_BOX(combo));
 
-	gtk_combo_box_set_wrap_width(GTK_COMBO_BOX(combo), 3);
+	for (i = 0; i < sc->dicts->len; i++)
+	{
+		gtk_combo_box_append_text(GTK_COMBO_BOX(combo), g_ptr_array_index(sc->dicts, i));
+
+		if (p_utils->str_equal(g_ptr_array_index(sc->dicts, i), sc->default_language))
+			gtk_combo_box_set_active(GTK_COMBO_BOX(combo), i);
+	}
+
+	if (sc->dicts->len > 20)
+		gtk_combo_box_set_wrap_width(GTK_COMBO_BOX(combo), 3);
+	else if (sc->dicts->len > 10)
+		gtk_combo_box_set_wrap_width(GTK_COMBO_BOX(combo), 2);
 	gtk_box_pack_start(GTK_BOX(vbox), combo, FALSE, FALSE, 0);
 
+	check = gtk_check_button_new_with_label(_("Check spelling while typing"));
+	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), sc->check_while_typing);
+	gtk_box_pack_start(GTK_BOX(vbox), check, FALSE, FALSE, 0);
+
 	g_object_set_data(G_OBJECT(dialog), "combo", combo);
+	g_object_set_data(G_OBJECT(dialog), "check", check);
 	g_signal_connect(dialog, "response", G_CALLBACK(on_configure_response), NULL);
 
 	gtk_widget_show_all(vbox);
@@ -403,7 +577,20 @@
 
 void plugin_cleanup(void)
 {
-	g_free(language);
-	g_free(config_file);
+	guint i;
+	for (i = 0; i < sc->dicts->len; i++)
+	{
+		g_free(g_ptr_array_index(sc->dicts, i));
+	}
+	g_ptr_array_free(sc->dicts, TRUE);
+
+	g_signal_handler_disconnect(main_widgets->window, sc->signal_id);
+
+	enchant_broker_free_dict(sc->broker, sc->dict);
+	enchant_broker_free(sc->broker);
+
+	g_free(sc->default_language);
+	g_free(sc->config_file);
+	g_free(sc);
 	gtk_widget_destroy(plugin_fields->menu_item);
 }


This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.



More information about the Plugins-Commits mailing list