summaryrefslogtreecommitdiff
path: root/pico/osdep/mswin_spell.c
diff options
context:
space:
mode:
authorEduardo Chappa <echappa@gmx.com>2013-02-03 00:59:38 -0700
committerEduardo Chappa <echappa@gmx.com>2013-02-03 00:59:38 -0700
commit094ca96844842928810f14844413109fc6cdd890 (patch)
treee60efbb980f38ba9308ccb4fb2b77b87bbc115f3 /pico/osdep/mswin_spell.c
downloadalpine-094ca96844842928810f14844413109fc6cdd890.tar.xz
Initial Alpine Version
Diffstat (limited to 'pico/osdep/mswin_spell.c')
-rw-r--r--pico/osdep/mswin_spell.c547
1 files changed, 547 insertions, 0 deletions
diff --git a/pico/osdep/mswin_spell.c b/pico/osdep/mswin_spell.c
new file mode 100644
index 00000000..1379beb6
--- /dev/null
+++ b/pico/osdep/mswin_spell.c
@@ -0,0 +1,547 @@
+/*
+ * ========================================================================
+ * MSWIN_SPELL.C
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include "../headers.h"
+
+#include "mswin_aspell.h"
+#include "mswin_spell.h"
+
+#include <windowsx.h>
+
+typedef struct WORD_INFO
+{
+ ASPELLINFO *aspellinfo; // Aspell information
+
+ const char *utf8_err_message; // Error message, if any
+
+ char *word_utf8; // utf-8
+ LPTSTR word_lptstr;
+
+ int word_doto; // UCS
+ LINE *word_dotp; //
+ int word_size; //
+} WORD_INFO;
+
+extern HINSTANCE ghInstance;
+extern HWND ghTTYWnd;
+
+/*
+ * Function prototypes
+ */
+static int get_next_bad_word(WORD_INFO *word_info);
+static void free_word_info_words(WORD_INFO *word_info);
+static INT_PTR CALLBACK spell_dlg_proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
+
+/*
+ * spell() - check for potentially missspelled words and offer them for
+ * correction. Microsoft Windows specific version.
+ */
+int
+spell(int f, int n)
+{
+ int w_marko_bak;
+ LINE *w_markp_bak;
+ WORD_INFO word_info;
+ ASPELLINFO *aspellinfo;
+
+ emlwrite(_("Checking spelling..."), NULL); /* greetings! */
+
+ memset(&word_info, 0, sizeof(WORD_INFO));
+
+ aspellinfo = speller_init(NULL);
+ if(!aspellinfo ||
+ (word_info.utf8_err_message = speller_get_error_message(aspellinfo)))
+ {
+ if(word_info.utf8_err_message)
+ emlwrite((char *)word_info.utf8_err_message, NULL); /* sad greetings! */
+ else
+ emlwrite(_("Spelling: initializing Aspell failed"), NULL);
+
+ speller_close(aspellinfo);
+ return -1;
+ }
+
+ // Back up current mark.
+ w_marko_bak = curwp->w_marko;
+ w_markp_bak = curwp->w_markp;
+
+ setimark(0, 1);
+ gotobob(0, 1);
+
+ word_info.aspellinfo = aspellinfo;
+
+ if(get_next_bad_word(&word_info))
+ {
+ DialogBoxParam(ghInstance, MAKEINTRESOURCE(DLG_SPELL), ghTTYWnd,
+ spell_dlg_proc, (LPARAM)&word_info);
+ }
+
+ // Restore original mark if possible.
+ if(curwp->w_markp)
+ {
+ // Clear any set marks.
+ setmark(0, 1);
+
+ if(w_markp_bak && llength(w_markp_bak))
+ {
+ // Make sure we don't set mark off the line.
+ if(w_marko_bak >= llength(w_markp_bak))
+ w_marko_bak = llength(w_markp_bak) - 1;
+
+ curwp->w_marko = w_marko_bak;
+ curwp->w_markp = w_markp_bak;
+ }
+ }
+
+ if(word_info.utf8_err_message)
+ emlwrite((char *)word_info.utf8_err_message, NULL);
+ else
+ emlwrite(_("Done checking spelling"), NULL);
+
+ speller_close(aspellinfo);
+ free_word_info_words(&word_info);
+
+ swapimark(0, 1);
+ curwp->w_flag |= WFHARD|WFMODE;
+
+ update();
+ return 1;
+}
+
+/*
+ * handle_cb_editchange() - combobox contents have changed so enable/disable
+ * the Change and Change All buttons accordingly.
+ */
+static void
+handle_cb_editchange(HWND hDlg, HWND hwnd_combo)
+{
+ int text_len = ComboBox_GetTextLength(hwnd_combo);
+
+ Button_Enable(GetDlgItem(hDlg, ID_BUTTON_CHANGE), !!text_len);
+ Button_Enable(GetDlgItem(hDlg, ID_BUTTON_CHANGEALL), !!text_len);
+}
+
+/*
+ * spell_dlg_proc_set_word() - Set up the dialog to display spelling
+ * information and suggestions for the current word.
+ */
+static void
+spell_dlg_proc_set_word(HWND hDlg, WORD_INFO *word_info)
+{
+ TCHAR dlg_title[256];
+ HWND hwnd_combo = GetDlgItem(hDlg, ID_COMBO_SUGGESTIONS);
+
+ // Clear the combobox.
+ ComboBox_ResetContent(hwnd_combo);
+
+ // Set the dialog box title
+ _sntprintf(dlg_title, ARRAYSIZE(dlg_title), TEXT("Not in Dictionary: %s"),
+ word_info->word_lptstr);
+ dlg_title[ARRAYSIZE(dlg_title) - 1] = 0;
+ SetWindowText(hDlg, dlg_title);
+
+ // Populate the combobox with suggestions
+ if(speller_suggestion_init(word_info->aspellinfo, word_info->word_utf8, -1))
+ {
+ const char *suggestion_utf8;
+
+ while(suggestion_utf8 = speller_suggestion_getnext(word_info->aspellinfo))
+ {
+ LPTSTR suggestion_lptstr = utf8_to_lptstr((LPSTR)suggestion_utf8);
+
+ if(suggestion_lptstr)
+ {
+ ComboBox_AddString(hwnd_combo, suggestion_lptstr);
+ fs_give((void **)&suggestion_lptstr);
+ }
+ }
+
+ // Select the first one.
+ ComboBox_SetCurSel(hwnd_combo, 0);
+
+ speller_suggestion_close(word_info->aspellinfo);
+ }
+
+ handle_cb_editchange(hDlg, hwnd_combo);
+}
+
+/*
+ * spell_dlg_next_word() - Move to the next misspelled word and display
+ * the information for it. If no more bad words, kill the dialog.
+ */
+static void
+spell_dlg_next_word(HWND hDlg, WORD_INFO *word_info)
+{
+ if(!get_next_bad_word(word_info))
+ {
+ EndDialog(hDlg, 0);
+ return;
+ }
+
+ spell_dlg_proc_set_word(hDlg, word_info);
+}
+
+/*
+ * chword_n() - change the given word_len pointed to by the curwp->w_dot
+ * pointers to the word in cb
+ */
+static void
+chword_n(int wb_len, UCS *cb)
+{
+ ldelete(wb_len, NULL); /* not saved in kill buffer */
+
+ while(*cb != '\0')
+ linsert(1, *cb++);
+
+ curwp->w_flag |= WFEDIT;
+}
+
+/*
+ * replace_all() - replace all instances of oldword with newword from
+ * the current '.' on. Mark is where curwp will get restored to.
+ */
+static void
+replace_all(UCS *oldword, int oldword_len, UCS *newword)
+{
+ int wrap;
+
+ // Mark should be at "." right now - ie the start of the word we're
+ // checking. We're going to use mark below to restore curwp since
+ // chword_n can potentially realloc LINES and it knows about markp.
+ assert(curwp->w_markp);
+ assert(curwp->w_markp == curwp->w_dotp);
+ assert(curwp->w_marko == curwp->w_doto);
+
+ curwp->w_bufp->b_mode |= MDEXACT; /* case sensitive */
+ while(forscan(&wrap, oldword, NULL, 0, 1))
+ {
+ if(wrap)
+ break; /* wrap NOT allowed! */
+
+ /*
+ * We want to minimize the number of substrings that we report
+ * as matching a misspelled word...
+ */
+ if(lgetc(curwp->w_dotp, 0).c != '>')
+ {
+ LINE *lp = curwp->w_dotp; /* for convenience */
+ int off = curwp->w_doto;
+
+ if(off == 0 || !ucs4_isalpha(lgetc(lp, off - 1).c))
+ {
+ off += oldword_len;
+
+ if(off == llength(lp) || !ucs4_isalpha(lgetc(lp, off).c))
+ {
+ // Replace this word
+ chword_n(oldword_len, newword);
+ }
+ }
+ }
+
+ forwchar(0, 1); /* move on... */
+
+ }
+ curwp->w_bufp->b_mode ^= MDEXACT; /* case insensitive */
+
+ curwp->w_dotp = curwp->w_markp;
+ curwp->w_doto = curwp->w_marko;
+}
+
+/*
+ * handle_button_change() - Someone pressed the Change or Change All button.
+ * So deal with it.
+ */
+static void
+handle_button_change(HWND hDlg, WORD_INFO *word_info, int do_replace_all)
+{
+ int str_lptstr_len;
+ TCHAR str_lptstr[128];
+
+ // Get the length of what they want to change to.
+ str_lptstr_len = ComboBox_GetText(GetDlgItem(hDlg, ID_COMBO_SUGGESTIONS),
+ str_lptstr, ARRAYSIZE(str_lptstr));
+ if(str_lptstr_len)
+ {
+ UCS *str_ucs4;
+ char *str_utf8;
+
+ // Notify the speller so it'll suggest this word next time around.
+ str_utf8 = lptstr_to_utf8(str_lptstr);
+ if(str_utf8)
+ {
+ speller_replace_word(word_info->aspellinfo,
+ word_info->word_utf8, -1, str_utf8, -1);
+ fs_give((void **)&str_utf8);
+ }
+
+ str_ucs4 = lptstr_to_ucs4(str_lptstr);
+ if(!str_ucs4)
+ {
+ // Uh oh - massive error.
+ word_info->utf8_err_message = _("Spelling: lptstr_to_ucs4() failed");
+ EndDialog(hDlg, -1);
+ return;
+ }
+
+ // Move back to start of this word.
+ curwp->w_doto = word_info->word_doto;
+ curwp->w_dotp = word_info->word_dotp;
+
+ if(do_replace_all)
+ {
+ int i;
+ int word_size;
+ UCS word[128];
+
+ word_size = word_info->word_size;
+ if(word_size >= ARRAYSIZE(word) - 1)
+ word_size = ARRAYSIZE(word) - 1;
+
+ for(i = 0; i < word_size; i++)
+ word[i] = lgetc(curwp->w_dotp, curwp->w_doto + i).c;
+
+ word[i] = 0;
+
+ replace_all(word, word_size, str_ucs4);
+
+ // Skip over the word we just inserted.
+ forwchar(FALSE, (int)ucs4_strlen(str_ucs4));
+ }
+ else
+ {
+ // Swap it with the new improved version.
+ chword_n(word_info->word_size, str_ucs4);
+ }
+ fs_give((void **)&str_ucs4);
+ }
+
+ update();
+ spell_dlg_next_word(hDlg, word_info);
+}
+
+/*
+ * center_dialog() - Center a dialog with respect to its parent.
+ */
+static void
+center_dialog(HWND hDlg)
+{
+ int dx;
+ int dy;
+ RECT rcDialog;
+ RECT rcParent;
+
+ GetWindowRect(GetParent(hDlg), &rcParent);
+ GetWindowRect(hDlg, &rcDialog);
+
+ dx = ((rcParent.right - rcParent.left) -
+ (rcDialog.right - rcDialog.left)) / 2 + rcParent.left - rcDialog.left;
+
+ dy = ((rcParent.bottom - rcParent.top) -
+ (rcDialog.bottom - rcDialog.top)) / 2 + rcParent.top - rcDialog.top;
+
+ OffsetRect(&rcDialog, dx, dy);
+
+ SetWindowPos(hDlg, NULL, rcDialog.left, rcDialog.top, 0, 0,
+ SWP_NOSIZE | SWP_NOZORDER);
+}
+
+/*
+ * spell_dlg_on_command() - Handle the WM_COMMAND stuff for the spell dialog.
+ */
+static void
+spell_dlg_on_command(HWND hDlg, int id, HWND hwndCtl, UINT codeNotify)
+{
+ WORD_INFO *word_info =
+ (WORD_INFO *)(LONG_PTR)GetWindowLongPtr(hDlg, GWLP_USERDATA);
+
+ switch(id)
+ {
+ case ID_COMBO_SUGGESTIONS:
+ if(codeNotify == CBN_EDITCHANGE)
+ handle_cb_editchange(hDlg, hwndCtl);
+ break;
+
+ case ID_BUTTON_CHANGE:
+ handle_button_change(hDlg, word_info, 0);
+ break;
+
+ case ID_BUTTON_CHANGEALL:
+ handle_button_change(hDlg, word_info, 1);
+ break;
+
+ case ID_BUTTON_IGNOREONCE:
+ spell_dlg_next_word(hDlg, word_info);
+ break;
+
+ case ID_BUTTON_IGNOREALL:
+ speller_ignore_all(word_info->aspellinfo, word_info->word_utf8, -1);
+ spell_dlg_next_word(hDlg, word_info);
+ break;
+
+ case ID_BUTTON_ADD_TO_DICT:
+ speller_add_to_dictionary(word_info->aspellinfo, word_info->word_utf8, -1);
+ spell_dlg_next_word(hDlg, word_info);
+ break;
+
+ case ID_BUTTON_CANCEL:
+ EndDialog(hDlg, 0);
+ break;
+ }
+}
+
+/*
+ * spell_dlg_proc() - Main spell dialog proc.
+ */
+static INT_PTR CALLBACK
+spell_dlg_proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ WORD_INFO *word_info;
+
+ switch(message)
+ {
+ case WM_INITDIALOG:
+ center_dialog(hDlg);
+
+ // Limit how much junk they can type in.
+ ComboBox_LimitText(GetDlgItem(hDlg, ID_COMBO_SUGGESTIONS), 128);
+
+ word_info = (WORD_INFO *)lParam;
+ SetWindowLongPtr(hDlg, GWLP_USERDATA, (LONG_PTR)word_info);
+
+ spell_dlg_proc_set_word(hDlg, word_info);
+ return TRUE;
+
+ case WM_COMMAND:
+ HANDLE_WM_COMMAND(hDlg, wParam, lParam, spell_dlg_on_command);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/*
+ * get_next_word() - Get the next word from dot. And skip words in
+ * quoted lines.
+ */
+static int
+get_next_word(WORD_INFO *word_info)
+{
+ // Skip quoted lines
+ while(lgetc(curwp->w_dotp, 0).c == '>')
+ {
+ if(curwp->w_dotp == curbp->b_linep)
+ return 0;
+
+ curwp->w_dotp = lforw(curwp->w_dotp);
+ curwp->w_doto = 0;
+ curwp->w_flag |= WFMOVE;
+ }
+
+ // Move to a word.
+ while(inword() == FALSE)
+ {
+ if(forwchar(FALSE, 1) == FALSE)
+ {
+ // There is no next word.
+ return 0;
+ }
+ }
+
+ // If mark is currently set, clear it.
+ if(curwp->w_markp)
+ setmark(0, 1);
+
+ // Set mark to beginning of this word.
+ curwp->w_markp = curwp->w_dotp;
+ curwp->w_marko = curwp->w_doto;
+
+ // Get word length
+ word_info->word_doto = curwp->w_doto;
+ word_info->word_dotp = curwp->w_dotp;
+ word_info->word_size = 0;
+
+ while((inword() != FALSE) && (word_info->word_dotp == curwp->w_dotp))
+ {
+ word_info->word_size++;
+
+ if(forwchar(FALSE, 1) == FALSE)
+ break;
+ }
+
+ word_info->word_utf8 = ucs4_to_utf8_cpystr_n(
+ (UCS *)&word_info->word_dotp->l_text[word_info->word_doto],
+ word_info->word_size);
+ return 1;
+}
+
+/*
+ * get_next_word() - free the word_info members word_utf8 and word_lptstr.
+ */
+static void
+free_word_info_words(WORD_INFO *word_info)
+{
+ if(word_info->word_utf8)
+ {
+ fs_give((void **)&word_info->word_utf8);
+ word_info->word_utf8 = NULL;
+ }
+
+ if(word_info->word_lptstr)
+ {
+ fs_give((void **)&word_info->word_lptstr);
+ word_info->word_lptstr = NULL;
+ }
+}
+
+/*
+ * get_next_bad_word() - Search from '.' for the next word we think is
+ * rotten. Mark it and highlight it.
+ */
+static int
+get_next_bad_word(WORD_INFO *word_info)
+{
+ free_word_info_words(word_info);
+
+ while(get_next_word(word_info))
+ {
+ int ret;
+
+ /* pass over words that contain @ */
+ if(strchr(word_info->word_utf8, '@'))
+ ret = 1;
+ else
+ ret = speller_check_word(word_info->aspellinfo, word_info->word_utf8, -1);
+
+ if(ret == -1)
+ {
+ word_info->utf8_err_message =
+ speller_get_error_message(word_info->aspellinfo);
+ if(!word_info->utf8_err_message)
+ word_info->utf8_err_message = _("Spelling: speller_check_word() failed");
+ return 0;
+ }
+ else if(ret == 0)
+ {
+ // Highlight word.
+ update();
+
+ word_info->word_lptstr = utf8_to_lptstr(word_info->word_utf8);
+ return 1;
+ }
+
+ free_word_info_words(word_info);
+ }
+
+ return 0;
+}