Kevin Koltzau <kevin@plop.org> Handle notepad log feature.
[reactos.git] / reactos / subsys / system / notepad / dialog.c
index 9c352d4..a44a1c6 100644 (file)
@@ -139,34 +139,99 @@ static VOID DoSaveFile(VOID)
 {
     HANDLE hFile;
     DWORD dwNumWrite;
-    LPSTR pTemp;
+    LPWSTR pTemp;
+    LPVOID pConverted = NULL;
     DWORD size;
+    BYTE bom[3];
+    int iBomSize = 0;
+    int iCodePage = -1;
+    int iNewSize;
+    int i;
 
     hFile = CreateFile(Globals.szFileName, GENERIC_WRITE, FILE_SHARE_WRITE,
-                       NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+                       NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
     if(hFile == INVALID_HANDLE_VALUE)
     {
         ShowLastError();
         return;
     }
 
-    size = GetWindowTextLengthA(Globals.hEdit) + 1;
-    pTemp = HeapAlloc(GetProcessHeap(), 0, size);
+    size = GetWindowTextLengthW(Globals.hEdit) + 1;
+    pTemp = HeapAlloc(GetProcessHeap(), 0, size * sizeof(*pTemp));
     if (!pTemp)
     {
-       CloseHandle(hFile);
+        CloseHandle(hFile);
         ShowLastError();
         return;
     }
-    size = GetWindowTextA(Globals.hEdit, pTemp, size);
+    size = GetWindowTextW(Globals.hEdit, pTemp, size);
+
+    switch(Globals.iEncoding)
+    {
+        case ENCODING_ANSI:
+            iCodePage = CP_ACP;
+            break;
+
+        case ENCODING_UNICODE:
+            pConverted = pTemp;
+            iBomSize = 2;
+            bom[0] = 0xFF;
+            bom[1] = 0xFE;
+            break;
+
+        case ENCODING_UNICODE_BE:
+            pConverted = pTemp;
+            iBomSize = 2;
+            bom[0] = 0xFE;
+            bom[1] = 0xFF;
+
+            /* flip the endianness */
+            for (i = 0; i < size; i++)
+            {
+                pTemp[i] = ((pTemp[i] & 0x00FF) << 8)
+                           | ((pTemp[i] & 0xFF00) >> 8);
+            }
+            break;
+
+        case ENCODING_UTF8:
+            iCodePage = CP_UTF8;
+            iBomSize = 3;
+            bom[0] = 0xEF;
+            bom[1] = 0xBB;
+            bom[2] = 0xBF;
+            break;
+    }
+
+    if (iCodePage >= 0)
+    {
+        iNewSize = WideCharToMultiByte(iCodePage, 0, pTemp, size, NULL, 0, NULL, NULL);
+        pConverted = HeapAlloc(GetProcessHeap(), 0, iNewSize);
+        if (!pConverted)
+        {
+            HeapFree(GetProcessHeap(), 0, pTemp);
+            CloseHandle(hFile);
+            ShowLastError();
+            return;
+        }
+        WideCharToMultiByte(iCodePage, 0, pTemp, size, pConverted, iNewSize, NULL, NULL);
+    }
+    else
+    {
+        iNewSize = size * sizeof(WCHAR);
+    }
 
-    if (!WriteFile(hFile, pTemp, size, &dwNumWrite, NULL))
+    if ((iBomSize > 0) && !WriteFile(hFile, bom, iBomSize, &dwNumWrite, NULL))
+        ShowLastError();
+    else if (!WriteFile(hFile, pConverted, iNewSize, &dwNumWrite, NULL))
         ShowLastError();
     else
         SendMessage(Globals.hEdit, EM_SETMODIFY, FALSE, 0);
 
     CloseHandle(hFile);
     HeapFree(GetProcessHeap(), 0, pTemp);
+
+    if (iCodePage >= 0)
+        HeapFree(GetProcessHeap(), 0, pConverted);
 }
 
 /**
@@ -206,46 +271,52 @@ BOOL DoCloseFile(void)
 
 void DoOpenFile(LPCWSTR szFileName)
 {
+    static const WCHAR dotlog[] = { '.','L','O','G',0 };
     HANDLE hFile;
     LPSTR pTemp;
+    LPWSTR pTemp2 = NULL;
     DWORD size;
     DWORD dwNumRead;
+    WCHAR log[5];
+    LPWSTR p;
+    LPBYTE p2;
+    int iCodePage;
+    int iNewSize;
 
     /* Close any files and prompt to save changes */
     if (!DoCloseFile())
-       return;
+        return;
 
     hFile = CreateFile(szFileName, GENERIC_READ, FILE_SHARE_READ, NULL,
-       OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+                       OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
     if(hFile == INVALID_HANDLE_VALUE)
     {
-       ShowLastError();
-       return;
+        ShowLastError();
+        return;
     }
 
     size = GetFileSize(hFile, NULL);
     if (size == INVALID_FILE_SIZE)
     {
-       CloseHandle(hFile);
-       ShowLastError();
-       return;
+        CloseHandle(hFile);
+        ShowLastError();
+        return;
     }
-    size++;
 
-    pTemp = HeapAlloc(GetProcessHeap(), 0, size);
+    pTemp = HeapAlloc(GetProcessHeap(), 0, size + sizeof(WCHAR));
     if (!pTemp)
     {
-       CloseHandle(hFile);
-       ShowLastError();
-       return;
+        CloseHandle(hFile);
+        ShowLastError();
+        return;
     }
 
     if (!ReadFile(hFile, pTemp, size, &dwNumRead, NULL))
     {
-       CloseHandle(hFile);
-       HeapFree(GetProcessHeap(), 0, pTemp);
-       ShowLastError();
-       return;
+        CloseHandle(hFile);
+        HeapFree(GetProcessHeap(), 0, pTemp);
+        ShowLastError();
+        return;
     }
 
     CloseHandle(hFile);
@@ -253,19 +324,71 @@ void DoOpenFile(LPCWSTR szFileName)
 
     if (IsTextUnicode(pTemp, dwNumRead, NULL))
     {
-       LPWSTR p = (LPWSTR)pTemp;
-       /* We need to strip BOM Unicode character, SetWindowTextW won't do it for us. */
-       if (*p == 0xFEFF || *p == 0xFFFE) p++;
-       SetWindowTextW(Globals.hEdit, p);
+        p = (LPWSTR)pTemp;
+        p[dwNumRead / 2] = 0;
+
+        /* We need to strip BOM Unicode character, SetWindowTextW won't do it for us. */
+        if (*p == 0xFEFF)
+        {
+            Globals.iEncoding = ENCODING_UNICODE_BE;
+            p++;
+        }
+        else if (*p == 0xFFFE)
+        {
+            Globals.iEncoding = ENCODING_UNICODE;
+            p++;
+        }
     }
     else
-       SetWindowTextA(Globals.hEdit, pTemp);
+    {
+        p2 = (LPBYTE)pTemp;
+        if ((p2[0] == 0xEF) && (p2[1] == 0xBB) && (p2[2] == 0xBF))
+        {
+            iCodePage = CP_UTF8;
+            Globals.iEncoding = ENCODING_UTF8;
+            p2 += 3;
+            dwNumRead -= 3;
+        }
+        else
+        {
+            iCodePage = CP_ACP;
+            Globals.iEncoding = ENCODING_ANSI;
+        }
+
+        iNewSize = MultiByteToWideChar(iCodePage, 0, (LPCSTR)p2, dwNumRead, NULL, 0);
+        pTemp2 = HeapAlloc(GetProcessHeap(), 0, (iNewSize + 1) * sizeof(*pTemp2));
+        if (!pTemp2)
+        {
+            CloseHandle(hFile);
+            HeapFree(GetProcessHeap(), 0, pTemp);
+            ShowLastError();
+            return;
+        }
+        MultiByteToWideChar(iCodePage, 0, (LPCSTR)p2, dwNumRead, pTemp2, iNewSize);
+        pTemp2[iNewSize] = 0;
+        p = pTemp2;
+    }
+    SetWindowTextW(Globals.hEdit, p);
 
     HeapFree(GetProcessHeap(), 0, pTemp);
+    if (pTemp2)
+       HeapFree(GetProcessHeap(), 0, pTemp2);
 
     SendMessage(Globals.hEdit, EM_SETMODIFY, FALSE, 0);
     SendMessage(Globals.hEdit, EM_EMPTYUNDOBUFFER, 0, 0);
     SetFocus(Globals.hEdit);
+    
+    /*  If the file starts with .LOG, add a time/date at the end and set cursor after
+     *  See http://support.microsoft.com/?kbid=260563
+     */
+    if (GetWindowTextW(Globals.hEdit, log, sizeof(log)/sizeof(log[0])) && !lstrcmp(log, dotlog))
+    {
+       static const WCHAR lfW[] = { '\r','\n',0 };
+       SendMessage(Globals.hEdit, EM_SETSEL, GetWindowTextLength(Globals.hEdit), -1);
+       SendMessage(Globals.hEdit, EM_REPLACESEL, TRUE, (LPARAM)lfW);
+       DIALOG_EditTimeDate();
+       SendMessage(Globals.hEdit, EM_REPLACESEL, TRUE, (LPARAM)lfW);
+    }
 
     SetFileName(szFileName);
     UpdateWindowCaption();
@@ -286,15 +409,18 @@ VOID DIALOG_FileNew(VOID)
 VOID DIALOG_FileOpen(VOID)
 {
     OPENFILENAME openfilename;
-    WCHAR szPath[MAX_PATH];
     WCHAR szDir[MAX_PATH];
+    WCHAR szPath[MAX_PATH];
     static const WCHAR szDefaultExt[] = { 't','x','t',0 };
     static const WCHAR txt_files[] = { '*','.','t','x','t',0 };
 
     ZeroMemory(&openfilename, sizeof(openfilename));
 
     GetCurrentDirectory(SIZEOF(szDir), szDir);
-    lstrcpy(szPath, txt_files);
+    if (Globals.szFileName[0] == 0)
+        lstrcpy(szPath, txt_files);
+    else
+        lstrcpy(szPath, Globals.szFileName);
 
     openfilename.lStructSize       = sizeof(openfilename);
     openfilename.hwndOwner         = Globals.hMainWnd;
@@ -325,18 +451,59 @@ VOID DIALOG_FileSave(VOID)
         DoSaveFile();
 }
 
+static UINT_PTR CALLBACK DIALOG_FileSaveAs_Hook(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+    WCHAR szText[128];
+    HWND hCombo;
+    OFNOTIFY *pNotify;
+
+    switch(msg)
+    {
+        case WM_INITDIALOG:
+            hCombo = GetDlgItem(hDlg, ID_ENCODING);
+
+            LoadString(Globals.hInstance, STRING_ANSI, szText, SIZEOF(szText));
+            SendMessage(hCombo, CB_ADDSTRING, 0, (LPARAM) szText);
+
+            LoadString(Globals.hInstance, STRING_UNICODE, szText, SIZEOF(szText));
+            SendMessage(hCombo, CB_ADDSTRING, 0, (LPARAM) szText);
+
+            LoadString(Globals.hInstance, STRING_UNICODE_BE, szText, SIZEOF(szText));
+            SendMessage(hCombo, CB_ADDSTRING, 0, (LPARAM) szText);
+
+            LoadString(Globals.hInstance, STRING_UTF8, szText, SIZEOF(szText));
+            SendMessage(hCombo, CB_ADDSTRING, 0, (LPARAM) szText);
+
+            SendMessage(hCombo, CB_SETCURSEL, Globals.iEncoding, 0);
+            break;
+
+        case WM_NOTIFY:
+            if (((NMHDR *) lParam)->code == CDN_FILEOK)
+            {
+                pNotify = (OFNOTIFY *) lParam;
+                hCombo = GetDlgItem(hDlg, ID_ENCODING);
+                Globals.iEncoding = SendMessage(hCombo, CB_GETCURSEL, 0, 0);
+            }
+            break;
+    }
+    return 0;
+}
+
 VOID DIALOG_FileSaveAs(VOID)
 {
     OPENFILENAME saveas;
-    WCHAR szPath[MAX_PATH];
     WCHAR szDir[MAX_PATH];
+    WCHAR szPath[MAX_PATH];
     static const WCHAR szDefaultExt[] = { 't','x','t',0 };
     static const WCHAR txt_files[] = { '*','.','t','x','t',0 };
 
     ZeroMemory(&saveas, sizeof(saveas));
 
     GetCurrentDirectory(SIZEOF(szDir), szDir);
-    lstrcpy(szPath, txt_files);
+    if (Globals.szFileName[0] == 0)
+        lstrcpy(szPath, txt_files);
+    else
+        lstrcpy(szPath, Globals.szFileName);
 
     saveas.lStructSize       = sizeof(OPENFILENAME);
     saveas.hwndOwner         = Globals.hMainWnd;
@@ -346,8 +513,10 @@ VOID DIALOG_FileSaveAs(VOID)
     saveas.nMaxFile          = SIZEOF(szPath);
     saveas.lpstrInitialDir   = szDir;
     saveas.Flags             = OFN_PATHMUSTEXIST | OFN_OVERWRITEPROMPT |
-        OFN_HIDEREADONLY;
+        OFN_HIDEREADONLY | OFN_EXPLORER | OFN_ENABLETEMPLATE | OFN_ENABLEHOOK;
     saveas.lpstrDefExt       = szDefaultExt;
+    saveas.lpTemplateName    = MAKEINTRESOURCE(DIALOG_ENCODING);
+    saveas.lpfnHook          = DIALOG_FileSaveAs_Hook;
 
     if (GetSaveFileName(&saveas)) {
         SetFileName(szPath);
@@ -385,15 +554,15 @@ VOID DIALOG_FilePrint(VOID)
     hdrFont.lfQuality = PROOF_QUALITY;
     hdrFont.lfPitchAndFamily = VARIABLE_PITCH | FF_ROMAN;
     lstrcpy(hdrFont.lfFaceName, times_new_romanW);
-    
+
     font = CreateFontIndirect(&hdrFont);
-    
+
     /* Get Current Settings */
     ZeroMemory(&printer, sizeof(printer));
     printer.lStructSize           = sizeof(printer);
     printer.hwndOwner             = Globals.hMainWnd;
     printer.hInstance             = Globals.hInstance;
-    
+
     /* Set some default flags */
     printer.Flags                 = PD_RETURNDC;
     printer.nFromPage             = 0;
@@ -414,24 +583,24 @@ VOID DIALOG_FilePrint(VOID)
     di.lpszDocName = Globals.szFileTitle;
     di.lpszOutput = NULL;
     di.lpszDatatype = NULL;
-    di.fwType = 0; 
+    di.fwType = 0;
 
     if (StartDoc(printer.hDC, &di) <= 0) return;
-    
+
     /* Get the page dimensions in pixels. */
     cWidthPels = GetDeviceCaps(printer.hDC, HORZRES);
     cHeightPels = GetDeviceCaps(printer.hDC, VERTRES);
 
     /* Get the file text */
-    size = GetWindowTextLength(Globals.hEdit) + 1;
+    size = GetWindowTextLengthW(Globals.hEdit) + 1;
     pTemp = HeapAlloc(GetProcessHeap(), 0, size * sizeof(WCHAR));
     if (!pTemp)
     {
         ShowLastError();
         return;
     }
-    size = GetWindowText(Globals.hEdit, pTemp, size);
-    
+    size = GetWindowTextW(Globals.hEdit, pTemp, size);
+
     border = 150;
     for (copycount=1; copycount <= printer.nCopies; copycount++) {
         i = 0;
@@ -445,10 +614,10 @@ VOID DIALOG_FilePrint(VOID)
                 dopage = 1;
             else
                 dopage = 0;
-            
+
             old_font = SelectObject(printer.hDC, font);
             GetTextExtentPoint32(printer.hDC, letterM, 1, &szMetric);
-                
+
             if (dopage) {
                 if (StartPage(printer.hDC) <= 0) {
                     static const WCHAR failedW[] = { 'S','t','a','r','t','P','a','g','e',' ','f','a','i','l','e','d',0 };
@@ -463,14 +632,14 @@ VOID DIALOG_FilePrint(VOID)
                 */
                 TextOut(printer.hDC, border*2, border+szMetric.cy/2, Globals.szFileTitle, lstrlen(Globals.szFileTitle));
             }
-            
+
             /* The starting point for the main text */
             xLeft = border*2;
             yTop = border+szMetric.cy*4;
-            
+
             SelectObject(printer.hDC, old_font);
-            GetTextExtentPoint32(printer.hDC, letterM, 1, &szMetric); 
-            
+            GetTextExtentPoint32(printer.hDC, letterM, 1, &szMetric);
+
             /* Since outputting strings is giving me problems, output the main
             text one character at a time.
             */
@@ -485,7 +654,7 @@ VOID DIALOG_FilePrint(VOID)
                     xLeft += szMetric.cx;
                 }
             } while (i++<size && yTop<(cHeightPels-border*2));
-            
+
             if (dopage)
                 EndPage(printer.hDC);
             pagecount++;
@@ -565,6 +734,32 @@ VOID DIALOG_EditTimeDate(VOID)
 
 VOID DIALOG_EditWrap(VOID)
 {
+    static const WCHAR editW[] = { 'e','d','i','t',0 };
+    DWORD dwStyle = WS_CHILD | WS_VISIBLE | WS_BORDER | WS_VSCROLL |
+                    ES_AUTOVSCROLL | ES_MULTILINE;
+    RECT rc;
+    DWORD size;
+    LPWSTR pTemp;
+
+    size = GetWindowTextLength(Globals.hEdit) + 1;
+    pTemp = HeapAlloc(GetProcessHeap(), 0, size * sizeof(WCHAR));
+    if (!pTemp)
+    {
+        ShowLastError();
+        return;
+    }
+    GetWindowText(Globals.hEdit, pTemp, size);
+    DestroyWindow(Globals.hEdit);
+    GetClientRect(Globals.hMainWnd, &rc);
+    if( Globals.bWrapLongLines ) dwStyle |= WS_HSCROLL | ES_AUTOHSCROLL;
+    Globals.hEdit = CreateWindowEx(WS_EX_CLIENTEDGE, editW, NULL, dwStyle,
+                         0, 0, rc.right, rc.bottom, Globals.hMainWnd,
+                         NULL, Globals.hInstance, NULL);
+    SendMessage(Globals.hEdit, WM_SETFONT, (WPARAM)Globals.hFont, (LPARAM)FALSE);
+    SetWindowTextW(Globals.hEdit, pTemp);
+    SetFocus(Globals.hEdit);
+    HeapFree(GetProcessHeap(), 0, pTemp);
+    
     Globals.bWrapLongLines = !Globals.bWrapLongLines;
     CheckMenuItem(GetMenu(Globals.hMainWnd), CMD_WRAP,
         MF_BYCOMMAND | (Globals.bWrapLongLines ? MF_CHECKED : MF_UNCHECKED));
@@ -700,8 +895,8 @@ static INT_PTR WINAPI DIALOG_PAGESETUP_DlgProc(HWND hDlg, UINT msg, WPARAM wPara
           return TRUE;
         }
 
-       default:
-           break;
+        default:
+          break;
         }
       break;