notepad: Detect if saving will lose information.

This commit is contained in:
Alexander Scott-Johns 2009-06-30 00:24:18 +01:00 committed by Alexandre Julliard
parent 67766392bf
commit 84fd1c84f8
3 changed files with 89 additions and 17 deletions

View file

@ -131,4 +131,11 @@ memory."
STRING_UNICODE_LE, "Unicode (UTF-16)" STRING_UNICODE_LE, "Unicode (UTF-16)"
STRING_UNICODE_BE, "Unicode (UTF-16 big-endian)" STRING_UNICODE_BE, "Unicode (UTF-16 big-endian)"
STRING_LOSS_OF_UNICODE_CHARACTERS, "%s\n\
This file contains Unicode characters which will be lost if \n\
you save this file in the %s encoding. \n\
To keep these characters, click Cancel, and then select \n\
one of the Unicode options in the Encoding drop down list. \n\
Continue?"
} }

View file

@ -142,6 +142,23 @@ static int AlertFileNotSaved(LPCWSTR szFileName)
MB_ICONQUESTION|MB_YESNOCANCEL); MB_ICONQUESTION|MB_YESNOCANCEL);
} }
static int AlertUnicodeCharactersLost(LPCWSTR szFileName)
{
WCHAR szMsgFormat[MAX_STRING_LEN];
WCHAR szEnc[MAX_STRING_LEN];
WCHAR szMsg[ARRAY_SIZE(szMsgFormat) + MAX_PATH + ARRAY_SIZE(szEnc)];
WCHAR szCaption[MAX_STRING_LEN];
LoadStringW(Globals.hInstance, STRING_LOSS_OF_UNICODE_CHARACTERS,
szMsgFormat, ARRAY_SIZE(szMsgFormat));
load_encoding_name(ENCODING_ANSI, szEnc, ARRAY_SIZE(szEnc));
wnsprintfW(szMsg, ARRAY_SIZE(szMsg), szMsgFormat, szFileName, szEnc);
LoadStringW(Globals.hInstance, STRING_NOTEPAD, szCaption,
ARRAY_SIZE(szCaption));
return MessageBoxW(Globals.hMainWnd, szMsg, szCaption,
MB_OKCANCEL|MB_ICONEXCLAMATION);
}
/** /**
* Returns: * Returns:
* TRUE - if file exists * TRUE - if file exists
@ -158,8 +175,30 @@ BOOL FileExists(LPCWSTR szFilename)
return (hFile != INVALID_HANDLE_VALUE); return (hFile != INVALID_HANDLE_VALUE);
} }
static inline BOOL is_conversion_to_ansi_lossy(LPCWSTR textW, int lenW)
{
BOOL ret = FALSE;
WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, textW, lenW, NULL, 0,
NULL, &ret);
return ret;
}
static VOID DoSaveFile(VOID) typedef enum
{
SAVED_OK,
SAVE_FAILED,
SHOW_SAVEAS_DIALOG
} SAVE_STATUS;
/* szFileName is the filename to save under; enc is the encoding to use.
*
* If the function succeeds, it returns SAVED_OK.
* If the function fails, it returns SAVE_FAILED.
* If Unicode data could be lost due to conversion to a non-Unicode character
* set, a warning is displayed. The user can continue (and the function carries
* on), or cancel (and the function returns SHOW_SAVEAS_DIALOG).
*/
static SAVE_STATUS DoSaveFile(LPCWSTR szFileName, ENCODING enc)
{ {
int lenW; int lenW;
WCHAR* textW; WCHAR* textW;
@ -174,12 +213,12 @@ static VOID DoSaveFile(VOID)
if (!textW) if (!textW)
{ {
ShowLastError(); ShowLastError();
return; return SAVE_FAILED;
} }
textW[0] = (WCHAR) 0xfeff; textW[0] = (WCHAR) 0xfeff;
lenW = GetWindowTextW(Globals.hEdit, textW+1, lenW) + 1; lenW = GetWindowTextW(Globals.hEdit, textW+1, lenW) + 1;
switch (Globals.encFile) switch (enc)
{ {
case ENCODING_UTF16BE: case ENCODING_UTF16BE:
byteswap_wide_string(textW, lenW); byteswap_wide_string(textW, lenW);
@ -197,46 +236,54 @@ static VOID DoSaveFile(VOID)
{ {
ShowLastError(); ShowLastError();
HeapFree(GetProcessHeap(), 0, textW); HeapFree(GetProcessHeap(), 0, textW);
return; return SAVE_FAILED;
} }
WideCharToMultiByte(CP_UTF8, 0, textW, lenW, pBytes, size, NULL, NULL); WideCharToMultiByte(CP_UTF8, 0, textW, lenW, pBytes, size, NULL, NULL);
HeapFree(GetProcessHeap(), 0, textW); HeapFree(GetProcessHeap(), 0, textW);
break; break;
default: default:
if (is_conversion_to_ansi_lossy(textW+1, lenW-1)
&& AlertUnicodeCharactersLost(szFileName) == IDCANCEL)
{
HeapFree(GetProcessHeap(), 0, textW);
return SHOW_SAVEAS_DIALOG;
}
size = WideCharToMultiByte(CP_ACP, 0, textW+1, lenW-1, NULL, 0, NULL, NULL); size = WideCharToMultiByte(CP_ACP, 0, textW+1, lenW-1, NULL, 0, NULL, NULL);
pBytes = HeapAlloc(GetProcessHeap(), 0, size); pBytes = HeapAlloc(GetProcessHeap(), 0, size);
if (!pBytes) if (!pBytes)
{ {
ShowLastError(); ShowLastError();
HeapFree(GetProcessHeap(), 0, textW); HeapFree(GetProcessHeap(), 0, textW);
return; return SAVE_FAILED;
} }
WideCharToMultiByte(CP_ACP, 0, textW+1, lenW-1, pBytes, size, NULL, NULL); WideCharToMultiByte(CP_ACP, 0, textW+1, lenW-1, pBytes, size, NULL, NULL);
HeapFree(GetProcessHeap(), 0, textW); HeapFree(GetProcessHeap(), 0, textW);
break; break;
} }
hFile = CreateFileW(Globals.szFileName, GENERIC_WRITE, FILE_SHARE_WRITE, hFile = CreateFileW(szFileName, GENERIC_WRITE, FILE_SHARE_WRITE,
NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if(hFile == INVALID_HANDLE_VALUE) if(hFile == INVALID_HANDLE_VALUE)
{ {
ShowLastError(); ShowLastError();
HeapFree(GetProcessHeap(), 0, pBytes); HeapFree(GetProcessHeap(), 0, pBytes);
return; return SAVE_FAILED;
} }
if (!WriteFile(hFile, pBytes, size, &dwNumWrite, NULL)) if (!WriteFile(hFile, pBytes, size, &dwNumWrite, NULL))
{ {
ShowLastError(); ShowLastError();
CloseHandle(hFile); CloseHandle(hFile);
HeapFree(GetProcessHeap(), 0, pBytes); HeapFree(GetProcessHeap(), 0, pBytes);
return; return SAVE_FAILED;
} }
SetEndOfFile(hFile); SetEndOfFile(hFile);
CloseHandle(hFile); CloseHandle(hFile);
HeapFree(GetProcessHeap(), 0, pBytes); HeapFree(GetProcessHeap(), 0, pBytes);
SendMessageW(Globals.hEdit, EM_SETMODIFY, FALSE, 0); SendMessageW(Globals.hEdit, EM_SETMODIFY, FALSE, 0);
return SAVED_OK;
} }
/** /**
@ -570,14 +617,20 @@ VOID DIALOG_FileOpen(VOID)
DoOpenFile(openfilename.lpstrFile, Globals.encOfnCombo); DoOpenFile(openfilename.lpstrFile, Globals.encOfnCombo);
} }
/* Return FALSE to cancel close */
BOOL DIALOG_FileSave(VOID) BOOL DIALOG_FileSave(VOID)
{ {
if (Globals.szFileName[0] == '\0') if (Globals.szFileName[0] == '\0')
return DIALOG_FileSaveAs(); return DIALOG_FileSaveAs();
else else
DoSaveFile(); {
return TRUE; switch (DoSaveFile(Globals.szFileName, Globals.encFile))
{
case SAVED_OK: return TRUE;
case SHOW_SAVEAS_DIALOG: return DIALOG_FileSaveAs();
default: return FALSE;
}
}
} }
BOOL DIALOG_FileSaveAs(VOID) BOOL DIALOG_FileSaveAs(VOID)
@ -611,13 +664,23 @@ BOOL DIALOG_FileSaveAs(VOID)
Globals.encOfnCombo = Globals.encFile; Globals.encOfnCombo = Globals.encFile;
Globals.bOfnIsOpenDialog = FALSE; Globals.bOfnIsOpenDialog = FALSE;
if (GetSaveFileNameW(&saveas)) { retry:
if (!GetSaveFileNameW(&saveas))
return FALSE;
switch (DoSaveFile(szPath, Globals.encOfnCombo))
{
case SAVED_OK:
SetFileNameAndEncoding(szPath, Globals.encOfnCombo); SetFileNameAndEncoding(szPath, Globals.encOfnCombo);
UpdateWindowCaption(); UpdateWindowCaption();
DoSaveFile();
return TRUE; return TRUE;
}
case SHOW_SAVEAS_DIALOG:
goto retry;
default:
return FALSE; return FALSE;
}
} }
typedef struct { typedef struct {

View file

@ -88,6 +88,8 @@
#define STRING_UNICODE_LE 0x180 #define STRING_UNICODE_LE 0x180
#define STRING_UNICODE_BE 0x181 #define STRING_UNICODE_BE 0x181
#define STRING_LOSS_OF_UNICODE_CHARACTERS 0x182
/* Open/Save As dialog template */ /* Open/Save As dialog template */
#define IDD_OFN_TEMPLATE 0x190 #define IDD_OFN_TEMPLATE 0x190
#define IDC_OFN_ENCCOMBO 0x191 #define IDC_OFN_ENCCOMBO 0x191