1 /*
2  * Wordpad implementation - Registry functions
3  *
4  * Copyright 2007 by Alexander N. Sørnes <alex@thehandofagony.com>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20 
21 #define WIN32_NO_STATUS
22 #define WIN32_LEAN_AND_MEAN
23 #include <windows.h>
24 #include <shlobj.h>
25 #include <richedit.h>
26 
27 #include "wordpad.h"
28 
29 static const WCHAR key_recentfiles[] = {'R','e','c','e','n','t',' ','f','i','l','e',
30                                         ' ','l','i','s','t',0};
31 static const WCHAR key_options[] = {'O','p','t','i','o','n','s',0};
32 static const WCHAR key_settings[] = {'S','e','t','t','i','n','g','s',0};
33 static const WCHAR key_rtf[] = {'R','T','F',0};
34 static const WCHAR key_text[] = {'T','e','x','t',0};
35 
36 static const WCHAR var_file[] = {'F','i','l','e','%','d',0};
37 static const WCHAR var_framerect[] = {'F','r','a','m','e','R','e','c','t',0};
38 static const WCHAR var_barstate0[] = {'B','a','r','S','t','a','t','e','0',0};
39 static const WCHAR var_wrap[] = {'W','r','a','p',0};
40 static const WCHAR var_maximized[] = {'M','a','x','i','m','i','z','e','d',0};
41 
42 static LRESULT registry_get_handle(HKEY *hKey, LPDWORD action, LPCWSTR subKey)
43 {
44     LONG ret;
45     static const WCHAR wszProgramKey[] = {'S','o','f','t','w','a','r','e','\\',
46         'M','i','c','r','o','s','o','f','t','\\',
47         'W','i','n','d','o','w','s','\\',
48         'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
49         'A','p','p','l','e','t','s','\\',
50         'W','o','r','d','p','a','d',0};
51         LPWSTR key = (LPWSTR)wszProgramKey;
52 
53         if(subKey)
54         {
55             WCHAR backslash[] = {'\\',0};
56             key = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
57                             (lstrlenW(wszProgramKey)+lstrlenW(subKey)+lstrlenW(backslash)+1)
58                                     *sizeof(WCHAR));
59 
60             if(!key)
61                 return 1;
62 
63             lstrcpyW(key, wszProgramKey);
64             lstrcatW(key, backslash);
65             lstrcatW(key, subKey);
66         }
67 
68         if(action)
69         {
70             ret = RegCreateKeyExW(HKEY_CURRENT_USER, key, 0, NULL, REG_OPTION_NON_VOLATILE,
71                                   KEY_READ | KEY_WRITE, NULL, hKey, action);
72         } else
73         {
74             ret = RegOpenKeyExW(HKEY_CURRENT_USER, key, 0, KEY_READ | KEY_WRITE, hKey);
75         }
76 
77         if(subKey)
78             HeapFree(GetProcessHeap(), 0, key);
79 
80         return ret;
81 }
82 
83 void registry_set_options(HWND hMainWnd)
84 {
85     HKEY hKey = 0;
86     DWORD action;
87 
88     if(registry_get_handle(&hKey, &action, key_options) == ERROR_SUCCESS)
89     {
90         WINDOWPLACEMENT wp;
91         DWORD isMaximized;
92 
93         wp.length = sizeof(WINDOWPLACEMENT);
94         GetWindowPlacement(hMainWnd, &wp);
95         isMaximized = (wp.showCmd == SW_SHOWMAXIMIZED);
96 
97         RegSetValueExW(hKey, var_framerect, 0, REG_BINARY, (LPBYTE)&wp.rcNormalPosition, sizeof(RECT));
98         RegSetValueExW(hKey, var_maximized, 0, REG_DWORD, (LPBYTE)&isMaximized, sizeof(DWORD));
99 
100         registry_set_pagemargins(hKey);
101         RegCloseKey(hKey);
102     }
103 
104     if(registry_get_handle(&hKey, &action, key_settings) == ERROR_SUCCESS)
105     {
106         registry_set_previewpages(hKey);
107         RegCloseKey(hKey);
108     }
109 }
110 
111 void registry_read_winrect(RECT* rc)
112 {
113     HKEY hKey = 0;
114     DWORD size = sizeof(RECT);
115 
116     if(registry_get_handle(&hKey, 0, key_options) != ERROR_SUCCESS ||
117        RegQueryValueExW(hKey, var_framerect, 0, NULL, (LPBYTE)rc, &size) !=
118        ERROR_SUCCESS || size != sizeof(RECT))
119         SetRect(rc, 0, 0, 600, 300);
120 
121     RegCloseKey(hKey);
122 }
123 
124 void registry_read_maximized(DWORD *bMaximized)
125 {
126     HKEY hKey = 0;
127     DWORD size = sizeof(DWORD);
128 
129     if(registry_get_handle(&hKey, 0, key_options) != ERROR_SUCCESS ||
130        RegQueryValueExW(hKey, var_maximized, 0, NULL, (LPBYTE)bMaximized, &size) !=
131        ERROR_SUCCESS || size != sizeof(DWORD))
132     {
133         *bMaximized = FALSE;
134     }
135 
136     RegCloseKey(hKey);
137 }
138 
139 static void truncate_path(LPWSTR file, LPWSTR out, LPWSTR pos1, LPWSTR pos2)
140 {
141     static const WCHAR dots[] = {'.','.','.',0};
142 
143     *++pos1 = 0;
144 
145     lstrcatW(out, file);
146     lstrcatW(out, dots);
147     lstrcatW(out, pos2);
148 }
149 
150 static void format_filelist_filename(LPWSTR file, LPWSTR out)
151 {
152     LPWSTR pos_basename;
153     LPWSTR truncpos1, truncpos2;
154     WCHAR myDocs[MAX_PATH];
155 
156     SHGetFolderPathW(NULL, CSIDL_PERSONAL, NULL, SHGFP_TYPE_CURRENT, myDocs);
157     pos_basename = file_basename(file);
158     truncpos1 = NULL;
159     truncpos2 = NULL;
160 
161     *(pos_basename-1) = 0;
162     if(!lstrcmpiW(file, myDocs) || (lstrlenW(pos_basename) > FILELIST_ENTRY_LENGTH))
163     {
164         truncpos1 = pos_basename;
165         *(pos_basename-1) = '\\';
166     } else
167     {
168         LPWSTR pos;
169         BOOL morespace = FALSE;
170 
171         *(pos_basename-1) = '\\';
172 
173         for(pos = file; pos < pos_basename; pos++)
174         {
175             if(*pos == '\\' || *pos == '/')
176             {
177                 if(truncpos1)
178                 {
179                     if((pos - file + lstrlenW(pos_basename)) > FILELIST_ENTRY_LENGTH)
180                         break;
181 
182                     truncpos1 = pos;
183                     morespace = TRUE;
184                     break;
185                 }
186 
187                 if((pos - file + lstrlenW(pos_basename)) > FILELIST_ENTRY_LENGTH)
188                     break;
189 
190                 truncpos1 = pos;
191             }
192         }
193 
194         if(morespace)
195         {
196             for(pos = pos_basename; pos >= truncpos1; pos--)
197             {
198                 if(*pos == '\\' || *pos == '/')
199                 {
200                     if((truncpos1 - file + lstrlenW(pos_basename) + pos_basename - pos) > FILELIST_ENTRY_LENGTH)
201                         break;
202 
203                     truncpos2 = pos;
204                 }
205             }
206         }
207     }
208 
209     if(truncpos1 == pos_basename)
210         lstrcatW(out, pos_basename);
211     else if(truncpos1 == truncpos2 || !truncpos2)
212         lstrcatW(out, file);
213     else
214         truncate_path(file, out, truncpos1, truncpos2);
215 }
216 
217 void registry_read_filelist(HWND hMainWnd)
218 {
219     HKEY hFileKey;
220 
221     if(registry_get_handle(&hFileKey, 0, key_recentfiles) == ERROR_SUCCESS)
222     {
223         WCHAR itemText[MAX_PATH+3], buffer[MAX_PATH];
224         /* The menu item name is not the same as the file name, so we need to store
225         the file name here */
226         static WCHAR file1[MAX_PATH], file2[MAX_PATH], file3[MAX_PATH], file4[MAX_PATH];
227         WCHAR numFormat[] = {'&','%','d',' ',0};
228         LPWSTR pFile[] = {file1, file2, file3, file4};
229         DWORD pathSize = MAX_PATH*sizeof(WCHAR);
230         int i;
231         WCHAR key[6];
232         MENUITEMINFOW mi;
233         HMENU hMenu = GetMenu(hMainWnd);
234 
235         mi.cbSize = sizeof(MENUITEMINFOW);
236         mi.fMask = MIIM_ID | MIIM_DATA | MIIM_STRING | MIIM_FTYPE;
237         mi.fType = MFT_STRING;
238         mi.dwTypeData = itemText;
239         mi.wID = ID_FILE_RECENT1;
240 
241         RemoveMenu(hMenu, ID_FILE_RECENT_SEPARATOR, MF_BYCOMMAND);
242         for(i = 0; i < FILELIST_ENTRIES; i++)
243         {
244             wsprintfW(key, var_file, i+1);
245             RemoveMenu(hMenu, ID_FILE_RECENT1+i, MF_BYCOMMAND);
246             if(RegQueryValueExW(hFileKey, (LPWSTR)key, 0, NULL, (LPBYTE)pFile[i], &pathSize)
247                != ERROR_SUCCESS)
248                 break;
249 
250             mi.dwItemData = (ULONG_PTR)pFile[i];
251             wsprintfW(itemText, numFormat, i+1);
252 
253             lstrcpyW(buffer, pFile[i]);
254 
255             format_filelist_filename(buffer, itemText);
256 
257             InsertMenuItemW(hMenu, ID_FILE_EXIT, FALSE, &mi);
258             mi.wID++;
259             pathSize = MAX_PATH*sizeof(WCHAR);
260         }
261         mi.fType = MFT_SEPARATOR;
262         mi.fMask = MIIM_FTYPE | MIIM_ID;
263         InsertMenuItemW(hMenu, ID_FILE_EXIT, FALSE, &mi);
264 
265         RegCloseKey(hFileKey);
266     }
267 }
268 
269 void registry_set_filelist(LPCWSTR newFile, HWND hMainWnd)
270 {
271     HKEY hKey;
272     DWORD action;
273 
274     if(registry_get_handle(&hKey, &action, key_recentfiles) == ERROR_SUCCESS)
275     {
276         LPCWSTR pFiles[FILELIST_ENTRIES];
277         int i;
278         HMENU hMenu = GetMenu(hMainWnd);
279         MENUITEMINFOW mi;
280         WCHAR buffer[6];
281 
282         mi.cbSize = sizeof(MENUITEMINFOW);
283         mi.fMask = MIIM_DATA;
284 
285         for(i = 0; i < FILELIST_ENTRIES; i++)
286             pFiles[i] = NULL;
287 
288         for(i = 0; GetMenuItemInfoW(hMenu, ID_FILE_RECENT1+i, FALSE, &mi); i++)
289             pFiles[i] = (LPWSTR)mi.dwItemData;
290 
291         if(lstrcmpiW(newFile, pFiles[0]))
292         {
293             for(i = 0; i < FILELIST_ENTRIES && pFiles[i]; i++)
294             {
295                 if(!lstrcmpiW(pFiles[i], newFile))
296                 {
297                     int j;
298                     for(j = 0; j < i; j++)
299                     {
300                         pFiles[i-j] = pFiles[i-j-1];
301                     }
302                     pFiles[0] = NULL;
303                     break;
304                 }
305             }
306 
307             if(!pFiles[0])
308             {
309                 pFiles[0] = newFile;
310             } else
311             {
312                 for(i = 0; i < FILELIST_ENTRIES-1; i++)
313                     pFiles[FILELIST_ENTRIES-1-i] = pFiles[FILELIST_ENTRIES-2-i];
314 
315                 pFiles[0] = newFile;
316             }
317 
318             for(i = 0; i < FILELIST_ENTRIES && pFiles[i]; i++)
319             {
320                 wsprintfW(buffer, var_file, i+1);
321                 RegSetValueExW(hKey, (LPWSTR)&buffer, 0, REG_SZ, (const BYTE*)pFiles[i],
322                                (lstrlenW(pFiles[i])+1)*sizeof(WCHAR));
323             }
324         }
325         RegCloseKey(hKey);
326     }
327     registry_read_filelist(hMainWnd);
328 }
329 
330 int reg_formatindex(WPARAM format)
331 {
332     return (format & SF_TEXT) ? 1 : 0;
333 }
334 
335 void registry_read_options(void)
336 {
337     HKEY hKey;
338 
339     if(registry_get_handle(&hKey, 0, key_options) != ERROR_SUCCESS)
340         registry_read_pagemargins(NULL);
341     else
342     {
343         registry_read_pagemargins(hKey);
344         RegCloseKey(hKey);
345     }
346 
347     if(registry_get_handle(&hKey, 0, key_settings) != ERROR_SUCCESS) {
348         registry_read_previewpages(NULL);
349     } else {
350         registry_read_previewpages(hKey);
351         RegCloseKey(hKey);
352     }
353 }
354 
355 static void registry_read_formatopts(int index, LPCWSTR key, DWORD barState[], DWORD wordWrap[])
356 {
357     HKEY hKey;
358     DWORD action = 0;
359     BOOL fetched = FALSE;
360     barState[index] = 0;
361     wordWrap[index] = 0;
362 
363     if(registry_get_handle(&hKey, &action, key) != ERROR_SUCCESS)
364         return;
365 
366     if(action == REG_OPENED_EXISTING_KEY)
367     {
368         DWORD size = sizeof(DWORD);
369 
370         if(RegQueryValueExW(hKey, var_barstate0, 0, NULL, (LPBYTE)&barState[index],
371            &size) == ERROR_SUCCESS)
372             fetched = TRUE;
373     }
374 
375     if(!fetched)
376         barState[index] = (1 << BANDID_TOOLBAR) | (1 << BANDID_FORMATBAR) | (1 << BANDID_RULER) | (1 << BANDID_STATUSBAR);
377 
378     fetched = FALSE;
379     if(action == REG_OPENED_EXISTING_KEY)
380     {
381         DWORD size = sizeof(DWORD);
382         if(RegQueryValueExW(hKey, var_wrap, 0, NULL, (LPBYTE)&wordWrap[index],
383            &size) == ERROR_SUCCESS)
384             fetched = TRUE;
385     }
386 
387     if (!fetched)
388     {
389         if(index == reg_formatindex(SF_RTF))
390             wordWrap[index] = ID_WORDWRAP_WINDOW;
391         else if(index == reg_formatindex(SF_TEXT))
392             wordWrap[index] = ID_WORDWRAP_NONE;
393     }
394 
395     RegCloseKey(hKey);
396 }
397 
398 void registry_read_formatopts_all(DWORD barState[], DWORD wordWrap[])
399 {
400     registry_read_formatopts(reg_formatindex(SF_RTF), key_rtf, barState, wordWrap);
401     registry_read_formatopts(reg_formatindex(SF_TEXT), key_text, barState, wordWrap);
402 }
403 
404 static void registry_set_formatopts(int index, LPCWSTR key, DWORD barState[], DWORD wordWrap[])
405 {
406     HKEY hKey;
407     DWORD action = 0;
408 
409     if(registry_get_handle(&hKey, &action, key) == ERROR_SUCCESS)
410     {
411         RegSetValueExW(hKey, var_barstate0, 0, REG_DWORD, (LPBYTE)&barState[index],
412                        sizeof(DWORD));
413         RegSetValueExW(hKey, var_wrap, 0, REG_DWORD, (LPBYTE)&wordWrap[index],
414                        sizeof(DWORD));
415         RegCloseKey(hKey);
416     }
417 }
418 
419 void registry_set_formatopts_all(DWORD barState[], DWORD wordWrap[])
420 {
421     registry_set_formatopts(reg_formatindex(SF_RTF), key_rtf, barState, wordWrap);
422     registry_set_formatopts(reg_formatindex(SF_TEXT), key_text, barState, wordWrap);
423 }
424