1 /*
2 * Wordpad implementation
3 *
4 * Copyright 2004 by Krzysztof Foltman
5 * Copyright 2007-2008 by Alexander N. Sørnes <alex@thehandofagony.com>
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 */
21
22 #define WIN32_LEAN_AND_MEAN
23 #define _WIN32_IE 0x0400
24
25 #include <stdarg.h>
26 #include <stdlib.h>
27 #include <ctype.h>
28 #include <stdio.h>
29 #include <assert.h>
30
31 #include <windows.h>
32 #include <richedit.h>
33 #include <commctrl.h>
34 #include <commdlg.h>
35 #include <shellapi.h>
36 #include <math.h>
37 #include <errno.h>
38
39 #include "wordpad.h"
40
41 #ifdef NONAMELESSUNION
42 # define U(x) (x).u
43 # define U2(x) (x).u2
44 # define U3(x) (x).u3
45 #else
46 # define U(x) (x)
47 # define U2(x) (x)
48 # define U3(x) (x)
49 #endif
50
51 /* use LoadString */
52 static const WCHAR xszAppTitle[] = {'W','i','n','e',' ','W','o','r','d','p','a','d',0};
53
54 static const WCHAR wszRichEditClass[] = {'R','I','C','H','E','D','I','T','2','','W',0};
55 static const WCHAR wszMainWndClass[] = {'W','O','R','D','P','A','D','T','O','P',0};
56 static const WCHAR wszAppTitle[] = {'W','i','n','e',' ','W','o','r','d','p','a','d',0};
57
58 static const WCHAR stringFormat[] = {'%','2','d','\0'};
59
60 static HWND hMainWnd;
61 static HWND hEditorWnd;
62 static HWND hFindWnd;
63 static HMENU hPopupMenu;
64
65 static UINT ID_FINDMSGSTRING;
66
67 static DWORD wordWrap[2];
68 static DWORD barState[2];
69 static WPARAM fileFormat = SF_RTF;
70
71 static WCHAR wszFileName[MAX_PATH];
72 static WCHAR wszFilter[MAX_STRING_LEN*4+6*3+5];
73 static WCHAR wszDefaultFileName[MAX_STRING_LEN];
74 static WCHAR wszSaveChanges[MAX_STRING_LEN];
75 static WCHAR units_cmW[MAX_STRING_LEN];
76
77 static char units_cmA[MAX_STRING_LEN];
78
79 static LRESULT OnSize( HWND hWnd, WPARAM wParam, LPARAM lParam );
80
81 /* Load string resources */
82 static void DoLoadStrings(void)
83 {
84 LPWSTR p = wszFilter;
85 static const WCHAR files_rtf[] = {'*','.','r','t','f','\0'};
86 static const WCHAR files_txt[] = {'*','.','t','x','t','\0'};
87 static const WCHAR files_all[] = {'*','.','*','\0'};
88
89 HINSTANCE hInstance = (HINSTANCE)GetWindowLongPtr(hMainWnd, GWLP_HINSTANCE);
90
91 LoadStringW(hInstance, STRING_RICHTEXT_FILES_RTF, p, MAX_STRING_LEN);
92 p += lstrlenW(p) + 1;
93 lstrcpyW(p, files_rtf);
94 p += lstrlenW(p) + 1;
95 LoadStringW(hInstance, STRING_TEXT_FILES_TXT, p, MAX_STRING_LEN);
96 p += lstrlenW(p) + 1;
97 lstrcpyW(p, files_txt);
98 p += lstrlenW(p) + 1;
99 LoadStringW(hInstance, STRING_TEXT_FILES_UNICODE_TXT, p, MAX_STRING_LEN);
100 p += lstrlenW(p) + 1;
101 lstrcpyW(p, files_txt);
102 p += lstrlenW(p) + 1;
103 LoadStringW(hInstance, STRING_ALL_FILES, p, MAX_STRING_LEN);
104 p += lstrlenW(p) + 1;
105 lstrcpyW(p, files_all);
106 p += lstrlenW(p) + 1;
107 *p = '\0';
108
109 p = wszDefaultFileName;
110 LoadStringW(hInstance, STRING_DEFAULT_FILENAME, p, MAX_STRING_LEN);
111
112 p = wszSaveChanges;
113 LoadStringW(hInstance, STRING_PROMPT_SAVE_CHANGES, p, MAX_STRING_LEN);
114
115 LoadStringA(hInstance, STRING_UNITS_CM, units_cmA, MAX_STRING_LEN);
116 LoadStringW(hInstance, STRING_UNITS_CM, units_cmW, MAX_STRING_LEN);
117 }
118
119 static void AddButton(HWND hwndToolBar, int nImage, int nCommand)
120 {
121 TBBUTTON button;
122
123 ZeroMemory(&button, sizeof(button));
124 button.iBitmap = nImage;
125 button.idCommand = nCommand;
126 button.fsState = TBSTATE_ENABLED;
127 button.fsStyle = TBSTYLE_BUTTON;
128 button.dwData = 0;
129 button.iString = -1;
130 SendMessageW(hwndToolBar, TB_ADDBUTTONSW, 1, (LPARAM)&button);
131 }
132
133 static void AddSeparator(HWND hwndToolBar)
134 {
135 TBBUTTON button;
136
137 ZeroMemory(&button, sizeof(button));
138 button.iBitmap = -1;
139 button.idCommand = 0;
140 button.fsState = 0;
141 button.fsStyle = TBSTYLE_SEP;
142 button.dwData = 0;
143 button.iString = -1;
144 SendMessageW(hwndToolBar, TB_ADDBUTTONSW, 1, (LPARAM)&button);
145 }
146
147 static DWORD CALLBACK stream_in(DWORD_PTR cookie, LPBYTE buffer, LONG cb, LONG *pcb)
148 {
149 HANDLE hFile = (HANDLE)cookie;
150 DWORD read;
151
152 if(!ReadFile(hFile, buffer, cb, &read, 0))
153 return 1;
154
155 *pcb = read;
156
157 return 0;
158 }
159
160 static DWORD CALLBACK stream_out(DWORD_PTR cookie, LPBYTE buffer, LONG cb, LONG *pcb)
161 {
162 DWORD written;
163 int ret;
164 HANDLE hFile = (HANDLE)cookie;
165
166 ret = WriteFile(hFile, buffer, cb, &written, 0);
167
168 if(!ret || (cb != written))
169 return 1;
170
171 *pcb = cb;
172
173 return 0;
174 }
175
176 LPWSTR file_basename(LPWSTR path)
177 {
178 LPWSTR pos = path + lstrlenW(path);
179
180 while(pos > path)
181 {
182 if(*pos == '\\' || *pos == '/')
183 {
184 pos++;
185 break;
186 }
187 pos--;
188 }
189 return pos;
190 }
191
192 static void set_caption(LPCWSTR wszNewFileName)
193 {
194 static const WCHAR wszSeparator[] = {' ','-',' '};
195 WCHAR *wszCaption;
196 SIZE_T length = 0;
197
198 if(!wszNewFileName)
199 wszNewFileName = wszDefaultFileName;
200 else
201 wszNewFileName = file_basename((LPWSTR)wszNewFileName);
202
203 wszCaption = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
204 lstrlenW(wszNewFileName)*sizeof(WCHAR)+sizeof(wszSeparator)+sizeof(wszAppTitle));
205
206 if(!wszCaption)
207 return;
208
209 memcpy(wszCaption, wszNewFileName, lstrlenW(wszNewFileName)*sizeof(WCHAR));
210 length += lstrlenW(wszNewFileName);
211 memcpy(wszCaption + length, wszSeparator, sizeof(wszSeparator));
212 length += sizeof(wszSeparator) / sizeof(WCHAR);
213 memcpy(wszCaption + length, wszAppTitle, sizeof(wszAppTitle));
214
215 SetWindowTextW(hMainWnd, wszCaption);
216
217 HeapFree(GetProcessHeap(), 0, wszCaption);
218 }
219
220 static BOOL validate_endptr(LPCSTR endptr, BOOL units)
221 {
222 if(!endptr)
223 return FALSE;
224 if(!*endptr)
225 return TRUE;
226
227 while(*endptr == ' ')
228 endptr++;
229
230 if(!units)
231 return *endptr == '\0';
232
233 /* FIXME: Allow other units and convert between them */
234 if(!lstrcmpA(endptr, units_cmA))
235 endptr += 2;
236
237 return *endptr == '\0';
238 }
239
240 static BOOL number_from_string(LPCWSTR string, float *num, BOOL units)
241 {
242 double ret;
243 char buffer[MAX_STRING_LEN];
244 char *endptr = buffer;
245
246 WideCharToMultiByte(CP_ACP, 0, string, -1, buffer, MAX_STRING_LEN, NULL, NULL);
247 *num = 0;
248 errno = 0;
249 ret = strtod(buffer, &endptr);
250
251 if((ret == 0 && errno != 0) || endptr == buffer || !validate_endptr(endptr, units))
252 {
253 return FALSE;
254 } else
255 {
256 *num = (float)ret;
257 return TRUE;
258 }
259 }
260
261 static void set_size(float size)
262 {
263 CHARFORMAT2W fmt;
264
265 ZeroMemory(&fmt, sizeof(fmt));
266 fmt.cbSize = sizeof(fmt);
267 fmt.dwMask = CFM_SIZE;
268 fmt.yHeight = (int)(size * 20.0);
269 SendMessageW(hEditorWnd, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&fmt);
270 }
271
272 static void on_sizelist_modified(HWND hwndSizeList, LPWSTR wszNewFontSize)
273 {
274 WCHAR sizeBuffer[MAX_STRING_LEN];
275 CHARFORMAT2W format;
276
277 ZeroMemory(&format, sizeof(format));
278 format.cbSize = sizeof(format);
279 SendMessageW(hEditorWnd, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&format);
280
281 wsprintfW(sizeBuffer, stringFormat, format.yHeight / 20);
282 if(lstrcmpW(sizeBuffer, wszNewFontSize))
283 {
284 float size = 0;
285 if(number_from_string((LPCWSTR) wszNewFontSize, &size, FALSE)
286 && size > 0)
287 {
288 set_size(size);
289 } else
290 {
291 SetWindowTextW(hwndSizeList, sizeBuffer);
292 MessageBoxW(hMainWnd, MAKEINTRESOURCEW(STRING_INVALID_NUMBER),
293 wszAppTitle, MB_OK | MB_ICONINFORMATION);
294 }
295 }
296 }
297
298 static void add_size(HWND hSizeListWnd, unsigned size)
299 {
300 WCHAR buffer[3];
301 COMBOBOXEXITEMW cbItem;
302 cbItem.mask = CBEIF_TEXT;
303 cbItem.iItem = -1;
304
305 wsprintfW(buffer, stringFormat, size);
306 cbItem.pszText = (LPWSTR)buffer;
307 SendMessageW(hSizeListWnd, CBEM_INSERTITEMW, 0, (LPARAM)&cbItem);
308 }
309
310 static void populate_size_list(HWND hSizeListWnd)
311 {
312 HWND hReBarWnd = GetDlgItem(hMainWnd, IDC_REBAR);
313 HWND hFontListWnd = GetDlgItem(hReBarWnd, IDC_FONTLIST);
314 COMBOBOXEXITEMW cbFontItem;
315 CHARFORMAT2W fmt;
316 HWND hListEditWnd = (HWND)SendMessageW(hSizeListWnd, CBEM_GETEDITCONTROL, 0, 0);
317 HDC hdc = GetDC(hMainWnd);
318 static const unsigned choices[] = {8,9,10,11,12,14,16,18,20,22,24,26,28,36,48,72};
319 WCHAR buffer[3];
320 int i;
321 DWORD fontStyle;
322
323 ZeroMemory(&fmt, sizeof(fmt));
324 fmt.cbSize = sizeof(fmt);
325 SendMessageW(hEditorWnd, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&fmt);
326
327 cbFontItem.mask = CBEIF_LPARAM;
328 cbFontItem.iItem = SendMessageW(hFontListWnd, CB_FINDSTRINGEXACT, -1, (LPARAM)fmt.szFaceName);
329 SendMessageW(hFontListWnd, CBEM_GETITEMW, 0, (LPARAM)&cbFontItem);
330
331 fontStyle = (DWORD)LOWORD(cbFontItem.lParam);
332
333 SendMessageW(hSizeListWnd, CB_RESETCONTENT, 0, 0);
334
335 if((fontStyle & RASTER_FONTTYPE) && cbFontItem.iItem)
336 {
337 add_size(hSizeListWnd, (BYTE)MulDiv(HIWORD(cbFontItem.lParam), 72,
338 GetDeviceCaps(hdc, LOGPIXELSY)));
339 } else
340 {
341 for(i = 0; i < sizeof(choices)/sizeof(choices[0]); i++)
342 add_size(hSizeListWnd, choices[i]);
343 }
344
345 wsprintfW(buffer, stringFormat, fmt.yHeight / 20);
346 SendMessageW(hListEditWnd, WM_SETTEXT, 0, (LPARAM)buffer);
347 }
348
349 static void update_size_list(void)
350 {
351 HWND hReBar = GetDlgItem(hMainWnd, IDC_REBAR);
352 HWND hwndSizeList = GetDlgItem(hReBar, IDC_SIZELIST);
353 HWND hwndSizeListEdit = (HWND)SendMessageW(hwndSizeList, CBEM_GETEDITCONTROL, 0, 0);
354 WCHAR fontSize[MAX_STRING_LEN], sizeBuffer[MAX_STRING_LEN];
355 CHARFORMAT2W fmt;
356
357 ZeroMemory(&fmt, sizeof(fmt));
358 fmt.cbSize = sizeof(fmt);
359
360 SendMessageW(hEditorWnd, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&fmt);
361
362 SendMessageW(hwndSizeListEdit, WM_GETTEXT, MAX_PATH, (LPARAM)fontSize);
363 wsprintfW(sizeBuffer, stringFormat, fmt.yHeight / 20);
364
365 if(lstrcmpW(fontSize, sizeBuffer))
366 SendMessageW(hwndSizeListEdit, WM_SETTEXT, 0, (LPARAM)sizeBuffer);
367 }
368
369 static void update_font_list(void)
370 {
371 HWND hReBar = GetDlgItem(hMainWnd, IDC_REBAR);
372 HWND hFontList = GetDlgItem(hReBar, IDC_FONTLIST);
373 HWND hFontListEdit = (HWND)SendMessageW(hFontList, CBEM_GETEDITCONTROL, 0, 0);
374 WCHAR fontName[MAX_STRING_LEN];
375 CHARFORMAT2W fmt;
376
377 ZeroMemory(&fmt, sizeof(fmt));
378 fmt.cbSize = sizeof(fmt);
379
380 SendMessageW(hEditorWnd, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&fmt);
381 if (!SendMessageW(hFontListEdit, WM_GETTEXT, MAX_PATH, (LPARAM)fontName)) return;
382
383 if(lstrcmpW(fontName, fmt.szFaceName))
384 {
385 SendMessageW(hFontListEdit, WM_SETTEXT, 0, (LPARAM)fmt.szFaceName);
386 populate_size_list(GetDlgItem(hReBar, IDC_SIZELIST));
387 } else
388 {
389 update_size_list();
390 }
391 }
392
393 static void clear_formatting(void)
394 {
395 PARAFORMAT2 pf;
396
397 pf.cbSize = sizeof(pf);
398 pf.dwMask = PFM_ALIGNMENT;
399 pf.wAlignment = PFA_LEFT;
400 SendMessageW(hEditorWnd, EM_SETPARAFORMAT, 0, (LPARAM)&pf);
401 }
402
403 static int fileformat_number(WPARAM format)
404 {
405 int number = 0;
406
407 if(format == SF_TEXT)
408 {
409 number = 1;
410 } else if (format == (SF_TEXT | SF_UNICODE))
411 {
412 number = 2;
413 }
414 return number;
415 }
416
417 static WPARAM fileformat_flags(int format)
418 {
419 WPARAM flags[] = { SF_RTF , SF_TEXT , SF_TEXT | SF_UNICODE };
420
421 return flags[format];
422 }
423
424 static void set_font(LPCWSTR wszFaceName)
425 {
426 HWND hReBarWnd = GetDlgItem(hMainWnd, IDC_REBAR);
427 HWND hSizeListWnd = GetDlgItem(hReBarWnd, IDC_SIZELIST);
428 HWND hFontListWnd = GetDlgItem(hReBarWnd, IDC_FONTLIST);
429 HWND hFontListEditWnd = (HWND)SendMessageW(hFontListWnd, CBEM_GETEDITCONTROL, 0, 0);
430 CHARFORMAT2W fmt;
431
432 ZeroMemory(&fmt, sizeof(fmt));
433
434 fmt.cbSize = sizeof(fmt);
435 fmt.dwMask = CFM_FACE;
436
437 lstrcpyW(fmt.szFaceName, wszFaceName);
438
439 SendMessageW(hEditorWnd, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&fmt);
440
441 populate_size_list(hSizeListWnd);
442
443 SendMessageW(hFontListEditWnd, WM_SETTEXT, 0, (LPARAM)(LPWSTR)wszFaceName);
444 }
445
446 static void set_default_font(void)
447 {
448 static const WCHAR richTextFont[] = {'T','i','m','e','s',' ','N','e','w',' ',
449 'R','o','m','a','n',0};
450 static const WCHAR plainTextFont[] = {'C','o','u','r','i','e','r',' ','N','e','w',0};
451 CHARFORMAT2W fmt;
452 LPCWSTR font;
453
454 ZeroMemory(&fmt, sizeof(fmt));
455
456 fmt.cbSize = sizeof(fmt);
457 fmt.dwMask = CFM_FACE | CFM_BOLD | CFM_ITALIC | CFM_UNDERLINE;
458 fmt.dwEffects = 0;
459
460 if(fileFormat & SF_RTF)
461 font = richTextFont;
462 else
463 font = plainTextFont;
464
465 lstrcpyW(fmt.szFaceName, font);
466
467 SendMessageW(hEditorWnd, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM)&fmt);
468 }
469
470 static void on_fontlist_modified(LPWSTR wszNewFaceName)
471 {
472 CHARFORMAT2W format;
473 ZeroMemory(&format, sizeof(format));
474 format.cbSize = sizeof(format);
475 SendMessageW(hEditorWnd, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&format);
476
477 if(lstrcmpW(format.szFaceName, wszNewFaceName))
478 set_font((LPCWSTR) wszNewFaceName);
479 }
480
481 static void add_font(LPCWSTR fontName, DWORD fontType, HWND hListWnd, NEWTEXTMETRICEXW *ntmc)
482 {
483 COMBOBOXEXITEMW cbItem;
484 WCHAR buffer[MAX_PATH];
485 int fontHeight = 0;
486
487 cbItem.mask = CBEIF_TEXT;
488 cbItem.pszText = buffer;
489 cbItem.cchTextMax = MAX_STRING_LEN;
490 cbItem.iItem = 0;
491
492 while(SendMessageW(hListWnd, CBEM_GETITEMW, 0, (LPARAM)&cbItem))
493 {
494 if(lstrcmpiW(cbItem.pszText, fontName) <= 0)
495 cbItem.iItem++;
496 else
497 break;
498 }
499 cbItem.pszText = HeapAlloc( GetProcessHeap(), 0, (lstrlenW(fontName) + 1)*sizeof(WCHAR) );
500 lstrcpyW( cbItem.pszText, fontName );
501
502 cbItem.mask |= CBEIF_LPARAM;
503 if(fontType & RASTER_FONTTYPE)
504 fontHeight = ntmc->ntmTm.tmHeight - ntmc->ntmTm.tmInternalLeading;
505
506 cbItem.lParam = MAKELONG(fontType,fontHeight);
507 SendMessageW(hListWnd, CBEM_INSERTITEMW, 0, (LPARAM)&cbItem);
508 HeapFree( GetProcessHeap(), 0, cbItem.pszText );
509 }
510
511 static void dialog_choose_font(void)
512 {
513 CHOOSEFONTW cf;
514 LOGFONTW lf;
515 CHARFORMAT2W fmt;
516 HDC hDC = GetDC(hMainWnd);
517
518 ZeroMemory(&cf, sizeof(cf));
519 cf.lStructSize = sizeof(cf);
520 cf.hwndOwner = hMainWnd;
521 cf.lpLogFont = &lf;
522 cf.Flags = CF_SCREENFONTS | CF_NOSCRIPTSEL | CF_INITTOLOGFONTSTRUCT | CF_EFFECTS;
523
524 ZeroMemory(&fmt, sizeof(fmt));
525 fmt.cbSize = sizeof(fmt);
526
527 SendMessageW(hEditorWnd, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&fmt);
528 lstrcpyW(cf.lpLogFont->lfFaceName, fmt.szFaceName);
529 cf.lpLogFont->lfItalic = (fmt.dwEffects & CFE_ITALIC) ? TRUE : FALSE;
530 cf.lpLogFont->lfWeight = (fmt.dwEffects & CFE_BOLD) ? FW_BOLD : FW_NORMAL;
531 cf.lpLogFont->lfUnderline = (fmt.dwEffects & CFE_UNDERLINE) ? TRUE : FALSE;
532 cf.lpLogFont->lfStrikeOut = (fmt.dwEffects & CFE_STRIKEOUT) ? TRUE : FALSE;
533 cf.lpLogFont->lfHeight = -MulDiv(fmt.yHeight / 20, GetDeviceCaps(hDC, LOGPIXELSY), 72);
534 cf.rgbColors = fmt.crTextColor;
535
536 if(ChooseFontW(&cf))
537 {
538 ZeroMemory(&fmt, sizeof(fmt));
539 fmt.cbSize = sizeof(fmt);
540 fmt.dwMask = CFM_BOLD | CFM_ITALIC | CFM_SIZE | CFM_UNDERLINE | CFM_STRIKEOUT | CFM_COLOR;
541 fmt.yHeight = cf.iPointSize * 2;
542
543 if(cf.nFontType & BOLD_FONTTYPE)
544 fmt.dwEffects |= CFE_BOLD;
545 if(cf.nFontType & ITALIC_FONTTYPE)
546 fmt.dwEffects |= CFE_ITALIC;
547 if(cf.lpLogFont->lfUnderline == TRUE)
548 fmt.dwEffects |= CFE_UNDERLINE;
549 if(cf.lpLogFont->lfStrikeOut == TRUE)
550 fmt.dwEffects |= CFE_STRIKEOUT;
551
552 fmt.crTextColor = cf.rgbColors;
553
554 SendMessageW(hEditorWnd, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&fmt);
555 set_font(cf.lpLogFont->lfFaceName);
556 }
557 }
558
559
560 int CALLBACK enum_font_proc(const LOGFONTW *lpelfe, const TEXTMETRICW *lpntme,
561 DWORD FontType, LPARAM lParam)
562 {
563 HWND hListWnd = (HWND) lParam;
564
565 if(SendMessageW(hListWnd, CB_FINDSTRINGEXACT, -1, (LPARAM)lpelfe->lfFaceName) == CB_ERR)
566 {
567
568 add_font((LPWSTR)lpelfe->lfFaceName, FontType, hListWnd, (NEWTEXTMETRICEXW*)lpntme);
569 }
570
571 return 1;
572 }
573
574 static void populate_font_list(HWND hListWnd)
575 {
576 HDC hdc = GetDC(hMainWnd);
577 LOGFONTW fontinfo;
578 HWND hListEditWnd = (HWND)SendMessageW(hListWnd, CBEM_GETEDITCONTROL, 0, 0);
579 CHARFORMAT2W fmt;
580
581 fontinfo.lfCharSet = DEFAULT_CHARSET;
582 *fontinfo.lfFaceName = '\0';
583 fontinfo.lfPitchAndFamily = 0;
584
585 EnumFontFamiliesExW(hdc, &fontinfo, enum_font_proc,
586 (LPARAM)hListWnd, 0);
587
588 ZeroMemory(&fmt, sizeof(fmt));
589 fmt.cbSize = sizeof(fmt);
590 SendMessageW(hEditorWnd, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM)&fmt);
591 SendMessageW(hListEditWnd, WM_SETTEXT, 0, (LPARAM)fmt.szFaceName);
592 }
593
594 static void update_window(void)
595 {
596 RECT rect;
597
598 GetClientRect(hMainWnd, &rect);
599
600 OnSize(hMainWnd, SIZE_RESTORED, MAKELPARAM(rect.right, rect.bottom));
601 }
602
603 static BOOL is_bar_visible(int bandId)
604 {
605 return barState[reg_formatindex(fileFormat)] & (1 << bandId);
606 }
607
608 static void store_bar_state(int bandId, BOOL show)
609 {
610 int formatIndex = reg_formatindex(fileFormat);
611
612 if(show)
613 barState[formatIndex] |= (1 << bandId);
614 else
615 barState[formatIndex] &= ~(1 << bandId);
616 }
617
618 static void set_toolbar_state(int bandId, BOOL show)
619 {
620 HWND hwndReBar = GetDlgItem(hMainWnd, IDC_REBAR);
621
622 SendMessageW(hwndReBar, RB_SHOWBAND, SendMessageW(hwndReBar, RB_IDTOINDEX, bandId, 0), show);
623
624 if(bandId == BANDID_TOOLBAR)
625 {
626 REBARBANDINFOW rbbinfo;
627 int index = SendMessageW(hwndReBar, RB_IDTOINDEX, BANDID_FONTLIST, 0);
628
629 rbbinfo.cbSize = sizeof(rbbinfo);
630 rbbinfo.fMask = RBBIM_STYLE;
631
632 SendMessageW(hwndReBar, RB_GETBANDINFO, index, (LPARAM)&rbbinfo);
633
634 if(!show)
635 rbbinfo.fStyle &= ~RBBS_BREAK;
636 else
637 rbbinfo.fStyle |= RBBS_BREAK;
638
639 SendMessageW(hwndReBar, RB_SETBANDINFO, index, (LPARAM)&rbbinfo);
640 }
641
642 if(bandId == BANDID_TOOLBAR || bandId == BANDID_FORMATBAR || bandId == BANDID_RULER)
643 store_bar_state(bandId, show);
644 }
645
646 static void set_statusbar_state(BOOL show)
647 {
648 HWND hStatusWnd = GetDlgItem(hMainWnd, IDC_STATUSBAR);
649
650 ShowWindow(hStatusWnd, show ? SW_SHOW : SW_HIDE);
651 store_bar_state(BANDID_STATUSBAR, show);
652 }
653
654 static void set_bar_states(void)
655 {
656 set_toolbar_state(BANDID_TOOLBAR, is_bar_visible(BANDID_TOOLBAR));
657 set_toolbar_state(BANDID_FONTLIST, is_bar_visible(BANDID_FORMATBAR));
658 set_toolbar_state(BANDID_SIZELIST, is_bar_visible(BANDID_FORMATBAR));
659 set_toolbar_state(BANDID_FORMATBAR, is_bar_visible(BANDID_FORMATBAR));
660 set_toolbar_state(BANDID_RULER, is_bar_visible(BANDID_RULER));
661 set_statusbar_state(is_bar_visible(BANDID_STATUSBAR));
662
663 update_window();
664 }
665
666 static void preview_exit(HWND hMainWnd)
667 {
668 HINSTANCE hInstance = (HINSTANCE)GetWindowLongPtr(hMainWnd, GWLP_HINSTANCE);
669 HMENU hMenu = LoadMenuW(hInstance, MAKEINTRESOURCEW(IDM_MAINMENU));
670 HWND hEditorWnd = GetDlgItem(hMainWnd, IDC_EDITOR);
671
672 set_bar_states();
673 ShowWindow(hEditorWnd, TRUE);
674
675 close_preview(hMainWnd);
676
677 SetMenu(hMainWnd, hMenu);
678 registry_read_filelist(hMainWnd);
679
680 update_window();
681 }
682
683 static void set_fileformat(WPARAM format)
684 {
685 HICON hIcon;
686 HINSTANCE hInstance = (HINSTANCE)GetWindowLongPtr(hMainWnd, GWLP_HINSTANCE);
687 fileFormat = format;
688
689 if(format & SF_TEXT)
690 hIcon = LoadIconW(hInstance, MAKEINTRESOURCEW(IDI_TXT));
691 else
692 hIcon = LoadIconW(hInstance, MAKEINTRESOURCEW(IDI_RTF));
693
694 SendMessageW(hMainWnd, WM_SETICON, ICON_BIG, (LPARAM)hIcon);
695
696 set_bar_states();
697 set_default_font();
698 target_device(hMainWnd, wordWrap[reg_formatindex(fileFormat)]);
699 }
700
701 static void ShowOpenError(DWORD Code)
702 {
703 LPWSTR Message;
704
705 switch(Code)
706 {
707 case ERROR_ACCESS_DENIED:
708 Message = MAKEINTRESOURCEW(STRING_OPEN_ACCESS_DENIED);
709 break;
710
711 default:
712 Message = MAKEINTRESOURCEW(STRING_OPEN_FAILED);
713 }
714 MessageBoxW(hMainWnd, Message, wszAppTitle, MB_ICONEXCLAMATION | MB_OK);
715 }
716
717 static void DoOpenFile(LPCWSTR szOpenFileName)
718 {
719 HANDLE hFile;
720 EDITSTREAM es;
721 char fileStart[5];
722 DWORD readOut;
723 WPARAM format = SF_TEXT;
724
725 hFile = CreateFileW(szOpenFileName, GENERIC_READ, FILE_SHARE_READ, NULL,
726 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
727 if (hFile == INVALID_HANDLE_VALUE)
728 {
729 ShowOpenError(GetLastError());
730 return;
731 }
732
733 ReadFile(hFile, fileStart, 5, &readOut, NULL);
734 SetFilePointer(hFile, 0, NULL, FILE_BEGIN);
735
736 if(readOut >= 2 && (BYTE)fileStart[0] == 0xff && (BYTE)fileStart[1] == 0xfe)
737 {
738 format = SF_TEXT | SF_UNICODE;
739 SetFilePointer(hFile, 2, NULL, FILE_BEGIN);
740 } else if(readOut >= 5)
741 {
742 static const char header[] = "{\\rtf";
743 static const BYTE STG_magic[] = { 0xd0,0xcf,0x11,0xe0 };
744
745 if(!memcmp(header, fileStart, 5))
746 format = SF_RTF;
747 else if (!memcmp(STG_magic, fileStart, sizeof(STG_magic)))
748 {
749 CloseHandle(hFile);
750 MessageBoxW(hMainWnd, MAKEINTRESOURCEW(STRING_OLE_STORAGE_NOT_SUPPORTED), wszAppTitle,
751 MB_OK | MB_ICONEXCLAMATION);
752 return;
753 }
754 }
755
756 es.dwCookie = (DWORD_PTR)hFile;
757 es.pfnCallback = stream_in;
758
759 clear_formatting();
760 set_fileformat(format);
761 SendMessageW(hEditorWnd, EM_STREAMIN, format, (LPARAM)&es);
762
763 CloseHandle(hFile);
764
765 SetFocus(hEditorWnd);
766
767 set_caption(szOpenFileName);
768
769 lstrcpyW(wszFileName, szOpenFileName);
770 SendMessageW(hEditorWnd, EM_SETMODIFY, FALSE, 0);
771 registry_set_filelist(szOpenFileName, hMainWnd);
772 update_font_list();
773 }
774
775 static void ShowWriteError(DWORD Code)
776 {
777 LPWSTR Message;
778
779 switch(Code)
780 {
781 case ERROR_ACCESS_DENIED:
782 Message = MAKEINTRESOURCEW(STRING_WRITE_ACCESS_DENIED);
783 break;
784
785 default:
786 Message = MAKEINTRESOURCEW(STRING_WRITE_FAILED);
787 }
788 MessageBoxW(hMainWnd, Message, wszAppTitle, MB_ICONEXCLAMATION | MB_OK);
789 }
790
791 static void DoSaveFile(LPCWSTR wszSaveFileName, WPARAM format)
792 {
793 HANDLE hFile;
794 EDITSTREAM stream;
795 LRESULT ret;
796
797 hFile = CreateFileW(wszSaveFileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
798 FILE_ATTRIBUTE_NORMAL, NULL);
799
800 if(hFile == INVALID_HANDLE_VALUE)
801 {
802 ShowWriteError(GetLastError());
803 return;
804 }
805
806 if(format == (SF_TEXT | SF_UNICODE))
807 {
808 static const BYTE unicode[] = {0xff,0xfe};
809 DWORD writeOut;
810 WriteFile(hFile, &unicode, sizeof(unicode), &writeOut, 0);
811
812 if(writeOut != sizeof(unicode))
813 return;
814 }
815
816 stream.dwCookie = (DWORD_PTR)hFile;
817 stream.pfnCallback = stream_out;
818
819 ret = SendMessageW(hEditorWnd, EM_STREAMOUT, format, (LPARAM)&stream);
820
821 CloseHandle(hFile);
822
823 SetFocus(hEditorWnd);
824
825 if(!ret)
826 {
827 GETTEXTLENGTHEX gt;
828 gt.flags = GTL_DEFAULT;
829 gt.codepage = 1200;
830
831 if(SendMessageW(hEditorWnd, EM_GETTEXTLENGTHEX, (WPARAM)>, 0))
832 return;
833 }
834
835 lstrcpyW(wszFileName, wszSaveFileName);
836 set_caption(wszFileName);
837 SendMessageW(hEditorWnd, EM_SETMODIFY, FALSE, 0);
838 set_fileformat(format);
839 }
840
841 static void DialogSaveFile(void)
842 {
843 OPENFILENAMEW sfn;
844
845 WCHAR wszFile[MAX_PATH] = {'\0'};
846 static const WCHAR wszDefExt[] = {'r','t','f','\0'};
847
848 ZeroMemory(&sfn, sizeof(sfn));
849
850 sfn.lStructSize = sizeof(sfn);
851 sfn.Flags = OFN_HIDEREADONLY | OFN_PATHMUSTEXIST | OFN_OVERWRITEPROMPT;
852 sfn.hwndOwner = hMainWnd;
853 sfn.lpstrFilter = wszFilter;
854 sfn.lpstrFile = wszFile;
855 sfn.nMaxFile = MAX_PATH;