~ubuntu-branches/debian/stretch/alpine/stretch

« back to all changes in this revision

Viewing changes to pico/osdep/mswin_spell.c

  • Committer: Bazaar Package Importer
  • Author(s): Asheesh Laroia
  • Date: 2007-02-17 13:17:42 UTC
  • Revision ID: james.westby@ubuntu.com-20070217131742-99x5c6cpg1pbkdhw
Tags: upstream-0.82+dfsg
ImportĀ upstreamĀ versionĀ 0.82+dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * ========================================================================
 
3
 * MSWIN_SPELL.C
 
4
 *
 
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
 
8
 *
 
9
 *     http://www.apache.org/licenses/LICENSE-2.0
 
10
 *
 
11
 * ========================================================================
 
12
 */
 
13
 
 
14
#include "../headers.h"
 
15
 
 
16
#include "mswin_aspell.h"
 
17
#include "mswin_spell.h"
 
18
 
 
19
#include <windowsx.h>
 
20
 
 
21
typedef struct WORD_INFO
 
22
{
 
23
    ASPELLINFO *aspellinfo;         // Aspell information
 
24
 
 
25
    const char *utf8_err_message;   // Error message, if any
 
26
 
 
27
    char       *word_utf8;          // utf-8
 
28
    LPTSTR      word_lptstr;
 
29
 
 
30
    int         word_doto;          // UCS
 
31
    LINE       *word_dotp;          //
 
32
    int         word_size;          //
 
33
} WORD_INFO;
 
34
 
 
35
extern HINSTANCE        ghInstance;
 
36
extern HWND             ghTTYWnd;
 
37
 
 
38
/*
 
39
 * Function prototypes
 
40
 */
 
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);
 
44
 
 
45
/*
 
46
 * spell() - check for potentially missspelled words and offer them for
 
47
 *      correction. Microsoft Windows specific version.
 
48
 */
 
49
int
 
50
spell(int f, int n)
 
51
{
 
52
    int w_marko_bak;
 
53
    LINE *w_markp_bak;
 
54
    WORD_INFO word_info;
 
55
    ASPELLINFO *aspellinfo;
 
56
 
 
57
    emlwrite(_("Checking spelling..."), NULL);  /* greetings! */
 
58
 
 
59
    memset(&word_info, 0, sizeof(WORD_INFO));
 
60
 
 
61
    aspellinfo = speller_init(NULL);
 
62
    if(!aspellinfo ||
 
63
        (word_info.utf8_err_message = speller_get_error_message(aspellinfo)))
 
64
    {
 
65
        if(word_info.utf8_err_message)
 
66
            emlwrite((char *)word_info.utf8_err_message, NULL);  /* sad greetings! */
 
67
        else
 
68
            emlwrite(_("Spelling: initializing Aspell failed"), NULL);
 
69
 
 
70
        speller_close(aspellinfo);
 
71
        return -1;
 
72
    }
 
73
 
 
74
    // Back up current mark.
 
75
    w_marko_bak = curwp->w_marko;
 
76
    w_markp_bak = curwp->w_markp;
 
77
 
 
78
    setimark(0, 1);
 
79
    gotobob(0, 1);
 
80
 
 
81
    word_info.aspellinfo = aspellinfo;
 
82
 
 
83
    if(get_next_bad_word(&word_info))
 
84
    {
 
85
        DialogBoxParam(ghInstance, MAKEINTRESOURCE(DLG_SPELL), ghTTYWnd,
 
86
            spell_dlg_proc, (LPARAM)&word_info);
 
87
    }
 
88
 
 
89
    // Restore original mark if possible.
 
90
    if(curwp->w_markp)
 
91
    {
 
92
        // Clear any set marks.
 
93
        setmark(0, 1);
 
94
 
 
95
        if(w_markp_bak && llength(w_markp_bak))
 
96
        {
 
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;
 
100
 
 
101
            curwp->w_marko = w_marko_bak;
 
102
            curwp->w_markp = w_markp_bak;
 
103
        }
 
104
    }
 
105
 
 
106
    if(word_info.utf8_err_message)
 
107
        emlwrite((char *)word_info.utf8_err_message, NULL);
 
108
    else
 
109
        emlwrite(_("Done checking spelling"), NULL);
 
110
 
 
111
    speller_close(aspellinfo);
 
112
    free_word_info_words(&word_info);
 
113
 
 
114
    swapimark(0, 1);
 
115
    curwp->w_flag |= WFHARD|WFMODE;
 
116
 
 
117
    update();
 
118
    return 1;
 
119
}
 
