wordpad: Fixed a bug in setting the recent file list in the registry.
[wine] / programs / wordpad / registry.c
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 #include <windows.h>
22 #include <shlobj.h>
23 #include <richedit.h>
24
25 #include "wordpad.h"
26
27 static const WCHAR key_recentfiles[] = {'R','e','c','e','n','t',' ','f','i','l','e',
28                                         ' ','l','i','s','t',0};
29 static const WCHAR key_options[] = {'O','p','t','i','o','n','s',0};
30 static const WCHAR key_rtf[] = {'R','T','F',0};
31 static const WCHAR key_text[] = {'T','e','x','t',0};
32
33 static const WCHAR var_file[] = {'F','i','l','e','%','d',0};
34 static const WCHAR var_framerect[] = {'F','r','a','m','e','R','e','c','t',0};
35 static const WCHAR var_barstate0[] = {'B','a','r','S','t','a','t','e','0',0};
36
37 static LRESULT registry_get_handle(HKEY *hKey, LPDWORD action, LPCWSTR subKey)
38 {
39     LONG ret;
40     static const WCHAR wszProgramKey[] = {'S','o','f','t','w','a','r','e','\\',
41         'M','i','c','r','o','s','o','f','t','\\',
42         'W','i','n','d','o','w','s','\\',
43         'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
44         'A','p','p','l','e','t','s','\\',
45         'W','o','r','d','p','a','d',0};
46         LPWSTR key = (LPWSTR)wszProgramKey;
47
48         if(subKey)
49         {
50             WCHAR backslash[] = {'\\',0};
51             key = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
52                             (lstrlenW(wszProgramKey)+lstrlenW(subKey)+lstrlenW(backslash)+1)
53                                     *sizeof(WCHAR));
54
55             if(!key)
56                 return 1;
57
58             lstrcpyW(key, wszProgramKey);
59             lstrcatW(key, backslash);
60             lstrcatW(key, subKey);
61         }
62
63         if(action)
64         {
65             ret = RegCreateKeyExW(HKEY_CURRENT_USER, key, 0, NULL, REG_OPTION_NON_VOLATILE,
66                                   KEY_READ | KEY_WRITE, NULL, hKey, action);
67         } else
68         {
69             ret = RegOpenKeyExW(HKEY_CURRENT_USER, key, 0, KEY_READ | KEY_WRITE, hKey);
70         }
71
72         if(subKey)
73             HeapFree(GetProcessHeap(), 0, key);
74
75         return ret;
76 }
77
78 void registry_set_options(HWND hMainWnd)
79 {
80     HKEY hKey;
81     DWORD action;
82
83     if(registry_get_handle(&hKey, &action, (LPWSTR)key_options) == ERROR_SUCCESS)
84     {
85         RECT rc;
86
87         GetWindowRect(hMainWnd, &rc);
88
89         RegSetValueExW(hKey, var_framerect, 0, REG_BINARY, (LPBYTE)&rc, sizeof(RECT));
90
91         registry_set_pagemargins(hKey);
92     }
93
94     RegCloseKey(hKey);
95 }
96
97 void registry_read_winrect(RECT* rc)
98 {
99     HKEY hKey;
100     DWORD size = sizeof(RECT);
101
102     if(registry_get_handle(&hKey, 0, (LPWSTR)key_options) != ERROR_SUCCESS ||
103        RegQueryValueExW(hKey, var_framerect, 0, NULL, (LPBYTE)rc, &size) !=
104        ERROR_SUCCESS || size != sizeof(RECT))
105     {
106         rc->top = 0;
107         rc->left = 0;
108         rc->bottom = 300;
109         rc->right = 600;
110     }
111
112     RegCloseKey(hKey);
113 }
114
115 static void truncate_path(LPWSTR file, LPWSTR out, LPWSTR pos1, LPWSTR pos2)
116 {
117     static const WCHAR dots[] = {'.','.','.',0};
118
119     *++pos1 = 0;
120
121     lstrcatW(out, file);
122     lstrcatW(out, dots);
123     lstrcatW(out, pos2);
124 }
125
126 static void format_filelist_filename(LPWSTR file, LPWSTR out)
127 {
128     LPWSTR pos_basename;
129     LPWSTR truncpos1, truncpos2;
130     WCHAR myDocs[MAX_STRING_LEN];
131
132     SHGetFolderPathW(NULL, CSIDL_PERSONAL, NULL, SHGFP_TYPE_CURRENT, (LPWSTR)&myDocs);
133     pos_basename = file_basename(file);
134     truncpos1 = NULL;
135     truncpos2 = NULL;
136
137     *(pos_basename-1) = 0;
138     if(!lstrcmpiW(file, myDocs) || (lstrlenW(pos_basename) > FILELIST_ENTRY_LENGTH))
139     {
140         truncpos1 = pos_basename;
141         *(pos_basename-1) = '\\';
142     } else
143     {
144         LPWSTR pos;
145         BOOL morespace = FALSE;
146
147         *(pos_basename-1) = '\\';
148
149         for(pos = file; pos < pos_basename; pos++)
150         {
151             if(*pos == '\\' || *pos == '/')
152             {
153                 if(truncpos1)
154                 {
155                     if((pos - file + lstrlenW(pos_basename)) > FILELIST_ENTRY_LENGTH)
156                         break;
157
158                     truncpos1 = pos;
159                     morespace = TRUE;
160                     break;
161                 }
162
163                 if((pos - file + lstrlenW(pos_basename)) > FILELIST_ENTRY_LENGTH)
164                     break;
165
166                 truncpos1 = pos;
167             }
168         }
169
170         if(morespace)
171         {
172             for(pos = pos_basename; pos >= truncpos1; pos--)
173             {
174                 if(*pos == '\\' || *pos == '/')
175                 {
176                     if((truncpos1 - file + lstrlenW(pos_basename) + pos_basename - pos) > FILELIST_ENTRY_LENGTH)
177                         break;
178
179                     truncpos2 = pos;
180                 }
181             }
182         }
183     }
184
185     if(truncpos1 == pos_basename)
186         lstrcatW(out, pos_basename);
187     else if(truncpos1 == truncpos2 || !truncpos2)
188         lstrcatW(out, file);
189     else
190         truncate_path(file, out, truncpos1, truncpos2);
191 }
192
193 void registry_read_filelist(HWND hMainWnd)
194 {
195     HKEY hFileKey;
196
197     if(registry_get_handle(&hFileKey, 0, key_recentfiles) == ERROR_SUCCESS)
198     {
199         WCHAR itemText[MAX_PATH+3], buffer[MAX_PATH];
200         /* The menu item name is not the same as the file name, so we need to store
201         the file name here */
202         static WCHAR file1[MAX_PATH], file2[MAX_PATH], file3[MAX_PATH], file4[MAX_PATH];
203         WCHAR numFormat[] = {'&','%','d',' ',0};
204         LPWSTR pFile[] = {file1, file2, file3, file4};
205         DWORD pathSize = MAX_PATH*sizeof(WCHAR);
206         int i;
207         WCHAR key[6];
208         MENUITEMINFOW mi;
209         HMENU hMenu = GetMenu(hMainWnd);
210
211         mi.cbSize = sizeof(MENUITEMINFOW);
212         mi.fMask = MIIM_ID | MIIM_DATA | MIIM_STRING | MIIM_FTYPE;
213         mi.fType = MFT_STRING;
214         mi.dwTypeData = itemText;
215         mi.wID = ID_FILE_RECENT1;
216
217         RemoveMenu(hMenu, ID_FILE_RECENT_SEPARATOR, MF_BYCOMMAND);
218         for(i = 0; i < FILELIST_ENTRIES; i++)
219         {
220             wsprintfW(key, var_file, i+1);
221             RemoveMenu(hMenu, ID_FILE_RECENT1+i, MF_BYCOMMAND);
222             if(RegQueryValueExW(hFileKey, (LPWSTR)key, 0, NULL, (LPBYTE)pFile[i], &pathSize)
223                != ERROR_SUCCESS)
224                 break;
225
226             mi.dwItemData = (ULONG_PTR)pFile[i];
227             wsprintfW(itemText, numFormat, i+1);
228
229             lstrcpyW(buffer, pFile[i]);
230
231             format_filelist_filename(buffer, itemText);
232
233             InsertMenuItemW(hMenu, ID_FILE_EXIT, FALSE, &mi);
234             mi.wID++;
235             pathSize = MAX_PATH*sizeof(WCHAR);
236         }
237         mi.fType = MFT_SEPARATOR;
238         mi.fMask = MIIM_FTYPE | MIIM_ID;
239         InsertMenuItemW(hMenu, ID_FILE_EXIT, FALSE, &mi);
240
241         RegCloseKey(hFileKey);
242     }
243 }
244
245 void registry_set_filelist(LPCWSTR newFile, HWND hMainWnd)
246 {
247     HKEY hKey;
248     DWORD action;
249
250     if(registry_get_handle(&hKey, &action, key_recentfiles) == ERROR_SUCCESS)
251     {
252         LPCWSTR pFiles[FILELIST_ENTRIES];
253         int i;
254         HMENU hMenu = GetMenu(hMainWnd);
255         MENUITEMINFOW mi;
256         WCHAR buffer[6];
257
258         mi.cbSize = sizeof(MENUITEMINFOW);
259         mi.fMask = MIIM_DATA;
260
261         for(i = 0; i < FILELIST_ENTRIES; i++)
262             pFiles[i] = NULL;
263
264         for(i = 0; GetMenuItemInfoW(hMenu, ID_FILE_RECENT1+i, FALSE, &mi); i++)
265             pFiles[i] = (LPWSTR)mi.dwItemData;
266
267         if(lstrcmpiW(newFile, pFiles[0]))
268         {
269             for(i = 0; pFiles[i] && i < FILELIST_ENTRIES; i++)
270             {
271                 if(!lstrcmpiW(pFiles[i], newFile))
272                 {
273                     int j;
274                     for(j = 0; pFiles[j] && j < i; j++)
275                     {
276                         pFiles[i-j] = pFiles[i-j-1];
277                     }
278                     pFiles[0] = NULL;
279                     break;
280                 }
281             }
282
283             if(!pFiles[0])
284             {
285                 pFiles[0] = newFile;
286             } else
287             {
288                 for(i = 0; i < FILELIST_ENTRIES-1; i++)
289                     pFiles[FILELIST_ENTRIES-1-i] = pFiles[FILELIST_ENTRIES-2-i];
290
291                 pFiles[0] = newFile;
292             }
293
294             for(i = 0; pFiles[i] && i < FILELIST_ENTRIES; i++)
295             {
296                 wsprintfW(buffer, var_file, i+1);
297                 RegSetValueExW(hKey, (LPWSTR)&buffer, 0, REG_SZ, (LPBYTE)pFiles[i],
298                                (lstrlenW(pFiles[i])+1)*sizeof(WCHAR));
299             }
300         }
301     }
302     RegCloseKey(hKey);
303     registry_read_filelist(hMainWnd);
304 }
305
306 int reg_formatindex(WPARAM format)
307 {
308     return (format & SF_TEXT) ? 1 : 0;
309 }
310
311 void registry_read_options(void)
312 {
313     HKEY hKey;
314
315     if(registry_get_handle(&hKey, 0, key_options) != ERROR_SUCCESS)
316         registry_read_pagemargins(NULL);
317     else
318         registry_read_pagemargins(hKey);
319
320     RegCloseKey(hKey);
321 }
322
323 static void registry_read_formatopts(int index, LPCWSTR key, DWORD barState[], DWORD wordWrap[])
324 {
325     HKEY hKey;
326     DWORD action = 0;
327     BOOL fetched = FALSE;
328     barState[index] = 0;
329     wordWrap[index] = 0;
330
331     if(registry_get_handle(&hKey, &action, key) != ERROR_SUCCESS)
332         return;
333
334     if(action == REG_OPENED_EXISTING_KEY)
335     {
336         DWORD size = sizeof(DWORD);
337
338         if(RegQueryValueExW(hKey, var_barstate0, 0, NULL, (LPBYTE)&barState[index],
339            &size) == ERROR_SUCCESS)
340             fetched = TRUE;
341     }
342
343     if(!fetched)
344         barState[index] = (1 << BANDID_TOOLBAR) | (1 << BANDID_FORMATBAR) | (1 << BANDID_RULER) | (1 << BANDID_STATUSBAR);
345
346     if(index == reg_formatindex(SF_RTF))
347         wordWrap[index] = ID_WORDWRAP_WINDOW;
348     else if(index == reg_formatindex(SF_TEXT))
349         wordWrap[index] = ID_WORDWRAP_WINDOW; /* FIXME: should be ID_WORDWRAP_NONE once we support it */
350
351     RegCloseKey(hKey);
352 }
353
354 void registry_read_formatopts_all(DWORD barState[], DWORD wordWrap[])
355 {
356     registry_read_formatopts(reg_formatindex(SF_RTF), key_rtf, barState, wordWrap);
357     registry_read_formatopts(reg_formatindex(SF_TEXT), key_text, barState, wordWrap);
358 }
359
360 static void registry_set_formatopts(int index, LPCWSTR key, DWORD barState[])
361 {
362     HKEY hKey;
363     DWORD action = 0;
364
365     if(registry_get_handle(&hKey, &action, key) == ERROR_SUCCESS)
366     {
367         RegSetValueExW(hKey, var_barstate0, 0, REG_DWORD, (LPBYTE)&barState[index],
368                        sizeof(DWORD));
369
370         RegCloseKey(hKey);
371     }
372 }
373
374 void registry_set_formatopts_all(DWORD barState[])
375 {
376     registry_set_formatopts(reg_formatindex(SF_RTF), key_rtf, barState);
377     registry_set_formatopts(reg_formatindex(SF_TEXT), key_text, barState);
378 }