2
* ========================================================================
5
* Licensed under the Apache License, Version 2.0 (the "License");
6
* you may not use this file except in compliance with the License.
7
* You may obtain a copy of the License at
9
* http://www.apache.org/licenses/LICENSE-2.0
11
* ========================================================================
14
#include "../headers.h"
16
#include "mswin_aspell.h"
17
#include "mswin_spell.h"
21
typedef struct WORD_INFO
23
ASPELLINFO *aspellinfo; // Aspell information
25
const char *utf8_err_message; // Error message, if any
27
char *word_utf8; // utf-8
35
extern HINSTANCE ghInstance;
41
static int get_next_bad_word(WORD_INFO *word_info);
42
static void free_word_info_words(WORD_INFO *word_info);
43
static INT_PTR CALLBACK spell_dlg_proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
46
* spell() - check for potentially missspelled words and offer them for
47
* correction. Microsoft Windows specific version.
55
ASPELLINFO *aspellinfo;
57
emlwrite(_("Checking spelling..."), NULL); /* greetings! */
59
memset(&word_info, 0, sizeof(WORD_INFO));
61
aspellinfo = speller_init(NULL);
63
(word_info.utf8_err_message = speller_get_error_message(aspellinfo)))
65
if(word_info.utf8_err_message)
66
emlwrite((char *)word_info.utf8_err_message, NULL); /* sad greetings! */
68
emlwrite(_("Spelling: initializing Aspell failed"), NULL);
70
speller_close(aspellinfo);
74
// Back up current mark.
75
w_marko_bak = curwp->w_marko;
76
w_markp_bak = curwp->w_markp;
81
word_info.aspellinfo = aspellinfo;
83
if(get_next_bad_word(&word_info))
85
DialogBoxParam(ghInstance, MAKEINTRESOURCE(DLG_SPELL), ghTTYWnd,
86
spell_dlg_proc, (LPARAM)&word_info);
89
// Restore original mark if possible.
92
// Clear any set marks.
95
if(w_markp_bak && llength(w_markp_bak))
97
// Make sure we don't set mark off the line.
98
if(w_marko_bak >= llength(w_markp_bak))
99
w_marko_bak = llength(w_markp_bak) - 1;
101
curwp->w_marko = w_marko_bak;
102
curwp->w_markp = w_markp_bak;
106
if(word_info.utf8_err_message)
107
emlwrite((char *)word_info.utf8_err_message, NULL);
109
emlwrite(_("Done checking spelling"), NULL);
111
speller_close(aspellinfo);
112
free_word_info_words(&word_info);
115
curwp->w_flag |= WFHARD|WFMODE;
122
* handle_cb_editchange() - combobox contents have changed so enable/disable
123
* the Change and Change All buttons accordingly.
126
handle_cb_editchange(HWND hDlg, HWND hwnd_combo)
128
int text_len = ComboBox_GetTextLength(hwnd_combo);
130
Button_Enable(GetDlgItem(hDlg, ID_BUTTON_CHANGE), !!text_len);
131
Button_Enable(GetDlgItem(hDlg, ID_BUTTON_CHANGEALL), !!text_len);
135
* spell_dlg_proc_set_word() - Set up the dialog to display spelling
136
* information and suggestions for the current word.
139
spell_dlg_proc_set_word(HWND hDlg, WORD_INFO *word_info)
141
TCHAR dlg_title[256];
142
HWND hwnd_combo = GetDlgItem(hDlg, ID_COMBO_SUGGESTIONS);
144
// Clear the combobox.
145
ComboBox_ResetContent(hwnd_combo);
147
// Set the dialog box title
148
_sntprintf(dlg_title, ARRAYSIZE(dlg_title), TEXT("Not in Dictionary: %s"),
149
word_info->word_lptstr);
150
dlg_title[ARRAYSIZE(dlg_title) - 1] = 0;
151
SetWindowText(hDlg, dlg_title);
153
// Populate the combobox with suggestions
154
if(speller_suggestion_init(word_info->aspellinfo, word_info->word_utf8, -1))
156
const char *suggestion_utf8;
158
while(suggestion_utf8 = speller_suggestion_getnext(word_info->aspellinfo))
160
LPTSTR suggestion_lptstr = utf8_to_lptstr((LPSTR)suggestion_utf8);
162
if(suggestion_lptstr)
164
ComboBox_AddString(hwnd_combo, suggestion_lptstr);
165
fs_give((void **)&suggestion_lptstr);
169
// Select the first one.
170
ComboBox_SetCurSel(hwnd_combo, 0);
172
speller_suggestion_close(word_info->aspellinfo);
175
handle_cb_editchange(hDlg, hwnd_combo);
179
* spell_dlg_next_word() - Move to the next misspelled word and display
180
* the information for it. If no more bad words, kill the dialog.
183
spell_dlg_next_word(HWND hDlg, WORD_INFO *word_info)
185
if(!get_next_bad_word(word_info))
191
spell_dlg_proc_set_word(hDlg, word_info);
195
* chword_n() - change the given word_len pointed to by the curwp->w_dot
196
* pointers to the word in cb
199
chword_n(int wb_len, UCS *cb)
201
ldelete(wb_len, NULL); /* not saved in kill buffer */
206
curwp->w_flag |= WFEDIT;
210
* replace_all() - replace all instances of oldword with newword from
211
* the current '.' on. Mark is where curwp will get restored to.
214
replace_all(UCS *oldword, int oldword_len, UCS *newword)
218
// Mark should be at "." right now - ie the start of the word we're
219
// checking. We're going to use mark below to restore curwp since
220
// chword_n can potentially realloc LINES and it knows about markp.
221
assert(curwp->w_markp);
222
assert(curwp->w_markp == curwp->w_dotp);
223
assert(curwp->w_marko == curwp->w_doto);
225
curwp->w_bufp->b_mode |= MDEXACT; /* case sensitive */
226
while(forscan(&wrap, oldword, NULL, 0, 1))
229
break; /* wrap NOT allowed! */
232
* We want to minimize the number of substrings that we report
233
* as matching a misspelled word...
235
if(lgetc(curwp->w_dotp, 0).c != '>')
237
LINE *lp = curwp->w_dotp; /* for convenience */
238
int off = curwp->w_doto;
240
if(off == 0 || !ucs4_isalpha(lgetc(lp, off - 1).c))
244
if(off == llength(lp) || !ucs4_isalpha(lgetc(lp, off).c))
247
chword_n(oldword_len, newword);
252
forwchar(0, 1); /* move on... */
255
curwp->w_bufp->b_mode ^= MDEXACT; /* case insensitive */
257
curwp->w_dotp = curwp->w_markp;
258
curwp->w_doto = curwp->w_marko;
262
* handle_button_change() - Someone pressed the Change or Change All button.
266
handle_button_change(HWND hDlg, WORD_INFO *word_info, int do_replace_all)
269
TCHAR str_lptstr[128];
271
// Get the length of what they want to change to.
272
str_lptstr_len = ComboBox_GetText(GetDlgItem(hDlg, ID_COMBO_SUGGESTIONS),
273
str_lptstr, ARRAYSIZE(str_lptstr));
279
// Notify the speller so it'll suggest this word next time around.
280
str_utf8 = lptstr_to_utf8(str_lptstr);
283
speller_replace_word(word_info->aspellinfo,
284
word_info->word_utf8, -1, str_utf8, -1);
285
fs_give((void **)&str_utf8);
288
str_ucs4 = lptstr_to_ucs4(str_lptstr);
291
// Uh oh - massive error.
292
word_info->utf8_err_message = _("Spelling: lptstr_to_ucs4() failed");
297
// Move back to start of this word.
298
curwp->w_doto = word_info->word_doto;
299
curwp->w_dotp = word_info->word_dotp;
307
word_size = word_info->word_size;
308
if(word_size >= ARRAYSIZE(word) - 1)
309
word_size = ARRAYSIZE(word) - 1;
311
for(i = 0; i < word_size; i++)
312
word[i] = lgetc(curwp->w_dotp, curwp->w_doto + i).c;
316
replace_all(word, word_size, str_ucs4);
318
// Skip over the word we just inserted.
319
forwchar(FALSE, (int)ucs4_strlen(str_ucs4));
323
// Swap it with the new improved version.
324
chword_n(word_info->word_size, str_ucs4);
326
fs_give((void **)&str_ucs4);
330
spell_dlg_next_word(hDlg, word_info);
334
* center_dialog() - Center a dialog with respect to its parent.
337
center_dialog(HWND hDlg)
344
GetWindowRect(GetParent(hDlg), &rcParent);
345
GetWindowRect(hDlg, &rcDialog);
347
dx = ((rcParent.right - rcParent.left) -
348
(rcDialog.right - rcDialog.left)) / 2 + rcParent.left - rcDialog.left;
350
dy = ((rcParent.bottom - rcParent.top) -
351
(rcDialog.bottom - rcDialog.top)) / 2 + rcParent.top - rcDialog.top;
353
OffsetRect(&rcDialog, dx, dy);
355
SetWindowPos(hDlg, NULL, rcDialog.left, rcDialog.top, 0, 0,
356
SWP_NOSIZE | SWP_NOZORDER);
360
* spell_dlg_on_command() - Handle the WM_COMMAND stuff for the spell dialog.
363
spell_dlg_on_command(HWND hDlg, int id, HWND hwndCtl, UINT codeNotify)
365
WORD_INFO *word_info =
366
(WORD_INFO *)(LONG_PTR)GetWindowLongPtr(hDlg, GWLP_USERDATA);
370
case ID_COMBO_SUGGESTIONS:
371
if(codeNotify == CBN_EDITCHANGE)
372
handle_cb_editchange(hDlg, hwndCtl);
375
case ID_BUTTON_CHANGE:
376
handle_button_change(hDlg, word_info, 0);
379
case ID_BUTTON_CHANGEALL:
380
handle_button_change(hDlg, word_info, 1);
383
case ID_BUTTON_IGNOREONCE:
384
spell_dlg_next_word(hDlg, word_info);
387
case ID_BUTTON_IGNOREALL:
388
speller_ignore_all(word_info->aspellinfo, word_info->word_utf8, -1);
389
spell_dlg_next_word(hDlg, word_info);
392
case ID_BUTTON_ADD_TO_DICT:
393
speller_add_to_dictionary(word_info->aspellinfo, word_info->word_utf8, -1);
394
spell_dlg_next_word(hDlg, word_info);
397
case ID_BUTTON_CANCEL:
404
* spell_dlg_proc() - Main spell dialog proc.
406
static INT_PTR CALLBACK
407
spell_dlg_proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
409
WORD_INFO *word_info;
416
// Limit how much junk they can type in.
417
ComboBox_LimitText(GetDlgItem(hDlg, ID_COMBO_SUGGESTIONS), 128);
419
word_info = (WORD_INFO *)lParam;
420
SetWindowLongPtr(hDlg, GWLP_USERDATA, (LONG_PTR)word_info);
422
spell_dlg_proc_set_word(hDlg, word_info);
426
HANDLE_WM_COMMAND(hDlg, wParam, lParam, spell_dlg_on_command);
434
* get_next_word() - Get the next word from dot. And skip words in
438
get_next_word(WORD_INFO *word_info)
441
while(lgetc(curwp->w_dotp, 0).c == '>')
443
if(curwp->w_dotp == curbp->b_linep)
446
curwp->w_dotp = lforw(curwp->w_dotp);
448
curwp->w_flag |= WFMOVE;
452
while(inword() == FALSE)
454
if(forwchar(FALSE, 1) == FALSE)
456
// There is no next word.
461
// If mark is currently set, clear it.
465
// Set mark to beginning of this word.
466
curwp->w_markp = curwp->w_dotp;
467
curwp->w_marko = curwp->w_doto;
470
word_info->word_doto = curwp->w_doto;
471
word_info->word_dotp = curwp->w_dotp;
472
word_info->word_size = 0;
474
while((inword() != FALSE) && (word_info->word_dotp == curwp->w_dotp))
476
word_info->word_size++;
478
if(forwchar(FALSE, 1) == FALSE)
482
word_info->word_utf8 = ucs4_to_utf8_cpystr_n(
483
(UCS *)&word_info->word_dotp->l_text[word_info->word_doto],
484
word_info->word_size);
489
* get_next_word() - free the word_info members word_utf8 and word_lptstr.
492
free_word_info_words(WORD_INFO *word_info)
494
if(word_info->word_utf8)
496
fs_give((void **)&word_info->word_utf8);
497
word_info->word_utf8 = NULL;
500
if(word_info->word_lptstr)
502
fs_give((void **)&word_info->word_lptstr);
503
word_info->word_lptstr = NULL;
508
* get_next_bad_word() - Search from '.' for the next word we think is
509
* rotten. Mark it and highlight it.
512
get_next_bad_word(WORD_INFO *word_info)
514
free_word_info_words(word_info);
516
while(get_next_word(word_info))
520
ret = speller_check_word(word_info->aspellinfo, word_info->word_utf8, -1);
523
word_info->utf8_err_message =
524
speller_get_error_message(word_info->aspellinfo);
525
if(!word_info->utf8_err_message)
526
word_info->utf8_err_message = _("Spelling: speller_check_word() failed");
534
word_info->word_lptstr = utf8_to_lptstr(word_info->word_utf8);
538
free_word_info_words(word_info);