120
 
 
121
/*
 
122
 * handle_cb_editchange() - combobox contents have changed so enable/disable
 
123
 *      the Change and Change All buttons accordingly.
 
124
 */
 
125
static void
 
126
handle_cb_editchange(HWND hDlg, HWND hwnd_combo)
 
127
{
 
128
    int text_len = ComboBox_GetTextLength(hwnd_combo);
 
129
 
 
130
    Button_Enable(GetDlgItem(hDlg, ID_BUTTON_CHANGE), !!text_len);
 
131
    Button_Enable(GetDlgItem(hDlg, ID_BUTTON_CHANGEALL), !!text_len);
 
132
}
 
133
 
 
134
/*
 
135
 * spell_dlg_proc_set_word() - Set up the dialog to display spelling
 
136
 *      information and suggestions for the current word.
 
137
 */
 
138
static void
 
139
spell_dlg_proc_set_word(HWND hDlg, WORD_INFO *word_info)
 
140
{
 
141
    TCHAR dlg_title[256];
 
142
    HWND hwnd_combo = GetDlgItem(hDlg, ID_COMBO_SUGGESTIONS);
 
143
 
 
144
    // Clear the combobox.
 
145
    ComboBox_ResetContent(hwnd_combo);
 
146
 
 
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);
 
152
 
 
153
    // Populate the combobox with suggestions
 
154
    if(speller_suggestion_init(word_info->aspellinfo, word_info->word_utf8, -1))
 
155
    {
 
156
        const char *suggestion_utf8;
 
157
 
 
158
        while(suggestion_utf8 = speller_suggestion_getnext(word_info->aspellinfo))
 
159
        {
 
160
            LPTSTR suggestion_lptstr = utf8_to_lptstr((LPSTR)suggestion_utf8);
 
161
 
 
162
            if(suggestion_lptstr)
 
163
            {
 
164
                ComboBox_AddString(hwnd_combo, suggestion_lptstr);
 
165
                fs_give((void **)&suggestion_lptstr);
 
166
            }
 
167
        }
 
168
 
 
169
        // Select the first one.
 
170
        ComboBox_SetCurSel(hwnd_combo, 0);
 
171
 
 
172
        speller_suggestion_close(word_info->aspellinfo);
 
173
    }
 
174
 
 
175
    handle_cb_editchange(hDlg, hwnd_combo);
 
176
}
 
177
 
 
178
/*
 
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.
 
181
 */
 
182
static void
 
183
spell_dlg_next_word(HWND hDlg, WORD_INFO *word_info)
 
184
{
 
185
    if(!get_next_bad_word(word_info))
 
186
    {
 
187
        EndDialog(hDlg, 0);
 
188
        return;
 
189
    }
 
190
 
 
191
    spell_dlg_proc_set_word(hDlg, word_info);
 
192
}
 
193
 
 
194
/*
 
195
 * chword_n() - change the given word_len pointed to by the curwp->w_dot
 
196
 *      pointers to the word in cb
 
197
 */
 
198
static void
 
199
chword_n(int wb_len, UCS *cb)
 
200
{
 
201
    ldelete(wb_len, NULL);  /* not saved in kill buffer */
 
202
 
 
203
    while(*cb != '\0')
 
204
        linsert(1, *cb++);
 
205
 
 
206
    curwp->w_flag |= WFEDIT;
 
207
}
 
208
 
 
209
/*
 
210
 * replace_all() - replace all instances of oldword with newword from
 
211
 *      the current '.' on. Mark is where curwp will get restored to.
 
212
 */
 
213
static void
 
214
replace_all(UCS *oldword, int oldword_len, UCS *newword)
 
215
{
 
216
    int             wrap;
 
217
 
 
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);
 
224
 
 
225
    curwp->w_bufp->b_mode |= MDEXACT;           /* case sensitive */
 
226
    while(forscan(&wrap, oldword, NULL, 0, 1))
 
227
    {
 
228
        if(wrap)
 
229
            break;                              /* wrap NOT allowed! */
 
230
 
 
231
        /*
 
232
         * We want to minimize the number of substrings that we report
 
233
         * as matching a misspelled word...
 
234
         */
 
235
        if(lgetc(curwp->w_dotp, 0).c != '>')
 
236
        {
 
237
            LINE *lp  = curwp->w_dotp;          /* for convenience */
 
238
            int   off = curwp->w_doto;
 
239
 
 
240
            if(off == 0 || !ucs4_isalpha(lgetc(lp, off - 1).c))
 
241
            {
 
242
                off += oldword_len;
 
243
 
 
244
                if(off == llength(lp) || !ucs4_isalpha(lgetc(lp, off).c))
 
245
                {
 
246
                    // Replace this word
 
247
                    chword_n(oldword_len, newword);
 
248
                }
 
249
            }
 
250
        }
 
251
 
 
252
        forwchar(0, 1);                         /* move on... */
 
253
 
 
254
    }
 
255
    curwp->w_bufp->b_mode ^= MDEXACT;           /* case insensitive */
 
256
 
 
257
    curwp->w_dotp = curwp->w_markp;
 
258
    curwp->w_doto = curwp->w_marko;
 
259
}
 
260
 
 
261
/*
 
262
 * handle_button_change() - Someone pressed the Change or Change All button.
 
263
 *      So deal with it.
 
264
 */
 
265
static void
 
266
handle_button_change(HWND hDlg, WORD_INFO *word_info, int do_replace_all)
 
267
{
 
268
    int str_lptstr_len;
 
269
    TCHAR str_lptstr[128];
 
270
 
 
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));
 
274
    if(str_lptstr_len)
 
275
    {
 
276
        UCS *str_ucs4;
 
277
        char *str_utf8;
 
278
 
 
279
        // Notify the speller so it'll suggest this word next time around.
 
280
        str_utf8 = lptstr_to_utf8(str_lptstr);
 
281
        if(str_utf8)
 
282
        {
 
283
            speller_replace_word(word_info->aspellinfo,
 
284
                word_info->word_utf8, -1, str_utf8, -1);
 
285
            fs_give((void **)&str_utf8);
 
286
        }
 
287
 
 
288
        str_ucs4 = lptstr_to_ucs4(str_lptstr);
 
289
        if(!str_ucs4)
 
290
        {
 
291
            // Uh oh - massive error.
 
292
            word_info->utf8_err_message = _("Spelling: lptstr_to_ucs4() failed");
 
293
            EndDialog(hDlg, -1);
 
294
            return;
 
295
        }
 
296
 
 
297
        // Move back to start of this word.
 
298
        curwp->w_doto = word_info->word_doto;
 
299
        curwp->w_dotp = word_info->word_dotp;
 
300
 
 
301
        if(do_replace_all)
 
302
        {
 
303
            int i;
 
304
            int word_size;
 
305
            UCS word[128];
 
306
 
 
307
            word_size = word_info->word_size;
 
308
            if(word_size >= ARRAYSIZE(word) - 1)
 
309
                word_size = ARRAYSIZE(word) - 1;
 
310
 
 
311
            for(i = 0; i < word_size; i++)
 
312
                word[i] = lgetc(curwp->w_dotp, curwp->w_doto + i).c;
 
313
 
 
314
            word[i] = 0;
 
315
 
 
316
            replace_all(word, word_size, str_ucs4);
 
317
 
 
318
            // Skip over the word we just inserted.
 
319
            forwchar(FALSE, (int)ucs4_strlen(str_ucs4));
 
320
        }
 
321
        else
 
322
        {
 
323
            // Swap it with the new improved version.
 
324
            chword_n(word_info->word_size, str_ucs4);
 
325
        }
 
326
        fs_give((void **)&str_ucs4);
 
327
    }
 
