From 6a18937898292e97c15289e5ecd5d8f1c2205110 Mon Sep 17 00:00:00 2001 From: Eduardo Chappa Date: Mon, 10 Mar 2014 00:17:48 -0600 Subject: [PATCH] * Bump version to 2.19.9. * Fix windows strings to match version. --- VERSION | 2 +- alpine/osdep/mswin.rc | 8 +- alpine/osdep/mswinver.c | 5 +- configure | 20 +- doc/alpine.1 | 2 +- doc/tech-notes/index.html | 2 +- include/config.wnt.h | 6 +- mapi/pmapi.c | 2 +- mapi/pmapi.rc | 8 +- pico/osdep/mswin_spell.c | 1094 ++++++++++++++++++++++----------------------- pith/pine.hlp | 28 +- po/Makefile.in | 2 +- 12 files changed, 589 insertions(+), 590 deletions(-) diff --git a/VERSION b/VERSION index 3983e5a..2a7d0a4 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.19.8 +2.19.9 diff --git a/alpine/osdep/mswin.rc b/alpine/osdep/mswin.rc index 61cd7d7..37835b9 100644 --- a/alpine/osdep/mswin.rc +++ b/alpine/osdep/mswin.rc @@ -244,8 +244,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 2,01,0,0 - PRODUCTVERSION 2,01,0,0 + FILEVERSION 2,19,9,0 + PRODUCTVERSION 2,19,9,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -267,12 +267,12 @@ BEGIN #else VALUE "FileDescription", "Alpine\0" #endif - VALUE "FileVersion", "2.19.8\0" + VALUE "FileVersion", "2.19.9\0" VALUE "InternalName", "alpine\0" VALUE "LegalCopyright", "Copyright 2006-2009\0" VALUE "OriginalFilename", "alpine.exe\0" VALUE "ProductName", "alpine\0" - VALUE "ProductVersion", "2.19.8\0" + VALUE "ProductVersion", "2.19.9\0" END END BLOCK "VarFileInfo" diff --git a/alpine/osdep/mswinver.c b/alpine/osdep/mswinver.c index e968022..8723ee1 100644 --- a/alpine/osdep/mswinver.c +++ b/alpine/osdep/mswinver.c @@ -13,7 +13,8 @@ */ #define VER_MAJOR 2 -#define VER_MINOR 1 +#define VER_MINOR 19 + extern char datestamp[]; @@ -33,7 +34,7 @@ mswin_majorver() int mswin_minorver() { - return(VER_MINOR); + return(VER_MINOR 19); } diff --git a/configure b/configure index 46b7bf3..2666e09 100755 --- a/configure +++ b/configure @@ -1,7 +1,7 @@ #! /bin/sh # From configure.ac Rev:9 by chappa@washington.edu. # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.69 for alpine 2.19.8. +# Generated by GNU Autoconf 2.69 for alpine 2.19.9. # # Report bugs to . # @@ -730,8 +730,8 @@ MAKEFLAGS= # Identity of this package. PACKAGE_NAME='alpine' PACKAGE_TARNAME='alpine' -PACKAGE_VERSION='2.19.8' -PACKAGE_STRING='alpine 2.19.8' +PACKAGE_VERSION='2.19.9' +PACKAGE_STRING='alpine 2.19.9' PACKAGE_BUGREPORT='chappa@washington.edu' PACKAGE_URL='' @@ -1596,7 +1596,7 @@ if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures alpine 2.19.8 to adapt to many kinds of systems. +\`configure' configures alpine 2.19.9 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1666,7 +1666,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of alpine 2.19.8:";; + short | recursive ) echo "Configuration of alpine 2.19.9:";; esac cat <<\_ACEOF @@ -1952,7 +1952,7 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -alpine configure 2.19.8 +alpine configure 2.19.9 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. @@ -2558,7 +2558,7 @@ cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by alpine $as_me 2.19.8, which was +It was created by alpine $as_me 2.19.9, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ @@ -3379,7 +3379,7 @@ fi # Define the identity of the package. PACKAGE='alpine' - VERSION='2.19.8' + VERSION='2.19.9' cat >>confdefs.h <<_ACEOF @@ -20335,7 +20335,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by alpine $as_me 2.19.8, which was +This file was extended by alpine $as_me 2.19.9, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -20401,7 +20401,7 @@ _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ -alpine config.status 2.19.8 +alpine config.status 2.19.9 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" diff --git a/doc/alpine.1 b/doc/alpine.1 index 1816fc6..ef750f8 100644 --- a/doc/alpine.1 +++ b/doc/alpine.1 @@ -1,4 +1,4 @@ -.TH alpine 1 "Version 2.19.8" +.TH alpine 1 "Version 2.19.9" .SH NAME alpine \- an Alternatively Licensed Program for Internet News and Email .SH SYNTAX diff --git a/doc/tech-notes/index.html b/doc/tech-notes/index.html index f99eeae..60a59bb 100644 --- a/doc/tech-notes/index.html +++ b/doc/tech-notes/index.html @@ -3,7 +3,7 @@

