notepad: Allow user to choose which encoding to open and save files in.
diff --git a/programs/notepad/dialog.c b/programs/notepad/dialog.c
index 67a21a7..cef0dce 100644
--- a/programs/notepad/dialog.c
+++ b/programs/notepad/dialog.c
@@ -45,6 +45,28 @@
     for (i = 0; i < num; i++) str[i] = RtlUshortByteSwap(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();
@@ -286,14 +308,13 @@
     return ret;
 }
 
-void DoOpenFile(LPCWSTR szFileName)
+void DoOpenFile(LPCWSTR szFileName, ENCODING enc)
 {
     static const WCHAR dotlog[] = { '.','L','O','G',0 };
     HANDLE hFile;
     LPSTR pTemp;
     DWORD size;
     DWORD dwNumRead;
-    ENCODING enc;
     BOOL succeeded;
     WCHAR log[5];
 
@@ -340,7 +361,18 @@
     pTemp[size] = 0;    /* make sure it's  (char)'\0'-terminated */
     pTemp[size+1] = 0;  /* make sure it's (WCHAR)'\0'-terminated */
 
-    enc = detect_encoding_of_buffer(pTemp, size);
+    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.
@@ -413,6 +445,97 @@
     }
 }
 
+/* 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;
@@ -433,13 +556,18 @@
     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);
+        DoOpenFile(openfilename.lpstrFile, Globals.encOfnCombo);
 }
 
 
@@ -472,12 +600,19 @@
     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();
         return TRUE;