328
 
 
329
    update();
 
330
    spell_dlg_next_word(hDlg, word_info);
 
331
}
 
332
 
 
333
/*
 
334
 * center_dialog() - Center a dialog with respect to its parent.
 
335
 */
 
336
static void
 
337
center_dialog(HWND hDlg)
 
338
{
 
339
    int dx;
 
340
    int dy;
 
341
    RECT rcDialog;
 
342
    RECT rcParent;
 
343
 
 
344
    GetWindowRect(GetParent(hDlg), &rcParent);
 
345
    GetWindowRect(hDlg, &rcDialog);
 
346
 
 
347
    dx = ((rcParent.right - rcParent.left) -
 
348
            (rcDialog.right - rcDialog.left)) / 2 + rcParent.left - rcDialog.left;
 
349
 
 
350
    dy = ((rcParent.bottom - rcParent.top) -
 
351
            (rcDialog.bottom - rcDialog.top)) / 2 + rcParent.top - rcDialog.top;
 
352
 
 
353
    OffsetRect(&rcDialog, dx, dy);
 
354
 
 
355
    SetWindowPos(hDlg, NULL, rcDialog.left, rcDialog.top, 0, 0,
 
356
        SWP_NOSIZE | SWP_NOZORDER);
 
357
}
 
358
 
 
359
/*
 
360
 * spell_dlg_on_command() - Handle the WM_COMMAND stuff for the spell dialog.
 
361
 */
 
362
static void
 
363
spell_dlg_on_command(HWND hDlg, int id, HWND hwndCtl, UINT codeNotify)
 
364
{
 
365
    WORD_INFO *word_info =
 
366
        (WORD_INFO *)(LONG_PTR)GetWindowLongPtr(hDlg, GWLP_USERDATA);
 
367
 
 
368
    switch(id)
 
369
    {
 
370
    case ID_COMBO_SUGGESTIONS:
 
371
        if(codeNotify == CBN_EDITCHANGE)
 
372
            handle_cb_editchange(hDlg, hwndCtl);
 
373
        break;
 
374
 
 
375
    case ID_BUTTON_CHANGE:
 
376
        handle_button_change(hDlg, word_info, 0);
 
377
        break;
 
378
 
 
379
    case ID_BUTTON_CHANGEALL:
 
380
        handle_button_change(hDlg, word_info, 1);
 
381
        break;
 
382
 
 
383
    case ID_BUTTON_IGNOREONCE:
 
384
        spell_dlg_next_word(hDlg, word_info);
 
385
        break;
 
386
 
 
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);
 
390
        break;
 
391
 
 
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);
 
395
        break;
 
396
 
 
397
    case ID_BUTTON_CANCEL:
 
398
        EndDialog(hDlg, 0);
 
399
        break;
 
400
    }
 
401
}
 
402
 
 
403
/*
 
404
 * spell_dlg_proc() - Main spell dialog proc.
 
405
 */
 
406
static INT_PTR CALLBACK
 
407
spell_dlg_proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
 
408
{
 
409
    WORD_INFO *word_info;
 
410
 
 
411
    switch(message)
 
412
    {
 
413
    case WM_INITDIALOG:
 
414
        center_dialog(hDlg);
 
415
 
 
416
        // Limit how much junk they can type in.
 
417
        ComboBox_LimitText(GetDlgItem(hDlg, ID_COMBO_SUGGESTIONS), 128);
 
418
 
 
419
        word_info = (WORD_INFO *)lParam;
 
420
        SetWindowLongPtr(hDlg, GWLP_USERDATA, (LONG_PTR)word_info);
 
421
 
 
422
        spell_dlg_proc_set_word(hDlg, word_info);
 
423
        return TRUE;
 
424
 
 
425
    case WM_COMMAND:
 
426
        HANDLE_WM_COMMAND(hDlg, wParam, lParam, spell_dlg_on_command);
 
427
        return TRUE;
 
428
    }
 
429
 
 
430
    return FALSE;
 
431
}
 
