xref: /reactos/base/shell/progman/main.c (revision c2c66aff)
1 /*
2  * Program Manager
3  *
4  * Copyright 1996 Ulrich Schmid
5  * Copyright 2002 Sylvain Petreolle
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 /*
23  * PROJECT:         ReactOS Program Manager
24  * COPYRIGHT:       GPL - See COPYING in the top level directory
25  * FILE:            base/shell/progman/main.c
26  * PURPOSE:         ProgMan entry point & MDI window
27  * PROGRAMMERS:     Ulrich Schmid
28  *                  Sylvain Petreolle
29  *                  Hermes Belusca-Maito (hermes.belusca@sfr.fr)
30  */
31 
32 #include "progman.h"
33 
34 #include <shellapi.h>
35 
36 #define WC_MDICLIENTA        "MDICLIENT"
37 #define WC_MDICLIENTW       L"MDICLIENT"
38 
39 #ifdef UNICODE
40 #define WC_MDICLIENT        WC_MDICLIENTW
41 #else
42 #define WC_MDICLIENT        WC_MDICLIENTA
43 #endif
44 
45 GLOBALS Globals;
46 
47 static VOID MAIN_LoadGroups(VOID);
48 static VOID MAIN_MenuCommand(HWND hWnd, WPARAM wParam, LPARAM lParam);
49 static ATOM MAIN_RegisterMainWinClass(VOID);
50 static VOID MAIN_CreateMainWindow(VOID);
51 static VOID MAIN_CreateMDIWindow(VOID);
52 static VOID MAIN_AutoStart(VOID);
53 
54 
55 #define BUFFER_SIZE 1024
56 
57 
58 
59 /*
60  * Memory management functions
61  */
62 PVOID
63 Alloc(IN DWORD  dwFlags,
64       IN SIZE_T dwBytes)
65 {
66     return HeapAlloc(GetProcessHeap(), dwFlags, dwBytes);
67 }
68 
69 BOOL
70 Free(IN PVOID lpMem)
71 {
72     return HeapFree(GetProcessHeap(), 0, lpMem);
73 }
74 
75 PVOID
76 ReAlloc(IN DWORD  dwFlags,
77         IN PVOID  lpMem,
78         IN SIZE_T dwBytes)
79 {
80     return HeapReAlloc(GetProcessHeap(), dwFlags, lpMem, dwBytes);
81 }
82 
83 PVOID
84 AppendToBuffer(IN PVOID   pBuffer,
85                IN PSIZE_T pdwBufferSize,
86                IN PVOID   pData,
87                IN SIZE_T  dwDataSize)
88 {
89     PVOID  pTmp;
90     SIZE_T dwBufferSize;
91 
92     dwBufferSize = dwDataSize + *pdwBufferSize;
93 
94     if (pBuffer)
95         pTmp = ReAlloc(0, pBuffer, dwBufferSize);
96     else
97         pTmp = Alloc(0, dwBufferSize);
98 
99     if (!pTmp)
100         return NULL;
101 
102     memcpy((PVOID)((ULONG_PTR)pTmp + *pdwBufferSize), pData, dwDataSize);
103     *pdwBufferSize = dwBufferSize;
104 
105     return pTmp;
106 }
107 
108 
109 
110 /*
111  * Debugging helpers
112  */
113 VOID
114 PrintStringV(IN LPCWSTR szStr,
115              IN va_list args)
116 {
117     WCHAR Buffer[4096];
118 
119     _vsnwprintf(Buffer, ARRAYSIZE(Buffer), szStr, args);
120     MessageBoxW(Globals.hMainWnd, Buffer, L"Information", MB_OK);
121 }
122 
123 VOID
124 PrintString(IN LPCWSTR szStr, ...)
125 {
126     va_list args;
127 
128     va_start(args, szStr);
129     PrintStringV(szStr, args);
130     va_end(args);
131 }
132 
133 VOID
134 PrintResourceString(IN UINT uID, ...)
135 {
136     WCHAR Buffer[4096];
137     va_list args;
138 
139     va_start(args, uID);
140     LoadStringW(Globals.hInstance, uID, Buffer, ARRAYSIZE(Buffer));
141     PrintStringV(Buffer, args);
142     va_end(args);
143 }
144 
145 VOID
146 PrintWin32Error(IN LPWSTR Message, IN DWORD ErrorCode)
147 {
148     LPWSTR lpMsgBuf;
149 
150     FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
151                    NULL, ErrorCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
152                    (LPWSTR)&lpMsgBuf, 0, NULL);
153 
154     PrintString(L"%s: %s\n", Message, lpMsgBuf);
155     LocalFree(lpMsgBuf);
156 }
157 
158 int ShowLastWin32Error(VOID)
159 {
160     DWORD dwError;
161     LPWSTR lpMsgBuf = NULL;
162     WCHAR Buffer[4096];
163 
164     dwError = GetLastError();
165 
166     FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
167                    NULL, dwError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
168                    (LPWSTR)&lpMsgBuf, 0, NULL);
169     _snwprintf(Buffer, ARRAYSIZE(Buffer), L"Error %d: %s\n", dwError, lpMsgBuf);
170     LocalFree(lpMsgBuf);
171     return MessageBoxW(Globals.hMainWnd, Buffer, L"Error", MB_OK);
172 }
173 
174 
175 
176 
177 
178 
179 
180 /* Copied and adapted from dll/win32/userenv/environment.c!GetUserAndDomainName */
181 static
182 BOOL
183 GetUserAndDomainName(OUT LPWSTR* UserName,
184                      OUT LPWSTR* DomainName)
185 {
186     BOOL bRet = TRUE;
187     HANDLE hToken;
188     DWORD cbTokenBuffer = 0;
189     PTOKEN_USER pUserToken;
190 
191     LPWSTR lpUserName   = NULL;
192     LPWSTR lpDomainName = NULL;
193     DWORD  cbUserName   = 0;
194     DWORD  cbDomainName = 0;
195 
196     SID_NAME_USE SidNameUse;
197 
198     /* Get the process token */
199     if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
200         return FALSE;
201 
202     /* Retrieve token's information */
203     if (!GetTokenInformation(hToken, TokenUser, NULL, 0, &cbTokenBuffer))
204     {
205         if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
206         {
207             CloseHandle(hToken);
208             return FALSE;
209         }
210     }
211 
212     pUserToken = Alloc(HEAP_ZERO_MEMORY, cbTokenBuffer);
213     if (!pUserToken)
214     {
215         CloseHandle(hToken);
216         return FALSE;
217     }
218 
219     if (!GetTokenInformation(hToken, TokenUser, pUserToken, cbTokenBuffer, &cbTokenBuffer))
220     {
221         Free(pUserToken);
222         CloseHandle(hToken);
223         return FALSE;
224     }
225 
226     CloseHandle(hToken);
227 
228     /* Retrieve the domain and user name */
229     if (!LookupAccountSidW(NULL,
230                            pUserToken->User.Sid,
231                            NULL,
232                            &cbUserName,
233                            NULL,
234                            &cbDomainName,
235                            &SidNameUse))
236     {
237         if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
238         {
239             bRet = FALSE;
240             goto done;
241         }
242     }
243 
244     lpUserName = Alloc(HEAP_ZERO_MEMORY, cbUserName * sizeof(WCHAR));
245     if (lpUserName == NULL)
246     {
247         bRet = FALSE;
248         goto done;
249     }
250 
251     lpDomainName = Alloc(HEAP_ZERO_MEMORY, cbDomainName * sizeof(WCHAR));
252     if (lpDomainName == NULL)
253     {
254         bRet = FALSE;
255         goto done;
256     }
257 
258     if (!LookupAccountSidW(NULL,
259                            pUserToken->User.Sid,
260                            lpUserName,
261                            &cbUserName,
262                            lpDomainName,
263                            &cbDomainName,
264                            &SidNameUse))
265     {
266         bRet = FALSE;
267         goto done;
268     }
269 
270     *UserName   = lpUserName;
271     *DomainName = lpDomainName;
272 
273 done:
274     if (bRet == FALSE)
275     {
276         if (lpUserName != NULL)
277             Free(lpUserName);
278 
279         if (lpDomainName != NULL)
280             Free(lpDomainName);
281     }
282 
283     Free(pUserToken);
284 
285     return bRet;
286 }
287 
288 
289 
290 
291 
292 
293 static
294 VOID
295 MAIN_SetMainWindowTitle(VOID)
296 {
297     LPWSTR caption;
298     SIZE_T size;
299 
300     LPWSTR lpDomainName = NULL;
301     LPWSTR lpUserName   = NULL;
302 
303     if (GetUserAndDomainName(&lpUserName, &lpDomainName) && lpUserName && lpDomainName)
304     {
305         size = (256 + 3 + wcslen(lpDomainName) + wcslen(lpUserName) + 1) * sizeof(WCHAR);
306         caption = Alloc(HEAP_ZERO_MEMORY, size);
307         if (caption)
308         {
309             StringCbPrintfW(caption, size, L"%s - %s\\%s", szTitle, lpDomainName, lpUserName);
310             SetWindowTextW(Globals.hMainWnd, caption);
311             Free(caption);
312         }
313         else
314         {
315             SetWindowTextW(Globals.hMainWnd, szTitle);
316         }
317     }
318     else
319     {
320         SetWindowTextW(Globals.hMainWnd, szTitle);
321     }
322 
323     if (lpUserName)   Free(lpUserName);
324     if (lpDomainName) Free(lpDomainName);
325 }
326 
327 
328 
329 
330 static
331 BOOL
332 MAIN_LoadSettings(VOID)
333 {
334     LPWSTR lpszTmp;
335     LPWSTR lpszSection;
336     LONG lRet;
337     WCHAR dummy[2];
338     LPWSTR lpszKeyValue;
339     const LPCWSTR lpszIniFile = L"progman.ini";
340     WCHAR szWinDir[MAX_PATH];
341     LPWSTR lpszKey;
342     DWORD Value;
343     HKEY hKey;
344     BOOL bIsIniMigrated;
345     DWORD dwSize;
346     LPWSTR lpszSections;
347     LPWSTR lpszData;
348     DWORD dwRet;
349     DWORD dwType;
350     LPWSTR lpszValue;
351 
352     bIsIniMigrated = FALSE;
353     lpszSections = NULL;
354     lpszData = NULL;
355 
356     /* Try to create/open the Program Manager user key */
357     if (RegCreateKeyExW(HKEY_CURRENT_USER,
358                         L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Program Manager",
359                         0,
360                         NULL,
361                         REG_OPTION_NON_VOLATILE,
362                         KEY_READ | KEY_WRITE,
363                         NULL,
364                         &Globals.hKeyProgMan,
365                         NULL) != ERROR_SUCCESS)
366     {
367         return FALSE;
368     }
369 
370     /*
371      * TODO: Add the explanation for the migration...
372      */
373     dwSize = sizeof(Value);
374     lRet = RegQueryValueExW(Globals.hKeyProgMan, L"IniMigrated", NULL, &dwType, (LPBYTE)&Value, &dwSize);
375     if (lRet != ERROR_SUCCESS || dwType != REG_DWORD)
376         Value = 0;
377     bIsIniMigrated = !!Value;
378 
379     if (bIsIniMigrated)
380     {
381         /* The migration was already done, just load the settings */
382         goto LoadSettings;
383     }
384 
385     /* Perform the migration */
386 
387     bIsIniMigrated = TRUE;
388     dwSize = ARRAYSIZE(dummy);
389     SetLastError(0);
390     GetPrivateProfileSectionW(L"Settings", dummy, dwSize, lpszIniFile);
391     if (GetLastError() == ERROR_FILE_NOT_FOUND)
392         goto MigrationDone;
393 
394     SetLastError(0);
395     GetPrivateProfileSectionW(L"Groups", dummy, dwSize, lpszIniFile);
396     if (GetLastError() == ERROR_FILE_NOT_FOUND)
397         goto MigrationDone;
398 
399     GetWindowsDirectoryW(szWinDir, ARRAYSIZE(szWinDir));
400     // NOTE: GCC complains we cannot use the "\u2022" (UNICODE Code Point) notation for specifying the bullet character,
401     // because it's only available in C++ or in C99. On the contrary MSVC is fine with it.
402     // Instead we use a hex specification for the character: "\x2022".
403     // Note also that the character "\x07" gives also a bullet, but a larger one.
404     PrintString(
405         L"The Program Manager has detected the presence of a legacy settings file PROGMAN.INI in the directory '%s' "
406         L"and is going to migrate its contents into the current-user Program Manager settings registry key:\n"
407         L"HKCU\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Program Manager"
408         L"\n\n"
409         L"\x2022 The migration operation will potentially overwrite all the existing current-user Program Manager settings in the registry by those stored in the PROGMAN.INI file.\n"
410         L"\n"
411         L"\x2022 The migration is done once, so that, at the next launch of the Program Manager, the new migrated settings are directly used.\n"
412         L"\n"
413         L"\x2022 It is possible to trigger later the migration by manually deleting the registry value \"IniMigrated\" under the current-user Program Manager settings registry key (specified above).\n"
414         L"\n"
415         L"Would you like to migrate its contents into the registry?",
416         szWinDir);
417 
418     for (dwSize = BUFFER_SIZE; ; dwSize += BUFFER_SIZE)
419     {
420         lpszSections = Alloc(0, dwSize * sizeof(WCHAR));
421         dwRet = GetPrivateProfileSectionNamesW(lpszSections, dwSize, lpszIniFile);
422         if (dwRet < dwSize - 2)
423             break;
424         Free(lpszSections);
425     }
426     lpszSection = lpszSections;
427     while (*lpszSection)
428     {
429         lRet = RegCreateKeyExW(Globals.hKeyProgMan,
430                                 lpszSection,
431                                 0,
432                                 NULL,
433                                 REG_OPTION_NON_VOLATILE,
434                                 KEY_WRITE,
435                                 NULL,
436                                 &hKey,
437                                 NULL);
438         if (lRet == ERROR_SUCCESS)
439         {
440             for (dwSize = BUFFER_SIZE; ; dwSize += BUFFER_SIZE)
441             {
442                 lpszData = Alloc(0, dwSize * sizeof(WCHAR));
443                 dwRet = GetPrivateProfileSectionW(lpszSection, lpszData, dwSize, lpszIniFile);
444                 if (dwRet < dwSize - 2)
445                     break;
446                 Free(lpszData);
447             }
448             lpszKeyValue = lpszData;
449             while (*lpszKeyValue)
450             {
451                 lpszKey = lpszKeyValue;
452                 lpszValue = wcschr(lpszKeyValue, L'=');
453                 lpszKeyValue += (wcslen(lpszKeyValue) + 1);
454                 if (lpszValue)
455                 {
456                     *lpszValue = '\0';
457                     ++lpszValue;
458                     Value = wcstoul(lpszValue, &lpszTmp, 0);
459                     if (lpszTmp - lpszValue >= wcslen(lpszValue))
460                     {
461                         lpszValue = (LPWSTR)&Value;
462                         dwSize = sizeof(Value);
463                         dwType = REG_DWORD;
464                     }
465                     else
466                     {
467                         dwSize = wcslen(lpszValue) * sizeof(WCHAR);
468                         dwType = REG_SZ;
469                     }
470                 }
471                 else
472                 {
473                     dwSize = 0;
474                     dwType = REG_DWORD;
475                 }
476                 lRet = RegSetValueExW(hKey, lpszKey, 0, dwType, (LPBYTE)lpszValue, dwSize);
477             }
478             Free(lpszData);
479             RegCloseKey(hKey);
480             lpszSection += (wcslen(lpszSection) + 1);
481         }
482     }
483     Free(lpszSections);
484 
485 MigrationDone:
486     Value = TRUE;
487     RegSetValueExW(Globals.hKeyProgMan, L"IniMigrated", 0, REG_DWORD, (LPBYTE)&Value, sizeof(Value));
488 
489 
490 LoadSettings:
491     /* Create the necessary registry keys for the Program Manager and load its settings from the registry */
492 
493     lRet = RegCreateKeyExW(Globals.hKeyProgMan,
494                            L"Settings",
495                            0,
496                            NULL,
497                            REG_OPTION_NON_VOLATILE,
498                            KEY_READ | KEY_WRITE,
499                            NULL,
500                            &Globals.hKeyPMSettings,
501                            NULL);
502 
503     lRet = RegCreateKeyExW(Globals.hKeyProgMan,
504                            L"Common Groups",
505                            0,
506                            NULL,
507                            REG_OPTION_NON_VOLATILE,
508                            KEY_READ | KEY_WRITE,
509                            NULL,
510                            &Globals.hKeyPMCommonGroups,
511                            NULL);
512 
513     lRet = RegCreateKeyExW(Globals.hKeyProgMan,
514                            L"Groups",
515                            0,
516                            NULL,
517                            REG_OPTION_NON_VOLATILE,
518                            KEY_READ | KEY_WRITE,
519                            NULL,
520                            &Globals.hKeyPMAnsiGroups,
521                            NULL);
522 
523     lRet = RegCreateKeyExW(Globals.hKeyProgMan,
524                            L"UNICODE Groups",
525                            0,
526                            NULL,
527                            REG_OPTION_NON_VOLATILE,
528                            KEY_READ | KEY_WRITE,
529                            NULL,
530                            &Globals.hKeyPMUnicodeGroups,
531                            NULL);
532 
533     lRet = RegCreateKeyExW(HKEY_CURRENT_USER,
534                            L"Program Groups",
535                            0,
536                            NULL,
537                            REG_OPTION_NON_VOLATILE,
538                            KEY_READ | KEY_WRITE,
539                            NULL,
540                            &Globals.hKeyAnsiGroups,
541                            NULL);
542 
543     lRet = RegCreateKeyExW(HKEY_CURRENT_USER,
544                            L"UNICODE Program Groups",
545                            0,
546                            NULL,
547                            REG_OPTION_NON_VOLATILE,
548                            KEY_READ | KEY_WRITE,
549                            NULL,
550                            &Globals.hKeyUnicodeGroups,
551                            NULL);
552 
553     lRet = RegCreateKeyExW(HKEY_LOCAL_MACHINE,
554                            L"SOFTWARE\\Program Groups",
555                            0,
556                            NULL,
557                            REG_OPTION_NON_VOLATILE,
558                            KEY_READ | KEY_WRITE,
559                            NULL,
560                            &Globals.hKeyCommonGroups,
561                            NULL);
562 
563     dwSize = sizeof(Globals.bAutoArrange);
564     RegQueryValueExW(Globals.hKeyPMSettings, L"AutoArrange", NULL, &dwType, (LPBYTE)&Globals.bAutoArrange, &dwSize);
565 
566     dwSize = sizeof(Globals.bMinOnRun);
567     RegQueryValueExW(Globals.hKeyPMSettings, L"MinOnRun", NULL, &dwType, (LPBYTE)&Globals.bMinOnRun, &dwSize);
568 
569     dwSize = sizeof(Globals.bSaveSettings);
570     RegQueryValueExW(Globals.hKeyPMSettings, L"SaveSettings", NULL, &dwType, (LPBYTE)&Globals.bSaveSettings, &dwSize);
571 
572     return TRUE;
573 }
574 
575 static
576 BOOL
577 MAIN_SaveSettings(VOID)
578 {
579     WINDOWPLACEMENT WndPl;
580     DWORD dwSize;
581     WCHAR buffer[100];
582 
583     WndPl.length = sizeof(WndPl);
584     GetWindowPlacement(Globals.hMainWnd, &WndPl);
585     StringCbPrintfW(buffer, sizeof(buffer),
586                     L"%d %d %d %d %d",
587                     WndPl.rcNormalPosition.left,
588                     WndPl.rcNormalPosition.top,
589                     WndPl.rcNormalPosition.right,
590                     WndPl.rcNormalPosition.bottom,
591                     WndPl.showCmd);
592 
593     dwSize = wcslen(buffer) * sizeof(WCHAR);
594     RegSetValueExW(Globals.hKeyPMSettings, L"Window", 0, REG_SZ, (LPBYTE)buffer, dwSize);
595 
596     return TRUE;
597 }
598 
599 
600 /***********************************************************************
601  *
602  *           WinMain
603  */
604 
605 INT WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, INT nCmdShow)
606 {
607     MSG msg;
608     INITCOMMONCONTROLSEX icex;
609 
610     /*
611      * Set our shutdown parameters: we want to shutdown the very last,
612      * but before any TaskMgr instance (which has a shutdown level of 1).
613      */
614     SetProcessShutdownParameters(2, 0);
615 
616     Globals.hInstance    = hInstance;
617     Globals.hGroups      = NULL;
618     Globals.hActiveGroup = NULL;
619 
620     /* Load Program Manager's settings */
621     MAIN_LoadSettings();
622 
623     /* Load the default icons */
624     Globals.hDefaultIcon       = LoadIconW(NULL, MAKEINTRESOURCEW(IDI_WINLOGO));
625     Globals.hMainIcon          = LoadIconW(Globals.hInstance, MAKEINTRESOURCEW(IDI_APPICON));
626     Globals.hPersonalGroupIcon = LoadIconW(Globals.hInstance, MAKEINTRESOURCEW(IDI_GROUP_PERSONAL_ICON));
627     Globals.hCommonGroupIcon   = LoadIconW(Globals.hInstance, MAKEINTRESOURCEW(IDI_GROUP_COMMON_ICON));
628 
629     /* Initialize the common controls */
630     icex.dwSize = sizeof(icex);
631     icex.dwICC  = ICC_HOTKEY_CLASS | ICC_LISTVIEW_CLASSES; // | ICC_STANDARD_CLASSES;
632     InitCommonControlsEx(&icex);
633 
634     /* Register the window classes */
635     if (!hPrevInstance) // FIXME: Unused on Win32!
636     {
637         if (!MAIN_RegisterMainWinClass())   goto Quit;
638         if (!GROUP_RegisterGroupWinClass()) goto Quit;
639     }
640 
641     /* Set up the strings, the main window, the accelerators, the menu, and the MDI child window */
642     STRING_LoadStrings();
643     MAIN_CreateMainWindow();
644     Globals.hAccel = LoadAcceleratorsW(Globals.hInstance, MAKEINTRESOURCEW(IDA_ACCEL));
645     STRING_LoadMenus();
646     MAIN_CreateMDIWindow();
647 
648     /* Load all the groups */
649     // MAIN_CreateGroups();
650     MAIN_LoadGroups();
651 
652     /* Load the Startup group: start the initial applications */
653     MAIN_AutoStart();
654 
655     /* Message loop */
656     while (GetMessageW(&msg, NULL, 0, 0))
657     {
658         if (!TranslateMDISysAccel(Globals.hMDIWnd, &msg) &&
659             !TranslateAcceleratorW(Globals.hMainWnd, Globals.hAccel, &msg))
660         {
661             TranslateMessage(&msg);
662             DispatchMessageW(&msg);
663         }
664     }
665 
666 Quit:
667 
668     /* Save the settings, close the registry keys and quit */
669 
670     // MAIN_SaveSettings();
671     RegCloseKey(Globals.hKeyCommonGroups);
672     RegCloseKey(Globals.hKeyUnicodeGroups);
673     RegCloseKey(Globals.hKeyAnsiGroups);
674     RegCloseKey(Globals.hKeyPMUnicodeGroups);
675     RegCloseKey(Globals.hKeyPMAnsiGroups);
676     RegCloseKey(Globals.hKeyPMCommonGroups);
677     RegCloseKey(Globals.hKeyPMSettings);
678     RegCloseKey(Globals.hKeyProgMan);
679 
680     return 0;
681 }
682 
683 /***********************************************************************
684  *
685  *           MAIN_CreateGroups
686  */
687 
688 #if 0
689 static VOID MAIN_CreateGroups(VOID)
690 {
691   CHAR buffer[BUFFER_SIZE];
692   CHAR szPath[MAX_PATHNAME_LEN];
693   CHAR key[20], *ptr;
694 
695   /* Initialize groups according the `Order' entry of `progman.ini' */
696   GetPrivateProfileStringA("Settings", "Order", "", buffer, sizeof(buffer), Globals.lpszIniFile);
697   ptr = buffer;
698   while (ptr < buffer + sizeof(buffer))
699     {
700       int num, skip, ret;
701       ret = sscanf(ptr, "%d%n", &num, &skip);
702       if (ret == 0)
703 	MAIN_MessageBoxIDS_s(IDS_FILE_READ_ERROR_s, Globals.lpszIniFile, IDS_ERROR, MB_OK);
704       if (ret != 1) break;
705 
706       sprintf(key, "Group%d", num);
707       GetPrivateProfileStringA("Groups", key, "", szPath,
708 			      sizeof(szPath), Globals.lpszIniFile);
709       if (!szPath[0]) continue;
710 
711       GRPFILE_ReadGroupFile(szPath);
712 
713       ptr += skip;
714     }
715   /* FIXME initialize other groups, not enumerated by `Order' */
716 }
717 #endif
718 
719 static VOID MAIN_LoadGroups(VOID)
720 {
721 }
722 
723 /***********************************************************************
724  *
725  *           MAIN_AutoStart
726  */
727 
728 static VOID MAIN_AutoStart(VOID)
729 {
730     LONG lRet;
731     DWORD dwSize;
732     DWORD dwType;
733 
734     PROGGROUP* hGroup;
735     PROGRAM* hProgram;
736 
737     WCHAR buffer[BUFFER_SIZE];
738 
739     dwSize = sizeof(buffer);
740     lRet = RegQueryValueExW(Globals.hKeyPMSettings, L"Startup", NULL, &dwType, (LPBYTE)buffer, &dwSize);
741     if (lRet != ERROR_SUCCESS || dwType != REG_SZ)
742         return;
743 
744     for (hGroup = Globals.hGroups; hGroup; hGroup = hGroup->hNext)
745     {
746         if (_wcsicmp(buffer, hGroup->hName) == 0)
747         {
748             for (hProgram = hGroup->hPrograms; hProgram; hProgram = hProgram->hNext)
749                 PROGRAM_ExecuteProgram(hProgram);
750         }
751     }
752 }
753 
754 /***********************************************************************
755  *
756  *           MAIN_MainWndProc
757  */
758 
759 static LRESULT CALLBACK MAIN_MainWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
760 {
761     switch (uMsg)
762     {
763         case WM_INITMENU:
764         {
765             PROGGROUP* hActiveGroup = GROUP_ActiveGroup();
766             if (hActiveGroup)
767             {
768                 if (PROGRAM_ActiveProgram(hActiveGroup))
769                 {
770                     EnableMenuItem(Globals.hFileMenu, PM_OPEN, MF_ENABLED);
771                     EnableMenuItem(Globals.hFileMenu, PM_MOVE, MF_ENABLED);
772                     EnableMenuItem(Globals.hFileMenu, PM_COPY, MF_ENABLED);
773                     EnableMenuItem(Globals.hFileMenu, PM_DELETE    , MF_ENABLED);
774                     EnableMenuItem(Globals.hFileMenu, PM_ATTRIBUTES, MF_ENABLED);
775                 }
776                 else
777                 {
778                     if (!hActiveGroup->hWnd || IsIconic(hActiveGroup->hWnd))
779                         EnableMenuItem(Globals.hFileMenu, PM_OPEN, MF_ENABLED);
780                     else
781                         EnableMenuItem(Globals.hFileMenu, PM_OPEN, MF_GRAYED);
782 
783                     EnableMenuItem(Globals.hFileMenu, PM_MOVE, MF_GRAYED);
784                     EnableMenuItem(Globals.hFileMenu, PM_COPY, MF_GRAYED);
785                     EnableMenuItem(Globals.hFileMenu, PM_DELETE    , MF_ENABLED);
786                     EnableMenuItem(Globals.hFileMenu, PM_ATTRIBUTES, MF_ENABLED);
787                 }
788             }
789             else
790             {
791                 EnableMenuItem(Globals.hFileMenu, PM_OPEN, MF_GRAYED);
792                 EnableMenuItem(Globals.hFileMenu, PM_MOVE, MF_GRAYED);
793                 EnableMenuItem(Globals.hFileMenu, PM_COPY, MF_GRAYED);
794                 EnableMenuItem(Globals.hFileMenu, PM_DELETE    , MF_GRAYED);
795                 EnableMenuItem(Globals.hFileMenu, PM_ATTRIBUTES, MF_GRAYED);
796             }
797 
798             CheckMenuItem(Globals.hOptionMenu, PM_AUTO_ARRANGE,
799                           MF_BYCOMMAND | (Globals.bAutoArrange  ? MF_CHECKED : MF_UNCHECKED));
800             CheckMenuItem(Globals.hOptionMenu, PM_MIN_ON_RUN,
801                           MF_BYCOMMAND | (Globals.bMinOnRun     ? MF_CHECKED : MF_UNCHECKED));
802             CheckMenuItem(Globals.hOptionMenu, PM_SAVE_SETTINGS,
803                           MF_BYCOMMAND | (Globals.bSaveSettings ? MF_CHECKED : MF_UNCHECKED));
804             break;
805         }
806 
807         case WM_DESTROY:
808             if (Globals.bSaveSettings)
809                 MAIN_SaveSettings();
810             PostQuitMessage(0);
811             break;
812 
813         case WM_COMMAND:
814             if (LOWORD(wParam) < PM_FIRST_CHILD)
815                 MAIN_MenuCommand(hWnd, LOWORD(wParam), lParam);
816             break;
817     }
818 
819     return DefFrameProcW(hWnd, Globals.hMDIWnd, uMsg, wParam, lParam);
820 }
821 
822 
823 /***********************************************************************
824  *
825  *           MAIN_MenuCommand
826  */
827 
828 static VOID MAIN_MenuCommand(HWND hWnd, WPARAM wParam, LPARAM lParam)
829 {
830 #if 0
831   HLOCAL hActiveGroup    = GROUP_ActiveGroup();
832   HLOCAL hActiveProgram  = PROGRAM_ActiveProgram(hActiveGroup);
833   HWND   hActiveGroupWnd = GROUP_GroupWnd(hActiveGroup);
834 
835   switch(wParam)
836     {
837       /* Menu File */
838     case PM_NEW:
839       switch (DIALOG_New((hActiveGroupWnd && !IsIconic(hActiveGroupWnd)) ?
840 			 PM_NEW_PROGRAM : PM_NEW_GROUP))
841       {
842       case PM_NEW_PROGRAM:
843 	if (hActiveGroup) PROGRAM_NewProgram(hActiveGroup);
844 	break;
845 
846       case PM_NEW_GROUP:
847 	GROUP_NewGroup();
848 	break;
849       }
850       break;
851 
852 
853     case PM_DELETE:
854       if (hActiveProgram)
855 	{
856 	if (DIALOG_Delete(IDS_DELETE_PROGRAM_s, PROGRAM_ProgramName(hActiveProgram)))
857 	  PROGRAM_DeleteProgram(hActiveProgram, TRUE);
858 	}
859       else if (hActiveGroup)
860 	{
861 	if (DIALOG_Delete(IDS_DELETE_GROUP_s, GROUP_GroupName(hActiveGroup)))
862 	  GROUP_DeleteGroup(hActiveGroup);
863 	}
864       break;
865 
866 
867 
868     case PM_SAVE_SETTINGS:
869       Globals.bSaveSettings = !Globals.bSaveSettings;
870       CheckMenuItem(Globals.hOptionMenu, PM_SAVE_SETTINGS,
871 		    MF_BYCOMMAND | (Globals.bSaveSettings ?
872 				    MF_CHECKED : MF_UNCHECKED));
873       WritePrivateProfileStringA("Settings", "SaveSettings",
874 				Globals.bSaveSettings ? "1" : "0",
875 				Globals.lpszIniFile);
876       WritePrivateProfileStringA(NULL,NULL,NULL,Globals.lpszIniFile); /* flush it */
877       break;
878 
879 
880     case PM_ARRANGE:
881 
882       if (hActiveGroupWnd && !IsIconic(hActiveGroupWnd))
883 	ArrangeIconicWindows(hActiveGroupWnd);
884       else
885 	SendMessageW(Globals.hMDIWnd, WM_MDIICONARRANGE, 0, 0);
886       break;
887 
888     }
889 
890 
891 
892 
893 #endif
894 
895     DWORD Value;
896 
897     PROGGROUP* hActiveGroup;
898     PROGRAM* hActiveProgram;
899     HWND hActiveGroupWnd;
900 
901     hActiveGroup = GROUP_ActiveGroup();
902     hActiveProgram = PROGRAM_ActiveProgram(hActiveGroup);
903     hActiveGroupWnd = (hActiveGroup ? hActiveGroup->hWnd : NULL);
904 
905     switch (wParam)
906     {
907         /* Menu File */
908 
909         case PM_NEW:
910         {
911             BOOL Success;
912             INT nResult;
913 
914             if (!hActiveGroupWnd || IsIconic(hActiveGroupWnd))
915                 Success = DIALOG_New(PM_NEW_GROUP, &nResult);
916             else
917                 Success = DIALOG_New(PM_NEW_PROGRAM, &nResult);
918             if (!Success)
919                 break;
920 
921             if (nResult & 1)
922             {
923                 GROUPFORMAT format;
924                 BOOL bIsCommonGroup;
925 
926                 format = (nResult & 0xC) >> 2;
927                 bIsCommonGroup = (nResult & 2) != 0;
928                 GROUP_NewGroup(format, bIsCommonGroup);
929             }
930             else if (hActiveGroup)
931             {
932                 PROGRAM_NewProgram(hActiveGroup);
933             }
934 
935             break;
936         }
937 
938         case PM_OPEN:
939             if (hActiveProgram)
940                 PROGRAM_ExecuteProgram(hActiveProgram);
941             else if (hActiveGroupWnd)
942                 OpenIcon(hActiveGroupWnd);
943             break;
944 
945         case PM_MOVE:
946         case PM_COPY:
947             if (hActiveProgram)
948                 PROGRAM_CopyMoveProgram(hActiveProgram, wParam == PM_MOVE);
949             break;
950 
951         case PM_DELETE:
952         {
953             if (hActiveProgram)
954             {
955                 if (DIALOG_Delete(IDS_DELETE_PROGRAM_s, hActiveProgram->hName))
956                     PROGRAM_DeleteProgram(hActiveProgram, TRUE);
957             }
958             else if (hActiveGroup && DIALOG_Delete(IDS_DELETE_GROUP_s, hActiveGroup->hName))
959             {
960                 GROUP_DeleteGroup(hActiveGroup);
961             }
962             break;
963         }
964 
965         case PM_ATTRIBUTES:
966             if (hActiveProgram)
967                 PROGRAM_ModifyProgram(hActiveProgram);
968             else if (hActiveGroup)
969                 GROUP_ModifyGroup(hActiveGroup);
970             break;
971 
972         case PM_EXECUTE:
973             DIALOG_Execute();
974             break;
975 
976         case PM_EXIT:
977             // MAIN_SaveSettings();
978             PostQuitMessage(0);
979             break;
980 
981 
982         /* Menu Options */
983 
984         case PM_AUTO_ARRANGE:
985             Globals.bAutoArrange = !Globals.bAutoArrange;
986             CheckMenuItem(Globals.hOptionMenu, PM_AUTO_ARRANGE,
987                           MF_BYCOMMAND | (Globals.bAutoArrange ? MF_CHECKED : MF_UNCHECKED));
988             Value = Globals.bAutoArrange;
989             RegSetValueExW(Globals.hKeyPMSettings, L"AutoArrange", 0, REG_DWORD, (LPBYTE)&Value, sizeof(Value));
990             break;
991 
992         case PM_MIN_ON_RUN:
993             Globals.bMinOnRun = !Globals.bMinOnRun;
994             CheckMenuItem(Globals.hOptionMenu, PM_MIN_ON_RUN,
995                           MF_BYCOMMAND | (Globals.bMinOnRun ? MF_CHECKED : MF_UNCHECKED));
996             Value = Globals.bMinOnRun;
997             RegSetValueExW(Globals.hKeyPMSettings, L"MinOnRun", 0, REG_DWORD, (LPBYTE)&Value, sizeof(Value));
998             break;
999 
1000         case PM_SAVE_SETTINGS:
1001             Globals.bSaveSettings = !Globals.bSaveSettings;
1002             CheckMenuItem(Globals.hOptionMenu, PM_SAVE_SETTINGS,
1003                           MF_BYCOMMAND | (Globals.bSaveSettings ? MF_CHECKED : MF_UNCHECKED));
1004             Value = Globals.bSaveSettings;
1005             RegSetValueExW(Globals.hKeyPMSettings, L"SaveSettings", 0, REG_DWORD, (LPBYTE)&Value, sizeof(Value));
1006             break;
1007 
1008         case PM_SAVE_SETTINGS_NOW:
1009             MAIN_SaveSettings();
1010             break;
1011 
1012 
1013         /* Menu Windows */
1014 
1015         case PM_OVERLAP:
1016             SendMessageW(Globals.hMDIWnd, WM_MDICASCADE, 0, 0);
1017             break;
1018 
1019         case PM_SIDE_BY_SIDE:
1020             SendMessageW(Globals.hMDIWnd, WM_MDITILE, MDITILE_VERTICAL, 0);
1021             break;
1022 
1023         case PM_ARRANGE:
1024             if (!hActiveGroupWnd || IsIconic(hActiveGroupWnd))
1025                 SendMessageW(Globals.hMDIWnd, WM_MDIICONARRANGE, 0, 0);
1026             else
1027                 SendMessageA(hActiveGroup->hListView, LVM_ARRANGE, 0, 0);
1028             break;
1029 
1030 
1031         /* Menu Help */
1032 
1033         case PM_CONTENTS:
1034             if (!WinHelpW(Globals.hMainWnd, L"progman.hlp", HELP_CONTENTS, 0))
1035                 MAIN_MessageBoxIDS(IDS_WINHELP_ERROR, IDS_ERROR, MB_OK);
1036             break;
1037 
1038         case PM_ABOUT:
1039             ShellAboutW(hWnd, szTitle, NULL, Globals.hMainIcon);
1040             break;
1041 
1042         default:
1043             MAIN_MessageBoxIDS(IDS_NOT_IMPLEMENTED, IDS_ERROR, MB_OK);
1044             break;
1045     }
1046 
1047 }
1048 
1049 /***********************************************************************
1050  *
1051  *           MAIN_RegisterMainWinClass
1052  */
1053 
1054 static ATOM MAIN_RegisterMainWinClass(VOID)
1055 {
1056     WNDCLASSW wndClass;
1057 
1058     wndClass.style         = CS_HREDRAW | CS_VREDRAW;
1059     wndClass.lpfnWndProc   = MAIN_MainWndProc;
1060     wndClass.cbClsExtra    = 0;
1061     wndClass.cbWndExtra    = 0;
1062     wndClass.hInstance     = Globals.hInstance;
1063     wndClass.hIcon         = Globals.hMainIcon;
1064     wndClass.hCursor       = LoadCursorW(NULL, MAKEINTRESOURCEW(IDC_ARROW));
1065     wndClass.hbrBackground = (HBRUSH)GetStockObject(NULL_BRUSH);
1066     wndClass.lpszMenuName  = NULL;
1067     wndClass.lpszClassName = STRING_MAIN_WIN_CLASS_NAME;
1068 
1069     return RegisterClassW(&wndClass);
1070 }
1071 
1072 /***********************************************************************
1073  *
1074  *           MAIN_CreateMainWindow
1075  */
1076 
1077 static VOID MAIN_CreateMainWindow(VOID)
1078 {
1079     INT left, top, right, bottom;
1080     INT width, height;
1081     INT nCmdShow;
1082     WCHAR buffer[100];
1083 
1084     LONG lRet;
1085     DWORD dwSize;
1086     DWORD dwType;
1087 
1088     Globals.hMDIWnd   = NULL;
1089     Globals.hMainMenu = NULL;
1090 
1091     /* Get the geometry of the main window */
1092     dwSize = sizeof(buffer);
1093     lRet = RegQueryValueExW(Globals.hKeyPMSettings, L"Window", NULL, &dwType, (LPBYTE)buffer, &dwSize);
1094     if (lRet != ERROR_SUCCESS || dwType != REG_SZ)
1095         buffer[0] = '\0';
1096 
1097     if (swscanf(buffer, L"%d %d %d %d %d", &left, &top, &right, &bottom, &nCmdShow) == 5)
1098     {
1099         width  = right  - left;
1100         height = bottom - top;
1101     }
1102     else
1103     {
1104         left = top = width = height = CW_USEDEFAULT;
1105         nCmdShow = SW_SHOWNORMAL;
1106     }
1107 
1108     /* Create the main window */
1109     Globals.hMainWnd =
1110         CreateWindowW(STRING_MAIN_WIN_CLASS_NAME,
1111                       szTitle,
1112                       WS_OVERLAPPEDWINDOW, // /* | WS_CLIPSIBLINGS | WS_CLIPCHILDREN */
1113                       left, top, width, height,
1114                       NULL, NULL,
1115                       Globals.hInstance,
1116                       NULL);
1117 
1118     MAIN_SetMainWindowTitle();
1119     ShowWindow(Globals.hMainWnd, nCmdShow);
1120     UpdateWindow(Globals.hMainWnd);
1121 }
1122 
1123 /***********************************************************************
1124  *
1125  *           MAIN_CreateMDIWindow
1126  */
1127 
1128 static VOID MAIN_CreateMDIWindow(VOID)
1129 {
1130     CLIENTCREATESTRUCT ccs;
1131     RECT rect;
1132 
1133     /* Get the geometry of the MDI window */
1134     GetClientRect(Globals.hMainWnd, &rect);
1135 
1136     ccs.hWindowMenu  = Globals.hWindowsMenu;
1137     ccs.idFirstChild = PM_FIRST_CHILD;
1138 
1139     /* Create MDI Window */
1140     Globals.hMDIWnd =
1141         CreateWindowW(WC_MDICLIENT, NULL, WS_CHILD | WS_CLIPCHILDREN | WS_VSCROLL | WS_HSCROLL, // WS_CHILDWINDOW | ...
1142                       rect.left, rect.top,
1143                       rect.right - rect.left, rect.bottom - rect.top,
1144                       Globals.hMainWnd, 0,
1145                       Globals.hInstance, &ccs);
1146 
1147     /* Reset the background of the MDI client window (default: COLOR_APPWORKSPACE + 1) */
1148     SetClassLongPtrW(Globals.hMDIWnd, GCLP_HBRBACKGROUND, (COLOR_WINDOW + 1));
1149 
1150     ShowWindow(Globals.hMDIWnd, SW_SHOW);
1151     UpdateWindow(Globals.hMDIWnd);
1152 }
1153 
1154 /**********************************************************************/
1155 /***********************************************************************
1156  *
1157  *           MAIN_MessageBoxIDS
1158  */
1159 INT MAIN_MessageBoxIDS(UINT ids_text, UINT ids_title, WORD type)
1160 {
1161     WCHAR text[MAX_STRING_LEN];
1162     WCHAR title[MAX_STRING_LEN];
1163 
1164     LoadStringW(Globals.hInstance, ids_text , text , ARRAYSIZE(text));
1165     LoadStringW(Globals.hInstance, ids_title, title, ARRAYSIZE(title));
1166 
1167     return MessageBoxW(Globals.hMainWnd, text, title, type);
1168 }
1169 
1170 /***********************************************************************
1171  *
1172  *           MAIN_MessageBoxIDS_s
1173  */
1174 INT MAIN_MessageBoxIDS_s(UINT ids_text, LPCWSTR str, UINT ids_title, WORD type)
1175 {
1176     WCHAR text[MAX_STRING_LEN];
1177     WCHAR title[MAX_STRING_LEN];
1178     WCHAR newtext[MAX_STRING_LEN + MAX_PATHNAME_LEN];
1179 
1180     LoadStringW(Globals.hInstance, ids_text , text , ARRAYSIZE(text));
1181     LoadStringW(Globals.hInstance, ids_title, title, ARRAYSIZE(title));
1182     wsprintfW(newtext, text, str);
1183 
1184     return MessageBoxW(Globals.hMainWnd, newtext, title, type);
1185 }
1186 
1187 /***********************************************************************
1188  *
1189  *           MAIN_ReplaceString
1190  */
1191 
1192 VOID MAIN_ReplaceString(LPWSTR* string, LPWSTR replace)
1193 {
1194     LPWSTR newstring;
1195 
1196     newstring = Alloc(HEAP_ZERO_MEMORY, (wcslen(replace) + 1) * sizeof(WCHAR));
1197     if (newstring)
1198     {
1199         wcscpy(newstring, replace);
1200         *string = newstring;
1201     }
1202     else
1203     {
1204         MAIN_MessageBoxIDS(IDS_OUT_OF_MEMORY, IDS_ERROR, MB_OK);
1205     }
1206 }
1207