[3/4] notepad: Allow user to choose which encoding to open and save files in.

Alexander Scott-Johns alexander.scott.johns at googlemail.com
Mon Jun 29 19:25:14 CDT 2009


---
 programs/notepad/En.rc         |   10 +++
 programs/notepad/dialog.c      |  147 ++++++++++++++++++++++++++++++++++++++--
 programs/notepad/main.h        |   16 +++--
 programs/notepad/notepad_res.h |    7 ++
 4 files changed, 169 insertions(+), 11 deletions(-)
-------------- next part --------------
From 0926abcf200c96d89cd1e80e0bbbd1fcb638450b Mon Sep 17 00:00:00 2001
From: Alexander Scott-Johns <alexander.scott.johns at googlemail.com>
Date: Mon, 29 Jun 2009 23:50:39 +0100
Subject: notepad: Allow user to choose which encoding to open and save files in.

---
 programs/notepad/En.rc         |   10 +++
 programs/notepad/dialog.c      |  147 ++++++++++++++++++++++++++++++++++++++--
 programs/notepad/main.h        |   16 +++--
 programs/notepad/notepad_res.h |    7 ++
 4 files changed, 169 insertions(+), 11 deletions(-)

diff --git a/programs/notepad/En.rc b/programs/notepad/En.rc
index c4a4baf..8a2ceb8 100644
--- a/programs/notepad/En.rc
+++ b/programs/notepad/En.rc
@@ -91,6 +91,14 @@ PUSHBUTTON    "Cancel",     IDCANCEL,               180, 21, 40, 15, WS_TABSTOP
 PUSHBUTTON    "&Help",       IDHELP,                180, 39, 40, 15, WS_TABSTOP
 }
 
+IDD_OFN_TEMPLATE  DIALOG  DISCARDABLE 50,50,300,15
+STYLE DS_3DLOOK | DS_CONTROL | WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_BORDER
+FONT 8, "MS Shell Dlg"
+BEGIN
+  LTEXT "Encoding:", -1, 5,0, 50,12
+  COMBOBOX IDC_OFN_ENCCOMBO, 53,0, 156,48, WS_CHILD | WS_VSCROLL | CBS_DROPDOWNLIST
+END
+
 STRINGTABLE DISCARDABLE
 {
 STRING_PAGESETUP_HEADERVALUE,   "&f"
@@ -119,4 +127,6 @@ STRING_OUT_OF_MEMORY,                   "Not enough memory to complete this \
 task.\nClose one or more applications to increase the amount of\nfree \
 memory."
 
+STRING_UNICODE_LE,     "Unicode (UTF-16)"
+STRING_UNICODE_BE,     "Unicode (UTF-16 big-endian)"
 }
diff --git a/programs/notepad/dialog.c b/programs/notepad/dialog.c
index 10d4ddd..51ad695 100644
--- a/programs/notepad/dialog.c
+++ b/programs/notepad/dialog.c
@@ -45,6 +45,28 @@ static inline void byteswap_wide_string(LPWSTR str, UINT num)
         str[i] = (WCHAR) MAKEWORD(HIBYTE((WORD) str[i]), LOBYTE((WORD) str[i]));
 }
 
