/* * Notepad (dialog.c) * * Copyright 1998,99 Marcel Baur <mbaur@g26.ethz.ch> * Copyright 2002 Sylvain Petreolle <spetreolle@yahoo.fr> * Copyright 2002 Andriy Palamarchuk * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #define UNICODE #include <assert.h> #include <stdio.h> #include <windows.h> #include <commdlg.h> #include "main.h" #include "license.h" #include "dialog.h" static const WCHAR helpfileW[] = { 'n','o','t','e','p','a','d','.','h','l','p',0 }; static INT_PTR WINAPI DIALOG_PAGESETUP_DlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam); VOID ShowLastError(void) { DWORD error = GetLastError(); if (error != NO_ERROR) { LPWSTR lpMsgBuf; WCHAR szTitle[MAX_STRING_LEN]; LoadString(Globals.hInstance, STRING_ERROR, szTitle, SIZEOF(szTitle)); FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, error, 0, (LPTSTR) &lpMsgBuf, 0, NULL); MessageBox(NULL, lpMsgBuf, szTitle, MB_OK | MB_ICONERROR); LocalFree(lpMsgBuf); } } /** * Sets the caption of the main window according to Globals.szFileTitle: * Notepad - (untitled) if no file is open * Notepad - [filename] if a file is given */ static void UpdateWindowCaption(void) { WCHAR szCaption[MAX_STRING_LEN]; WCHAR szUntitled[MAX_STRING_LEN]; LoadString(Globals.hInstance, STRING_NOTEPAD, szCaption, SIZEOF(szCaption)); if (Globals.szFileTitle[0] != '\0') { static const WCHAR bracket_lW[] = { ' ','-',' ','[',0 }; static const WCHAR bracket_rW[] = { ']',0 }; lstrcat(szCaption, bracket_lW); lstrcat(szCaption, Globals.szFileTitle); lstrcat(szCaption, bracket_rW); } else { static const WCHAR hyphenW[] = { ' ','-',' ',0 }; LoadString(Globals.hInstance, STRING_UNTITLED, szUntitled, SIZEOF(szUntitled)); lstrcat(szCaption, hyphenW); lstrcat(szCaption, szUntitled); } SetWindowText(Globals.hMainWnd, szCaption); } static void AlertFileNotFound(LPCWSTR szFileName) { WCHAR szMessage[MAX_STRING_LEN]; WCHAR szResource[MAX_STRING_LEN]; /* Load and format szMessage */ LoadString(Globals.hInstance, STRING_NOTFOUND, szResource, SIZEOF(szResource)); wsprintf(szMessage, szResource, szFileName); /* Load szCaption */ LoadString(Globals.hInstance, STRING_ERROR, szResource, SIZEOF(szResource)); /* Display Modal Dialog */ MessageBox(Globals.hMainWnd, szMessage, szResource, MB_ICONEXCLAMATION); } static int AlertFileNotSaved(LPCWSTR szFileName) { WCHAR szMessage[MAX_STRING_LEN]; WCHAR szResource[MAX_STRING_LEN]; WCHAR szUntitled[MAX_STRING_LEN]; LoadString(Globals.hInstance, STRING_UNTITLED, szUntitled, SIZEOF(szUntitled)); /* Load and format Message */ LoadString(Globals.hInstance, STRING_NOTSAVED, szResource, SIZEOF(szResource)); wsprintf(szMessage, szResource, szFileName[0] ? szFileName : szUntitled); /* Load Caption */ LoadString(Globals.hInstance, STRING_ERROR, szResource, SIZEOF(szResource)); /* Display modal */ return MessageBox(Globals.hMainWnd, szMessage, szResource, MB_ICONEXCLAMATION|MB_YESNOCANCEL); } /** * Returns: * TRUE - if file exists * FALSE - if file does not exist */ BOOL FileExists(LPCWSTR szFilename) { WIN32_FIND_DATA entry; HANDLE hFile; hFile = FindFirstFile(szFilename, &entry); FindClose(hFile); return (hFile != INVALID_HANDLE_VALUE); } static VOID DoSaveFile(VOID) { HANDLE hFile; DWORD dwNumWrite; LPSTR pTemp; DWORD size; hFile = CreateFile(Globals.szFileName, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if(hFile == INVALID_HANDLE_VALUE) { ShowLastError(); return; } size = GetWindowTextLengthA(Globals.hEdit) + 1; pTemp = HeapAlloc(GetProcessHeap(), 0, size); if (!pTemp) { CloseHandle(hFile); ShowLastError(); return; } size = GetWindowTextA(Globals.hEdit, pTemp, size); if (!WriteFile(hFile, pTemp, size, &dwNumWrite, NULL)) ShowLastError(); else SendMessage(Globals.hEdit, EM_SETMODIFY, FALSE, 0); CloseHandle(hFile); HeapFree(GetProcessHeap(), 0, pTemp); } /** * Returns: * TRUE - User agreed to close (both save/don't save) * FALSE - User cancelled close by selecting "Cancel" */ BOOL DoCloseFile(void) { int nResult; static const WCHAR empty_strW[] = { 0 }; if (SendMessage(Globals.hEdit, EM_GETMODIFY, 0, 0)) { /* prompt user to save changes */ nResult = AlertFileNotSaved(Globals.szFileName); switch (nResult) { case IDYES: DIALOG_FileSave(); break; case IDNO: break; case IDCANCEL: return(FALSE); break; default: return(FALSE); break; } /* switch */ } /* if */ SetFileName(empty_strW); UpdateWindowCaption(); return(TRUE); } void DoOpenFile(LPCWSTR szFileName) { HANDLE hFile; LPSTR pTemp; DWORD size; DWORD dwNumRead; /* Close any files and prompt to save changes */ if (!DoCloseFile()) return; hFile = CreateFile(szFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if(hFile == INVALID_HANDLE_VALUE) { ShowLastError(); return; } size = GetFileSize(hFile, NULL); if (size == INVALID_FILE_SIZE) { CloseHandle(hFile); ShowLastError(); return; } size++; pTemp = HeapAlloc(GetProcessHeap(), 0, size); if (!pTemp) { CloseHandle(hFile); ShowLastError(); return; } if (!ReadFile(hFile, pTemp, size, &dwNumRead, NULL)) { CloseHandle(hFile); HeapFree(GetProcessHeap(), 0, pTemp); ShowLastError(); return; } CloseHandle(hFile); pTemp[dwNumRead] = 0; 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); } else SetWindowTextA(Globals.hEdit, pTemp); HeapFree(GetProcessHeap(), 0, pTemp); SendMessage(Globals.hEdit, EM_SETMODIFY, FALSE, 0); SendMessage(Globals.hEdit, EM_EMPTYUNDOBUFFER, 0, 0); SetFocus(Globals.hEdit); SetFileName(szFileName); UpdateWindowCaption(); } VOID DIALOG_FileNew(VOID) { static const WCHAR empty_strW[] = { 0 }; /* Close any files and promt to save changes */ if (DoCloseFile()) { SetWindowText(Globals.hEdit, empty_strW); SendMessage(Globals.hEdit, EM_EMPTYUNDOBUFFER, 0, 0); SetFocus(Globals.hEdit); } } VOID DIALOG_FileOpen(VOID) { OPENFILENAME openfilename; WCHAR szPath[MAX_PATH]; WCHAR szDir[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); openfilename.lStructSize = sizeof(openfilename); openfilename.hwndOwner = Globals.hMainWnd; openfilename.hInstance = Globals.hInstance; openfilename.lpstrFilter = Globals.szFilter; openfilename.lpstrFile = szPath; openfilename.nMaxFile = SIZEOF(szPath); openfilename.lpstrInitialDir = szDir; openfilename.Flags = OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY; openfilename.lpstrDefExt = szDefaultExt; if (GetOpenFileName(&openfilename)) { if (FileExists(openfilename.lpstrFile)) DoOpenFile(openfilename.lpstrFile); else AlertFileNotFound(openfilename.lpstrFile); } } VOID DIALOG_FileSave(VOID) { if (Globals.szFileName[0] == '\0') DIALOG_FileSaveAs(); else DoSaveFile(); } VOID DIALOG_FileSaveAs(VOID) { OPENFILENAME saveas; WCHAR szPath[MAX_PATH]; WCHAR szDir[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); saveas.lStructSize = sizeof(OPENFILENAME); saveas.hwndOwner = Globals.hMainWnd; saveas.hInstance = Globals.hInstance; saveas.lpstrFilter = Globals.szFilter; saveas.lpstrFile = szPath; saveas.nMaxFile = SIZEOF(szPath); saveas.lpstrInitialDir = szDir; saveas.Flags = OFN_PATHMUSTEXIST | OFN_OVERWRITEPROMPT | OFN_HIDEREADONLY; saveas.lpstrDefExt = szDefaultExt; if (GetSaveFileName(&saveas)) { SetFileName(szPath); UpdateWindowCaption(); DoSaveFile(); } } VOID DIALOG_FilePrint(VOID) { DOCINFO di; PRINTDLG printer; SIZE szMetric; int cWidthPels, cHeightPels, border; int xLeft, yTop, pagecount, dopage, copycount; unsigned int i; LOGFONT hdrFont; HFONT font, old_font=0; DWORD size; LPWSTR pTemp; static const WCHAR times_new_romanW[] = { 'T','i','m','e','s',' ','N','e','w',' ','R','o','m','a','n',0 }; /* Get a small font and print some header info on each page */ hdrFont.lfHeight = 100; hdrFont.lfWidth = 0; hdrFont.lfEscapement = 0; hdrFont.lfOrientation = 0; hdrFont.lfWeight = FW_BOLD; hdrFont.lfItalic = 0; hdrFont.lfUnderline = 0; hdrFont.lfStrikeOut = 0; hdrFont.lfCharSet = ANSI_CHARSET; hdrFont.lfOutPrecision = OUT_DEFAULT_PRECIS; hdrFont.lfClipPrecision = CLIP_DEFAULT_PRECIS; 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; printer.nMinPage = 1; /* we really need to calculate number of pages to set nMaxPage and nToPage */ printer.nToPage = 0; printer.nMaxPage = -1; /* Let commdlg manage copy settings */ printer.nCopies = (WORD)PD_USEDEVMODECOPIES; if (!PrintDlg(&printer)) return; assert(printer.hDC != 0); /* initialize DOCINFO */ di.cbSize = sizeof(DOCINFO); di.lpszDocName = Globals.szFileTitle; di.lpszOutput = NULL; di.lpszDatatype = NULL; 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; pTemp = HeapAlloc(GetProcessHeap(), 0, size * sizeof(WCHAR)); if (!pTemp) { ShowLastError(); return; } size = GetWindowText(Globals.hEdit, pTemp, size); border = 150; for (copycount=1; copycount <= printer.nCopies; copycount++) { i = 0; pagecount = 1; do { static const WCHAR letterM[] = { 'M',0 }; if (pagecount >= printer.nFromPage && /* ((printer.Flags & PD_PAGENUMS) == 0 || pagecount <= printer.nToPage))*/ pagecount <= printer.nToPage) 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 }; static const WCHAR errorW[] = { 'P','r','i','n','t',' ','E','r','r','o','r',0 }; MessageBox(Globals.hMainWnd, failedW, errorW, MB_ICONEXCLAMATION); return; } /* Write a rectangle and header at the top of each page */ Rectangle(printer.hDC, border, border, cWidthPels-border, border+szMetric.cy*2); /* I don't know what's up with this TextOut command. This comes out kind of mangled. */ 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); /* Since outputting strings is giving me problems, output the main text one character at a time. */ do { if (pTemp[i] == '\n') { xLeft = border*2; yTop += szMetric.cy; } else if (pTemp[i] != '\r') { if (dopage) TextOut(printer.hDC, xLeft, yTop, &pTemp[i], 1); xLeft += szMetric.cx; } } while (i++<size && yTop<(cHeightPels-border*2)); if (dopage) EndPage(printer.hDC); pagecount++; } while (i<size); } EndDoc(printer.hDC); DeleteDC(printer.hDC); HeapFree(GetProcessHeap(), 0, pTemp); } VOID DIALOG_FilePrinterSetup(VOID) { PRINTDLG printer; ZeroMemory(&printer, sizeof(printer)); printer.lStructSize = sizeof(printer); printer.hwndOwner = Globals.hMainWnd; printer.hInstance = Globals.hInstance; printer.Flags = PD_PRINTSETUP; printer.nCopies = 1; PrintDlg(&printer); } VOID DIALOG_FileExit(VOID) { PostMessage(Globals.hMainWnd, WM_CLOSE, 0, 0l); } VOID DIALOG_EditUndo(VOID) { SendMessage(Globals.hEdit, EM_UNDO, 0, 0); } VOID DIALOG_EditCut(VOID) { SendMessage(Globals.hEdit, WM_CUT, 0, 0); } VOID DIALOG_EditCopy(VOID) { SendMessage(Globals.hEdit, WM_COPY, 0, 0); } VOID DIALOG_EditPaste(VOID) { SendMessage(Globals.hEdit, WM_PASTE, 0, 0); } VOID DIALOG_EditDelete(VOID) { SendMessage(Globals.hEdit, WM_CLEAR, 0, 0); } VOID DIALOG_EditSelectAll(VOID) { SendMessage(Globals.hEdit, EM_SETSEL, 0, (LPARAM)-1); } VOID DIALOG_EditTimeDate(VOID) { SYSTEMTIME st; WCHAR szDate[MAX_STRING_LEN]; static const WCHAR spaceW[] = { ' ',0 }; GetLocalTime(&st); GetTimeFormat(LOCALE_USER_DEFAULT, 0, &st, NULL, szDate, MAX_STRING_LEN); SendMessage(Globals.hEdit, EM_REPLACESEL, TRUE, (LPARAM)szDate); SendMessage(Globals.hEdit, EM_REPLACESEL, TRUE, (LPARAM)spaceW); GetDateFormat(LOCALE_USER_DEFAULT, DATE_LONGDATE, &st, NULL, szDate, MAX_STRING_LEN); SendMessage(Globals.hEdit, EM_REPLACESEL, TRUE, (LPARAM)szDate); } 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)); } VOID DIALOG_SelectFont(VOID) { CHOOSEFONT cf; LOGFONT lf=Globals.lfFont; ZeroMemory( &cf, sizeof(cf) ); cf.lStructSize=sizeof(cf); cf.hwndOwner=Globals.hMainWnd; cf.lpLogFont=&lf; cf.Flags=CF_SCREENFONTS; if( ChooseFont(&cf) ) { HFONT currfont=Globals.hFont; Globals.hFont=CreateFontIndirect( &lf ); Globals.lfFont=lf; SendMessage( Globals.hEdit, WM_SETFONT, (WPARAM)Globals.hFont, (LPARAM)TRUE ); if( currfont!=NULL ) DeleteObject( currfont ); } } VOID DIALOG_Search(VOID) { ZeroMemory(&Globals.find, sizeof(Globals.find)); Globals.find.lStructSize = sizeof(Globals.find); Globals.find.hwndOwner = Globals.hMainWnd; Globals.find.hInstance = Globals.hInstance; Globals.find.lpstrFindWhat = Globals.szFindText; Globals.find.wFindWhatLen = SIZEOF(Globals.szFindText); Globals.find.Flags = FR_DOWN; /* We only need to create the modal FindReplace dialog which will */ /* notify us of incoming events using hMainWnd Window Messages */ Globals.hFindReplaceDlg = FindText(&Globals.find); assert(Globals.hFindReplaceDlg !=0); } VOID DIALOG_SearchNext(VOID) { /* FIXME: Search Next */ DIALOG_Search(); } VOID DIALOG_HelpContents(VOID) { WinHelp(Globals.hMainWnd, helpfileW, HELP_INDEX, 0); } VOID DIALOG_HelpSearch(VOID) { /* Search Help */ } VOID DIALOG_HelpHelp(VOID) { WinHelp(Globals.hMainWnd, helpfileW, HELP_HELPONHELP, 0); } VOID DIALOG_HelpLicense(VOID) { WineLicense(Globals.hMainWnd); } VOID DIALOG_HelpNoWarranty(VOID) { WineWarranty(Globals.hMainWnd); } VOID DIALOG_HelpAboutWine(VOID) { static const WCHAR notepadW[] = { 'N','o','t','e','p','a','d','\n',0 }; WCHAR szNotepad[MAX_STRING_LEN]; LoadString(Globals.hInstance, STRING_NOTEPAD, szNotepad, SIZEOF(szNotepad)); ShellAbout(Globals.hMainWnd, szNotepad, notepadW, 0); } /*********************************************************************** * * DIALOG_FilePageSetup */ VOID DIALOG_FilePageSetup(void) { DialogBox(Globals.hInstance, MAKEINTRESOURCE(DIALOG_PAGESETUP), Globals.hMainWnd, DIALOG_PAGESETUP_DlgProc); } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * DIALOG_PAGESETUP_DlgProc */ static INT_PTR WINAPI DIALOG_PAGESETUP_DlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam) { switch (msg) { case WM_COMMAND: switch (wParam) { case IDOK: /* save user input and close dialog */ GetDlgItemText(hDlg, 0x141, Globals.szHeader, SIZEOF(Globals.szHeader)); GetDlgItemText(hDlg, 0x143, Globals.szFooter, SIZEOF(Globals.szFooter)); GetDlgItemText(hDlg, 0x14A, Globals.szMarginTop, SIZEOF(Globals.szMarginTop)); GetDlgItemText(hDlg, 0x150, Globals.szMarginBottom, SIZEOF(Globals.szMarginBottom)); GetDlgItemText(hDlg, 0x147, Globals.szMarginLeft, SIZEOF(Globals.szMarginLeft)); GetDlgItemText(hDlg, 0x14D, Globals.szMarginRight, SIZEOF(Globals.szMarginRight)); EndDialog(hDlg, IDOK); return TRUE; case IDCANCEL: /* discard user input and close dialog */ EndDialog(hDlg, IDCANCEL); return TRUE; case IDHELP: { /* FIXME: Bring this to work */ static const WCHAR sorryW[] = { 'S','o','r','r','y',',',' ','n','o',' ','h','e','l','p',' ','a','v','a','i','l','a','b','l','e',0 }; static const WCHAR helpW[] = { 'H','e','l','p',0 }; MessageBox(Globals.hMainWnd, sorryW, helpW, MB_ICONEXCLAMATION); return TRUE; } default: break; } break; case WM_INITDIALOG: /* fetch last user input prior to display dialog */ SetDlgItemText(hDlg, 0x141, Globals.szHeader); SetDlgItemText(hDlg, 0x143, Globals.szFooter); SetDlgItemText(hDlg, 0x14A, Globals.szMarginTop); SetDlgItemText(hDlg, 0x150, Globals.szMarginBottom); SetDlgItemText(hDlg, 0x147, Globals.szMarginLeft); SetDlgItemText(hDlg, 0x14D, Globals.szMarginRight); break; } return FALSE; }