1 /*
2  *     'File Types' tab property sheet of Folder Options
3  *
4  * Copyright 2007 Johannes Anderwald <johannes.anderwald@reactos.org>
5  * Copyright 2016-2018 Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.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 #include "precomp.h"
23 #include <atlpath.h>
24 
25 WINE_DEFAULT_DEBUG_CHANNEL (fprop);
26 
27 // rundll32.exe shell32.dll,Options_RunDLL 0
28 
29 /////////////////////////////////////////////////////////////////////////////
30 
31 EXTERN_C BOOL PathIsExeW(LPCWSTR lpszPath);
32 
33 #define FTA_MODIFYMASK (FTA_OpenIsSafe) // Bits modified by EditTypeDlg
34 #define NOASSOCRESID IDI_SHELL_DOCUMENT
35 #define SUPPORT_EXTENSIONWITHOUTPROGID 1 // NT5 does not support these but NT6 does
36 
37 #define ASSOC_CCHMAX (32 + 1) // Extension or protocol (INTERNET_MAX_SCHEME_LENGTH)
38 #define TYPENAME_CCHMAX max(100, RTL_FIELD_SIZE(SHFILEINFOA, szTypeName))
39 #define ICONLOCATION_CCHMAX (MAX_PATH + 1 + 11)
40 
41 typedef struct _FILE_TYPE_ENTRY
42 {
43     WCHAR FileExtension[ASSOC_CCHMAX];
44     WCHAR FileDescription[TYPENAME_CCHMAX];
45     WCHAR ClassKey[MAX_PATH];
46     DWORD EditFlags;
47     WCHAR AppName[64];
48     HICON hIconSmall;
49     WCHAR ProgramPath[MAX_PATH];
50     WCHAR IconPath[MAX_PATH];
51     INT nIconIndex;
52 
IsExtension_FILE_TYPE_ENTRY53     bool IsExtension() const
54     {
55         return FileExtension[0] == '.';
56     }
GetAssocForDisplay_FILE_TYPE_ENTRY57     LPCWSTR GetAssocForDisplay() const
58     {
59         return FileExtension + (FileExtension[0] == '.');
60     }
InvalidateTypeName_FILE_TYPE_ENTRY61     void InvalidateTypeName()
62     {
63         FileDescription[0] = FileDescription[1] = UNICODE_NULL;
64     }
InvalidateDefaultApp_FILE_TYPE_ENTRY65     void InvalidateDefaultApp()
66     {
67         ProgramPath[0] = ProgramPath[1] = AppName[0] = AppName[1] = UNICODE_NULL;
68     }
Initialize_FILE_TYPE_ENTRY69     void Initialize()
70     {
71         ClassKey[0] = UNICODE_NULL;
72         IconPath[0] = UNICODE_NULL;
73         nIconIndex = 0;
74         InvalidateTypeName();
75         InvalidateDefaultApp();
76     }
DestroyIcons_FILE_TYPE_ENTRY77     void DestroyIcons()
78     {
79         if (hIconSmall)
80             DestroyIcon(hIconSmall);
81         hIconSmall = NULL;
82     }
83 } FILE_TYPE_ENTRY, *PFILE_TYPE_ENTRY;
84 
85 typedef struct _FILE_TYPE_GLOBALS
86 {
87     HIMAGELIST himlSmall;
88     UINT IconSize;
89     HICON hDefExtIconSmall;
90     HBITMAP hOpenWithImage;
91     HANDLE hHeap;
92     WCHAR NoneString[42];
93     INT8 SortCol, SortReverse;
94     UINT Restricted;
95 } FILE_TYPE_GLOBALS, *PFILE_TYPE_GLOBALS;
96 
97 static DWORD
GetRegDWORD(HKEY hKey,LPCWSTR Name,DWORD & Value,DWORD DefaultValue=0,BOOL Strict=FALSE)98 GetRegDWORD(HKEY hKey, LPCWSTR Name, DWORD &Value, DWORD DefaultValue = 0, BOOL Strict = FALSE)
99 {
100     DWORD cb = sizeof(DWORD), type;
101     LRESULT ec = RegQueryValueExW(hKey, Name, 0, &type, (BYTE*)&Value, &cb);
102     if (ec == ERROR_SUCCESS)
103     {
104         if ((type == REG_DWORD && cb == sizeof(DWORD)) ||
105             (!Strict && type == REG_BINARY && (cb && cb <= sizeof(DWORD))))
106         {
107             Value &= (0xffffffffUL >> (32 - cb * 8));
108             return ec;
109         }
110     }
111     Value = DefaultValue;
112     return ec ? ec : ERROR_BAD_FORMAT;
113 }
114 
115 static DWORD
GetRegDWORD(HKEY hKey,LPCWSTR Name,DWORD DefaultValue=0)116 GetRegDWORD(HKEY hKey, LPCWSTR Name, DWORD DefaultValue = 0)
117 {
118     GetRegDWORD(hKey, Name, DefaultValue, DefaultValue, FALSE);
119     return DefaultValue;
120 }
121 
122 static HRESULT
GetClassKey(const FILE_TYPE_ENTRY & FTE,LPCWSTR & SubKey)123 GetClassKey(const FILE_TYPE_ENTRY &FTE, LPCWSTR &SubKey)
124 {
125     HRESULT hr = S_OK;
126     LPCWSTR path = FTE.IsExtension() ? FTE.ClassKey : FTE.FileExtension;
127 #if SUPPORT_EXTENSIONWITHOUTPROGID
128     if (!*path && FTE.IsExtension())
129     {
130         path = FTE.FileExtension;
131         hr = S_FALSE;
132     }
133 #endif
134     ASSERT(*path);
135     SubKey = path;
136     return hr;
137 }
138 
139 static void
QuoteAppPathForCommand(CStringW & path)140 QuoteAppPathForCommand(CStringW &path)
141 {
142     if (path.Find(' ') >= 0 && path.Find('\"') < 0)
143         path = CStringW(L"\"") + path + L"\"";
144 }
145 
146 static BOOL
DeleteExt(HWND hwndDlg,LPCWSTR pszExt)147 DeleteExt(HWND hwndDlg, LPCWSTR pszExt)
148 {
149     if (*pszExt != L'.')
150         return FALSE;
151 
152     // open ".ext" key
153     HKEY hKey;
154     if (RegOpenKeyExW(HKEY_CLASSES_ROOT, pszExt, 0, KEY_READ, &hKey) != ERROR_SUCCESS)
155         return FALSE;
156 
157     // query "extfile" key name
158     WCHAR ProgId[MAX_PATH] = { 0 };
159     DWORD cb = sizeof(ProgId);
160     RegQueryValueExW(hKey, NULL, NULL, NULL, LPBYTE(ProgId), &cb);
161     RegCloseKey(hKey);
162 
163     // FIXME: Should verify that no other extensions are using this ProgId
164     // delete "extfile" key (if any)
165     if (ProgId[0])
166         SHDeleteKeyW(HKEY_CLASSES_ROOT, ProgId);
167 
168     // delete ".ext" key
169     BOOL ret = (SHDeleteKeyW(HKEY_CLASSES_ROOT, pszExt) == ERROR_SUCCESS);
170 
171     // notify
172     SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_FLUSHNOWAIT, NULL, NULL);
173 
174     return ret;
175 }
176 
177 static inline HICON
DoExtractIcon(LPCWSTR IconPath,INT iIndex,UINT cx,UINT cy)178 DoExtractIcon(LPCWSTR IconPath, INT iIndex, UINT cx, UINT cy)
179 {
180     return SHELL32_SHExtractIcon(IconPath, iIndex, cx, cy);
181 }
182 
183 static HICON
DoExtractIcon(LPCWSTR IconPath,INT iIndex=0,BOOL bSmall=FALSE)184 DoExtractIcon(LPCWSTR IconPath, INT iIndex = 0, BOOL bSmall = FALSE)
185 {
186     UINT cx = GetSystemMetrics(bSmall ? SM_CXSMICON : SM_CXICON);
187     UINT cy = GetSystemMetrics(bSmall ? SM_CYSMICON : SM_CYICON);
188     return DoExtractIcon(IconPath, iIndex, cx, cy);
189 }
190 
191 static BOOL
GetFileTypeIconsEx(PFILE_TYPE_ENTRY Entry,LPCWSTR IconLocation,UINT IconSize)192 GetFileTypeIconsEx(PFILE_TYPE_ENTRY Entry, LPCWSTR IconLocation, UINT IconSize)
193 {
194     Entry->hIconSmall = NULL;
195     if (lstrcmpW(IconLocation, L"%1") == 0)
196     {
197         LPCWSTR ext = Entry->FileExtension;
198         if (!lstrcmpiW(ext, L".exe") || !lstrcmpiW(ext, L".scr"))
199         {
200             Entry->hIconSmall = HICON(LoadImageW(shell32_hInstance, MAKEINTRESOURCEW(IDI_SHELL_EXE),
201                                                  IMAGE_ICON, IconSize, IconSize, 0));
202             Entry->nIconIndex = -IDI_SHELL_EXE;
203         }
204         // Set the icon path to %1 on purpose so PickIconDlg will issue a warning
205         StringCbCopyW(Entry->IconPath, sizeof(Entry->IconPath), IconLocation);
206     }
207     else
208     {
209         // Expand the REG_EXPAND_SZ string by environment variables
210         if (ExpandEnvironmentStringsW(IconLocation, Entry->IconPath, _countof(Entry->IconPath)))
211         {
212             Entry->nIconIndex = PathParseIconLocationW(Entry->IconPath);
213             Entry->hIconSmall = DoExtractIcon(Entry->IconPath, Entry->nIconIndex, IconSize, IconSize);
214         }
215     }
216     return Entry->hIconSmall != NULL;
217 }
218 
219 static BOOL
GetFileTypeIconsByKey(HKEY hKey,PFILE_TYPE_ENTRY Entry,UINT IconSize)220 GetFileTypeIconsByKey(HKEY hKey, PFILE_TYPE_ENTRY Entry, UINT IconSize)
221 {
222     Entry->hIconSmall = NULL;
223 
224     HKEY hDefIconKey;
225     LONG nResult = RegOpenKeyExW(hKey, L"DefaultIcon", 0, KEY_READ, &hDefIconKey);
226     if (nResult != ERROR_SUCCESS)
227         return FALSE;
228 
229     // Get the icon location
230     WCHAR szLocation[ICONLOCATION_CCHMAX];
231     DWORD dwSize = sizeof(szLocation);
232     nResult = RegQueryValueExW(hDefIconKey, NULL, NULL, NULL, LPBYTE(szLocation), &dwSize);
233     szLocation[_countof(szLocation) - 1] = UNICODE_NULL;
234 
235     RegCloseKey(hDefIconKey);
236     if (nResult != ERROR_SUCCESS || !szLocation[0])
237         return FALSE;
238 
239     return GetFileTypeIconsEx(Entry, szLocation, IconSize);
240 }
241 
242 static LPCWSTR
GetProgramPath(PFILE_TYPE_ENTRY Entry)243 GetProgramPath(PFILE_TYPE_ENTRY Entry)
244 {
245     if (!Entry->ProgramPath[1] && !Entry->ProgramPath[0])
246     {
247         DWORD cch = _countof(Entry->ProgramPath);
248         if (FAILED(AssocQueryStringW(ASSOCF_INIT_IGNOREUNKNOWN, ASSOCSTR_EXECUTABLE,
249                                      Entry->FileExtension, NULL, Entry->ProgramPath, &cch)))
250         {
251             Entry->ProgramPath[0] = UNICODE_NULL;
252             Entry->ProgramPath[1] = TRUE;
253         }
254     }
255     return Entry->ProgramPath;
256 }
257 
258 static BOOL
QueryFileDescription(LPCWSTR ProgramPath,LPWSTR pszName,INT cchName)259 QueryFileDescription(LPCWSTR ProgramPath, LPWSTR pszName, INT cchName)
260 {
261     SHFILEINFOW fi;
262     fi.szDisplayName[0] = UNICODE_NULL;
263     if (SHGetFileInfoW(ProgramPath, 0, &fi, sizeof(fi), SHGFI_DISPLAYNAME))
264     {
265         StringCchCopyW(pszName, cchName, fi.szDisplayName);
266         return TRUE;
267     }
268     return !!GetFileTitleW(ProgramPath, pszName, cchName);
269 }
270 
271 static LPCWSTR
GetAppName(PFILE_TYPE_ENTRY Entry)272 GetAppName(PFILE_TYPE_ENTRY Entry)
273 {
274     if (!Entry->AppName[1] && !Entry->AppName[0])
275     {
276         LPCWSTR exe = GetProgramPath(Entry);
277         if (!*exe || !QueryFileDescription(exe, Entry->AppName, _countof(Entry->AppName)))
278         {
279             Entry->AppName[0] = UNICODE_NULL;
280             Entry->AppName[1] = TRUE;
281         }
282     }
283     return Entry->AppName;
284 }
285 
286 static LPWSTR
GetTypeName(PFILE_TYPE_ENTRY Entry,PFILE_TYPE_GLOBALS pG)287 GetTypeName(PFILE_TYPE_ENTRY Entry, PFILE_TYPE_GLOBALS pG)
288 {
289     if (!Entry->FileDescription[1] && !Entry->FileDescription[0])
290     {
291         Entry->FileDescription[1] = TRUE;
292         if (Entry->IsExtension())
293         {
294             SHFILEINFOW fi;
295             if (SHGetFileInfoW(Entry->FileExtension, 0, &fi, sizeof(fi), SHGFI_TYPENAME |
296                                SHGFI_USEFILEATTRIBUTES) && *fi.szTypeName)
297             {
298                 StringCchCopyW(Entry->FileDescription, _countof(Entry->FileDescription), fi.szTypeName);
299             }
300         }
301         else
302         {
303             // FIXME: Fix and use ASSOCSTR_FRIENDLYDOCNAME
304             DWORD cb = sizeof(Entry->FileDescription), Fallback = TRUE;
305             LPCWSTR ClassKey;
306             HRESULT hr = GetClassKey(*Entry, ClassKey);
307             HKEY hKey;
308             if (SUCCEEDED(hr) && !RegOpenKeyExW(HKEY_CLASSES_ROOT, ClassKey, 0, KEY_READ, &hKey))
309             {
310                 Fallback = RegQueryValueExW(hKey, NULL, 0, NULL, (BYTE*)Entry->FileDescription, &cb) != ERROR_SUCCESS ||
311                            !*Entry->FileDescription;
312                 RegCloseKey(hKey);
313             }
314             if (Fallback)
315             {
316                 StringCchCopyW(Entry->FileDescription, _countof(Entry->FileDescription), Entry->FileExtension);
317             }
318         }
319     }
320     return Entry->FileDescription;
321 }
322 
323 static void
InitializeDefaultIcons(PFILE_TYPE_GLOBALS pG)324 InitializeDefaultIcons(PFILE_TYPE_GLOBALS pG)
325 {
326     const INT ResId = NOASSOCRESID;
327     if (!pG->hDefExtIconSmall)
328     {
329         pG->hDefExtIconSmall = HICON(LoadImageW(shell32_hInstance, MAKEINTRESOURCEW(ResId),
330                                                 IMAGE_ICON, pG->IconSize, pG->IconSize, 0));
331     }
332 
333     if (!ImageList_GetImageCount(pG->himlSmall))
334     {
335         int idx = ImageList_AddIcon(pG->himlSmall, pG->hDefExtIconSmall);
336         ASSERT(idx == 0);
337     }
338 }
339 
340 static BOOL
Normalize(PFILE_TYPE_ENTRY Entry)341 Normalize(PFILE_TYPE_ENTRY Entry)
342 {
343     // We don't need this information until somebody tries to edit the entry
344     if (!Entry->IconPath[0])
345     {
346         StringCbCopyW(Entry->IconPath, sizeof(Entry->IconPath), g_pszShell32);
347         Entry->nIconIndex = NOASSOCRESID > 1 ? -NOASSOCRESID : 0;
348     }
349     return TRUE;
350 }
351 
352 /////////////////////////////////////////////////////////////////////////////
353 // EditTypeDlg
354 
355 #define LISTBOX_MARGIN  2
356 
357 enum EDITTYPEFLAGS { ETF_ALWAYSEXT = 1 << 0, ETF_BROWSESAME = 1 << 1 };
358 
359 typedef struct EDITTYPE_DIALOG
360 {
361     HWND hwndLV;
362     PFILE_TYPE_GLOBALS pG;
363     PFILE_TYPE_ENTRY pEntry;
364     CSimpleMap<CStringW, CStringW> CommandLineMap;
365     CAtlList<CStringW> ModifiedVerbs;
366     WCHAR szIconPath[MAX_PATH];
367     INT nIconIndex;
368     bool ChangedIcon;
369     WCHAR szDefaultVerb[VERBKEY_CCHMAX];
370     WCHAR TypeName[TYPENAME_CCHMAX];
371 } EDITTYPE_DIALOG, *PEDITTYPE_DIALOG;
372 
373 static void
EditTypeDlg_OnChangeIcon(HWND hwndDlg,PEDITTYPE_DIALOG pEditType)374 EditTypeDlg_OnChangeIcon(HWND hwndDlg, PEDITTYPE_DIALOG pEditType)
375 {
376     WCHAR szPath[MAX_PATH];
377     INT IconIndex = pEditType->nIconIndex;
378     ExpandEnvironmentStringsW(pEditType->szIconPath, szPath, _countof(szPath));
379     if (PickIconDlg(hwndDlg, szPath, _countof(szPath), &IconIndex))
380     {
381         HICON hIconLarge = DoExtractIcon(szPath, IconIndex, FALSE);
382 
383         // replace Windows directory with "%SystemRoot%" (for portability)
384         WCHAR szWinDir[MAX_PATH];
385         UINT lenWinDir = GetWindowsDirectoryW(szWinDir, _countof(szWinDir));
386         if (StrStrIW(szPath, szWinDir) == szPath)
387         {
388             CPathW str(L"%SystemRoot%");
389             str.Append(&szPath[lenWinDir]);
390             StringCbCopyW(szPath, sizeof(szPath), str);
391         }
392 
393         // update EDITTYPE_DIALOG
394         StringCbCopyW(pEditType->szIconPath, sizeof(pEditType->szIconPath), szPath);
395         pEditType->nIconIndex = IconIndex;
396         pEditType->ChangedIcon = true;
397 
398         // set icon to dialog
399         HICON hOld = (HICON)SendDlgItemMessageW(hwndDlg, IDC_EDITTYPE_ICON, STM_SETICON, (WPARAM)hIconLarge, 0);
400         if (hOld)
401             DestroyIcon(hOld);
402     }
403 }
404 
405 static BOOL
EditTypeDlg_OnDrawItem(HWND hwndDlg,LPDRAWITEMSTRUCT pDraw,PEDITTYPE_DIALOG pEditType)406 EditTypeDlg_OnDrawItem(HWND hwndDlg, LPDRAWITEMSTRUCT pDraw, PEDITTYPE_DIALOG pEditType)
407 {
408     WCHAR szText[MAX_PATH];
409     HFONT hFont, hFont2;
410 
411     if (!pDraw)
412         return FALSE;
413 
414     // fill rect and set colors
415     if (pDraw->itemState & ODS_SELECTED)
416     {
417         FillRect(pDraw->hDC, &pDraw->rcItem, HBRUSH(COLOR_HIGHLIGHT + 1));
418         SetTextColor(pDraw->hDC, GetSysColor(COLOR_HIGHLIGHTTEXT));
419         SetBkColor(pDraw->hDC, GetSysColor(COLOR_HIGHLIGHT));
420     }
421     else
422     {
423         FillRect(pDraw->hDC, &pDraw->rcItem, HBRUSH(COLOR_WINDOW + 1));
424         SetTextColor(pDraw->hDC, GetSysColor(COLOR_WINDOWTEXT));
425         SetBkColor(pDraw->hDC, GetSysColor(COLOR_WINDOW));
426     }
427 
428     // get listbox text
429     HWND hwndListBox = GetDlgItem(hwndDlg, IDC_EDITTYPE_LISTBOX);
430     SendMessageW(hwndListBox, LB_GETTEXT, pDraw->itemID, (LPARAM)szText);
431 
432     // is it default?
433     hFont = (HFONT)SendMessageW(hwndListBox, WM_GETFONT, 0, 0);
434     if (lstrcmpiW(pEditType->szDefaultVerb, szText) == 0)
435     {
436         // default. set bold
437         LOGFONTW lf;
438         GetObject(hFont, sizeof(lf), &lf);
439         lf.lfWeight = FW_BOLD;
440         hFont2 = CreateFontIndirectW(&lf);
441         if (hFont2)
442         {
443             HGDIOBJ hFontOld = SelectObject(pDraw->hDC, hFont2);
444             InflateRect(&pDraw->rcItem, -LISTBOX_MARGIN, -LISTBOX_MARGIN);
445             DrawTextW(pDraw->hDC, szText, -1, &pDraw->rcItem,
446                       DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_NOPREFIX);
447             InflateRect(&pDraw->rcItem, LISTBOX_MARGIN, LISTBOX_MARGIN);
448             SelectObject(pDraw->hDC, hFontOld);
449             DeleteObject(hFont2);
450         }
451     }
452     else
453     {
454         // non-default
455         InflateRect(&pDraw->rcItem, -LISTBOX_MARGIN, -LISTBOX_MARGIN);
456         DrawTextW(pDraw->hDC, szText, -1, &pDraw->rcItem,
457                   DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_NOPREFIX);
458         InflateRect(&pDraw->rcItem, LISTBOX_MARGIN, LISTBOX_MARGIN);
459     }
460 
461     // draw focus rect
462     if (pDraw->itemState & ODS_FOCUS)
463     {
464         DrawFocusRect(pDraw->hDC, &pDraw->rcItem);
465     }
466     return TRUE;
467 }
468 
469 static BOOL
EditTypeDlg_OnMeasureItem(HWND hwndDlg,LPMEASUREITEMSTRUCT pMeasure,PEDITTYPE_DIALOG pEditType)470 EditTypeDlg_OnMeasureItem(HWND hwndDlg, LPMEASUREITEMSTRUCT pMeasure, PEDITTYPE_DIALOG pEditType)
471 {
472     if (!pMeasure)
473         return FALSE;
474 
475     HWND hwndLB = GetDlgItem(hwndDlg, IDC_EDITTYPE_LISTBOX);
476 
477     HDC hDC = GetDC(hwndLB);
478     if (hDC)
479     {
480         TEXTMETRICW tm;
481         GetTextMetricsW(hDC, &tm);
482         pMeasure->itemHeight = tm.tmHeight + LISTBOX_MARGIN * 2;
483         ReleaseDC(hwndLB, hDC);
484         return TRUE;
485     }
486     return FALSE;
487 }
488 
489 /////////////////////////////////////////////////////////////////////////////
490 // NewExtDlg
491 
492 typedef struct NEWEXT_DIALOG
493 {
494     HWND hwndLV;
495     RECT rcDlg;
496     BOOL bAdvanced;
497     INT dy;
498     WCHAR szExt[16];
499     WCHAR szFileType[64];
500 } NEWEXT_DIALOG, *PNEWEXT_DIALOG;
501 
502 static VOID
NewExtDlg_OnAdvanced(HWND hwndDlg,PNEWEXT_DIALOG pNewExt)503 NewExtDlg_OnAdvanced(HWND hwndDlg, PNEWEXT_DIALOG pNewExt)
504 {
505     // If "Advanced" button was clicked, then we shrink or expand the dialog.
506     RECT rc, rc1, rc2;
507 
508     GetWindowRect(hwndDlg, &rc);
509     rc.bottom = rc.top + (pNewExt->rcDlg.bottom - pNewExt->rcDlg.top);
510 
511     GetWindowRect(GetDlgItem(hwndDlg, IDOK), &rc1);
512     MapWindowPoints(NULL, hwndDlg, (LPPOINT)&rc1, 2);
513 
514     GetWindowRect(GetDlgItem(hwndDlg, IDCANCEL), &rc2);
515     MapWindowPoints(NULL, hwndDlg, (LPPOINT)&rc2, 2);
516 
517     HWND hClassCombo = GetDlgItem(hwndDlg, IDC_NEWEXT_COMBOBOX);
518     if (pNewExt->bAdvanced)
519     {
520         rc1.top += pNewExt->dy;
521         rc1.bottom += pNewExt->dy;
522 
523         rc2.top += pNewExt->dy;
524         rc2.bottom += pNewExt->dy;
525 
526         ShowWindow(GetDlgItem(hwndDlg, IDC_NEWEXT_ASSOC), SW_SHOWNOACTIVATE);
527         ShowWindow(hClassCombo, SW_SHOWNOACTIVATE);
528 
529         CStringW strLeft(MAKEINTRESOURCEW(IDS_NEWEXT_ADVANCED_LEFT));
530         SetDlgItemTextW(hwndDlg, IDC_NEWEXT_ADVANCED, strLeft);
531 
532         SetFocus(hClassCombo);
533     }
534     else
535     {
536         rc1.top -= pNewExt->dy;
537         rc1.bottom -= pNewExt->dy;
538 
539         rc2.top -= pNewExt->dy;
540         rc2.bottom -= pNewExt->dy;
541 
542         ShowWindow(GetDlgItem(hwndDlg, IDC_NEWEXT_ASSOC), SW_HIDE);
543         ShowWindow(hClassCombo, SW_HIDE);
544 
545         CStringW strRight(MAKEINTRESOURCEW(IDS_NEWEXT_ADVANCED_RIGHT));
546         SetDlgItemTextW(hwndDlg, IDC_NEWEXT_ADVANCED, strRight);
547 
548         rc.bottom -= pNewExt->dy;
549 
550         SendMessageW(hClassCombo, CB_SETCURSEL, 0, 0); // Reset the combo to the "new class" mode
551     }
552 
553     HDWP hDWP = BeginDeferWindowPos(3);
554 
555     if (hDWP)
556         hDWP = DeferWindowPos(hDWP, GetDlgItem(hwndDlg, IDOK), NULL,
557                               rc1.left, rc1.top, rc1.right - rc1.left, rc1.bottom - rc1.top,
558                               SWP_NOACTIVATE | SWP_NOZORDER);
559     if (hDWP)
560         hDWP = DeferWindowPos(hDWP, GetDlgItem(hwndDlg, IDCANCEL), NULL,
561                               rc2.left, rc2.top, rc2.right - rc2.left, rc2.bottom - rc2.top,
562                               SWP_NOACTIVATE | SWP_NOZORDER);
563     if (hDWP)
564         hDWP = DeferWindowPos(hDWP, hwndDlg, NULL,
565                               rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top,
566                               SWP_NOACTIVATE | SWP_NOZORDER);
567 
568     if (hDWP)
569         EndDeferWindowPos(hDWP);
570 }
571 
572 static BOOL
NewExtDlg_OnInitDialog(HWND hwndDlg,PNEWEXT_DIALOG pNewExt)573 NewExtDlg_OnInitDialog(HWND hwndDlg, PNEWEXT_DIALOG pNewExt)
574 {
575     pNewExt->bAdvanced = FALSE;
576 
577     // get window rectangle
578     GetWindowRect(hwndDlg, &pNewExt->rcDlg);
579 
580     // get delta Y
581     RECT rc1, rc2;
582     GetWindowRect(GetDlgItem(hwndDlg, IDC_NEWEXT_EDIT), &rc1);
583     GetWindowRect(GetDlgItem(hwndDlg, IDC_NEWEXT_COMBOBOX), &rc2);
584     pNewExt->dy = rc2.top - rc1.top;
585 
586     // initialize
587     CStringW strText(MAKEINTRESOURCEW(IDS_NEWEXT_NEW));
588     SendDlgItemMessageW(hwndDlg, IDC_NEWEXT_COMBOBOX, CB_ADDSTRING, 0, (LPARAM)(LPCWSTR)strText);
589     SendDlgItemMessageW(hwndDlg, IDC_NEWEXT_COMBOBOX, CB_SETCURSEL, 0, 0);
590     SendDlgItemMessageW(hwndDlg, IDC_NEWEXT_EDIT, EM_SETLIMITTEXT, _countof(pNewExt->szExt) - 1, 0);
591 
592     // shrink it first time
593     NewExtDlg_OnAdvanced(hwndDlg, pNewExt);
594 
595     return TRUE;
596 }
597 
598 static BOOL
NewExtDlg_OnOK(HWND hwndDlg,PNEWEXT_DIALOG pNewExt)599 NewExtDlg_OnOK(HWND hwndDlg, PNEWEXT_DIALOG pNewExt)
600 {
601     LV_FINDINFO find;
602     INT iItem;
603 
604     GetDlgItemTextW(hwndDlg, IDC_NEWEXT_EDIT, pNewExt->szExt, _countof(pNewExt->szExt));
605     StrTrimW(pNewExt->szExt, g_pszSpace);
606     _wcsupr(pNewExt->szExt);
607 
608 #if 0
609     // FIXME: Implement the "choose existing class" mode
610     GetDlgItemTextW(hwndDlg, IDC_NEWEXT_COMBOBOX, pNewExt->szFileType, _countof(pNewExt->szFileType));
611     StrTrimW(pNewExt->szFileType, g_pszSpace);
612 #endif
613     pNewExt->szFileType[0] = UNICODE_NULL; // "new class" mode
614 
615     if (pNewExt->szExt[0] == 0)
616     {
617         CStringW strText(MAKEINTRESOURCEW(IDS_NEWEXT_SPECIFY_EXT));
618         CStringW strTitle(MAKEINTRESOURCEW(IDS_FILE_TYPES));
619         MessageBoxW(hwndDlg, strText, strTitle, MB_ICONERROR);
620         return FALSE;
621     }
622 
623     ZeroMemory(&find, sizeof(find));
624     find.flags = LVFI_STRING;
625     if (pNewExt->szExt[0] == L'.')
626     {
627         find.psz = &pNewExt->szExt[1];
628     }
629     else
630     {
631         find.psz = pNewExt->szExt;
632     }
633 
634     iItem = ListView_FindItem(pNewExt->hwndLV, -1, &find);
635     if (iItem >= 0)
636     {
637         // already exists
638 
639         // get file type
640         WCHAR szFileType[TYPENAME_CCHMAX];
641         LV_ITEM item;
642         ZeroMemory(&item, sizeof(item));
643         item.mask = LVIF_TEXT;
644         item.pszText = szFileType;
645         item.cchTextMax = _countof(szFileType);
646         item.iItem = iItem;
647         item.iSubItem = 1;
648         ListView_GetItem(pNewExt->hwndLV, &item);
649 
650         // get text
651         CStringW strText;
652         strText.Format(IDS_NEWEXT_ALREADY_ASSOC, find.psz, szFileType, find.psz, szFileType);
653 
654         // get title
655         CStringW strTitle;
656         strTitle.LoadString(IDS_NEWEXT_EXT_IN_USE);
657 
658         if (MessageBoxW(hwndDlg, strText, strTitle, MB_ICONWARNING | MB_YESNO) == IDNO)
659         {
660             return FALSE;
661         }
662 
663         // Delete the extension
664         CStringW strExt(L".");
665         strExt += find.psz;
666         strExt.MakeLower();
667         if (DeleteExt(hwndDlg, strExt))
668             ListView_DeleteItem(pNewExt->hwndLV, iItem);
669     }
670 
671     EndDialog(hwndDlg, IDOK);
672     return TRUE;
673 }
674 
675 // IDD_NEWEXTENSION
676 static INT_PTR CALLBACK
NewExtDlgProc(HWND hwndDlg,UINT uMsg,WPARAM wParam,LPARAM lParam)677 NewExtDlgProc(
678     HWND hwndDlg,
679     UINT uMsg,
680     WPARAM wParam,
681     LPARAM lParam)
682 {
683     static PNEWEXT_DIALOG s_pNewExt = NULL;
684 
685     switch (uMsg)
686     {
687         case WM_INITDIALOG:
688             s_pNewExt = (PNEWEXT_DIALOG)lParam;
689             NewExtDlg_OnInitDialog(hwndDlg, s_pNewExt);
690             return TRUE;
691 
692         case WM_COMMAND:
693             switch (LOWORD(wParam))
694             {
695                 case IDOK:
696                     NewExtDlg_OnOK(hwndDlg, s_pNewExt);
697                     break;
698 
699                 case IDCANCEL:
700                     EndDialog(hwndDlg, IDCANCEL);
701                     break;
702 
703                 case IDC_NEWEXT_ADVANCED:
704                     s_pNewExt->bAdvanced = !s_pNewExt->bAdvanced;
705                     NewExtDlg_OnAdvanced(hwndDlg, s_pNewExt);
706                     break;
707             }
708             break;
709     }
710     return 0;
711 }
712 
713 static PFILE_TYPE_ENTRY
FileTypesDlg_InsertToLV(HWND hListView,LPCWSTR Assoc,INT iItem,PFILE_TYPE_GLOBALS pG)714 FileTypesDlg_InsertToLV(HWND hListView, LPCWSTR Assoc, INT iItem, PFILE_TYPE_GLOBALS pG)
715 {
716     PFILE_TYPE_ENTRY Entry;
717     HKEY hKey, hTemp;
718     LVITEMW lvItem;
719     DWORD dwSize;
720 
721     if (RegOpenKeyExW(HKEY_CLASSES_ROOT, Assoc, 0, KEY_READ, &hKey) != ERROR_SUCCESS)
722     {
723         return NULL;
724     }
725     if (Assoc[0] != L'.' && !RegValueExists(hKey, L"URL Protocol"))
726     {
727         RegCloseKey(hKey);
728         return NULL;
729     }
730 
731     Entry = (PFILE_TYPE_ENTRY)HeapAlloc(pG->hHeap, 0, sizeof(FILE_TYPE_ENTRY));
732     if (!Entry)
733     {
734         RegCloseKey(hKey);
735         return NULL;
736     }
737     Entry->Initialize();
738 
739     if (Assoc[0] == L'.')
740     {
741         if (PathIsExeW(Assoc))
742         {
743 exclude:
744             HeapFree(pG->hHeap, 0, Entry);
745             RegCloseKey(hKey);
746             return NULL;
747         }
748 
749         dwSize = sizeof(Entry->ClassKey);
750         if (RegQueryValueExW(hKey, NULL, NULL, NULL, LPBYTE(Entry->ClassKey), &dwSize))
751         {
752             Entry->ClassKey[0] = UNICODE_NULL; // No ProgId
753         }
754 #if SUPPORT_EXTENSIONWITHOUTPROGID
755         if (Entry->ClassKey[0] && !RegOpenKeyExW(HKEY_CLASSES_ROOT, Entry->ClassKey, 0, KEY_READ, &hTemp))
756         {
757             RegCloseKey(hKey);
758             hKey = hTemp;
759         }
760 #else
761         if (!Entry->ClassKey[0])
762             goto exclude;
763 #endif
764     }
765 
766     Entry->EditFlags = GetRegDWORD(hKey, L"EditFlags", 0);
767     if (Entry->EditFlags & FTA_Exclude)
768         goto exclude;
769 
770     wcscpy(Entry->FileExtension, Assoc);
771 
772     // get icon
773     Entry->IconPath[0] = UNICODE_NULL;
774     BOOL defaultIcon = !GetFileTypeIconsByKey(hKey, Entry, pG->IconSize);
775 
776     RegCloseKey(hKey);
777 
778     // add icon to imagelist
779     INT iSmallImage = 0;
780     if (!defaultIcon && Entry->hIconSmall)
781     {
782         iSmallImage = ImageList_AddIcon(pG->himlSmall, Entry->hIconSmall);
783     }
784 
785     lvItem.mask = LVIF_TEXT | LVIF_PARAM | LVIF_IMAGE;
786     lvItem.iItem = iItem;
787     lvItem.iSubItem = 0;
788     lvItem.lParam = (LPARAM)Entry;
789     lvItem.iImage = iSmallImage;
790     lvItem.pszText = Assoc[0] == L'.' ? _wcsupr(&Entry->FileExtension[1]) : pG->NoneString;
791     SendMessageW(hListView, LVM_INSERTITEMW, 0, (LPARAM)&lvItem);
792 
793     lvItem.mask = LVIF_TEXT;
794     lvItem.iItem = iItem;
795     lvItem.iSubItem = 1;
796     lvItem.pszText = LPSTR_TEXTCALLBACK;
797     ListView_SetItem(hListView, &lvItem);
798 
799     return Entry;
800 }
801 
802 static BOOL
FileTypesDlg_AddExt(HWND hwndDlg,LPCWSTR pszExt,LPCWSTR pszProgId,PFILE_TYPE_GLOBALS pG)803 FileTypesDlg_AddExt(HWND hwndDlg, LPCWSTR pszExt, LPCWSTR pszProgId, PFILE_TYPE_GLOBALS pG)
804 {
805     DWORD dwValue = 1;
806     HKEY hKey;
807     WCHAR szKey[13];    // max. "ft4294967295" + "\0"
808     LONG nResult;
809 
810     if (!*pszProgId)
811     {
812         pszProgId = szKey;
813         // Search the next "ft%06u" key name
814         do
815         {
816             StringCbPrintfW(szKey, sizeof(szKey), L"ft%06u", dwValue);
817 
818             nResult = RegOpenKeyEx(HKEY_CLASSES_ROOT, szKey, 0, KEY_READ, &hKey);
819             if (nResult != ERROR_SUCCESS)
820                 break;
821 
822             RegCloseKey(hKey);
823             ++dwValue;
824         } while (dwValue != 0);
825 
826         RegCloseKey(hKey);
827 
828         if (dwValue == 0)
829             return FALSE;
830 
831         // Create new "ft%06u" key
832         nResult = RegCreateKeyEx(HKEY_CLASSES_ROOT, szKey, 0, NULL, 0, KEY_WRITE, NULL, &hKey, NULL);
833         if (ERROR_SUCCESS != nResult)
834             return FALSE;
835         RegCloseKey(hKey);
836     }
837 
838     // Create the ".ext" key
839     WCHAR szExt[ASSOC_CCHMAX];
840     if (*pszExt == L'.') // The user is allowed to type the extension with or without the . in the new dialog!
841         ++pszExt;
842     StringCbPrintfW(szExt, sizeof(szExt), L".%s", pszExt);
843     _wcslwr(szExt);
844     nResult = RegCreateKeyEx(HKEY_CLASSES_ROOT, szExt, 0, NULL, 0, KEY_WRITE, NULL, &hKey, NULL);
845     _wcsupr(szExt);
846     if (ERROR_SUCCESS != nResult)
847         return FALSE;
848 
849     // Set the default value of ".ext" to "ft%06u"
850     RegSetString(hKey, NULL, pszProgId, REG_SZ);
851 
852     RegCloseKey(hKey);
853 
854     // Insert an item to the listview
855     HWND hListView = GetDlgItem(hwndDlg, IDC_FILETYPES_LISTVIEW);
856     INT iItem = ListView_GetItemCount(hListView);
857     if (!FileTypesDlg_InsertToLV(hListView, szExt, iItem, pG))
858         return FALSE;
859 
860     ListView_SetItemState(hListView, iItem, -1, LVIS_FOCUSED | LVIS_SELECTED);
861     ListView_EnsureVisible(hListView, iItem, FALSE);
862     return TRUE;
863 }
864 
865 static BOOL
FileTypesDlg_RemoveExt(HWND hwndDlg)866 FileTypesDlg_RemoveExt(HWND hwndDlg)
867 {
868     HWND hListView = GetDlgItem(hwndDlg, IDC_FILETYPES_LISTVIEW);
869 
870     INT iItem = ListView_GetNextItem(hListView, -1, LVNI_SELECTED);
871     if (iItem == -1)
872         return FALSE;
873 
874     WCHAR szExt[MAX_PATH];
875     szExt[0] = L'.';
876     ListView_GetItemText(hListView, iItem, 0, &szExt[1], _countof(szExt) - 1);
877     _wcslwr(szExt);
878 
879     if (DeleteExt(hwndDlg, szExt))
880     {
881         ListView_DeleteItem(hListView, iItem);
882     }
883     return TRUE;
884 }
885 
886 /////////////////////////////////////////////////////////////////////////////
887 // common code of NewActionDlg and EditActionDlg
888 
889 typedef struct ACTION_DIALOG
890 {
891     HWND hwndLB;
892     PFILE_TYPE_GLOBALS pG;
893     PFILE_TYPE_ENTRY pEntry;
894     WCHAR szAction[VERBKEY_CCHMAX];
895     WCHAR szApp[MAX_PATH];
896     BOOL bUseDDE;
897 } ACTION_DIALOG, *PACTION_DIALOG;
898 
899 static void
ActionDlg_OnBrowse(HWND hwndDlg,PACTION_DIALOG pNewAct,BOOL bEdit=FALSE)900 ActionDlg_OnBrowse(HWND hwndDlg, PACTION_DIALOG pNewAct, BOOL bEdit = FALSE)
901 {
902     WCHAR szFile[MAX_PATH];
903     szFile[0] = UNICODE_NULL;
904 
905     WCHAR szFilter[MAX_PATH];
906     LoadStringW(shell32_hInstance, IDS_EXE_FILTER, szFilter, _countof(szFilter));
907 
908     CStringW strTitle(MAKEINTRESOURCEW(IDS_OPEN_WITH));
909 
910     OPENFILENAMEW ofn;
911     ZeroMemory(&ofn, sizeof(ofn));
912     ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400W;
913     ofn.hwndOwner = hwndDlg;
914     ofn.lpstrFilter = szFilter;
915     ofn.lpstrFile = szFile;
916     ofn.nMaxFile = _countof(szFile);
917     ofn.lpstrTitle = strTitle;
918     ofn.Flags = OFN_EXPLORER | OFN_ENABLESIZING | OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY;
919     ofn.lpstrDefExt = L"exe";
920     if (GetOpenFileNameW(&ofn))
921     {
922         if (bEdit)
923         {
924             CStringW str = szFile;
925             QuoteAppPathForCommand(str);
926             str += L" \"%1\"";
927             SetDlgItemTextW(hwndDlg, IDC_ACTION_APP, str);
928         }
929         else
930         {
931             SetDlgItemTextW(hwndDlg, IDC_ACTION_APP, szFile);
932         }
933     }
934 }
935 
936 /////////////////////////////////////////////////////////////////////////////
937 // NewActionDlg
938 
939 static void
NewActionDlg_OnOK(HWND hwndDlg,PACTION_DIALOG pNewAct)940 NewActionDlg_OnOK(HWND hwndDlg, PACTION_DIALOG pNewAct)
941 {
942     // check action
943     GetDlgItemTextW(hwndDlg, IDC_ACTION_ACTION, pNewAct->szAction, _countof(pNewAct->szAction));
944     StrTrimW(pNewAct->szAction, g_pszSpace);
945     if (pNewAct->szAction[0] == 0)
946     {
947         // action was empty, error
948         HWND hwndCtrl = GetDlgItem(hwndDlg, IDC_ACTION_ACTION);
949         SendMessageW(hwndCtrl, EM_SETSEL, 0, -1);
950         SetFocus(hwndCtrl);
951         CStringW strText(MAKEINTRESOURCEW(IDS_SPECIFY_ACTION));
952         CStringW strTitle(MAKEINTRESOURCEW(IDS_FILE_TYPES));
953         MessageBoxW(hwndDlg, strText, strTitle, MB_ICONERROR);
954         return;
955     }
956 
957     // check app
958     GetDlgItemTextW(hwndDlg, IDC_ACTION_APP, pNewAct->szApp, _countof(pNewAct->szApp));
959     StrTrimW(pNewAct->szApp, g_pszSpace);
960     if (pNewAct->szApp[0] == 0 ||
961         GetFileAttributesW(pNewAct->szApp) == INVALID_FILE_ATTRIBUTES)
962     {
963         // app was empty or invalid
964         HWND hwndCtrl = GetDlgItem(hwndDlg, IDC_ACTION_APP);
965         SendMessageW(hwndCtrl, EM_SETSEL, 0, -1);
966         SetFocus(hwndCtrl);
967         CStringW strText(MAKEINTRESOURCEW(IDS_INVALID_PROGRAM));
968         CStringW strTitle(MAKEINTRESOURCEW(IDS_FILE_TYPES));
969         MessageBoxW(hwndDlg, strText, strTitle, MB_ICONERROR);
970         return;
971     }
972 
973     EndDialog(hwndDlg, IDOK);
974 }
975 
976 // IDD_ACTION
977 static INT_PTR CALLBACK
NewActionDlgProc(HWND hwndDlg,UINT uMsg,WPARAM wParam,LPARAM lParam)978 NewActionDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
979 {
980     static PACTION_DIALOG s_pNewAct = NULL;
981 
982     switch (uMsg)
983     {
984         case WM_INITDIALOG:
985             s_pNewAct = (PACTION_DIALOG)lParam;
986             s_pNewAct->bUseDDE = FALSE;
987             EnableWindow(GetDlgItem(hwndDlg, IDC_ACTION_USE_DDE), FALSE);
988             return TRUE;
989 
990         case WM_COMMAND:
991             switch (LOWORD(wParam))
992             {
993                 case IDOK:
994                     NewActionDlg_OnOK(hwndDlg, s_pNewAct);
995                     break;
996 
997                 case IDCANCEL:
998                     EndDialog(hwndDlg, IDCANCEL);
999                     break;
1000 
1001                 case IDC_ACTION_BROWSE:
1002                     ActionDlg_OnBrowse(hwndDlg, s_pNewAct, FALSE);
1003                     break;
1004             }
1005             break;
1006     }
1007     return 0;
1008 }
1009 
1010 /////////////////////////////////////////////////////////////////////////////
1011 // EditActionDlg
1012 
1013 static void
EditActionDlg_OnOK(HWND hwndDlg,PACTION_DIALOG pEditAct)1014 EditActionDlg_OnOK(HWND hwndDlg, PACTION_DIALOG pEditAct)
1015 {
1016     // check action
1017     GetDlgItemTextW(hwndDlg, IDC_ACTION_ACTION, pEditAct->szAction, _countof(pEditAct->szAction));
1018     StrTrimW(pEditAct->szAction, g_pszSpace);
1019     if (pEditAct->szAction[0] == 0)
1020     {
1021         // action was empty. show error
1022         HWND hwndCtrl = GetDlgItem(hwndDlg, IDC_ACTION_ACTION);
1023         SendMessageW(hwndCtrl, EM_SETSEL, 0, -1);
1024         SetFocus(hwndCtrl);
1025         CStringW strText(MAKEINTRESOURCEW(IDS_SPECIFY_ACTION));
1026         CStringW strTitle(MAKEINTRESOURCEW(IDS_FILE_TYPES));
1027         MessageBoxW(hwndDlg, strText, strTitle, MB_ICONERROR);
1028     }
1029 
1030     // check app
1031     GetDlgItemTextW(hwndDlg, IDC_ACTION_APP, pEditAct->szApp, _countof(pEditAct->szApp));
1032     StrTrimW(pEditAct->szApp, g_pszSpace);
1033     if (pEditAct->szApp[0] == 0)
1034     {
1035         // app was empty. show error
1036         HWND hwndCtrl = GetDlgItem(hwndDlg, IDC_ACTION_APP);
1037         SendMessageW(hwndCtrl, EM_SETSEL, 0, -1);
1038         SetFocus(hwndCtrl);
1039         CStringW strText(MAKEINTRESOURCEW(IDS_INVALID_PROGRAM));
1040         CStringW strTitle(MAKEINTRESOURCEW(IDS_FILE_TYPES));
1041         MessageBoxW(hwndDlg, strText, strTitle, MB_ICONERROR);
1042     }
1043 
1044     EndDialog(hwndDlg, IDOK);
1045 }
1046 
1047 // IDD_ACTION
1048 static INT_PTR CALLBACK
EditActionDlgProc(HWND hwndDlg,UINT uMsg,WPARAM wParam,LPARAM lParam)1049 EditActionDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
1050 {
1051     static PACTION_DIALOG s_pEditAct = NULL;
1052 
1053     switch (uMsg)
1054     {
1055         case WM_INITDIALOG:
1056             s_pEditAct = (PACTION_DIALOG)lParam;
1057             s_pEditAct->bUseDDE = FALSE;
1058             SetDlgItemTextW(hwndDlg, IDC_ACTION_ACTION, s_pEditAct->szAction);
1059             SetDlgItemTextW(hwndDlg, IDC_ACTION_APP, s_pEditAct->szApp);
1060             EnableWindow(GetDlgItem(hwndDlg, IDC_ACTION_USE_DDE), FALSE);
1061             EnableWindow(GetDlgItem(hwndDlg, IDC_ACTION_ACTION), FALSE);
1062             {
1063                 // set title
1064                 CStringW str(MAKEINTRESOURCEW(IDS_EDITING_ACTION));
1065                 str += GetTypeName(s_pEditAct->pEntry, s_pEditAct->pG);
1066                 SetWindowTextW(hwndDlg, str);
1067             }
1068             return TRUE;
1069 
1070         case WM_COMMAND:
1071             switch (LOWORD(wParam))
1072             {
1073                 case IDOK:
1074                     EditActionDlg_OnOK(hwndDlg, s_pEditAct);
1075                     break;
1076 
1077                 case IDCANCEL:
1078                     EndDialog(hwndDlg, IDCANCEL);
1079                     break;
1080 
1081                 case IDC_ACTION_BROWSE:
1082                     ActionDlg_OnBrowse(hwndDlg, s_pEditAct, TRUE);
1083                     break;
1084             }
1085             break;
1086     }
1087     return 0;
1088 }
1089 
1090 /////////////////////////////////////////////////////////////////////////////
1091 // EditTypeDlg
1092 
1093 static void
EditTypeDlg_Restrict(HWND hwndDlg,PEDITTYPE_DIALOG pEditType)1094 EditTypeDlg_Restrict(HWND hwndDlg, PEDITTYPE_DIALOG pEditType)
1095 {
1096     PFILE_TYPE_ENTRY pEntry = pEditType->pEntry;
1097     static const WORD map[] = {
1098         FTA_NoEditIcon, IDC_EDITTYPE_CHANGE_ICON,
1099         FTA_NoEditDesc, IDC_EDITTYPE_TEXT,
1100         FTA_NoNewVerb, IDC_EDITTYPE_NEW,
1101         FTA_NoEditVerb, IDC_EDITTYPE_EDIT_BUTTON,
1102         FTA_NoRemoveVerb, IDC_EDITTYPE_REMOVE,
1103         FTA_NoEditDflt, IDC_EDITTYPE_SET_DEFAULT
1104     };
1105     for (SIZE_T i = 0; i < _countof(map); i += 2)
1106     {
1107         if (pEntry->EditFlags & map[i + 0])
1108             EnableWindow(GetDlgItem(hwndDlg, map[i + 1]), FALSE);
1109     }
1110 }
1111 
1112 static BOOL
EditTypeDlg_UpdateEntryIcon(HWND hwndDlg,PEDITTYPE_DIALOG pEditType)1113 EditTypeDlg_UpdateEntryIcon(HWND hwndDlg, PEDITTYPE_DIALOG pEditType)
1114 {
1115     PFILE_TYPE_ENTRY pEntry = pEditType->pEntry;
1116     WCHAR buf[MAX_PATH];
1117 
1118     pEntry->IconPath[0] = UNICODE_NULL; // I want the default icon
1119     Normalize(pEntry);
1120     pEntry->DestroyIcons();
1121     pEntry->hIconSmall = DoExtractIcon(pEntry->IconPath, pEntry->nIconIndex, TRUE);
1122     if (ExpandEnvironmentStringsW(pEditType->szIconPath, buf, _countof(buf)) && buf[0])
1123     {
1124         HICON hIco = DoExtractIcon(buf, pEditType->nIconIndex, TRUE);
1125         if (hIco)
1126         {
1127             pEntry->DestroyIcons();
1128             pEntry->hIconSmall = hIco;
1129         }
1130     }
1131     StringCbCopyW(pEntry->IconPath, sizeof(pEntry->IconPath), pEditType->szIconPath);
1132     pEntry->nIconIndex = pEditType->nIconIndex;
1133 
1134     HWND hListView = pEditType->hwndLV;
1135     InitializeDefaultIcons(pEditType->pG);
1136     HIMAGELIST himlSmall = pEditType->pG->himlSmall;
1137     INT iSmallImage = ImageList_AddIcon(himlSmall, pEntry->hIconSmall);
1138 
1139     INT iItem = ListView_GetNextItem(hListView, -1, LVNI_SELECTED);
1140     if (iItem != -1)
1141     {
1142         LV_ITEMW Item = { LVIF_IMAGE, iItem };
1143         Item.iImage = iSmallImage;
1144         ListView_SetItem(hListView, &Item);
1145     }
1146     return TRUE;
1147 }
1148 
1149 static BOOL
EditTypeDlg_WriteClass(HWND hwndDlg,PEDITTYPE_DIALOG pEditType,LPCWSTR TypeName,EDITTYPEFLAGS Etf)1150 EditTypeDlg_WriteClass(HWND hwndDlg, PEDITTYPE_DIALOG pEditType,
1151                        LPCWSTR TypeName, EDITTYPEFLAGS Etf)
1152 {
1153     PFILE_TYPE_ENTRY pEntry = pEditType->pEntry;
1154     LPCWSTR ClassKey;
1155     HRESULT hr = GetClassKey(*pEntry, ClassKey);
1156     BOOL OnlyExt = hr != S_OK;
1157     HKEY hClassKey;
1158     if (FAILED(hr) || RegCreateKeyExW(HKEY_CLASSES_ROOT, ClassKey, 0, NULL, 0,
1159                                       KEY_QUERY_VALUE | KEY_WRITE, NULL,
1160                                       &hClassKey, NULL) != ERROR_SUCCESS)
1161     {
1162         return FALSE;
1163     }
1164 
1165     // Refresh the EditFlags
1166     DWORD dw;
1167     if (GetRegDWORD(hClassKey, L"EditFlags", dw, 0, FALSE) == ERROR_SUCCESS)
1168         pEntry->EditFlags = (dw & ~FTA_MODIFYMASK) | (pEntry->EditFlags & FTA_MODIFYMASK);
1169 
1170     if (!OnlyExt)
1171     {
1172         // Set class properties
1173         RegSetOrDelete(hClassKey, L"EditFlags", REG_DWORD, pEntry->EditFlags ? &pEntry->EditFlags : NULL, 4);
1174         if (pEntry->IsExtension())
1175         {
1176             RegSetOrDelete(hClassKey, L"AlwaysShowExt", REG_SZ, (Etf & ETF_ALWAYSEXT) ? L"" : NULL, 0);
1177         }
1178         if (RegKeyExists(hClassKey, L"DocObject"))
1179         {
1180             LRESULT ec = GetRegDWORD(hClassKey, L"BrowserFlags", dw, 0, TRUE);
1181             if (ec == ERROR_SUCCESS || ec == ERROR_FILE_NOT_FOUND)
1182             {
1183                 dw = (dw & ~8) | ((Etf & ETF_BROWSESAME) ? 0 : 8); // Note: 8 means NOT
1184                 RegSetOrDelete(hClassKey, L"BrowserFlags", REG_DWORD, dw ? &dw : NULL, sizeof(dw));
1185             }
1186         }
1187     }
1188     if (!(pEntry->EditFlags & FTA_NoEditDesc))
1189     {
1190         if (!OnlyExt)
1191             RegDeleteValueW(hClassKey, NULL); // Legacy name (in ProgId only)
1192 
1193         // Deleting this value is always the correct thing to do but Windows does not do this.
1194         // This means the user cannot override the translated known type names set by the OS.
1195         if (OnlyExt)
1196             RegDeleteValueW(hClassKey, L"FriendlyTypeName"); // MUI name (extension or ProgId)
1197 
1198         if (TypeName[0])
1199             RegSetString(hClassKey, OnlyExt ? L"FriendlyTypeName" : NULL, TypeName, REG_SZ);
1200         pEntry->InvalidateTypeName();
1201     }
1202 
1203     if (pEntry->IconPath[0] && !(pEntry->EditFlags & FTA_NoEditIcon) && pEditType->ChangedIcon)
1204     {
1205         HKEY hDefaultIconKey;
1206         if (RegCreateKeyExW(hClassKey, L"DefaultIcon", 0, NULL, 0, KEY_WRITE,
1207                             NULL, &hDefaultIconKey, NULL) == ERROR_SUCCESS)
1208         {
1209             DWORD type = REG_SZ;
1210             WCHAR buf[ICONLOCATION_CCHMAX];
1211             LPCWSTR fmt = L"%s,%d";
1212             if (!lstrcmpW(pEntry->IconPath, L"%1"))
1213             {
1214                 fmt = L"%s"; // No icon index for "%1"
1215             }
1216             else if (StrChrW(pEntry->IconPath, L'%'))
1217             {
1218                 type = REG_EXPAND_SZ;
1219             }
1220             StringCbPrintfW(buf, sizeof(buf), fmt, pEntry->IconPath, pEntry->nIconIndex);
1221 
1222             RegSetString(hDefaultIconKey, NULL, buf, type);
1223             RegCloseKey(hDefaultIconKey);
1224         }
1225     }
1226 
1227     HKEY hShellKey;
1228     if (RegCreateKeyExW(hClassKey, L"shell", 0, NULL, 0, KEY_READ | KEY_WRITE, NULL,
1229                         &hShellKey, NULL) != ERROR_SUCCESS)
1230     {
1231         RegCloseKey(hClassKey);
1232         return FALSE;
1233     }
1234 
1235     // set default action
1236     if (!(pEntry->EditFlags & FTA_NoEditDflt))
1237     {
1238         if (pEditType->szDefaultVerb[0])
1239             RegSetString(hShellKey, NULL, pEditType->szDefaultVerb, REG_SZ);
1240         else
1241             RegDeleteValueW(hShellKey, NULL);
1242     }
1243 
1244     // delete shell commands
1245     WCHAR szVerbName[VERBKEY_CCHMAX];
1246     DWORD dwIndex = 0;
1247     while (RegEnumKeyW(hShellKey, dwIndex, szVerbName, _countof(szVerbName)) == ERROR_SUCCESS)
1248     {
1249         if (pEditType->CommandLineMap.FindKey(szVerbName) == -1)
1250         {
1251             // doesn't exist in CommandLineMap, then delete it
1252             if (SHDeleteKeyW(hShellKey, szVerbName) == ERROR_SUCCESS)
1253             {
1254                 --dwIndex;
1255             }
1256         }
1257         ++dwIndex;
1258     }
1259 
1260     // write shell commands
1261     const INT nCount = pEditType->CommandLineMap.GetSize();
1262     for (INT i = 0; i < nCount; ++i)
1263     {
1264         const CStringW& key = pEditType->CommandLineMap.GetKeyAt(i);
1265         const CStringW& cmd = pEditType->CommandLineMap.GetValueAt(i);
1266         if (!pEditType->ModifiedVerbs.Find(key))
1267         {
1268             ASSERT(RegKeyExists(hShellKey, key));
1269             continue;
1270         }
1271 
1272         // create verb key
1273         HKEY hVerbKey;
1274         if (RegCreateKeyExW(hShellKey, key, 0, NULL, 0, KEY_WRITE, NULL,
1275                             &hVerbKey, NULL) == ERROR_SUCCESS)
1276         {
1277             // create command key
1278             HKEY hCommandKey;
1279             if (RegCreateKeyExW(hVerbKey, L"command", 0, NULL, 0, KEY_WRITE, NULL,
1280                                 &hCommandKey, NULL) == ERROR_SUCCESS)
1281             {
1282                 DWORD dwSize = (cmd.GetLength() + 1) * sizeof(WCHAR);
1283                 DWORD dwType = REG_SZ;
1284                 int exp;
1285                 if ((exp = cmd.Find('%', 0)) >= 0 && cmd.Find('%', exp + 1) >= 0)
1286                     dwType = REG_EXPAND_SZ;
1287                 RegSetValueExW(hCommandKey, NULL, 0, dwType, LPBYTE(LPCWSTR(cmd)), dwSize);
1288                 RegCloseKey(hCommandKey);
1289             }
1290 
1291             RegCloseKey(hVerbKey);
1292         }
1293     }
1294 
1295     RegCloseKey(hShellKey);
1296     RegCloseKey(hClassKey);
1297     return TRUE;
1298 }
1299 
1300 static BOOL
EditTypeDlg_ReadClass(HWND hwndDlg,PEDITTYPE_DIALOG pEditType,EDITTYPEFLAGS & Etf)1301 EditTypeDlg_ReadClass(HWND hwndDlg, PEDITTYPE_DIALOG pEditType, EDITTYPEFLAGS &Etf)
1302 {
1303     PFILE_TYPE_ENTRY pEntry = pEditType->pEntry;
1304     LPCWSTR ClassKey;
1305     HRESULT hr = GetClassKey(*pEntry, ClassKey);
1306     HKEY hClassKey;
1307     if (FAILED(hr) || RegOpenKeyExW(HKEY_CLASSES_ROOT, ClassKey, 0, KEY_READ, &hClassKey))
1308         return FALSE;
1309 
1310     UINT etfbits = (RegValueExists(hClassKey, L"AlwaysShowExt")) ? ETF_ALWAYSEXT : 0;
1311 
1312     // 8 in BrowserFlags listed in KB 162059 seems to be our bit
1313     BOOL docobj = RegKeyExists(hClassKey, L"DocObject");
1314     EnableWindow(GetDlgItem(hwndDlg, IDC_EDITTYPE_SAME_WINDOW), docobj);
1315     etfbits |= (docobj && (GetRegDWORD(hClassKey, L"BrowserFlags") & 8)) ? 0 : ETF_BROWSESAME;
1316 
1317     Etf = EDITTYPEFLAGS(etfbits);
1318 
1319     // open "shell" key
1320     HKEY hShellKey;
1321     if (RegOpenKeyExW(hClassKey, L"shell", 0, KEY_READ, &hShellKey) != ERROR_SUCCESS)
1322     {
1323         RegCloseKey(hClassKey);
1324         return FALSE;
1325     }
1326 
1327     WCHAR DefaultVerb[VERBKEY_CCHMAX];
1328     DWORD dwSize = sizeof(DefaultVerb);
1329     if (RegQueryValueExW(hShellKey, NULL, NULL, NULL,
1330                          LPBYTE(DefaultVerb), &dwSize) == ERROR_SUCCESS)
1331     {
1332         StringCbCopyW(pEditType->szDefaultVerb, sizeof(pEditType->szDefaultVerb), DefaultVerb);
1333     }
1334     else
1335     {
1336         StringCbCopyW(pEditType->szDefaultVerb, sizeof(pEditType->szDefaultVerb), L"open");
1337     }
1338 
1339     // enumerate shell verbs
1340     WCHAR szVerbName[VERBKEY_CCHMAX];
1341     DWORD dwIndex = 0;
1342     while (RegEnumKeyW(hShellKey, dwIndex, szVerbName, _countof(szVerbName)) == ERROR_SUCCESS)
1343     {
1344         // open verb key
1345         HKEY hVerbKey;
1346         LONG nResult = RegOpenKeyExW(hShellKey, szVerbName, 0, KEY_READ, &hVerbKey);
1347         if (nResult == ERROR_SUCCESS)
1348         {
1349             // open command key
1350             HKEY hCommandKey;
1351             nResult = RegOpenKeyExW(hVerbKey, L"command", 0, KEY_READ, &hCommandKey);
1352             if (nResult == ERROR_SUCCESS)
1353             {
1354                 // get command line
1355                 WCHAR szValue[MAX_PATH * 2];
1356                 dwSize = sizeof(szValue);
1357                 nResult = RegQueryValueExW(hCommandKey, NULL, NULL, NULL, LPBYTE(szValue), &dwSize);
1358                 if (nResult == ERROR_SUCCESS)
1359                 {
1360                     pEditType->CommandLineMap.SetAt(szVerbName, szValue);
1361                 }
1362 
1363                 RegCloseKey(hCommandKey);
1364             }
1365 
1366             RegCloseKey(hVerbKey);
1367         }
1368         SendDlgItemMessageW(hwndDlg, IDC_EDITTYPE_LISTBOX, LB_ADDSTRING, 0, LPARAM(szVerbName));
1369         ++dwIndex;
1370     }
1371 
1372     WCHAR TypeName[TYPENAME_CCHMAX];
1373     dwSize = sizeof(TypeName);
1374     if (!RegQueryValueExW(hClassKey, NULL, NULL, NULL, LPBYTE(TypeName), &dwSize))
1375     {
1376         TypeName[_countof(TypeName) - 1] = UNICODE_NULL; // Terminate
1377         SetDlgItemTextW(hwndDlg, IDC_EDITTYPE_TEXT, TypeName);
1378     }
1379 
1380     RegCloseKey(hShellKey);
1381     RegCloseKey(hClassKey);
1382     return TRUE;
1383 }
1384 
1385 static void
EditTypeDlg_OnOK(HWND hwndDlg,PEDITTYPE_DIALOG pEditType)1386 EditTypeDlg_OnOK(HWND hwndDlg, PEDITTYPE_DIALOG pEditType)
1387 {
1388     PFILE_TYPE_ENTRY pEntry = pEditType->pEntry;
1389 
1390     WCHAR TypeName[TYPENAME_CCHMAX];
1391     GetDlgItemTextW(hwndDlg, IDC_EDITTYPE_TEXT, TypeName, _countof(TypeName));
1392     StrTrimW(TypeName, g_pszSpace);
1393 
1394     UINT etf = 0;
1395     pEntry->EditFlags &= ~(FTA_MODIFYMASK);
1396     if (!SendDlgItemMessageW(hwndDlg, IDC_EDITTYPE_CONFIRM_OPEN, BM_GETCHECK, 0, 0))
1397         pEntry->EditFlags |= FTA_OpenIsSafe;
1398     if (SendDlgItemMessageW(hwndDlg, IDC_EDITTYPE_SHOW_EXT, BM_GETCHECK, 0, 0))
1399         etf |= ETF_ALWAYSEXT;
1400     if (SendDlgItemMessageW(hwndDlg, IDC_EDITTYPE_SAME_WINDOW, BM_GETCHECK, 0, 0))
1401         etf |= ETF_BROWSESAME;
1402 
1403     // update entry icon
1404     EditTypeDlg_UpdateEntryIcon(hwndDlg, pEditType);
1405 
1406     // write registry
1407     EditTypeDlg_WriteClass(hwndDlg, pEditType, TypeName, (EDITTYPEFLAGS)etf);
1408 
1409     pEntry->InvalidateDefaultApp();
1410 
1411     // update the icon cache
1412     SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_FLUSHNOWAIT, NULL, NULL);
1413 
1414     EndDialog(hwndDlg, IDOK);
1415 }
1416 
1417 static BOOL
EditTypeDlg_OnRemove(HWND hwndDlg,PEDITTYPE_DIALOG pEditType)1418 EditTypeDlg_OnRemove(HWND hwndDlg, PEDITTYPE_DIALOG pEditType)
1419 {
1420     // get current selection
1421     INT iItem = SendDlgItemMessageW(hwndDlg, IDC_EDITTYPE_LISTBOX, LB_GETCURSEL, 0, 0);
1422     if (iItem == LB_ERR)
1423         return FALSE;
1424 
1425     // ask user for removal
1426     CStringW strText(MAKEINTRESOURCEW(IDS_REMOVE_ACTION));
1427     CStringW strTitle(MAKEINTRESOURCEW(IDS_FILE_TYPES));
1428     if (MessageBoxW(hwndDlg, strText, strTitle, MB_ICONINFORMATION | MB_YESNO) == IDNO)
1429         return FALSE;
1430 
1431     // get text
1432     WCHAR szText[VERBKEY_CCHMAX];
1433     szText[0] = 0;
1434     SendDlgItemMessageW(hwndDlg, IDC_EDITTYPE_LISTBOX, LB_GETTEXT, iItem, (LPARAM)szText);
1435     StrTrimW(szText, g_pszSpace);
1436 
1437     // remove it
1438     pEditType->CommandLineMap.Remove(szText);
1439     SendDlgItemMessageW(hwndDlg, IDC_EDITTYPE_LISTBOX, LB_DELETESTRING, iItem, 0);
1440     return TRUE;
1441 }
1442 
1443 static void
EditTypeDlg_OnCommand(HWND hwndDlg,UINT id,UINT code,PEDITTYPE_DIALOG pEditType)1444 EditTypeDlg_OnCommand(HWND hwndDlg, UINT id, UINT code, PEDITTYPE_DIALOG pEditType)
1445 {
1446     INT iItem, iIndex;
1447     ACTION_DIALOG action;
1448     switch (id)
1449     {
1450         case IDOK:
1451             EditTypeDlg_OnOK(hwndDlg, pEditType);
1452             break;
1453 
1454         case IDCANCEL:
1455             EndDialog(hwndDlg, IDCANCEL);
1456             break;
1457 
1458         case IDC_EDITTYPE_CHANGE_ICON:
1459             EditTypeDlg_OnChangeIcon(hwndDlg, pEditType);
1460             break;
1461 
1462         case IDC_EDITTYPE_NEW:
1463             // open 'New Action' dialog
1464             action.bUseDDE = FALSE;
1465             action.hwndLB = GetDlgItem(hwndDlg, IDC_EDITTYPE_LISTBOX);
1466             action.pEntry = pEditType->pEntry;
1467             if (IDOK == DialogBoxParamW(shell32_hInstance, MAKEINTRESOURCEW(IDD_ACTION), hwndDlg,
1468                                         NewActionDlgProc, LPARAM(&action)))
1469             {
1470                 if (SendMessageW(action.hwndLB, LB_FINDSTRING, -1, (LPARAM)action.szAction) != LB_ERR)
1471                 {
1472                     // already exists, error
1473                     HWND hwndCtrl = GetDlgItem(hwndDlg, IDC_ACTION_ACTION);
1474                     SendMessageW(hwndCtrl, EM_SETSEL, 0, -1);
1475                     SetFocus(hwndCtrl);
1476 
1477                     CStringW strText, strTitle(MAKEINTRESOURCEW(IDS_FILE_TYPES));
1478                     strText.Format(IDS_ACTION_EXISTS, action.szAction);
1479                     MessageBoxW(hwndDlg, strText, strTitle, MB_ICONERROR);
1480                 }
1481                 else
1482                 {
1483                     // add it
1484                     CStringW strCommandLine = action.szApp;
1485                     QuoteAppPathForCommand(strCommandLine);
1486                     strCommandLine += L" \"%1\"";
1487                     pEditType->CommandLineMap.SetAt(action.szAction, strCommandLine);
1488                     pEditType->ModifiedVerbs.AddHead(action.szAction);
1489                     SendMessageW(action.hwndLB, LB_ADDSTRING, 0, LPARAM(action.szAction));
1490                     if (SendMessageW(action.hwndLB, LB_GETCOUNT, 0, 0) == 1)
1491                     {
1492                         // set default
1493                         StringCbCopyW(pEditType->szDefaultVerb, sizeof(pEditType->szDefaultVerb), action.szAction);
1494                         InvalidateRect(action.hwndLB, NULL, TRUE);
1495                     }
1496                 }
1497             }
1498             break;
1499 
1500         case IDC_EDITTYPE_LISTBOX:
1501             if (code == LBN_SELCHANGE)
1502             {
1503                 action.hwndLB = GetDlgItem(hwndDlg, IDC_EDITTYPE_LISTBOX);
1504                 INT iItem = SendMessageW(action.hwndLB, LB_GETCURSEL, 0, 0);
1505                 SendMessageW(action.hwndLB, LB_GETTEXT, iItem, LPARAM(action.szAction));
1506                 if (lstrcmpiW(action.szAction, pEditType->szDefaultVerb) == 0)
1507                 {
1508                     EnableWindow(GetDlgItem(hwndDlg, IDC_EDITTYPE_SET_DEFAULT), FALSE);
1509                 }
1510                 else
1511                 {
1512                     EnableWindow(GetDlgItem(hwndDlg, IDC_EDITTYPE_SET_DEFAULT), TRUE);
1513                 }
1514                 EditTypeDlg_Restrict(hwndDlg, pEditType);
1515                 break;
1516             }
1517             else if (code != LBN_DBLCLK)
1518             {
1519                 break;
1520             }
1521             // FALL THROUGH
1522 
1523         case IDC_EDITTYPE_EDIT_BUTTON:
1524             action.bUseDDE = FALSE;
1525             action.hwndLB = GetDlgItem(hwndDlg, IDC_EDITTYPE_LISTBOX);
1526             action.pG = pEditType->pG;
1527             action.pEntry = pEditType->pEntry;
1528             iItem = SendMessageW(action.hwndLB, LB_GETCURSEL, 0, 0);
1529             if (iItem == LB_ERR)
1530                 break;
1531 
1532             // get action
1533             SendMessageW(action.hwndLB, LB_GETTEXT, iItem, LPARAM(action.szAction));
1534 
1535             // get app
1536             {
1537                 iIndex = pEditType->CommandLineMap.FindKey(action.szAction);
1538                 CStringW str = pEditType->CommandLineMap.GetValueAt(iIndex);
1539                 StringCbCopyW(action.szApp, sizeof(action.szApp), LPCWSTR(str));
1540             }
1541 
1542             // open dialog
1543             if (IDOK == DialogBoxParamW(shell32_hInstance, MAKEINTRESOURCEW(IDD_ACTION), hwndDlg,
1544                                         EditActionDlgProc, LPARAM(&action)))
1545             {
1546                 SendMessageW(action.hwndLB, LB_DELETESTRING, iItem, 0);
1547                 SendMessageW(action.hwndLB, LB_INSERTSTRING, iItem, LPARAM(action.szAction));
1548                 pEditType->CommandLineMap.SetAt(action.szAction, action.szApp);
1549                 pEditType->ModifiedVerbs.AddHead(action.szAction);
1550             }
1551             break;
1552 
1553         case IDC_EDITTYPE_REMOVE:
1554             EditTypeDlg_OnRemove(hwndDlg, pEditType);
1555             break;
1556 
1557         case IDC_EDITTYPE_SET_DEFAULT:
1558             action.hwndLB = GetDlgItem(hwndDlg, IDC_EDITTYPE_LISTBOX);
1559             iItem = SendMessageW(action.hwndLB, LB_GETCURSEL, 0, 0);
1560             if (iItem == LB_ERR)
1561                 break;
1562 
1563             SendMessageW(action.hwndLB, LB_GETTEXT, iItem, LPARAM(action.szAction));
1564 
1565             // set default
1566             StringCbCopyW(pEditType->szDefaultVerb, sizeof(pEditType->szDefaultVerb), action.szAction);
1567             EnableWindow(GetDlgItem(hwndDlg, IDC_EDITTYPE_SET_DEFAULT), FALSE);
1568             InvalidateRect(action.hwndLB, NULL, TRUE);
1569             break;
1570     }
1571 }
1572 
1573 static BOOL
EditTypeDlg_OnInitDialog(HWND hwndDlg,PEDITTYPE_DIALOG pEditType)1574 EditTypeDlg_OnInitDialog(HWND hwndDlg, PEDITTYPE_DIALOG pEditType)
1575 {
1576     PFILE_TYPE_ENTRY pEntry = pEditType->pEntry;
1577     EDITTYPEFLAGS Etf;
1578     ExpandEnvironmentStringsW(pEntry->IconPath, pEditType->szIconPath, _countof(pEditType->szIconPath));
1579     pEditType->nIconIndex = pEntry->nIconIndex;
1580     StringCbCopyW(pEditType->szDefaultVerb, sizeof(pEditType->szDefaultVerb), L"open");
1581     pEditType->ChangedIcon = false;
1582 
1583     // set info
1584     HICON hIco = DoExtractIcon(pEditType->szIconPath, pEditType->nIconIndex);
1585     SendDlgItemMessageW(hwndDlg, IDC_EDITTYPE_ICON, STM_SETICON, (WPARAM)hIco, 0);
1586     EditTypeDlg_ReadClass(hwndDlg, pEditType, Etf);
1587     SendDlgItemMessageW(hwndDlg, IDC_EDITTYPE_CONFIRM_OPEN, BM_SETCHECK, !(pEntry->EditFlags & FTA_OpenIsSafe), 0);
1588     SendDlgItemMessageW(hwndDlg, IDC_EDITTYPE_SHOW_EXT, BM_SETCHECK, !!(Etf & ETF_ALWAYSEXT), 0);
1589     EnableWindow(GetDlgItem(hwndDlg, IDC_EDITTYPE_SHOW_EXT), pEntry->IsExtension());
1590     SendDlgItemMessageW(hwndDlg, IDC_EDITTYPE_SAME_WINDOW, BM_SETCHECK, !!(Etf & ETF_BROWSESAME), 0);
1591     InvalidateRect(GetDlgItem(hwndDlg, IDC_EDITTYPE_LISTBOX), NULL, TRUE);
1592 
1593     // select first item
1594     SendDlgItemMessageW(hwndDlg, IDC_EDITTYPE_LISTBOX, LB_SETCURSEL, 0, 0);
1595     // is listbox empty?
1596     if (SendDlgItemMessageW(hwndDlg, IDC_EDITTYPE_LISTBOX, LB_GETCOUNT, 0, 0) == 0)
1597     {
1598         EnableWindow(GetDlgItem(hwndDlg, IDC_EDITTYPE_EDIT_BUTTON), FALSE);
1599         EnableWindow(GetDlgItem(hwndDlg, IDC_EDITTYPE_REMOVE), FALSE);
1600         EnableWindow(GetDlgItem(hwndDlg, IDC_EDITTYPE_SET_DEFAULT), FALSE);
1601     }
1602     EditTypeDlg_Restrict(hwndDlg, pEditType);
1603     return TRUE;
1604 }
1605 
1606 // IDD_EDITTYPE
1607 static INT_PTR CALLBACK
EditTypeDlgProc(HWND hwndDlg,UINT uMsg,WPARAM wParam,LPARAM lParam)1608 EditTypeDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
1609 {
1610     static PEDITTYPE_DIALOG s_pEditType = NULL;
1611     LPDRAWITEMSTRUCT pDraw;
1612     LPMEASUREITEMSTRUCT pMeasure;
1613 
1614     switch (uMsg)
1615     {
1616         case WM_INITDIALOG:
1617             s_pEditType = (PEDITTYPE_DIALOG)lParam;
1618             return EditTypeDlg_OnInitDialog(hwndDlg, s_pEditType);
1619 
1620         case WM_DESTROY:
1621         {
1622             HICON hOld = (HICON)SendDlgItemMessageW(hwndDlg, IDC_EDITTYPE_ICON, STM_GETICON, 0, 0);
1623             if (hOld)
1624                 DestroyIcon(hOld);
1625             break;
1626         }
1627 
1628         case WM_DRAWITEM:
1629             pDraw = LPDRAWITEMSTRUCT(lParam);
1630             return EditTypeDlg_OnDrawItem(hwndDlg, pDraw, s_pEditType);
1631 
1632         case WM_MEASUREITEM:
1633             pMeasure = LPMEASUREITEMSTRUCT(lParam);
1634             return EditTypeDlg_OnMeasureItem(hwndDlg, pMeasure, s_pEditType);
1635 
1636         case WM_COMMAND:
1637             EditTypeDlg_OnCommand(hwndDlg, LOWORD(wParam), HIWORD(wParam), s_pEditType);
1638             break;
1639     }
1640 
1641     return 0;
1642 }
1643 
1644 /////////////////////////////////////////////////////////////////////////////
1645 // FileTypesDlg
1646 
1647 static INT CALLBACK
FileTypesDlg_CompareItems(LPARAM lParam1,LPARAM lParam2,LPARAM lParamSort)1648 FileTypesDlg_CompareItems(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
1649 {
1650     PFILE_TYPE_GLOBALS pG = (PFILE_TYPE_GLOBALS)lParamSort;
1651     PFILE_TYPE_ENTRY entry1 = (PFILE_TYPE_ENTRY)lParam1, entry2 = (PFILE_TYPE_ENTRY)lParam2;
1652     int x = 0;
1653     if (pG->SortCol == 1)
1654         x = wcsicmp(GetTypeName(entry1, pG), GetTypeName(entry2, pG));
1655     if (!x && !(x = entry1->IsExtension() - entry2->IsExtension()))
1656         x = wcsicmp(entry1->FileExtension, entry2->FileExtension);
1657     return x * pG->SortReverse;
1658 }
1659 
1660 static void
FileTypesDlg_Sort(PFILE_TYPE_GLOBALS pG,HWND hListView,INT Column=-1)1661 FileTypesDlg_Sort(PFILE_TYPE_GLOBALS pG, HWND hListView, INT Column = -1)
1662 {
1663     pG->SortReverse = pG->SortCol == Column ? pG->SortReverse * -1 : 1;
1664     pG->SortCol = Column < 0 ? 0 : (INT8) Column;
1665     ListView_SortItems(hListView, FileTypesDlg_CompareItems, (LPARAM)pG);
1666 }
1667 
1668 static VOID
FileTypesDlg_InitListView(HWND hwndDlg,HWND hListView)1669 FileTypesDlg_InitListView(HWND hwndDlg, HWND hListView)
1670 {
1671     RECT clientRect;
1672     LPCWSTR columnName;
1673     WCHAR szBuf[50];
1674 
1675     LVCOLUMNW col;
1676     col.mask = LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM | LVCF_FMT;
1677     col.fmt = 0;
1678 
1679     GetClientRect(hListView, &clientRect);
1680     INT column0Size = (clientRect.right - clientRect.left) / 4;
1681 
1682     columnName = L"Extensions"; // Default to English
1683     if (LoadStringW(shell32_hInstance, IDS_COLUMN_EXTENSION, szBuf, _countof(szBuf)))
1684         columnName = szBuf;
1685     col.pszText     = const_cast<LPWSTR>(columnName);
1686     col.iSubItem    = 0;
1687     col.cx          = column0Size;
1688     SendMessageW(hListView, LVM_INSERTCOLUMNW, 0, (LPARAM)&col);
1689 
1690     columnName = L"File Types"; // Default to English
1691     if (LoadStringW(shell32_hInstance, IDS_FILE_TYPES, szBuf, _countof(szBuf)))
1692     {
1693         columnName = szBuf;
1694     }
1695     else
1696     {
1697         ERR("Failed to load localized string!\n");
1698     }
1699     col.pszText     = const_cast<LPWSTR>(columnName);
1700     col.iSubItem    = 1;
1701     col.cx          = clientRect.right - clientRect.left - column0Size - GetSystemMetrics(SM_CYVSCROLL);
1702     SendMessageW(hListView, LVM_INSERTCOLUMNW, 1, (LPARAM)&col);
1703 
1704     const UINT lvexstyle = LVS_EX_FULLROWSELECT | LVS_EX_LABELTIP;
1705     ListView_SetExtendedListViewStyleEx(hListView, lvexstyle, lvexstyle);
1706 }
1707 
1708 static void
FileTypesDlg_SetGroupboxText(HWND hwndDlg,LPCWSTR Assoc)1709 FileTypesDlg_SetGroupboxText(HWND hwndDlg, LPCWSTR Assoc)
1710 {
1711     CStringW buf;
1712     buf.Format(IDS_FILE_DETAILS, Assoc);
1713     SetDlgItemTextW(hwndDlg, IDC_FILETYPES_DETAILS_GROUPBOX, buf.GetString());
1714 }
1715 
1716 static void
FileTypesDlg_Refresh(HWND hwndDlg,HWND hListView,PFILE_TYPE_GLOBALS pG)1717 FileTypesDlg_Refresh(HWND hwndDlg, HWND hListView, PFILE_TYPE_GLOBALS pG)
1718 {
1719     ListView_DeleteAllItems(hListView);
1720     ImageList_RemoveAll(pG->himlSmall);
1721     InitializeDefaultIcons(pG);
1722     FileTypesDlg_SetGroupboxText(hwndDlg, L"");
1723     SetDlgItemTextW(hwndDlg, IDC_FILETYPES_DESCRIPTION, L"");
1724     SetDlgItemTextW(hwndDlg, IDC_FILETYPES_APPNAME, L"");
1725     EnableWindow(GetDlgItem(hwndDlg, IDC_FILETYPES_DELETE), FALSE);
1726     RedrawWindow(hwndDlg, NULL, NULL, RDW_ALLCHILDREN | RDW_INVALIDATE | RDW_UPDATENOW);
1727 #if DBG
1728     DWORD statTickStart = GetTickCount();
1729 #endif
1730 
1731     INT iItem = 0;
1732     WCHAR szName[ASSOC_CCHMAX];
1733     DWORD dwName = _countof(szName);
1734     DWORD dwIndex = 0;
1735     SendMessage(hListView, WM_SETREDRAW, FALSE, 0);
1736     while (RegEnumKeyExW(HKEY_CLASSES_ROOT, dwIndex++, szName, &dwName,
1737                          NULL, NULL, NULL, NULL) == ERROR_SUCCESS)
1738     {
1739         if (FileTypesDlg_InsertToLV(hListView, szName, iItem, pG))
1740             ++iItem;
1741         dwName = _countof(szName);
1742     }
1743     FileTypesDlg_Sort(pG, hListView);
1744     SendMessage(hListView, WM_SETREDRAW, TRUE, 0);
1745     RedrawWindow(hListView, NULL, NULL, RDW_ALLCHILDREN | RDW_ERASE | RDW_FRAME | RDW_INVALIDATE);
1746 
1747 #if DBG
1748     DbgPrint("FT loaded %u (%ums)\n", iItem, GetTickCount() - statTickStart);
1749 #endif
1750     // select first item
1751     ListView_SetItemState(hListView, 0, -1, LVIS_FOCUSED | LVIS_SELECTED);
1752 }
1753 
1754 static PFILE_TYPE_GLOBALS
FileTypesDlg_Initialize(HWND hwndDlg)1755 FileTypesDlg_Initialize(HWND hwndDlg)
1756 {
1757     HWND hListView = GetDlgItem(hwndDlg, IDC_FILETYPES_LISTVIEW);
1758     PFILE_TYPE_GLOBALS pG = (PFILE_TYPE_GLOBALS)SHAlloc(sizeof(*pG));
1759     if (!pG)
1760         return pG;
1761 
1762     pG->SortReverse = 1;
1763     pG->hDefExtIconSmall = NULL;
1764     pG->hOpenWithImage = NULL;
1765     pG->IconSize = GetSystemMetrics(SM_CXSMICON); // Shell icons are always square
1766     pG->himlSmall = ImageList_Create(pG->IconSize, pG->IconSize, ILC_COLOR32 | ILC_MASK, 256, 20);
1767     pG->hHeap = GetProcessHeap();
1768 
1769     pG->NoneString[0] = UNICODE_NULL;
1770     LoadStringW(shell32_hInstance, IDS_NONE, pG->NoneString, _countof(pG->NoneString));
1771 
1772     if (!(pG->Restricted = SHRestricted(REST_NOFILEASSOCIATE)))
1773     {
1774         HKEY hKey;
1775         if (RegCreateKeyEx(HKEY_LOCAL_MACHINE, L"Software\\Classes", 0, NULL, 0, KEY_WRITE, NULL, &hKey, NULL))
1776             pG->Restricted = TRUE;
1777         else
1778             RegCloseKey(hKey);
1779     }
1780 
1781     FileTypesDlg_InitListView(hwndDlg, hListView);
1782     ListView_SetImageList(hListView, pG->himlSmall, LVSIL_SMALL);
1783     EnableWindow(GetDlgItem(hwndDlg, IDC_FILETYPES_NEW), !pG->Restricted);
1784 
1785     // Delay loading the items so the propertysheet has time to finalize the UI
1786     PostMessage(hListView, WM_KEYDOWN, VK_F5, 0);
1787     return pG;
1788 }
1789 
1790 static inline PFILE_TYPE_ENTRY
FileTypesDlg_GetEntry(HWND hListView,INT iItem=-1)1791 FileTypesDlg_GetEntry(HWND hListView, INT iItem = -1)
1792 {
1793     if (iItem == -1)
1794     {
1795         iItem = ListView_GetNextItem(hListView, -1, LVNI_SELECTED);
1796         if (iItem == -1)
1797             return NULL;
1798     }
1799 
1800     LV_ITEMW lvItem = { LVIF_PARAM, iItem };
1801     if (ListView_GetItem(hListView, &lvItem))
1802         return (PFILE_TYPE_ENTRY)lvItem.lParam;
1803 
1804     return NULL;
1805 }
1806 
1807 static void
FileTypesDlg_OnDelete(HWND hwndDlg)1808 FileTypesDlg_OnDelete(HWND hwndDlg)
1809 {
1810     CStringW strRemoveExt(MAKEINTRESOURCEW(IDS_REMOVE_EXT));
1811     CStringW strTitle(MAKEINTRESOURCEW(IDS_FILE_TYPES));
1812     if (MessageBoxW(hwndDlg, strRemoveExt, strTitle, MB_ICONQUESTION | MB_YESNO) == IDYES)
1813     {
1814         FileTypesDlg_RemoveExt(hwndDlg);
1815 
1816         // Select first item (Win2k3 does it)
1817         HWND hListView = GetDlgItem(hwndDlg, IDC_FILETYPES_LISTVIEW);
1818         ListView_SetItemState(hListView, 0, -1, LVIS_FOCUSED | LVIS_SELECTED);
1819     }
1820 }
1821 
1822 static void
FileTypesDlg_OnItemChanging(HWND hwndDlg,PFILE_TYPE_ENTRY pEntry,PFILE_TYPE_GLOBALS pG)1823 FileTypesDlg_OnItemChanging(HWND hwndDlg, PFILE_TYPE_ENTRY pEntry, PFILE_TYPE_GLOBALS pG)
1824 {
1825     HBITMAP &hbmProgram = pG->hOpenWithImage;
1826     LPCWSTR DispAssoc = pEntry->GetAssocForDisplay();
1827     LPCWSTR TypeName = GetTypeName(pEntry, pG);
1828     CStringW buf;
1829 
1830     // format buffer and set description
1831     FileTypesDlg_SetGroupboxText(hwndDlg, DispAssoc);
1832     if (pEntry->IsExtension())
1833         buf.Format(IDS_FILE_DETAILSADV, DispAssoc, TypeName, TypeName);
1834     else
1835         buf = L"";
1836     SetDlgItemTextW(hwndDlg, IDC_FILETYPES_DESCRIPTION, buf.GetString());
1837 
1838     // delete previous program image
1839     if (hbmProgram)
1840     {
1841         DeleteObject(hbmProgram);
1842         hbmProgram = NULL;
1843     }
1844 
1845     // set program name
1846     LPCWSTR appname = GetAppName(pEntry);
1847     SetDlgItemTextW(hwndDlg, IDC_FILETYPES_APPNAME, appname);
1848 
1849     // set program image
1850     HICON hIconSm = NULL;
1851     LPCWSTR exe = GetProgramPath(pEntry);
1852     if (*exe)
1853     {
1854         ExtractIconExW(exe, 0, NULL, &hIconSm, 1);
1855     }
1856     hbmProgram = BitmapFromIcon(hIconSm, 16, 16);
1857     DestroyIcon(hIconSm);
1858     SendDlgItemMessageW(hwndDlg, IDC_FILETYPES_ICON, STM_SETIMAGE, IMAGE_BITMAP, LPARAM(hbmProgram));
1859 
1860     // Enable/Disable the buttons
1861     EnableWindow(GetDlgItem(hwndDlg, IDC_FILETYPES_CHANGE),
1862                  !pG->Restricted && pEntry->IsExtension());
1863     EnableWindow(GetDlgItem(hwndDlg, IDC_FILETYPES_ADVANCED),
1864                  !(pEntry->EditFlags & FTA_NoEdit) && !pG->Restricted);
1865     EnableWindow(GetDlgItem(hwndDlg, IDC_FILETYPES_DELETE),
1866                  !(pEntry->EditFlags & FTA_NoRemove) && !pG->Restricted && pEntry->IsExtension());
1867 }
1868 
1869 // IDD_FOLDER_OPTIONS_FILETYPES
1870 INT_PTR CALLBACK
FolderOptionsFileTypesDlg(HWND hwndDlg,UINT uMsg,WPARAM wParam,LPARAM lParam)1871 FolderOptionsFileTypesDlg(
1872     HWND hwndDlg,
1873     UINT uMsg,
1874     WPARAM wParam,
1875     LPARAM lParam)
1876 {
1877     PFILE_TYPE_GLOBALS pGlobals = (PFILE_TYPE_GLOBALS)GetWindowLongPtrW(hwndDlg, DWLP_USER);
1878     if (!pGlobals && uMsg != WM_INITDIALOG)
1879         return FALSE;
1880     LPNMLISTVIEW lppl;
1881     PFILE_TYPE_ENTRY pEntry;
1882     NEWEXT_DIALOG newext;
1883     EDITTYPE_DIALOG edittype;
1884 
1885     switch (uMsg)
1886     {
1887         case WM_INITDIALOG:
1888             pGlobals = FileTypesDlg_Initialize(hwndDlg);
1889             SetWindowLongPtrW(hwndDlg, DWLP_USER, (LONG_PTR)pGlobals);
1890             return TRUE;
1891 
1892         case WM_DESTROY:
1893             SetWindowLongPtrW(hwndDlg, DWLP_USER, 0);
1894             if (pGlobals)
1895             {
1896                 DestroyIcon(pGlobals->hDefExtIconSmall);
1897                 DeleteObject(pGlobals->hOpenWithImage);
1898                 SHFree(pGlobals);
1899             }
1900             break;
1901 
1902         case WM_COMMAND:
1903             switch (LOWORD(wParam))
1904             {
1905                 case IDC_FILETYPES_NEW:
1906                     newext.hwndLV = GetDlgItem(hwndDlg, IDC_FILETYPES_LISTVIEW);
1907                     if (IDOK == DialogBoxParamW(shell32_hInstance, MAKEINTRESOURCEW(IDD_NEWEXTENSION),
1908                                                 hwndDlg, NewExtDlgProc, (LPARAM)&newext))
1909                     {
1910                         FileTypesDlg_AddExt(hwndDlg, newext.szExt, newext.szFileType, pGlobals);
1911                     }
1912                     break;
1913 
1914                 case IDC_FILETYPES_DELETE:
1915                     FileTypesDlg_OnDelete(hwndDlg);
1916                     break;
1917 
1918                 case IDC_FILETYPES_CHANGE:
1919                     pEntry = FileTypesDlg_GetEntry(GetDlgItem(hwndDlg, IDC_FILETYPES_LISTVIEW));
1920                     if (pEntry)
1921                     {
1922                         OPENASINFO oai = { pEntry->FileExtension, 0, OAIF_FORCE_REGISTRATION | OAIF_REGISTER_EXT };
1923                         if (SUCCEEDED(SHOpenWithDialog(hwndDlg, &oai)))
1924                         {
1925                             pEntry->InvalidateDefaultApp();
1926                             FileTypesDlg_OnItemChanging(hwndDlg, pEntry, pGlobals);
1927                         }
1928                     }
1929                     break;
1930 
1931                 case IDC_FILETYPES_ADVANCED:
1932                     edittype.hwndLV = GetDlgItem(hwndDlg, IDC_FILETYPES_LISTVIEW);
1933                     edittype.pG = pGlobals;
1934                     edittype.pEntry = FileTypesDlg_GetEntry(edittype.hwndLV);
1935                     if (Normalize(edittype.pEntry))
1936                     {
1937                         DialogBoxParamW(shell32_hInstance, MAKEINTRESOURCEW(IDD_EDITTYPE),
1938                                         hwndDlg, EditTypeDlgProc, (LPARAM)&edittype);
1939                         FileTypesDlg_OnItemChanging(hwndDlg, edittype.pEntry, pGlobals);
1940                     }
1941                     break;
1942             }
1943             break;
1944 
1945         case WM_NOTIFY:
1946             lppl = (LPNMLISTVIEW) lParam;
1947             switch (lppl->hdr.code)
1948             {
1949                 case LVN_GETDISPINFO:
1950                 {
1951                     LPNMLVDISPINFOW pLVDI = (LPNMLVDISPINFOW)lParam;
1952                     PFILE_TYPE_ENTRY entry = (PFILE_TYPE_ENTRY)pLVDI->item.lParam;
1953                     if (entry && (pLVDI->item.mask & LVIF_TEXT))
1954                     {
1955                         if (pLVDI->item.iSubItem == 1)
1956                         {
1957                             pLVDI->item.pszText = GetTypeName(entry, pGlobals);
1958                             pLVDI->item.mask |= LVIF_DI_SETITEM;
1959                         }
1960                     }
1961                     break;
1962                 }
1963 
1964                 case LVN_KEYDOWN:
1965                 {
1966                     LV_KEYDOWN *pKeyDown = (LV_KEYDOWN *)lParam;
1967                     switch (pKeyDown->wVKey)
1968                     {
1969                         case VK_DELETE:
1970                             FileTypesDlg_OnDelete(hwndDlg);
1971                             break;
1972                         case VK_F5:
1973                             FileTypesDlg_Refresh(hwndDlg, pKeyDown->hdr.hwndFrom, pGlobals);
1974                             break;
1975                     }
1976                     break;
1977                 }
1978 
1979                 case NM_DBLCLK:
1980                     SendMessage(hwndDlg, WM_COMMAND, IDC_FILETYPES_ADVANCED, 0);
1981                     break;
1982 
1983                 case LVN_DELETEALLITEMS:
1984                     return FALSE;   // send LVN_DELETEITEM
1985 
1986                 case LVN_DELETEITEM:
1987                     pEntry = FileTypesDlg_GetEntry(lppl->hdr.hwndFrom, lppl->iItem);
1988                     if (pEntry)
1989                     {
1990                         pEntry->DestroyIcons();
1991                         HeapFree(pGlobals->hHeap, 0, pEntry);
1992                     }
1993                     return FALSE;
1994 
1995                 case LVN_ITEMCHANGING:
1996                     pEntry = FileTypesDlg_GetEntry(lppl->hdr.hwndFrom, lppl->iItem);
1997                     if (!pEntry)
1998                     {
1999                         return TRUE;
2000                     }
2001 
2002                     if (!(lppl->uOldState & LVIS_FOCUSED) && (lppl->uNewState & LVIS_FOCUSED))
2003                     {
2004                         FileTypesDlg_OnItemChanging(hwndDlg, pEntry, pGlobals);
2005                     }
2006                     break;
2007 
2008                 case LVN_COLUMNCLICK:
2009                     FileTypesDlg_Sort(pGlobals, lppl->hdr.hwndFrom, lppl->iSubItem);
2010                     break;
2011 
2012                 case PSN_SETACTIVE:
2013                     // On page activation, set the focus to the listview
2014                     SetFocus(GetDlgItem(hwndDlg, IDC_FILETYPES_LISTVIEW));
2015                     break;
2016             }
2017             break;
2018     }
2019 
2020     return FALSE;
2021 }
2022