Alpine Technical Notes

-Version 2.19.8, March 2014 +Version 2.19.9, March 2014

Table of Contents

diff --git a/include/config.wnt.h b/include/config.wnt.h index 644ce1e..a6634dd 100644 --- a/include/config.wnt.h +++ b/include/config.wnt.h @@ -454,13 +454,13 @@ #define PACKAGE_NAME "alpine" /* Define to the full name and version of this package. */ -#define PACKAGE_STRING "alpine 2.19.8" +#define PACKAGE_STRING "alpine 2.19.9" /* Define to the one symbol short name of this package. */ #define PACKAGE_TARNAME "alpine" /* Define to the version of this package. */ -#define PACKAGE_VERSION "2.19.8" +#define PACKAGE_VERSION "2.19.9" /* Program users use to change their password */ /* #undef PASSWD_PROG */ @@ -552,7 +552,7 @@ #define UTF8_INTERNAL /* Version number of package */ -#define VERSION "2.19.8" +#define VERSION "2.19.9" /* Windows is just too different */ #ifndef _WINDOWS diff --git a/mapi/pmapi.c b/mapi/pmapi.c index 290492b..57be211 100644 --- a/mapi/pmapi.c +++ b/mapi/pmapi.c @@ -1952,7 +1952,7 @@ BOOL APIENTRY DllMain( now = time((time_t *)0); tm_now = localtime(&now); - fprintf(ms_global->dfd, "pmapi32.dll for Alpine Version 2.19.3\r\n"); + fprintf(ms_global->dfd, "pmapi32.dll for Alpine Version 2.19.9\r\n"); fprintf(ms_global->dfd, " Build date: %s\r\n", datestamp); fprintf(ms_global->dfd, " please report all bugs to chappa@gmx.com\r\n"); diff --git a/mapi/pmapi.rc b/mapi/pmapi.rc index 653329f..7fa070f 100644 --- a/mapi/pmapi.rc +++ b/mapi/pmapi.rc @@ -98,8 +98,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 2,01,0,0 - PRODUCTVERSION 2,01,0,0 + FILEVERSION 2,19,9,0 + PRODUCTVERSION 2,19,9,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x29L @@ -117,14 +117,14 @@ BEGIN VALUE "Comments", "alpine info: http://www.washington.edu/alpine\0" VALUE "CompanyName", "University of Washington\0" VALUE "FileDescription", "Simple MAPI DLL for Alpine for Windows\0" - VALUE "FileVersion", "2.19.8\0" + VALUE "FileVersion", "2.19.9\0" VALUE "InternalName", "pmapi32\0" VALUE "LegalCopyright", "Copyright ? 2006-2009\0" VALUE "LegalTrademarks", "Apache License, Version 2.0\0" VALUE "OriginalFilename", "pmapi32.dll\0" VALUE "PrivateBuild", " \0" VALUE "ProductName", "Simple MAPI for Alpine for Windows\0" - VALUE "ProductVersion", "2.19.8\0" + VALUE "ProductVersion", "2.19.9\0" VALUE "SpecialBuild", " \0" END END diff --git a/pico/osdep/mswin_spell.c b/pico/osdep/mswin_spell.c index 1379beb..e921cfc 100644 --- a/pico/osdep/mswin_spell.c +++ b/pico/osdep/mswin_spell.c @@ -1,547 +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 - -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; -} +/* + * ======================================================================== + * 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 + +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, 0, 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; +} diff --git a/pith/pine.hlp b/pith/pine.hlp index 6231a46..af3d820 100644 --- a/pith/pine.hlp +++ b/pith/pine.hlp @@ -181,28 +181,16 @@ Additions include: