xref: /reactos/base/shell/progman/dialog.c (revision 1734f297)
1 /*
2  * Program Manager
3  *
4  * Copyright 1996 Ulrich Schmid
5  * Copyright 2002 Sylvain Petreolle
6  * Copyright 2002 Andriy Palamarchuk
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21  */
22 
23 /*
24  * PROJECT:         ReactOS Program Manager
25  * COPYRIGHT:       GPL - See COPYING in the top level directory
26  * FILE:            base/shell/progman/dialog.c
27  * PURPOSE:         ProgMan dialog boxes
28  * PROGRAMMERS:     Ulrich Schmid
29  *                  Sylvain Petreolle
30  *                  Andriy Palamarchuk
31  *                  Hermes Belusca-Maito (hermes.belusca@sfr.fr)
32  */
33 
34 #include "progman.h"
35 
36 #include <commdlg.h>
37 
38 /***********************************************************************
39  *
40  *           DIALOG_AddFilterItem and DIALOG_BrowseXXX
41  */
42 
43 static
44 VOID
45 DIALOG_AddFilterItem(LPWSTR* p, UINT ids, LPCWSTR filter)
46 {
47     LoadStringW(Globals.hInstance, ids, *p, MAX_STRING_LEN);
48     *p += wcslen(*p) + 1;
49     lstrcpyW(*p, filter);
50     *p += wcslen(*p) + 1;
51     **p = '\0';
52 }
53 
54 static
55 BOOL
56 DIALOG_Browse(HWND hWnd, LPCWSTR lpszzFilter, LPWSTR lpstrFile, INT nMaxFile)
57 {
58     OPENFILENAMEW openfilename;
59     WCHAR szDir[MAX_PATH];
60 
61     ZeroMemory(&openfilename, sizeof(openfilename));
62 
63     GetCurrentDirectoryW(ARRAYSIZE(szDir), szDir);
64 
65     openfilename.lStructSize       = sizeof(openfilename);
66     openfilename.hwndOwner         = hWnd;
67     openfilename.hInstance         = Globals.hInstance;
68     openfilename.lpstrFilter       = lpszzFilter;
69     openfilename.lpstrFile         = lpstrFile;
70     openfilename.nMaxFile          = nMaxFile;
71     openfilename.lpstrInitialDir   = szDir;
72     openfilename.Flags             = OFN_EXPLORER;
73     openfilename.lpstrDefExt       = L"exe";
74     openfilename.lpstrCustomFilter = NULL;
75     openfilename.nMaxCustFilter    = 0;
76     openfilename.nFilterIndex      = 0;
77     openfilename.lpstrFileTitle    = NULL;
78     openfilename.nMaxFileTitle     = 0;
79     openfilename.lpstrTitle        = NULL;
80     openfilename.nFileOffset       = 0;
81     openfilename.nFileExtension    = 0;
82     openfilename.lCustData         = 0;
83     openfilename.lpfnHook          = NULL;
84     openfilename.lpTemplateName    = NULL;
85 
86     return GetOpenFileNameW(&openfilename);
87 }
88 
89 static
90 BOOL
91 DIALOG_BrowsePrograms(HWND hWnd, LPWSTR lpszFile, INT nMaxFile)
92 {
93     WCHAR szzFilter[2 * MAX_STRING_LEN + 100];
94     LPWSTR p = szzFilter;
95 
96     DIALOG_AddFilterItem(&p, IDS_PROGRAMS , L"*.exe;*.pif;*.com;*.bat;*.cmd");
97     DIALOG_AddFilterItem(&p, IDS_ALL_FILES, L"*.*");
98 
99     return DIALOG_Browse(hWnd, szzFilter, lpszFile, nMaxFile);
100 }
101 
102 static
103 BOOL
104 DIALOG_BrowseSymbols(HWND hWnd, LPWSTR lpszFile, INT nMaxFile)
105 {
106     WCHAR szzFilter[5 * MAX_STRING_LEN + 100];
107     LPWSTR p = szzFilter;
108 
109     DIALOG_AddFilterItem(&p, IDS_SYMBOL_FILES, L"*.ico;*.exe;*.dll");
110     DIALOG_AddFilterItem(&p, IDS_PROGRAMS, L"*.exe");
111     DIALOG_AddFilterItem(&p, IDS_LIBRARIES_DLL, L"*.dll");
112     DIALOG_AddFilterItem(&p, IDS_SYMBOLS_ICO, L"*.ico");
113     DIALOG_AddFilterItem(&p, IDS_ALL_FILES, L"*.*");
114 
115     return DIALOG_Browse(hWnd, szzFilter, lpszFile, nMaxFile);
116 }
117 
118 
119 
120 /***********************************************************************
121  *
122  *           DIALOG_New
123  */
124 
125 LPCWSTR GroupFormatToFormatName(GROUPFORMAT Format)
126 {
127     static const LPCWSTR FormatNames[] =
128     {
129         L"Windows 3.1",
130         L"NT Ansi",
131         L"NT Unicode"
132     };
133 
134     if (Format > NT_Unicode)
135         return NULL;
136     else
137         return FormatNames[Format];
138 }
139 
140 typedef struct _NEW_ITEM_CONTEXT
141 {
142     INT nDefault;
143     INT nResult;
144 } NEW_ITEM_CONTEXT, *PNEW_ITEM_CONTEXT;
145 
146 static
147 INT_PTR
148 CALLBACK
149 DIALOG_NEW_DlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
150 {
151     PNEW_ITEM_CONTEXT pNewItem;
152     GROUPFORMAT format;
153     INT iItem;
154 
155     pNewItem = (PNEW_ITEM_CONTEXT)GetWindowLongPtrW(hDlg, 8);
156 
157     switch (uMsg)
158     {
159         case WM_INITDIALOG:
160         {
161             pNewItem = (PNEW_ITEM_CONTEXT)lParam;
162             SetWindowLongPtrW(hDlg, 8, lParam);
163 
164             for (format = Win_311; format <= NT_Unicode; ++format)
165             {
166                 iItem = SendDlgItemMessageW(hDlg, PM_FORMAT, CB_INSERTSTRING, 0, (LPARAM)GroupFormatToFormatName(format));
167                 if (iItem != CB_ERR && iItem != CB_ERRSPACE)
168                     SendDlgItemMessageW(hDlg, PM_FORMAT, CB_SETITEMDATA, iItem, format);
169             }
170 
171             SendDlgItemMessageW(hDlg, PM_FORMAT, CB_SETCURSEL, 0, 0);
172             CheckRadioButton(hDlg, PM_NEW_GROUP, PM_NEW_PROGRAM, pNewItem->nDefault);
173             CheckRadioButton(hDlg, PM_PERSONAL_GROUP, PM_COMMON_GROUP, PM_PERSONAL_GROUP);
174 
175             EnableDlgItem(hDlg, PM_NEW_PROGRAM, GROUP_ActiveGroup() != NULL);
176 
177             SendMessageW(hDlg, WM_COMMAND, pNewItem->nDefault, 0);
178             break;
179         }
180 
181         case WM_COMMAND:
182             switch (LOWORD(wParam))
183             {
184                 case PM_NEW_GROUP:
185                 {
186                     CheckRadioButton(hDlg, PM_NEW_GROUP, PM_NEW_PROGRAM, wParam);
187                     EnableDlgItem(hDlg, PM_COMMON_GROUP  , TRUE);
188                     EnableDlgItem(hDlg, PM_PERSONAL_GROUP, TRUE);
189                     EnableDlgItem(hDlg, PM_FORMAT_TXT, TRUE);
190                     EnableDlgItem(hDlg, PM_FORMAT    , TRUE);
191                     return TRUE;
192                 }
193 
194                 case PM_NEW_PROGRAM:
195                 {
196                     CheckRadioButton(hDlg, PM_NEW_GROUP, PM_NEW_PROGRAM, wParam);
197                     EnableDlgItem(hDlg, PM_COMMON_GROUP  , FALSE);
198                     EnableDlgItem(hDlg, PM_PERSONAL_GROUP, FALSE);
199                     EnableDlgItem(hDlg, PM_FORMAT_TXT, FALSE);
200                     EnableDlgItem(hDlg, PM_FORMAT    , FALSE);
201                     return TRUE;
202                 }
203 
204                 case IDHELP:
205                     MAIN_MessageBoxIDS(IDS_NOT_IMPLEMENTED, IDS_ERROR, MB_OK);
206                     return TRUE;
207 
208                 case IDOK:
209                 {
210                     iItem = SendDlgItemMessageW(hDlg, PM_FORMAT, CB_GETCURSEL, 0, 0);
211 
212                     format = SendDlgItemMessageW(hDlg, PM_FORMAT, CB_GETITEMDATA, iItem, 0);
213                     format = min(max(format, Win_311), NT_Unicode);
214 
215                     pNewItem->nResult  =  !!IsDlgButtonChecked(hDlg, PM_NEW_GROUP);
216                     pNewItem->nResult |= (!!IsDlgButtonChecked(hDlg, PM_COMMON_GROUP) << 1);
217                     pNewItem->nResult |= (format << 2);
218 
219                     EndDialog(hDlg, IDOK);
220                     return TRUE;
221                 }
222 
223                 case IDCANCEL:
224                     EndDialog(hDlg, IDCANCEL);
225                     return TRUE;
226             }
227             return FALSE;
228     }
229 
230     return FALSE;
231 }
232 
233 BOOL DIALOG_New(INT nDefault, PINT pnResult)
234 {
235     INT_PTR ret;
236     NEW_ITEM_CONTEXT NewItem;
237 
238     *pnResult = 0;
239 
240     NewItem.nDefault = nDefault;
241     NewItem.nResult  = 0;
242     ret = DialogBoxParamW(Globals.hInstance, MAKEINTRESOURCEW(IDD_NEW), Globals.hMainWnd, DIALOG_NEW_DlgProc, (LPARAM)&NewItem);
243     if (ret == IDOK)
244         *pnResult = NewItem.nResult;
245 
246     return (ret == IDOK);
247 }
248 
249 
250 /***********************************************************************
251  *
252  *           DIALOG_CopyMove
253  */
254 
255 typedef struct _COPY_MOVE_CONTEXT
256 {
257     PROGRAM*   Program;
258     PROGGROUP* hToGroup;
259     BOOL       bMove;
260 } COPY_MOVE_CONTEXT, *PCOPY_MOVE_CONTEXT;
261 
262 static
263 INT_PTR
264 CALLBACK
265 DIALOG_COPY_MOVE_DlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
266 {
267     PROGGROUP* hGrpItem;
268     PROGGROUP* hGroup;
269 
270     PCOPY_MOVE_CONTEXT pCopyMove;
271     INT iItem;
272 
273     WCHAR text[MAX_STRING_LEN];
274 
275     pCopyMove = (PCOPY_MOVE_CONTEXT)GetWindowLongPtrW(hDlg, 8);
276 
277     switch (uMsg)
278     {
279         case WM_INITDIALOG:
280         {
281             pCopyMove = (PCOPY_MOVE_CONTEXT)lParam;
282             SetWindowLongPtrW(hDlg, 8, lParam);
283 
284             if (pCopyMove->bMove)
285             {
286                 LoadStringW(Globals.hInstance, IDS_MOVE_PROGRAM_1, text, ARRAYSIZE(text));
287                 SetWindowTextW(hDlg, text);
288                 LoadStringW(Globals.hInstance, IDS_MOVE_PROGRAM_2, text, ARRAYSIZE(text));
289                 SetDlgItemTextW(hDlg, PM_COPY_MOVE_TXT, text);
290             }
291 
292             /* List all the group names but the source group, in case we are doing a move */
293             for (hGroup = Globals.hGroups; hGroup; hGroup = hGroup->hNext)
294             {
295                 if (!pCopyMove->bMove || hGroup != pCopyMove->Program->hGroup)
296                 {
297                     iItem = SendDlgItemMessageW(hDlg, PM_TO_GROUP, CB_ADDSTRING, 0, (LPARAM)hGroup->hName);
298                     if (iItem != CB_ERR && iItem != CB_ERRSPACE)
299                         SendDlgItemMessageW(hDlg, PM_TO_GROUP, CB_SETITEMDATA, iItem, (LPARAM)hGroup);
300                 }
301             }
302             SendDlgItemMessageW(hDlg, PM_TO_GROUP, CB_SETCURSEL, 0, 0);
303             SetDlgItemTextW(hDlg, PM_PROGRAM   , pCopyMove->Program->hName);
304             SetDlgItemTextW(hDlg, PM_FROM_GROUP, pCopyMove->Program->hGroup->hName);
305             break;
306         }
307 
308         case WM_COMMAND:
309             switch (LOWORD(wParam))
310             {
311                 case IDHELP:
312                     MAIN_MessageBoxIDS(IDS_NOT_IMPLEMENTED, IDS_ERROR, MB_OK);
313                     return TRUE;
314 
315                 case IDOK:
316                 {
317                     /* Get the selected group */
318                     iItem = SendDlgItemMessageW(hDlg, PM_TO_GROUP, CB_GETCURSEL, 0, 0);
319                     hGrpItem = (PROGGROUP *)SendDlgItemMessageW(hDlg, PM_TO_GROUP, CB_GETITEMDATA, iItem, 0);
320                     /* Check that it is indeed in the group list */
321                     for (hGroup = Globals.hGroups; hGroup && hGroup != hGrpItem; hGroup = hGroup->hNext)
322                         ;
323                     if (pCopyMove->bMove)
324                     {
325                         if (hGrpItem == pCopyMove->Program->hGroup)
326                             hGrpItem = NULL;
327                     }
328                     pCopyMove->hToGroup = hGrpItem;
329                     EndDialog(hDlg, IDOK);
330                     return TRUE;
331                 }
332 
333                 case IDCANCEL:
334                     EndDialog(hDlg, IDCANCEL);
335                     return TRUE;
336             }
337             return FALSE;
338     }
339 
340     return FALSE;
341 }
342 
343 PROGGROUP* DIALOG_CopyMove(PROGRAM* hProgram, BOOL bMove)
344 {
345     COPY_MOVE_CONTEXT CopyMove;
346     INT_PTR ret;
347 
348     CopyMove.bMove    = bMove;
349     CopyMove.Program  = hProgram;
350     CopyMove.hToGroup = NULL;
351     ret = DialogBoxParamW(Globals.hInstance, MAKEINTRESOURCEW(IDD_COPY_MOVE), Globals.hMainWnd, DIALOG_COPY_MOVE_DlgProc, (LPARAM)&CopyMove);
352 
353     return (ret == IDOK ? CopyMove.hToGroup : NULL);
354 }
355 
356 /***********************************************************************
357  *
358  *           DIALOG_Delete
359  */
360 
361 BOOL DIALOG_Delete(UINT ids_text_s, LPCWSTR lpszName)
362 {
363     return (MAIN_MessageBoxIDS_s(ids_text_s, lpszName, IDS_DELETE, MB_YESNO | MB_DEFBUTTON2) == IDYES);
364 }
365 
366 
367 /* Adapted from dll/win32/shell32/dialogs/dialogs.cpp!EnableOkButtonFromEditContents */
368 BOOL ValidateEditContents(HWND hDlg, INT nIDEditItem)
369 {
370     BOOL Enable = FALSE;
371     LPWSTR psz;
372     INT Length, n;
373     HWND Edit;
374 
375     Edit = GetDlgItem(hDlg, nIDEditItem);
376     Length = GetWindowTextLengthW(Edit);
377 
378     if (Length <= 0)
379         return FALSE;
380 
381     psz = Alloc(0, (Length + 1) * sizeof(WCHAR));
382     if (psz)
383     {
384         GetWindowTextW(Edit, psz, Length + 1);
385         for (n = 0; n < Length && !Enable; ++n)
386             Enable = (psz[n] != ' ');
387         Free(psz);
388     }
389     else
390     {
391         Enable = TRUE;
392     }
393 
394     return Enable;
395 }
396 
397 
398 /***********************************************************************
399  *
400  *           DIALOG_GroupAttributes
401  */
402 
403 typedef struct _GROUP_ATTRIBUTES_CONTEXT
404 {
405     GROUPFORMAT format;
406     LPWSTR lpszTitle;
407     LPWSTR lpszGrpFile;
408     INT nSize;
409 } GROUP_ATTRIBUTES_CONTEXT, *PGROUP_ATTRIBUTES_CONTEXT;
410 
411 static
412 INT_PTR
413 CALLBACK
414 DIALOG_GROUP_DlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
415 {
416     PGROUP_ATTRIBUTES_CONTEXT pGroupAttributes;
417 
418     pGroupAttributes = (PGROUP_ATTRIBUTES_CONTEXT)GetWindowLongPtrW(hDlg, 8);
419 
420     switch (uMsg)
421     {
422         case WM_INITDIALOG:
423         {
424             DWORD evMask;
425 
426             pGroupAttributes = (PGROUP_ATTRIBUTES_CONTEXT)lParam;
427             SetWindowLongPtrW(hDlg, 8, lParam);
428 
429             /* Configure Richedit control for sending notification changes */
430             evMask = SendDlgItemMessageW(hDlg, PM_DESCRIPTION, EM_GETEVENTMASK, 0, 0) | ENM_CHANGE;
431             SendDlgItemMessageW(hDlg, PM_DESCRIPTION, EM_SETEVENTMASK, 0, (LPARAM)evMask);
432 
433             SetDlgItemTextW(hDlg, PM_DESCRIPTION, pGroupAttributes->lpszTitle);
434 
435             if (pGroupAttributes->format != Win_311)
436             {
437                 EnableDlgItem(hDlg, PM_FILE, FALSE);
438             }
439             else
440             {
441                 EnableDlgItem(hDlg, PM_FILE, TRUE);
442                 SetDlgItemTextW(hDlg, PM_FILE, pGroupAttributes->lpszGrpFile);
443             }
444 
445             EnableDlgItem(hDlg, IDOK, ValidateEditContents(hDlg, PM_DESCRIPTION));
446             break;
447         }
448 
449         case WM_COMMAND:
450             switch (LOWORD(wParam))
451             {
452                 case IDHELP:
453                     MAIN_MessageBoxIDS(IDS_NOT_IMPLEMENTED, IDS_ERROR, MB_OK);
454                     return TRUE;
455 
456                 case PM_DESCRIPTION:
457                 {
458                     if (HIWORD(wParam) == EN_CHANGE)
459                         EnableDlgItem(hDlg, IDOK, ValidateEditContents(hDlg, PM_DESCRIPTION));
460                     return FALSE;
461                 }
462 
463                 case IDOK:
464                 {
465                     GetDlgItemTextW(hDlg, PM_DESCRIPTION, pGroupAttributes->lpszTitle, pGroupAttributes->nSize);
466                     if (pGroupAttributes->format)
467                         *pGroupAttributes->lpszGrpFile = '\0';
468                     else
469                         GetDlgItemTextW(hDlg, PM_FILE, pGroupAttributes->lpszGrpFile, pGroupAttributes->nSize);
470                     EndDialog(hDlg, IDOK);
471                     return TRUE;
472                 }
473 
474                 case IDCANCEL:
475                     EndDialog(hDlg, IDCANCEL);
476                     return TRUE;
477             }
478             return FALSE;
479     }
480 
481     return FALSE;
482 }
483 
484 BOOL DIALOG_GroupAttributes(GROUPFORMAT format, LPWSTR lpszTitle, LPWSTR lpszGrpFile, INT nSize)
485 {
486     INT_PTR ret;
487     GROUP_ATTRIBUTES_CONTEXT GroupAttributes;
488 
489     GroupAttributes.format      = format;
490     GroupAttributes.nSize       = nSize;
491     GroupAttributes.lpszTitle   = lpszTitle;
492     GroupAttributes.lpszGrpFile = lpszGrpFile;
493 
494     ret = DialogBoxParamW(Globals.hInstance, MAKEINTRESOURCEW(IDD_GROUP), Globals.hMainWnd, DIALOG_GROUP_DlgProc, (LPARAM)&GroupAttributes);
495 
496     return (ret == IDOK);
497 }
498 
499 
500 /***********************************************************************
501  *
502  *           DIALOG_Symbol
503  */
504 
505 /* Adapted from dll/win32/shell32/dialogs/dialogs.cpp!EnumPickIconResourceProc */
506 static
507 BOOL
508 CALLBACK
509 EnumPickIconResourceProc(HMODULE hModule, LPCWSTR lpszType, LPWSTR lpszName, LONG_PTR lParam)
510 {
511     HICON hIcon;
512     HWND hDlgCtrl = (HWND)lParam;
513     WCHAR szName[100];
514 
515     if (IS_INTRESOURCE(lpszName))
516         StringCbPrintfW(szName, sizeof(szName), L"%u", (unsigned)(UINT_PTR)lpszName);
517     else
518         StringCbCopyW(szName, sizeof(szName), lpszName);
519 
520     hIcon = (HICON)LoadImageW(hModule, lpszName, IMAGE_ICON, 32, 32, LR_DEFAULTCOLOR);
521     if (hIcon == NULL)
522         return TRUE;
523 
524     SendMessageW(hDlgCtrl, LB_ADDSTRING, 0, (LPARAM)hIcon);
525     return TRUE;
526 }
527 
528 static
529 VOID
530 DestroyIconList(HWND hDlgCtrl)
531 {
532     HICON hIcon;
533     UINT count;
534 
535     count = SendMessageA(hDlgCtrl, LB_GETCOUNT, 0, 0);
536     if (count == LB_ERR || count == 0)
537         return;
538 
539     while (count-- > 0)
540     {
541         hIcon = (HICON)SendMessageA(hDlgCtrl, LB_GETITEMDATA, 0, 0);
542         DestroyIcon(hIcon);
543         SendMessageA(hDlgCtrl, LB_DELETESTRING, 0, 0);
544     }
545 }
546 
547 typedef struct _PICK_ICON_CONTEXT
548 {
549     HMODULE hLibrary;
550     HWND hDlgCtrl;
551     WCHAR szName[MAX_PATH]; // LPWSTR lpszIconFile; // INT nSize;
552     INT Index;
553     HICON hIcon;
554 } PICK_ICON_CONTEXT, *PPICK_ICON_CONTEXT;
555 
556 #if 0
557 
558 static struct
559 {
560   LPSTR  lpszIconFile;
561   INT    nSize;
562   HICON  *lphIcon;
563   INT    *lpnIconIndex;
564 } Symbol;
565 
566 #endif
567 
568 static
569 INT_PTR
570 CALLBACK
571 DIALOG_SYMBOL_DlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
572 {
573     WCHAR filename[MAX_PATHNAME_LEN];
574     PPICK_ICON_CONTEXT pIconContext;
575 
576     pIconContext = (PPICK_ICON_CONTEXT)GetWindowLongPtrW(hDlg, 8);
577 
578     switch (uMsg)
579     {
580         case WM_INITDIALOG:
581         {
582             pIconContext = (PPICK_ICON_CONTEXT)lParam;
583             SetWindowLongPtrW(hDlg, 8, lParam);
584 
585             pIconContext->hDlgCtrl = GetDlgItem(hDlg, PM_SYMBOL_LIST);
586             SetDlgItemTextW(hDlg, PM_ICON_FILE, pIconContext->szName);
587             SendMessageA(pIconContext->hDlgCtrl, LB_SETITEMHEIGHT, 0, 32);
588 
589             pIconContext->hLibrary = LoadLibraryExW(pIconContext->szName, NULL, /* NT6+: LOAD_LIBRARY_AS_IMAGE_RESOURCE | */ LOAD_LIBRARY_AS_DATAFILE);
590             if (pIconContext->hLibrary)
591             {
592                 EnumResourceNamesW(pIconContext->hLibrary,
593                                    (LPCWSTR)RT_GROUP_ICON,
594                                    EnumPickIconResourceProc,
595                                    (LONG_PTR)pIconContext->hDlgCtrl);
596                 FreeLibrary(pIconContext->hLibrary);
597                 pIconContext->hLibrary = NULL;
598             }
599             SendMessageA(pIconContext->hDlgCtrl, LB_SETCURSEL, pIconContext->Index, 0);
600             return TRUE;
601         }
602 
603         case WM_MEASUREITEM:
604         {
605             PMEASUREITEMSTRUCT measure = (PMEASUREITEMSTRUCT)lParam;
606             measure->itemWidth  = 32;
607             measure->itemHeight = 32;
608             return TRUE;
609         }
610 
611         case WM_DRAWITEM:
612         {
613             PDRAWITEMSTRUCT dis = (PDRAWITEMSTRUCT)lParam;
614 
615             if (dis->itemState & ODS_SELECTED)
616                 FillRect(dis->hDC, &dis->rcItem, (HBRUSH)(COLOR_HIGHLIGHT + 1));
617             else
618                 FillRect(dis->hDC, &dis->rcItem, (HBRUSH)(COLOR_WINDOW + 1));
619 
620             DrawIcon(dis->hDC, dis->rcItem.left, dis->rcItem.top, (HICON)dis->itemData);
621             return TRUE;
622         }
623 
624         case WM_COMMAND:
625             switch (LOWORD(wParam))
626             {
627                 case PM_BROWSE:
628                 {
629                     filename[0] = '\0';
630                     if (!DIALOG_BrowseSymbols(hDlg, filename, ARRAYSIZE(filename)))
631                         return TRUE;
632 
633                     if (_wcsnicmp(pIconContext->szName, filename, ARRAYSIZE(pIconContext->szName)) == 0)
634                         return TRUE;
635 
636                     SetDlgItemTextW(hDlg, PM_ICON_FILE, filename);
637                     DestroyIconList(pIconContext->hDlgCtrl);
638                     pIconContext->hLibrary = LoadLibraryExW(filename, NULL, /* NT6+: LOAD_LIBRARY_AS_IMAGE_RESOURCE | */ LOAD_LIBRARY_AS_DATAFILE);
639                     if (pIconContext->hLibrary)
640                     {
641                         EnumResourceNamesW(pIconContext->hLibrary,
642                                            (LPCWSTR)RT_GROUP_ICON,
643                                            EnumPickIconResourceProc,
644                                            (LONG_PTR)pIconContext->hDlgCtrl);
645                         FreeLibrary(pIconContext->hLibrary);
646                         pIconContext->hLibrary = NULL;
647                     }
648                     SendMessageA(pIconContext->hDlgCtrl, LB_SETCURSEL, 0, 0);
649                     return TRUE;
650                 }
651 
652                 case IDHELP:
653                     MAIN_MessageBoxIDS(IDS_NOT_IMPLEMENTED, IDS_ERROR, MB_OK);
654                     return TRUE;
655 
656                 case IDOK:
657                 {
658                     INT nCurSel = SendMessageW(pIconContext->hDlgCtrl, LB_GETCURSEL, 0, 0);
659                     GetDlgItemTextW(hDlg, PM_ICON_FILE, pIconContext->szName, ARRAYSIZE(pIconContext->szName));
660                     pIconContext->hIcon = (HICON)SendMessageA(pIconContext->hDlgCtrl, LB_GETITEMDATA, nCurSel, 0);
661                     pIconContext->hIcon = CopyIcon(pIconContext->hIcon);
662                     DestroyIconList(pIconContext->hDlgCtrl);
663                     EndDialog(hDlg, IDOK);
664                     return TRUE;
665                 }
666 
667                 case IDCANCEL:
668                     DestroyIconList(pIconContext->hDlgCtrl);
669                     EndDialog(hDlg, IDCANCEL);
670                     return TRUE;
671             }
672             return FALSE;
673     }
674 
675     return FALSE;
676 }
677 
678 static
679 VOID
680 DIALOG_Symbol(HWND hWnd, HICON *lphIcon, LPWSTR lpszIconFile, INT *lpnIconIndex, INT nSize)
681 {
682     PICK_ICON_CONTEXT IconContext;
683 
684     IconContext.Index = *lpnIconIndex;
685     StringCchCopyNW(IconContext.szName, ARRAYSIZE(IconContext.szName), lpszIconFile, nSize);
686 
687     if (DialogBoxParamW(Globals.hInstance, MAKEINTRESOURCEW(IDD_SYMBOL), hWnd, DIALOG_SYMBOL_DlgProc, (LPARAM)&IconContext) != IDOK)
688         return;
689 
690     StringCchCopyNW(lpszIconFile, nSize, IconContext.szName, ARRAYSIZE(IconContext.szName));
691     *lpnIconIndex = IconContext.Index;
692     *lphIcon = IconContext.hIcon;
693 }
694 
695 
696 /***********************************************************************
697  *
698  *           DIALOG_ProgramAttributes
699  */
700 
701 typedef struct _PROGRAM_ATTRIBUTES_CONTEXT
702 {
703     LPWSTR lpszTitle;
704     LPWSTR lpszCmdLine;
705     LPWSTR lpszWorkDir;
706     LPWSTR lpszIconFile;
707     LPWSTR lpszTmpIconFile;
708     INT    nSize;
709     INT*   lpnCmdShow;
710     INT*   lpnHotKey;
711     // HWND   hSelGroupWnd;
712     BOOL*  lpbNewVDM; // unused!
713     HICON* lphIcon;
714     HICON  hTmpIcon;
715     INT*   lpnIconIndex;
716     INT    nTmpIconIndex;
717     BOOL   bCheckBinaryType;
718 } PROGRAM_ATTRIBUTES_CONTEXT, *PPROGRAM_ATTRIBUTES_CONTEXT;
719 
720 static
721 INT_PTR
722 CALLBACK
723 DIALOG_PROGRAM_DlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
724 {
725     BOOL bEnable;
726     WCHAR filename[MAX_PATHNAME_LEN];
727     DWORD evMask;
728     DWORD dwBinaryType;
729     PPROGRAM_ATTRIBUTES_CONTEXT pProgramAttributes;
730 
731     pProgramAttributes = (PPROGRAM_ATTRIBUTES_CONTEXT)GetWindowLongPtrW(hDlg, 8);
732 
733     switch (uMsg)
734     {
735         case WM_INITDIALOG:
736         {
737             pProgramAttributes = (PPROGRAM_ATTRIBUTES_CONTEXT)lParam;
738             SetWindowLongPtrW(hDlg, 8, lParam);
739 
740             evMask = SendDlgItemMessageW(hDlg, PM_COMMAND_LINE, EM_GETEVENTMASK, 0, 0) | ENM_CHANGE;
741             SendDlgItemMessageW(hDlg, PM_COMMAND_LINE, EM_SETEVENTMASK, 0, evMask);
742 
743             SetDlgItemTextW(hDlg, PM_DESCRIPTION , pProgramAttributes->lpszTitle);
744             SetDlgItemTextW(hDlg, PM_COMMAND_LINE, pProgramAttributes->lpszCmdLine);
745             SetDlgItemTextW(hDlg, PM_DIRECTORY   , pProgramAttributes->lpszWorkDir);
746 
747             /*                                                  0x0F                                          0x06 */
748             SendDlgItemMessageW(hDlg, PM_HOT_KEY, HKM_SETRULES, HKCOMB_C | HKCOMB_A | HKCOMB_S | HKCOMB_NONE, HOTKEYF_CONTROL | HOTKEYF_ALT);
749             SendDlgItemMessageW(hDlg, PM_HOT_KEY, HKM_SETHOTKEY, *pProgramAttributes->lpnHotKey, 0);
750 
751             bEnable = ValidateEditContents(hDlg, PM_COMMAND_LINE);
752             EnableWindow(GetDlgItem(hDlg, IDOK), bEnable);
753             EnableWindow(GetDlgItem(hDlg, PM_OTHER_SYMBOL), bEnable);
754 
755             CheckDlgButton(hDlg, PM_SYMBOL, *pProgramAttributes->lpnCmdShow == SW_SHOWMINNOACTIVE);
756 
757             if (pProgramAttributes->bCheckBinaryType &&
758                 (!GetBinaryTypeW(pProgramAttributes->lpszCmdLine, &dwBinaryType) || dwBinaryType != SCS_WOW_BINARY) )
759             {
760                 EnableWindow(GetDlgItem(hDlg, PM_NEW_VDM), FALSE);
761                 CheckDlgButton(hDlg, PM_NEW_VDM, BST_CHECKED);
762             }
763             else
764             {
765                 EnableWindow(GetDlgItem(hDlg, PM_NEW_VDM), TRUE);
766                 CheckDlgButton(hDlg, PM_NEW_VDM, *pProgramAttributes->lpbNewVDM);
767             }
768             SendDlgItemMessageW(hDlg, PM_ICON, STM_SETICON, (WPARAM)pProgramAttributes->hTmpIcon, 0);
769             break;
770         }
771 
772         case WM_COMMAND:
773             switch (LOWORD(wParam))
774             {
775                 case PM_NEW_VDM:
776                     CheckDlgButton(hDlg, PM_NEW_VDM, !IsDlgButtonChecked(hDlg, PM_NEW_VDM));
777                     return TRUE;
778 
779                 case PM_BROWSE:
780                 {
781                     filename[0] = '\0';
782                     if (DIALOG_BrowsePrograms(hDlg, filename, ARRAYSIZE(filename)))
783                     {
784                         SetDlgItemTextW(hDlg, PM_COMMAND_LINE, filename);
785                         if (pProgramAttributes->bCheckBinaryType &&
786                             (!GetBinaryTypeW(filename, &dwBinaryType) || dwBinaryType != SCS_WOW_BINARY))
787                         {
788                             EnableWindow(GetDlgItem(hDlg, PM_NEW_VDM), FALSE);
789                             CheckDlgButton(hDlg, PM_NEW_VDM, BST_CHECKED);
790                         }
791                         else
792                         {
793                             EnableWindow(GetDlgItem(hDlg, PM_NEW_VDM), TRUE);
794                             CheckDlgButton(hDlg, PM_NEW_VDM, BST_UNCHECKED);
795                         }
796                     }
797                     return TRUE;
798                 }
799 
800                 case IDHELP:
801                     MAIN_MessageBoxIDS(IDS_NOT_IMPLEMENTED, IDS_ERROR, MB_OK);
802                     return TRUE;
803 
804                 case PM_SYMBOL:
805                     CheckDlgButton(hDlg, PM_SYMBOL, !IsDlgButtonChecked(hDlg, PM_SYMBOL));
806                     return TRUE;
807 
808                 case PM_OTHER_SYMBOL:
809                 {
810                     DIALOG_Symbol(hDlg,
811                                   &pProgramAttributes->hTmpIcon,
812                                   pProgramAttributes->lpszTmpIconFile,
813                                   &pProgramAttributes->nTmpIconIndex,
814                                   MAX_PATHNAME_LEN);
815 
816                     SendDlgItemMessageW(hDlg, PM_ICON, STM_SETICON, (WPARAM)pProgramAttributes->hTmpIcon, 0);
817                     return TRUE;
818                 }
819 
820                 case PM_COMMAND_LINE:
821                 {
822                     if (HIWORD(wParam) == EN_CHANGE)
823                     {
824                         bEnable = ValidateEditContents(hDlg, PM_COMMAND_LINE);
825                         EnableWindow(GetDlgItem(hDlg, IDOK), bEnable);
826                         EnableWindow(GetDlgItem(hDlg, PM_OTHER_SYMBOL), bEnable);
827                     }
828                     return FALSE;
829                 }
830 
831                 case IDOK:
832                 {
833                     GetDlgItemTextW(hDlg, PM_DESCRIPTION , pProgramAttributes->lpszTitle  , pProgramAttributes->nSize);
834                     GetDlgItemTextW(hDlg, PM_COMMAND_LINE, pProgramAttributes->lpszCmdLine, pProgramAttributes->nSize);
835                     GetDlgItemTextW(hDlg, PM_DIRECTORY   , pProgramAttributes->lpszWorkDir, pProgramAttributes->nSize);
836                     if (pProgramAttributes->hTmpIcon)
837                     {
838 #if 0
839     if (*pProgramAttributes->lphIcon)
840         DestroyIcon(*pProgramAttributes->lphIcon);
841 #endif
842                         *pProgramAttributes->lphIcon = pProgramAttributes->hTmpIcon;
843                         *pProgramAttributes->lpnIconIndex = pProgramAttributes->nTmpIconIndex;
844                         lstrcpynW(pProgramAttributes->lpszIconFile, pProgramAttributes->lpszTmpIconFile, pProgramAttributes->nSize);
845                     }
846                     *pProgramAttributes->lpnHotKey  = SendDlgItemMessageW(hDlg, PM_HOT_KEY, HKM_GETHOTKEY, 0, 0);
847                     *pProgramAttributes->lpnCmdShow = IsDlgButtonChecked(hDlg, PM_SYMBOL) ? SW_SHOWMINNOACTIVE : SW_SHOWNORMAL;
848                     *pProgramAttributes->lpbNewVDM  = IsDlgButtonChecked(hDlg, PM_NEW_VDM);
849                     EndDialog(hDlg, IDOK);
850                     return TRUE;
851                 }
852 
853                 case IDCANCEL:
854                     EndDialog(hDlg, IDCANCEL);
855                     return TRUE;
856             }
857             return FALSE;
858     }
859 
860     return FALSE;
861 }
862 
863 BOOL
864 DIALOG_ProgramAttributes(LPWSTR lpszTitle, LPWSTR lpszCmdLine, LPWSTR lpszWorkDir, LPWSTR lpszIconFile,
865                          HICON* lphIcon, INT* lpnIconIndex, INT* lpnHotKey, INT* lpnCmdShow, BOOL* lpbNewVDM, INT nSize)
866 {
867     INT_PTR ret;
868     WCHAR szTmpIconFile[MAX_PATHNAME_LEN];
869     PROGRAM_ATTRIBUTES_CONTEXT ProgramAttributes;
870     DWORD dwSize;
871     DWORD dwType;
872 
873     ProgramAttributes.nSize        = nSize;
874     ProgramAttributes.lpszTitle    = lpszTitle;
875     ProgramAttributes.lpszCmdLine  = lpszCmdLine;
876     ProgramAttributes.lpszWorkDir  = lpszWorkDir;
877     ProgramAttributes.lpszIconFile = lpszIconFile;
878     ProgramAttributes.lpnCmdShow   = lpnCmdShow;
879     ProgramAttributes.lpnHotKey    = lpnHotKey;
880     ProgramAttributes.lpbNewVDM    = lpbNewVDM;
881     ProgramAttributes.lphIcon      = lphIcon;
882     ProgramAttributes.lpnIconIndex = lpnIconIndex;
883 
884     dwSize = sizeof(ProgramAttributes.bCheckBinaryType);
885     if (RegQueryValueExW(Globals.hKeyPMSettings,
886                          L"CheckBinaryType",
887                          0,
888                          &dwType,
889                          (LPBYTE)&ProgramAttributes.bCheckBinaryType,
890                          &dwSize) != ERROR_SUCCESS
891       || dwType != REG_DWORD)
892     {
893         ProgramAttributes.bCheckBinaryType = TRUE;
894     }
895 
896 #if 0
897     ProgramAttributes.hTmpIcon = NULL;
898 #else
899     ProgramAttributes.hTmpIcon        = *lphIcon;
900 #endif
901     ProgramAttributes.nTmpIconIndex   = *lpnIconIndex;
902     ProgramAttributes.lpszTmpIconFile = szTmpIconFile;
903     wcsncpy(szTmpIconFile, lpszIconFile, ARRAYSIZE(szTmpIconFile));
904 
905     ret = DialogBoxParamW(Globals.hInstance, MAKEINTRESOURCEW(IDD_PROGRAM), Globals.hMainWnd, DIALOG_PROGRAM_DlgProc, (LPARAM)&ProgramAttributes);
906     return (ret == IDOK);
907 }
908 
909 
910 /***********************************************************************
911  *
912  *           DIALOG_Execute
913  */
914 
915 typedef struct _EXECUTE_CONTEXT
916 {
917     HKEY  hKeyPMRecentFilesList;
918     DWORD dwMaxFiles;
919     BOOL  bCheckBinaryType;
920 } EXECUTE_CONTEXT, *PEXECUTE_CONTEXT;
921 
922 static
923 INT_PTR
924 CALLBACK
925 DIALOG_EXECUTE_DlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
926 {
927     WCHAR szFile[MAX_PATHNAME_LEN]; // filename
928     DWORD BinaryType;
929     PEXECUTE_CONTEXT pExecuteContext;
930 
931     pExecuteContext = (PEXECUTE_CONTEXT)GetWindowLongPtrW(hDlg, 8);
932 
933     switch (uMsg)
934     {
935         case WM_INITDIALOG:
936         {
937             pExecuteContext = (PEXECUTE_CONTEXT)lParam;
938             SetWindowLongPtrW(hDlg, 8, lParam);
939 
940             EnableDlgItem(hDlg, IDOK, ValidateEditContents(hDlg, PM_COMMAND));
941 
942             if (pExecuteContext->bCheckBinaryType)
943             {
944                 EnableDlgItem(hDlg, PM_NEW_VDM, FALSE);
945                 CheckDlgButton(hDlg, PM_NEW_VDM, BST_CHECKED);
946             }
947             else
948             {
949                 EnableDlgItem(hDlg, PM_NEW_VDM, TRUE);
950             }
951 
952             break;
953         }
954 
955         case WM_COMMAND:
956             switch (LOWORD(wParam))
957             {
958                 case PM_SYMBOL:
959                     CheckDlgButton(hDlg, PM_SYMBOL, !IsDlgButtonChecked(hDlg, PM_SYMBOL));
960                     return TRUE;
961 
962                 case PM_NEW_VDM:
963                     CheckDlgButton(hDlg, PM_NEW_VDM, !IsDlgButtonChecked(hDlg, PM_NEW_VDM));
964                     return TRUE;
965 
966                 case PM_BROWSE:
967                 {
968                     szFile[0] = '\0';
969                     if (DIALOG_BrowsePrograms(hDlg, szFile, ARRAYSIZE(szFile)))
970                     {
971                         SetWindowTextW(GetDlgItem(hDlg, PM_COMMAND), szFile);
972                         SetFocus(GetDlgItem(hDlg, IDOK));
973                         if (pExecuteContext->bCheckBinaryType &&
974                             (!GetBinaryTypeW(szFile, &BinaryType) || BinaryType != SCS_WOW_BINARY) )
975                         {
976                             EnableDlgItem(hDlg, PM_NEW_VDM, FALSE);
977                             CheckDlgButton(hDlg, PM_NEW_VDM, BST_CHECKED);
978                         }
979                         else
980                         {
981                             EnableDlgItem(hDlg, PM_NEW_VDM, TRUE);
982                             CheckDlgButton(hDlg, PM_NEW_VDM, BST_UNCHECKED);
983                         }
984                     }
985                     return TRUE;
986                 }
987 
988                 case PM_COMMAND:
989                 {
990                     if (HIWORD(wParam) == CBN_EDITCHANGE)
991                     {
992                         EnableDlgItem(hDlg, IDOK, ValidateEditContents(hDlg, PM_COMMAND));
993                     }
994                     return FALSE;
995                 }
996 
997                 case IDHELP:
998                     MAIN_MessageBoxIDS(IDS_NOT_IMPLEMENTED, IDS_ERROR, MB_OK);
999                     return TRUE;
1000 
1001                 case IDOK:
1002                 {
1003                     GetDlgItemTextW(hDlg, PM_COMMAND, szFile, ARRAYSIZE(szFile));
1004                     ShellExecuteW(NULL, NULL, szFile, NULL, NULL, IsDlgButtonChecked(hDlg, PM_SYMBOL) ? SW_SHOWMINNOACTIVE : SW_SHOWNORMAL);
1005                     if (Globals.bMinOnRun)
1006                         CloseWindow(Globals.hMainWnd);
1007                     EndDialog(hDlg, IDOK);
1008                     return TRUE;
1009                 }
1010 
1011                 case IDCANCEL:
1012                     EndDialog(hDlg, IDCANCEL);
1013                     return TRUE;
1014             }
1015             return FALSE;
1016     }
1017 
1018     return FALSE;
1019 }
1020 
1021 VOID DIALOG_Execute(VOID)
1022 {
1023     EXECUTE_CONTEXT ExecuteContext;
1024     LONG lRet;
1025     DWORD dwSize;
1026     DWORD dwType;
1027 
1028     ExecuteContext.hKeyPMRecentFilesList = NULL;
1029     ExecuteContext.bCheckBinaryType = TRUE;
1030 
1031     lRet = RegCreateKeyExW(Globals.hKeyProgMan,
1032                            L"Recent File List",
1033                            0,
1034                            NULL,
1035                            0,
1036                            KEY_READ | KEY_WRITE,
1037                            NULL,
1038                            &ExecuteContext.hKeyPMRecentFilesList,
1039                            NULL);
1040     if (lRet == ERROR_SUCCESS)
1041     {
1042         dwSize = sizeof(ExecuteContext.dwMaxFiles);
1043         lRet = RegQueryValueExW(ExecuteContext.hKeyPMRecentFilesList,
1044                                 L"Max Files",
1045                                 NULL,
1046                                 &dwType,
1047                                 (LPBYTE)&ExecuteContext.dwMaxFiles,
1048                                 &dwSize);
1049         if (lRet != ERROR_SUCCESS || dwType != REG_DWORD)
1050         {
1051             ExecuteContext.dwMaxFiles = 4;
1052             dwSize = sizeof(ExecuteContext.dwMaxFiles);
1053             lRet = RegSetValueExW(ExecuteContext.hKeyPMRecentFilesList,
1054                                   L"Max Files",
1055                                   0,
1056                                   REG_DWORD,
1057                                   (LPBYTE)&ExecuteContext.dwMaxFiles,
1058                                   sizeof(ExecuteContext.dwMaxFiles));
1059         }
1060 
1061         dwSize = sizeof(ExecuteContext.bCheckBinaryType);
1062         lRet = RegQueryValueExW(Globals.hKeyPMSettings,
1063                                 L"CheckBinaryType",
1064                                 NULL,
1065                                 &dwType,
1066                                 (LPBYTE)&ExecuteContext.bCheckBinaryType,
1067                                 &dwSize);
1068         if (lRet != ERROR_SUCCESS || dwType != REG_DWORD)
1069         {
1070             ExecuteContext.bCheckBinaryType = TRUE;
1071         }
1072     }
1073 
1074     DialogBoxParamW(Globals.hInstance, MAKEINTRESOURCEW(IDD_EXECUTE), Globals.hMainWnd, DIALOG_EXECUTE_DlgProc, (LPARAM)&ExecuteContext);
1075 
1076     if (ExecuteContext.hKeyPMRecentFilesList)
1077         RegCloseKey(ExecuteContext.hKeyPMRecentFilesList);
1078 }
1079