432
 
 
433
/*
 
434
 * get_next_word() - Get the next word from dot. And skip words in
 
435
 *      quoted lines.
 
436
 */
 
437
static int
 
438
get_next_word(WORD_INFO *word_info)
 
439
{
 
440
    // Skip quoted lines
 
441
    while(lgetc(curwp->w_dotp, 0).c == '>')
 
442
    {
 
443
        if(curwp->w_dotp == curbp->b_linep)
 
444
            return 0;
 
445
 
 
446
        curwp->w_dotp  = lforw(curwp->w_dotp);
 
447
        curwp->w_doto  = 0;
 
448
        curwp->w_flag |= WFMOVE;
 
449
    }
 
450
 
 
451
    // Move to a word.
 
452
    while(inword() == FALSE)
 
453
    {
 
454
        if(forwchar(FALSE, 1) == FALSE)
 
455
        {
 
456
            // There is no next word.
 
457
            return 0;
 
458
        }
 
459
    }
 
460
 
 
461
    // If mark is currently set, clear it.
 
462
    if(curwp->w_markp)
 
463
        setmark(0, 1);
 
464
 
 
465
    // Set mark to beginning of this word.
 
466
    curwp->w_markp = curwp->w_dotp;
 
467
    curwp->w_marko = curwp->w_doto;
 
468
 
 
469
    // Get word length
 
470
    word_info->word_doto = curwp->w_doto;
 
471
    word_info->word_dotp = curwp->w_dotp;
 
472
    word_info->word_size = 0;
 
473
 
 
474
    while((inword() != FALSE) && (word_info->word_dotp == curwp->w_dotp))
 
475
    {
 
476
        word_info->word_size++;
 
477
 
 
478
        if(forwchar(FALSE, 1) == FALSE)
 
479
            break;
 
480
    }
 
481
 
 
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);
 
485
    return 1;
 
486
}
 
487
 
 
488
/*
 
489
 * get_next_word() - free the word_info members word_utf8 and word_lptstr.
 
490
 */
 
491
static void
 
492
free_word_info_words(WORD_INFO *word_info)
 
493
{
 
494
    if(word_info->word_utf8)
 
495
    {
 
496
        fs_give((void **)&word_info->word_utf8);
 
497
        word_info->word_utf8 = NULL;
 
498
    }
 
499
 
 
500
    if(word_info->word_lptstr)
 
501
    {
 
502
        fs_give((void **)&word_info->word_lptstr);
 
503
        word_info->word_lptstr = NULL;
 
504
    }
 
505
}
 
506
 
 
507
/*
 
508
 * get_next_bad_word() - Search from '.' for the next word we think is
 
509
 *      rotten. Mark it and highlight it.
 
510
 */
 
511
static int
 
512
get_next_bad_word(WORD_INFO *word_info)
 
513
{
 
514
    free_word_info_words(word_info);
 
515
 
 
516
    while(get_next_word(word_info))
 
517
    {
 
518
        int ret;
 
519
 
 
520
        ret = speller_check_word(word_info->aspellinfo, word_info->word_utf8, -1);
 
521
        if(ret == -1)
 
522
        {
 
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");
 
527
            return 0;
 
528
        }
 
529
        else if(ret == 0)
 
530
        {
 
531
            // Highlight word.
 
532
            update();
 
533
 
 
534
            word_info->word_lptstr = utf8_to_lptstr(word_info->word_utf8);
 
535
            return 1;
 
536
        }
 
537
 
 
538
        free_word_info_words(word_info);
 
539
    }
 
540
 
 
541
    return 0;
 
542
}