+static void load_encoding_name(ENCODING enc, WCHAR* buffer, int length)
+{
+    switch (enc)
+    {
+        case ENCODING_UTF16LE:
+            LoadStringW(Globals.hInstance, STRING_UNICODE_LE, buffer, length);
+            break;
+
+        case ENCODING_UTF16BE:
+            LoadStringW(Globals.hInstance, STRING_UNICODE_BE, buffer, length);
+            break;
+
+        default:
+        {
+            CPINFOEXW cpi;
+            GetCPInfoExW((enc==ENCODING_UTF8) ? CP_UTF8 : CP_ACP, 0, &cpi);
+            lstrcpynW(buffer, cpi.CodePageName, length);
+            break;
+        }
+    }
+}
+
 VOID ShowLastError(void)
 {
     DWORD error = GetLastError();
@@ -340,6 +362,16 @@ void DoOpenFile(LPCWSTR szFileName, ENCODING enc)
 
     if (enc == ENCODING_AUTO)
         enc = detect_encoding_of_buffer(pTemp, size);
+    else if (size >= 2 && (enc==ENCODING_UTF16LE || enc==ENCODING_UTF16BE))
+    {
+        /* If UTF-16 (BE or LE) is selected, and there is a UTF-16 BOM,
+         * override the selection (like native Notepad).
+         */
+        if ((BYTE)pTemp[0] == 0xff && (BYTE)pTemp[1] == 0xfe)
+            enc = ENCODING_UTF16LE;
+        else if ((BYTE)pTemp[0] == 0xfe && (BYTE)pTemp[1] == 0xff)
+            enc = ENCODING_UTF16BE;
+    }
 
     /* SetWindowTextUtf8 and SetWindowTextA try to allocate memory, so we
      * check if they succeed.
@@ -412,6 +444,97 @@ VOID DIALOG_FileNew(VOID)
     }
 }
 
+/* Used to detect encoding of files selected in Open dialog.
+ * Returns ENCODING_AUTO if file can't be read, etc.
+ */
+static ENCODING detect_encoding_of_file(LPCWSTR szFileName)
+{
+    DWORD size;
+    HANDLE hFile = CreateFileW(szFileName, GENERIC_READ, FILE_SHARE_READ, NULL,
+                               OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+    if (hFile == INVALID_HANDLE_VALUE)
+        return ENCODING_AUTO;
+    size = GetFileSize(hFile, NULL);
+    if (size == INVALID_FILE_SIZE)
+    {
+        CloseHandle(hFile);
+        return ENCODING_AUTO;
+    }
+    else
+    {
+        DWORD dwNumRead;
+        BYTE buffer[MAX_STRING_LEN];
+        if (!ReadFile(hFile, buffer, min(size, sizeof(buffer)), &dwNumRead, NULL))
+        {
+            CloseHandle(hFile);
+            return ENCODING_AUTO;
+        }
+        CloseHandle(hFile);
+        return detect_encoding_of_buffer(buffer, dwNumRead);
+    }
+}
+
+static UINT_PTR CALLBACK OfnHookProc(HWND hdlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+    static HWND hEncCombo;
+
+    switch (uMsg)
+    {
+    case WM_INITDIALOG:
+        {
+            ENCODING enc;
+            hEncCombo = GetDlgItem(hdlg, IDC_OFN_ENCCOMBO);
+            for (enc = MIN_ENCODING; enc <= MAX_ENCODING; enc++)
+            {
+                WCHAR szEnc[MAX_STRING_LEN];
+                load_encoding_name(enc, szEnc, ARRAY_SIZE(szEnc));
+                SendMessageW(hEncCombo, CB_ADDSTRING, 0, (LPARAM)szEnc);
+            }
+            SendMessageW(hEncCombo, CB_SETCURSEL, (WPARAM)Globals.encOfnCombo, 0);
+        }
+        break;
+
+    case WM_COMMAND:
+        if (LOWORD(wParam) == IDC_OFN_ENCCOMBO &&
+            HIWORD(wParam) == CBN_SELCHANGE)
+        {
+            int index = SendMessageW(hEncCombo, CB_GETCURSEL, 0, 0);
+            Globals.encOfnCombo = index==CB_ERR ? ENCODING_ANSI : (ENCODING)index;
+        }
+
+        break;
+
+    case WM_NOTIFY:
+        switch (((OFNOTIFYW*)lParam)->hdr.code)
+        {
+            case CDN_SELCHANGE:
+                if (Globals.bOfnIsOpenDialog)
+                {
+                    /* Check the start of the selected file for a BOM. */
+                    ENCODING enc;
+                    WCHAR szFileName[MAX_PATH];
+                    SendMessageW(GetParent(hdlg), CDM_GETFILEPATH,
+                                 ARRAY_SIZE(szFileName), (LPARAM)szFileName);
+                    enc = detect_encoding_of_file(szFileName);
+                    if (enc != ENCODING_AUTO)
+                    {
+                        Globals.encOfnCombo = enc;
+                        SendMessageW(hEncCombo, CB_SETCURSEL, (WPARAM)enc, 0);
+                    }
+                }
+                break;
+
+            default:
+                break;
+        }
+        break;
+
+    default:
+        break;
+    }
+    return 0;
+}
+
 VOID DIALOG_FileOpen(VOID)
 {
     OPENFILENAMEW openfilename;
@@ -432,13 +555,18 @@ VOID DIALOG_FileOpen(VOID)
     openfilename.lpstrFile         = szPath;
     openfilename.nMaxFile          = ARRAY_SIZE(szPath);
     openfilename.lpstrInitialDir   = szDir;
-    openfilename.Flags             = OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST |
-        OFN_HIDEREADONLY | OFN_ENABLESIZING;
+    openfilename.Flags = OFN_ENABLETEMPLATE | OFN_ENABLEHOOK | OFN_EXPLORER |
+                         OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST |
+                         OFN_HIDEREADONLY | OFN_ENABLESIZING;
+    openfilename.lpfnHook          = OfnHookProc;
+    openfilename.lpTemplateName    = MAKEINTRESOURCEW(IDD_OFN_TEMPLATE);
     openfilename.lpstrDefExt       = szDefaultExt;
 
+    Globals.encOfnCombo = ENCODING_ANSI;
+    Globals.bOfnIsOpenDialog = TRUE;
 
     if (GetOpenFileNameW(&openfilename))
-        DoOpenFile(openfilename.lpstrFile, ENCODING_AUTO);
+        DoOpenFile(openfilename.lpstrFile, Globals.encOfnCombo);
 }
 
 
@@ -471,12 +599,19 @@ BOOL DIALOG_FileSaveAs(VOID)
     saveas.lpstrFile         = szPath;
     saveas.nMaxFile          = ARRAY_SIZE(szPath);
     saveas.lpstrInitialDir   = szDir;
-    saveas.Flags             = OFN_PATHMUSTEXIST | OFN_OVERWRITEPROMPT |
-        OFN_HIDEREADONLY | OFN_ENABLESIZING;
+    saveas.Flags          = OFN_ENABLETEMPLATE | OFN_ENABLEHOOK | OFN_EXPLORER |
+                            OFN_PATHMUSTEXIST | OFN_OVERWRITEPROMPT |
+                            OFN_HIDEREADONLY | OFN_ENABLESIZING;
+    saveas.lpfnHook          = OfnHookProc;
+    saveas.lpTemplateName    = MAKEINTRESOURCEW(IDD_OFN_TEMPLATE);
     saveas.lpstrDefExt       = szDefaultExt;
 
+    /* Preset encoding to what file was opened/saved last with. */
+    Globals.encOfnCombo = Globals.encFile;
+    Globals.bOfnIsOpenDialog = FALSE;
+
     if (GetSaveFileNameW(&saveas)) {
-        SetFileNameAndEncoding(szPath, Globals.encFile);
+        SetFileNameAndEncoding(szPath, Globals.encOfnCombo);
         UpdateWindowCaption();
         DoSaveFile(szPath, Globals.encFile);
         return TRUE;
diff --git a/programs/notepad/main.h b/programs/notepad/main.h
index e5662ff..b672be6 100644
--- a/programs/notepad/main.h
+++ b/programs/notepad/main.h
@@ -25,15 +25,19 @@
 
 #define MAX_STRING_LEN      255
 
+/* Values are indexes of the items in the Encoding combobox. */
 typedef enum
 {
-    ENCODING_AUTO,
-    ENCODING_ANSI,
-    ENCODING_UTF16LE,
-    ENCODING_UTF16BE,
-    ENCODING_UTF8
+    ENCODING_AUTO    = -1,
+    ENCODING_ANSI    =  0,
+    ENCODING_UTF16LE =  1,
+    ENCODING_UTF16BE =  2,
+    ENCODING_UTF8    =  3
 } ENCODING;
 
+#define MIN_ENCODING   0
+#define MAX_ENCODING   3
+
 typedef struct
 {
   HANDLE   hInstance;
@@ -49,6 +53,8 @@ typedef struct
   WCHAR    szFileTitle[MAX_PATH];
   ENCODING encFile;
   WCHAR    szFilter[2 * MAX_STRING_LEN + 100];
+  ENCODING encOfnCombo;  /* Encoding selected in IDC_OFN_ENCCOMBO */
+  BOOL     bOfnIsOpenDialog;
   INT      iMarginTop;
   INT      iMarginBottom;
   INT      iMarginLeft;
diff --git a/programs/notepad/notepad_res.h b/programs/notepad/notepad_res.h
index 8176b85..3122d98 100644
--- a/programs/notepad/notepad_res.h
+++ b/programs/notepad/notepad_res.h
@@ -81,3 +81,10 @@
 
 #define STRING_NOTFOUND 0x17B
 #define STRING_OUT_OF_MEMORY 0x17C
+
+#define STRING_UNICODE_LE      0x180
+#define STRING_UNICODE_BE      0x181
+
+/* Open/Save As dialog template */
+#define IDD_OFN_TEMPLATE       0x190
+#define IDC_OFN_ENCCOMBO       0x191
-- 
1.5.6.3


More information about the wine-patches mailing list