1 /*
2 * ReactOS applications
3 * Copyright (C) 2001, 2002, 2003 ReactOS Team
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19 /*
20 * COPYRIGHT: See COPYING in the top level directory
21 * PROJECT: ReactOS "Welcome"/AutoRun application
22 * FILE: base/setup/welcome/welcome.c
23 * PROGRAMMERS: Eric Kohl
24 * Casper S. Hornstrup (chorns@users.sourceforge.net)
25 * Hermes Belusca-Maito
26 *
27 * NOTE:
28 * This utility can be customized by using localized INI configuration files.
29 * The default strings are stored in the utility's resources.
30 */
31
32 #include <stdarg.h>
33 #include <tchar.h>
34
35 #include <windef.h>
36 #include <winbase.h>
37 #include <wingdi.h>
38 #include <winnls.h>
39 #include <winuser.h>
40 #include <shellapi.h>
41 #include <strsafe.h>
42
43 #include <reactos/buildno.h>
44
45 #include "resource.h"
46
47 #define LIGHT_BLUE RGB(214, 239, 247)
48 #define DARK_BLUE RGB(107, 123, 140)
49
50 #define TITLE_WIDTH 480
51 #define TITLE_HEIGHT 93
52
53 /* GLOBALS ******************************************************************/
54
55 TCHAR szWindowClass[] = TEXT("WelcomeWindowClass");
56 TCHAR szAppTitle[80];
57
58 HINSTANCE hInstance;
59
60 HWND hWndMain = NULL;
61
62 HWND hWndCheckButton = NULL;
63 HWND hWndCloseButton = NULL;
64
65 BOOL bDisplayCheckBox = FALSE;
66 BOOL bDisplayExitBtn = TRUE;
67
68 #define BUFFER_SIZE 1024
69
70 #define TOPIC_TITLE_LENGTH 80
71 #define TOPIC_DESC_LENGTH 1024
72
73 typedef struct _TOPIC
74 {
75 HBITMAP hBitmap;
76 HWND hWndButton;
77
78 /*
79 * TRUE : szCommand contains a command (e.g. executable to run);
80 * FALSE: szCommand contains a custom "Welcome"/AutoRun action.
81 */
82 BOOL bIsCommand;
83
84 TCHAR szText[80];
85 TCHAR szTitle[TOPIC_TITLE_LENGTH];
86 TCHAR szDesc[TOPIC_DESC_LENGTH];
87 TCHAR szCommand[512];
88 TCHAR szArgs[512];
89 } TOPIC, *PTOPIC;
90
91 DWORD dwNumberTopics = 0;
92 PTOPIC* pTopics = NULL;
93
94 WNDPROC fnOldBtn;
95
96 TCHAR szDefaultTitle[TOPIC_TITLE_LENGTH];
97 TCHAR szDefaultDesc[TOPIC_DESC_LENGTH];
98
99 #define TOPIC_BTN_ID_BASE 100
100
101 INT nTopic = -1; // Active (focused) topic
102 INT nDefaultTopic = -1; // Default selected topic
103
104 HDC hdcMem = NULL;
105 HBITMAP hTitleBitmap = NULL;
106 HBITMAP hDefaultTopicBitmap = NULL;
107
108 HFONT hFontTopicButton;
109 HFONT hFontTopicTitle;
110 HFONT hFontTopicDescription;
111 HFONT hFontCheckButton;
112
113 HBRUSH hbrLightBlue;
114 HBRUSH hbrDarkBlue;
115
116 RECT rcTitlePanel;
117 RECT rcLeftPanel;
118 RECT rcRightPanel;
119
120
121 INT_PTR CALLBACK
122 MainWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
123
124
125 /* FUNCTIONS ****************************************************************/
126
GetLocaleName(IN LCID Locale,OUT LPTSTR lpLCData,IN SIZE_T cchData)127 INT GetLocaleName(IN LCID Locale, OUT LPTSTR lpLCData, IN SIZE_T cchData)
128 {
129 INT cchRet, cchRet2;
130
131 /* Try to retrieve the locale language name (LOCALE_SNAME is supported on Vista+) */
132 cchRet = GetLocaleInfo(Locale, LOCALE_SNAME, lpLCData, cchData);
133 if (cchRet || (GetLastError() != ERROR_INVALID_FLAGS))
134 return cchRet;
135
136 /*
137 * We failed because LOCALE_SNAME was unrecognized, so try to manually build
138 * a language name in the form xx-YY (WARNING: this method has its limitations).
139 */
140 cchRet = GetLocaleInfo(Locale, LOCALE_SISO639LANGNAME, lpLCData, cchData);
141 if (cchRet <= 1)
142 return cchRet;
143
144 lpLCData += (cchRet - 1);
145 cchData -= (cchRet - 1);
146 if (cchData <= 1)
147 return cchRet;
148
149 /* Try to get the second part; we add the '-' separator only if we succeed */
150 cchRet2 = GetLocaleInfo(Locale, LOCALE_SISO3166CTRYNAME, lpLCData + 1, cchData - 1);
151 if (cchRet2 <= 1)
152 return cchRet;
153 cchRet += cchRet2; // 'cchRet' already counts '-'.
154
155 *lpLCData = _T('-');
156
157 return cchRet;
158 }
159
TranslateEscapes(IN OUT LPTSTR lpString)160 VOID TranslateEscapes(IN OUT LPTSTR lpString)
161 {
162 LPTSTR pEscape = NULL; // Next backslash escape sequence.
163
164 while (lpString && *lpString)
165 {
166 /* Find the next backslash escape sequence */
167 pEscape = _tcschr(lpString, _T('\\'));
168 if (!pEscape)
169 break;
170
171 /* Go past the escape backslash */
172 lpString = pEscape + 1;
173
174 /* Find which sequence it is */
175 switch (*lpString)
176 {
177 case _T('\0'):
178 // *pEscape = _T('\0'); // Enable if one wants to convert \<NULL> into <NULL>.
179 // lpString = pEscape + 1; // Loop will stop at the next iteration.
180 break;
181
182 /* New-line and carriage return */
183 case _T('n'): case _T('r'):
184 // case _T('\\'): // others?
185 // So far we only need to deal with the newlines.
186 {
187 if (*lpString == _T('n'))
188 *pEscape = _T('\n');
189 else if (*lpString == _T('r'))
190 *pEscape = _T('\r');
191
192 memmove(lpString, lpString + 1, (_tcslen(lpString + 1) + 1) * sizeof(TCHAR));
193 break;
194 }
195
196 /* \xhhhh hexadecimal character specification */
197 case _T('x'):
198 {
199 LPTSTR lpStringNew;
200 *pEscape = (WCHAR)_tcstoul(lpString + 1, &lpStringNew, 16);
201 memmove(lpString, lpStringNew, (_tcslen(lpStringNew) + 1) * sizeof(TCHAR));
202 break;
203 }
204
205 /* Unknown escape sequence, ignore it */
206 default:
207 lpString++;
208 break;
209 }
210 }
211 }
212
213 /*
214 * Expands the path for the ReactOS Installer "reactos.exe".
215 * See also base/system/userinit/userinit.c!StartInstaller()
216 */
217 BOOL
ExpandInstallerPath(IN LPCTSTR lpInstallerName,OUT LPTSTR lpInstallerPath,IN SIZE_T PathSize)218 ExpandInstallerPath(
219 IN LPCTSTR lpInstallerName,
220 OUT LPTSTR lpInstallerPath,
221 IN SIZE_T PathSize)
222 {
223 SYSTEM_INFO SystemInfo;
224 SIZE_T cchInstallerNameLen;
225 PTSTR ptr;
226 DWORD dwAttribs;
227
228 cchInstallerNameLen = _tcslen(lpInstallerName);
229 if (PathSize < cchInstallerNameLen)
230 {
231 /* The buffer is not large enough to contain the installer file name */
232 *lpInstallerPath = 0;
233 return FALSE;
234 }
235
236 /*
237 * First, try to find the installer using the default drive, under
238 * the directory whose name corresponds to the currently-running
239 * CPU architecture.
240 */
241 GetSystemInfo(&SystemInfo);
242
243 *lpInstallerPath = 0;
244 GetModuleFileName(NULL, lpInstallerPath, PathSize - cchInstallerNameLen - 1);
245 ptr = _tcschr(lpInstallerPath, _T('\\'));
246 if (ptr)
247 *++ptr = 0;
248 else
249 *lpInstallerPath = 0;
250
251 /* Append the corresponding CPU architecture */
252 switch (SystemInfo.wProcessorArchitecture)
253 {
254 case PROCESSOR_ARCHITECTURE_INTEL:
255 StringCchCat(lpInstallerPath, PathSize, TEXT("I386"));
256 break;
257
258 case PROCESSOR_ARCHITECTURE_MIPS:
259 StringCchCat(lpInstallerPath, PathSize, TEXT("MIPS"));
260 break;
261
262 case PROCESSOR_ARCHITECTURE_ALPHA:
263 StringCchCat(lpInstallerPath, PathSize, TEXT("ALPHA"));
264 break;
265
266 case PROCESSOR_ARCHITECTURE_PPC:
267 StringCchCat(lpInstallerPath, PathSize, TEXT("PPC"));
268 break;
269
270 case PROCESSOR_ARCHITECTURE_SHX:
271 StringCchCat(lpInstallerPath, PathSize, TEXT("SHX"));
272 break;
273
274 case PROCESSOR_ARCHITECTURE_ARM:
275 StringCchCat(lpInstallerPath, PathSize, TEXT("ARM"));
276 break;
277
278 case PROCESSOR_ARCHITECTURE_IA64:
279 StringCchCat(lpInstallerPath, PathSize, TEXT("IA64"));
280 break;
281
282 case PROCESSOR_ARCHITECTURE_ALPHA64:
283 StringCchCat(lpInstallerPath, PathSize, TEXT("ALPHA64"));
284 break;
285
286 case PROCESSOR_ARCHITECTURE_AMD64:
287 StringCchCat(lpInstallerPath, PathSize, TEXT("AMD64"));
288 break;
289
290 // case PROCESSOR_ARCHITECTURE_MSIL: /* .NET CPU-independent code */
291 case PROCESSOR_ARCHITECTURE_UNKNOWN:
292 default:
293 SystemInfo.wProcessorArchitecture = PROCESSOR_ARCHITECTURE_UNKNOWN;
294 break;
295 }
296
297 if (SystemInfo.wProcessorArchitecture != PROCESSOR_ARCHITECTURE_UNKNOWN)
298 StringCchCat(lpInstallerPath, PathSize, TEXT("\\"));
299 StringCchCat(lpInstallerPath, PathSize, lpInstallerName);
300
301 dwAttribs = GetFileAttributes(lpInstallerPath);
302 if ((dwAttribs != INVALID_FILE_ATTRIBUTES) &&
303 !(dwAttribs & FILE_ATTRIBUTE_DIRECTORY))
304 {
305 /* We have found the installer */
306 return TRUE;
307 }
308
309 /*
310 * We failed. Try to find the installer from either the current
311 * ReactOS installation directory, or from our current directory.
312 */
313 *lpInstallerPath = 0;
314 if (GetWindowsDirectory(lpInstallerPath, PathSize - cchInstallerNameLen - 1))
315 StringCchCat(lpInstallerPath, PathSize, TEXT("\\"));
316 StringCchCat(lpInstallerPath, PathSize, lpInstallerName);
317
318 dwAttribs = GetFileAttributes(lpInstallerPath);
319 if ((dwAttribs != INVALID_FILE_ATTRIBUTES) &&
320 !(dwAttribs & FILE_ATTRIBUTE_DIRECTORY))
321 {
322 /* We have found the installer */
323 return TRUE;
324 }
325
326 /* Installer not found */
327 *lpInstallerPath = 0;
328 return FALSE;
329 }
330
InitializeTopicList(VOID)331 VOID InitializeTopicList(VOID)
332 {
333 dwNumberTopics = 0;
334 pTopics = NULL;
335 }
336
AddNewTopic(VOID)337 PTOPIC AddNewTopic(VOID)
338 {
339 PTOPIC pTopic, *pTopicsTmp;
340
341 /* Allocate (or reallocate) the list of topics */
342 if (!pTopics)
343 pTopicsTmp = HeapAlloc(GetProcessHeap(), 0, (dwNumberTopics + 1) * sizeof(*pTopics));
344 else
345 pTopicsTmp = HeapReAlloc(GetProcessHeap(), 0, pTopics, (dwNumberTopics + 1) * sizeof(*pTopics));
346 if (!pTopicsTmp)
347 return NULL; // Cannot reallocate more
348 pTopics = pTopicsTmp;
349
350 /* Allocate a new topic entry */
351 pTopic = HeapAlloc(GetProcessHeap(), 0, sizeof(*pTopic));
352 if (!pTopic)
353 return NULL; // Cannot reallocate more
354 pTopics[dwNumberTopics++] = pTopic;
355
356 /* Return the allocated topic entry */
357 return pTopic;
358 }
359
360 PTOPIC
AddNewTopicEx(IN LPTSTR szText OPTIONAL,IN LPTSTR szTitle OPTIONAL,IN LPTSTR szDesc OPTIONAL,IN LPTSTR szCommand OPTIONAL,IN LPTSTR szArgs OPTIONAL,IN LPTSTR szAction OPTIONAL)361 AddNewTopicEx(
362 IN LPTSTR szText OPTIONAL,
363 IN LPTSTR szTitle OPTIONAL,
364 IN LPTSTR szDesc OPTIONAL,
365 IN LPTSTR szCommand OPTIONAL,
366 IN LPTSTR szArgs OPTIONAL,
367 IN LPTSTR szAction OPTIONAL)
368 {
369 PTOPIC pTopic = AddNewTopic();
370 if (!pTopic)
371 return NULL;
372
373 if (szText && *szText)
374 StringCchCopy(pTopic->szText, ARRAYSIZE(pTopic->szText), szText);
375 else
376 *pTopic->szText = 0;
377
378 if (szTitle && *szTitle)
379 StringCchCopy(pTopic->szTitle, ARRAYSIZE(pTopic->szTitle), szTitle);
380 else
381 *pTopic->szTitle = 0;
382
383 if (szDesc && *szDesc)
384 {
385 StringCchCopy(pTopic->szDesc, ARRAYSIZE(pTopic->szDesc), szDesc);
386 TranslateEscapes(pTopic->szDesc);
387 }
388 else
389 {
390 *pTopic->szDesc = 0;
391 }
392
393 if (szCommand && *szCommand)
394 {
395 pTopic->bIsCommand = TRUE;
396
397 /* Check for special applications: ReactOS Installer */
398 if (_tcsicmp(szCommand, TEXT("reactos.exe")) == 0)
399 {
400 ExpandInstallerPath(szCommand, pTopic->szCommand, ARRAYSIZE(pTopic->szCommand));
401 }
402 else
403 {
404 /* Expand any environment string in the command line */
405 DWORD dwSize = ExpandEnvironmentStringsW(szCommand, NULL, 0);
406 if (dwSize <= ARRAYSIZE(pTopic->szCommand))
407 ExpandEnvironmentStringsW(szCommand, pTopic->szCommand, ARRAYSIZE(pTopic->szCommand));
408 else
409 StringCchCopy(pTopic->szCommand, ARRAYSIZE(pTopic->szCommand), szCommand);
410 }
411 }
412 else
413 {
414 pTopic->bIsCommand = FALSE;
415 *pTopic->szCommand = 0;
416 }
417
418 /* Only care about command arguments if we actually have a command */
419 if (*pTopic->szCommand)
420 {
421 if (szArgs && *szArgs)
422 {
423 StringCchCopy(pTopic->szArgs, ARRAYSIZE(pTopic->szArgs), szArgs);
424 }
425 else
426 {
427 /* Check for special applications: ReactOS Shell */
428 if (/* pTopic->szCommand && */ *pTopic->szCommand &&
429 _tcsicmp(pTopic->szCommand, TEXT("explorer.exe")) == 0)
430 {
431 #if 0
432 TCHAR CurrentDir[MAX_PATH];
433 GetCurrentDirectory(ARRAYSIZE(CurrentDir), CurrentDir);
434 #endif
435 StringCchCopy(pTopic->szArgs, ARRAYSIZE(pTopic->szArgs), TEXT("\\"));
436 }
437 else
438 {
439 *pTopic->szArgs = 0;
440 }
441 }
442 }
443 else
444 {
445 *pTopic->szArgs = 0;
446 }
447
448 /* Only care about custom actions if we actually don't have a command */
449 if (!*pTopic->szCommand && szAction && *szAction)
450 {
451 /*
452 * Re-use the pTopic->szCommand member. We distinguish with respect to
453 * a regular command by using the pTopic->bIsCommand flag.
454 */
455 pTopic->bIsCommand = FALSE;
456 StringCchCopy(pTopic->szCommand, ARRAYSIZE(pTopic->szCommand), szAction);
457 TranslateEscapes(pTopic->szCommand);
458 }
459
460 return pTopic;
461 }
462
463 static VOID
LoadLocalizedResourcesInternal(VOID)464 LoadLocalizedResourcesInternal(VOID)
465 {
466 #define MAX_NUMBER_INTERNAL_TOPICS 3
467
468 UINT i;
469 LPTSTR lpszCommand, lpszAction;
470 TOPIC newTopic, *pTopic;
471
472 for (i = 0; i < MAX_NUMBER_INTERNAL_TOPICS; ++i)
473 {
474 lpszCommand = NULL, lpszAction = NULL;
475
476 /* Retrieve the information */
477 if (!LoadString(hInstance, IDS_TOPIC_BUTTON0 + i, newTopic.szText, ARRAYSIZE(newTopic.szText)))
478 *newTopic.szText = 0;
479 if (!LoadString(hInstance, IDS_TOPIC_TITLE0 + i, newTopic.szTitle, ARRAYSIZE(newTopic.szTitle)))
480 *newTopic.szTitle = 0;
481 if (!LoadString(hInstance, IDS_TOPIC_DESC0 + i, newTopic.szDesc, ARRAYSIZE(newTopic.szDesc)))
482 *newTopic.szDesc = 0;
483
484 if (!LoadString(hInstance, IDS_TOPIC_COMMAND0 + i, newTopic.szCommand, ARRAYSIZE(newTopic.szCommand)))
485 *newTopic.szCommand = 0;
486
487 /* Only care about command arguments if we actually have a command */
488 if (*newTopic.szCommand)
489 {
490 lpszCommand = newTopic.szCommand;
491 if (!LoadString(hInstance, IDS_TOPIC_CMD_ARGS0 + i, newTopic.szArgs, ARRAYSIZE(newTopic.szArgs)))
492 *newTopic.szArgs = 0;
493 }
494 /* Only care about custom actions if we actually don't have a command */
495 else // if (!*newTopic.szCommand)
496 {
497 lpszAction = newTopic.szCommand;
498 if (!LoadString(hInstance, IDS_TOPIC_ACTION0 + i, newTopic.szCommand, ARRAYSIZE(newTopic.szCommand)))
499 *newTopic.szCommand = 0;
500 }
501
502 /* Allocate a new topic */
503 pTopic = AddNewTopicEx(newTopic.szText,
504 newTopic.szTitle,
505 newTopic.szDesc,
506 lpszCommand,
507 newTopic.szArgs,
508 lpszAction);
509 if (!pTopic)
510 break; // Cannot reallocate more
511 }
512 }
513
514 static BOOL
LoadLocalizedResourcesFromINI(LCID Locale,LPTSTR lpResPath)515 LoadLocalizedResourcesFromINI(LCID Locale, LPTSTR lpResPath)
516 {
517 DWORD dwRet;
518 DWORD dwAttribs;
519 DWORD dwSize;
520 TCHAR szBuffer[LOCALE_NAME_MAX_LENGTH];
521 TCHAR szIniPath[MAX_PATH];
522 LPTSTR lpszSections = NULL, lpszSection = NULL;
523 LPTSTR lpszCommand, lpszAction;
524 TOPIC newTopic, *pTopic;
525
526 /* Retrieve the locale name (on which the INI file name is based) */
527 dwRet = (DWORD)GetLocaleName(Locale, szBuffer, ARRAYSIZE(szBuffer));
528 if (!dwRet)
529 {
530 /* Fall back to english (US) */
531 StringCchCopy(szBuffer, ARRAYSIZE(szBuffer), TEXT("en-US"));
532 }
533
534 /* Build the INI file name */
535 StringCchPrintf(szIniPath, ARRAYSIZE(szIniPath),
536 TEXT("%s\\%s.ini"), lpResPath, szBuffer);
537
538 /* Verify that the file exists, otherwise fall back to english (US) */
539 dwAttribs = GetFileAttributes(szIniPath);
540 if ((dwAttribs == INVALID_FILE_ATTRIBUTES) ||
541 (dwAttribs & FILE_ATTRIBUTE_DIRECTORY))
542 {
543 StringCchCopy(szBuffer, ARRAYSIZE(szBuffer), TEXT("en-US"));
544
545 StringCchPrintf(szIniPath, ARRAYSIZE(szIniPath),
546 TEXT("%s\\%s.ini"), lpResPath, szBuffer);
547 }
548
549 /* Verify that the file exists, otherwise fall back to internal (localized) resource */
550 dwAttribs = GetFileAttributes(szIniPath);
551 if ((dwAttribs == INVALID_FILE_ATTRIBUTES) ||
552 (dwAttribs & FILE_ATTRIBUTE_DIRECTORY))
553 {
554 return FALSE; // For localized resources, see the general function.
555 }
556
557 /* Try to load the default localized strings */
558 GetPrivateProfileString(TEXT("Defaults"), TEXT("AppTitle"), TEXT("ReactOS - Welcome") /* default */,
559 szAppTitle, ARRAYSIZE(szAppTitle), szIniPath);
560 GetPrivateProfileString(TEXT("Defaults"), TEXT("DefaultTopicTitle"), TEXT("") /* default */,
561 szDefaultTitle, ARRAYSIZE(szDefaultTitle), szIniPath);
562 if (!GetPrivateProfileString(TEXT("Defaults"), TEXT("DefaultTopicDescription"), TEXT("") /* default */,
563 szDefaultDesc, ARRAYSIZE(szDefaultDesc), szIniPath))
564 {
565 *szDefaultDesc = 0;
566 }
567 else
568 {
569 TranslateEscapes(szDefaultDesc);
570 }
571
572 /* Allocate a buffer big enough to hold all the section names */
573 for (dwSize = BUFFER_SIZE; ; dwSize += BUFFER_SIZE)
574 {
575 lpszSections = HeapAlloc(GetProcessHeap(), 0, dwSize * sizeof(TCHAR));
576 if (!lpszSections)
577 return TRUE; // FIXME!
578 dwRet = GetPrivateProfileSectionNames(lpszSections, dwSize, szIniPath);
579 if (dwRet < dwSize - 2)
580 break;
581 HeapFree(GetProcessHeap(), 0, lpszSections);
582 }
583
584 /* Loop over the sections and load the topics */
585 lpszSection = lpszSections;
586 for (; lpszSection && *lpszSection; lpszSection += (_tcslen(lpszSection) + 1))
587 {
588 /* Ignore everything that is not a topic */
589 if (_tcsnicmp(lpszSection, TEXT("Topic"), 5) != 0)
590 continue;
591
592 lpszCommand = NULL, lpszAction = NULL;
593
594 /* Retrieve the information */
595 GetPrivateProfileString(lpszSection, TEXT("MenuText"), TEXT("") /* default */,
596 newTopic.szText, ARRAYSIZE(newTopic.szText), szIniPath);
597 GetPrivateProfileString(lpszSection, TEXT("Title"), TEXT("") /* default */,
598 newTopic.szTitle, ARRAYSIZE(newTopic.szTitle), szIniPath);
599 GetPrivateProfileString(lpszSection, TEXT("Description"), TEXT("") /* default */,
600 newTopic.szDesc, ARRAYSIZE(newTopic.szDesc), szIniPath);
601
602 GetPrivateProfileString(lpszSection, TEXT("ConfigCommand"), TEXT("") /* default */,
603 newTopic.szCommand, ARRAYSIZE(newTopic.szCommand), szIniPath);
604
605 /* Only care about command arguments if we actually have a command */
606 if (*newTopic.szCommand)
607 {
608 lpszCommand = newTopic.szCommand;
609 GetPrivateProfileString(lpszSection, TEXT("ConfigArgs"), TEXT("") /* default */,
610 newTopic.szArgs, ARRAYSIZE(newTopic.szArgs), szIniPath);
611 }
612 /* Only care about custom actions if we actually don't have a command */
613 else // if (!*newTopic.szCommand)
614 {
615 lpszAction = newTopic.szCommand;
616 GetPrivateProfileString(lpszSection, TEXT("Action"), TEXT("") /* default */,
617 newTopic.szCommand, ARRAYSIZE(newTopic.szCommand), szIniPath);
618 }
619
620 /* Allocate a new topic */
621 pTopic = AddNewTopicEx(newTopic.szText,
622 newTopic.szTitle,
623 newTopic.szDesc,
624 lpszCommand,
625 newTopic.szArgs,
626 lpszAction);
627 if (!pTopic)
628 break; // Cannot reallocate more
629 }
630
631 HeapFree(GetProcessHeap(), 0, lpszSections);
632
633 return TRUE;
634 }
635
636 static VOID
LoadConfiguration(VOID)637 LoadConfiguration(VOID)
638 {
639 DWORD dwAttribs;
640 BOOL bLoadDefaultResources;
641 TCHAR szAppPath[MAX_PATH];
642 TCHAR szIniPath[MAX_PATH];
643 TCHAR szResPath[MAX_PATH];
644
645 /* Initialize the topic list */
646 InitializeTopicList();
647
648 /*
649 * First, try to load the default internal (localized) strings.
650 * They can be redefined by the localized INI files.
651 */
652 if (!LoadString(hInstance, IDS_APPTITLE, szAppTitle, ARRAYSIZE(szAppTitle)))
653 StringCchCopy(szAppTitle, ARRAYSIZE(szAppTitle), TEXT("ReactOS - Welcome"));
654 if (!LoadString(hInstance, IDS_DEFAULT_TOPIC_TITLE, szDefaultTitle, ARRAYSIZE(szDefaultTitle)))
655 *szDefaultTitle = 0;
656 if (!LoadString(hInstance, IDS_DEFAULT_TOPIC_DESC, szDefaultDesc, ARRAYSIZE(szDefaultDesc)))
657 *szDefaultDesc = 0;
658
659 /* Retrieve the full path to this application */
660 GetModuleFileName(NULL, szAppPath, ARRAYSIZE(szAppPath));
661 if (*szAppPath)
662 {
663 LPTSTR lpFileName = _tcsrchr(szAppPath, _T('\\'));
664 if (lpFileName)
665 *lpFileName = 0;
666 else
667 *szAppPath = 0;
668 }
669
670 /* Build the full INI file path name */
671 StringCchPrintf(szIniPath, ARRAYSIZE(szIniPath), TEXT("%s\\welcome.ini"), szAppPath);
672
673 /* Verify that the file exists, otherwise use the default configuration */
674 dwAttribs = GetFileAttributes(szIniPath);
675 if ((dwAttribs == INVALID_FILE_ATTRIBUTES) ||
676 (dwAttribs & FILE_ATTRIBUTE_DIRECTORY))
677 {
678 /* Use the default internal (localized) resources */
679 LoadLocalizedResourcesInternal();
680 return;
681 }
682
683 /* Load the settings from the INI configuration file */
684 bDisplayCheckBox = !!GetPrivateProfileInt(TEXT("Welcome"), TEXT("DisplayCheckBox"), FALSE /* default */, szIniPath);
685 bDisplayExitBtn = !!GetPrivateProfileInt(TEXT("Welcome"), TEXT("DisplayExitButton"), TRUE /* default */, szIniPath);
686
687 /* Load the default internal (localized) resources if needed */
688 bLoadDefaultResources = !!GetPrivateProfileInt(TEXT("Welcome"), TEXT("LoadDefaultResources"), FALSE /* default */, szIniPath);
689 if (bLoadDefaultResources)
690 LoadLocalizedResourcesInternal();
691
692 GetPrivateProfileString(TEXT("Welcome"), TEXT("ResourceDir"), TEXT("") /* default */,
693 szResPath, ARRAYSIZE(szResPath), szIniPath);
694
695 /* Set the current directory to the one of this application, and retrieve the resources */
696 SetCurrentDirectory(szAppPath);
697 if (!LoadLocalizedResourcesFromINI(LOCALE_USER_DEFAULT, szResPath))
698 {
699 /*
700 * Loading localized resources from INI file failed, try to load the
701 * internal resources only if they were not already loaded earlier.
702 */
703 if (!bLoadDefaultResources)
704 LoadLocalizedResourcesInternal();
705 }
706 }
707
708 static VOID
FreeResources(VOID)709 FreeResources(VOID)
710 {
711 if (!pTopics)
712 return;
713
714 while (dwNumberTopics--)
715 {
716 if (pTopics[dwNumberTopics])
717 HeapFree(GetProcessHeap(), 0, pTopics[dwNumberTopics]);
718 }
719 HeapFree(GetProcessHeap(), 0, pTopics);
720 pTopics = NULL;
721 dwNumberTopics = 0;
722 }
723
724 #if 0
725 static VOID
726 ShowLastWin32Error(HWND hWnd)
727 {
728 LPTSTR lpMessageBuffer = NULL;
729 DWORD dwError = GetLastError();
730
731 if (dwError == ERROR_SUCCESS)
732 return;
733
734 if (!FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
735 FORMAT_MESSAGE_FROM_SYSTEM |
736 FORMAT_MESSAGE_IGNORE_INSERTS,
737 NULL,
738 dwError,
739 LANG_USER_DEFAULT,
740 (LPTSTR)&lpMessageBuffer,
741 0, NULL))
742 {
743 return;
744 }
745
746 MessageBox(hWnd, lpMessageBuffer, szAppTitle, MB_OK | MB_ICONERROR);
747 LocalFree(lpMessageBuffer);
748 }
749 #endif
750
751 int WINAPI
_tWinMain(HINSTANCE hInst,HINSTANCE hPrevInstance,LPTSTR lpszCmdLine,int nCmdShow)752 _tWinMain(HINSTANCE hInst,
753 HINSTANCE hPrevInstance,
754 LPTSTR lpszCmdLine,
755 int nCmdShow)
756 {
757 HANDLE hMutex = NULL;
758 WNDCLASSEX wndclass;
759 MSG msg;
760 HWND hWndFocus;
761 INT xPos, yPos;
762 INT xWidth, yHeight;
763 RECT rcWindow;
764 HICON hMainIcon;
765 HMENU hSystemMenu;
766 DWORD dwStyle = WS_OVERLAPPED | WS_CLIPCHILDREN | WS_CLIPSIBLINGS |
767 WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX;
768
769 BITMAP BitmapInfo;
770 ULONG ulInnerWidth = TITLE_WIDTH;
771 ULONG ulInnerHeight = (TITLE_WIDTH * 3) / 4;
772 ULONG ulTitleHeight = TITLE_HEIGHT + 3;
773
774 UNREFERENCED_PARAMETER(hPrevInstance);
775 UNREFERENCED_PARAMETER(lpszCmdLine);
776
777 /* Ensure only one instance is running */
778 hMutex = CreateMutex(NULL, FALSE, szWindowClass);
779 if (hMutex && (GetLastError() == ERROR_ALREADY_EXISTS))
780 {
781 /* If already started, find its window */
782 hWndMain = FindWindow(szWindowClass, NULL);
783
784 /* Activate window */
785 ShowWindow(hWndMain, SW_SHOWNORMAL);
786 SetForegroundWindow(hWndMain);
787
788 /* Close the mutex handle and quit */
789 CloseHandle(hMutex);
790 return 0;
791 }
792
793 #if 0
794 /* Mirroring is enabled from within the resources */
795 switch (GetUserDefaultUILanguage())
796 {
797 case MAKELANGID(LANG_HEBREW, SUBLANG_DEFAULT):
798 SetProcessDefaultLayout(LAYOUT_RTL);
799 break;
800
801 default:
802 break;
803 }
804 #endif
805
806 hInstance = hInst;
807
808 /* Load icons */
809 hMainIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_MAIN));
810
811 /* Register the window class */
812 wndclass.cbSize = sizeof(wndclass);
813 wndclass.style = CS_HREDRAW | CS_VREDRAW;
814 wndclass.lpfnWndProc = (WNDPROC)MainWndProc;
815 wndclass.cbClsExtra = 0;
816 wndclass.cbWndExtra = 0;
817 wndclass.hInstance = hInstance;
818 wndclass.hIcon = hMainIcon;
819 wndclass.hIconSm = NULL;
820 wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
821 wndclass.hbrBackground = NULL;
822 wndclass.lpszMenuName = NULL;
823 wndclass.lpszClassName = szWindowClass;
824
825 RegisterClassEx(&wndclass);
826
827 /* Load the banner bitmap, and compute the window dimensions */
828 hTitleBitmap = LoadBitmap(hInstance, MAKEINTRESOURCE(IDB_TITLE_BITMAP));
829 if (hTitleBitmap)
830 {
831 GetObject(hTitleBitmap, sizeof(BitmapInfo), &BitmapInfo);
832 ulInnerWidth = BitmapInfo.bmWidth;
833 ulInnerHeight = (ulInnerWidth * 3) / 4;
834 ulTitleHeight = BitmapInfo.bmHeight + 3;
835 DeleteObject(hTitleBitmap);
836 }
837 ulInnerHeight -= GetSystemMetrics(SM_CYCAPTION);
838
839 rcWindow.top = 0;
840 rcWindow.bottom = ulInnerHeight - 1;
841 rcWindow.left = 0;
842 rcWindow.right = ulInnerWidth - 1;
843
844 AdjustWindowRect(&rcWindow, dwStyle, FALSE);
845 xWidth = rcWindow.right - rcWindow.left;
846 yHeight = rcWindow.bottom - rcWindow.top;
847
848 /* Compute the window position */
849 xPos = (GetSystemMetrics(SM_CXSCREEN) - xWidth) / 2;
850 yPos = (GetSystemMetrics(SM_CYSCREEN) - yHeight) / 2;
851
852 rcTitlePanel.top = 0;
853 rcTitlePanel.bottom = ulTitleHeight;
854 rcTitlePanel.left = 0;
855 rcTitlePanel.right = ulInnerWidth - 1;
856
857 rcLeftPanel.top = rcTitlePanel.bottom;
858 rcLeftPanel.bottom = ulInnerHeight - 1;
859 rcLeftPanel.left = 0;
860 rcLeftPanel.right = ulInnerWidth / 3;
861
862 rcRightPanel.top = rcLeftPanel.top;
863 rcRightPanel.bottom = rcLeftPanel.bottom;
864 rcRightPanel.left = rcLeftPanel.right;
865 rcRightPanel.right = ulInnerWidth - 1;
866
867 /* Load the configuration and the resources */
868 LoadConfiguration();
869
870 /* Create main window */
871 hWndMain = CreateWindow(szWindowClass,
872 szAppTitle,
873 dwStyle,
874 xPos,
875 yPos,
876 xWidth,
877 yHeight,
878 0,
879 0,
880 hInstance,
881 NULL);
882
883 hSystemMenu = GetSystemMenu(hWndMain, FALSE);
884 if (hSystemMenu)
885 {
886 RemoveMenu(hSystemMenu, SC_SIZE, MF_BYCOMMAND);
887 RemoveMenu(hSystemMenu, SC_MAXIMIZE, MF_BYCOMMAND);
888 }
889
890 ShowWindow(hWndMain, nCmdShow);
891 UpdateWindow(hWndMain);
892
893 while (GetMessage(&msg, NULL, 0, 0) != FALSE)
894 {
895 /* Check for ENTER key presses */
896 if (msg.message == WM_KEYDOWN && msg.wParam == VK_RETURN)
897 {
898 /*
899 * The user pressed the ENTER key. Retrieve the handle to the
900 * child window that has the keyboard focus, and send it a
901 * WM_COMMAND message.
902 */
903 hWndFocus = GetFocus();
904 if (hWndFocus)
905 {
906 SendMessage(hWndMain, WM_COMMAND,
907 (WPARAM)GetDlgCtrlID(hWndFocus), (LPARAM)hWndFocus);
908 }
909 }
910 /* Allow using keyboard navigation */
911 else if (!IsDialogMessage(hWndMain, &msg))
912 {
913 TranslateMessage(&msg);
914 DispatchMessage(&msg);
915 }
916 }
917
918 /* Cleanup */
919 FreeResources();
920
921 /* Close the mutex handle and quit */
922 CloseHandle(hMutex);
923 return msg.wParam;
924 }
925
926
927 INT_PTR CALLBACK
ButtonSubclassWndProc(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam)928 ButtonSubclassWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
929 {
930 static WPARAM wParamOld = 0;
931 static LPARAM lParamOld = 0;
932
933 LONG i;
934
935 if (uMsg == WM_MOUSEMOVE)
936 {
937 /* Ignore mouse-move messages on the same point */
938 if ((wParam == wParamOld) && (lParam == lParamOld))
939 return 0;
940
941 /* Retrieve the topic index of this button */
942 i = GetWindowLongPtr(hWnd, GWLP_ID) - TOPIC_BTN_ID_BASE;
943
944 /*
945 * Change the focus to this button if the current topic index differs
946 * (we will receive WM_SETFOCUS afterwards).
947 */
948 if (nTopic != i)
949 SetFocus(hWnd);
950
951 wParamOld = wParam;
952 lParamOld = lParam;
953 }
954 else if (uMsg == WM_SETFOCUS)
955 {
956 /* Retrieve the topic index of this button */
957 i = GetWindowLongPtr(hWnd, GWLP_ID) - TOPIC_BTN_ID_BASE;
958
959 /* Change the current topic index and repaint the description panel */
960 if (nTopic != i)
961 {
962 nTopic = i;
963 InvalidateRect(hWndMain, &rcRightPanel, TRUE);
964 }
965 }
966 else if (uMsg == WM_KILLFOCUS)
967 {
968 /*
969 * We lost focus, either because the user changed button focus,
970 * or because the main window to which we belong went inactivated.
971 * If we are in the latter case, we ignore the focus change.
972 * If we are in the former case, we reset to the default topic.
973 */
974 if (GetParent(hWnd) == GetForegroundWindow())
975 {
976 nTopic = -1;
977 InvalidateRect(hWndMain, &rcRightPanel, TRUE);
978 }
979 }
980
981 return CallWindowProc(fnOldBtn, hWnd, uMsg, wParam, lParam);
982 }
983
984
985 static BOOL
RunAction(INT nTopic)986 RunAction(INT nTopic)
987 {
988 PCWSTR Command = NULL, Args = NULL;
989
990 if (nTopic < 0)
991 return TRUE;
992
993 Command = pTopics[nTopic]->szCommand;
994 if (/* !Command && */ !*Command)
995 return TRUE;
996
997 /* Check for known actions */
998 if (!pTopics[nTopic]->bIsCommand)
999 {
1000 if (!_tcsicmp(Command, TEXT("<exit>")))
1001 return FALSE;
1002
1003 if (!_tcsnicmp(Command, TEXT("<msg>"), 5))
1004 {
1005 MessageBox(hWndMain, Command + 5, TEXT("ReactOS"), MB_OK | MB_TASKMODAL);
1006 return TRUE;
1007 }
1008 }
1009 else
1010 /* Run the command */
1011 {
1012 Args = pTopics[nTopic]->szArgs;
1013 if (!*Args) Args = NULL;
1014 ShellExecute(NULL, NULL,
1015 Command, Args,
1016 NULL, SW_SHOWDEFAULT);
1017 }
1018
1019 return TRUE;
1020 }
1021
1022
1023 static DWORD
GetButtonHeight(HDC hDC,HFONT hFont,LPCTSTR szText,DWORD dwWidth)1024 GetButtonHeight(HDC hDC,
1025 HFONT hFont,
1026 LPCTSTR szText,
1027 DWORD dwWidth)
1028 {
1029 HFONT hOldFont;
1030 RECT rect;
1031
1032 rect.left = 0;
1033 rect.right = dwWidth - 20;
1034 rect.top = 0;
1035 rect.bottom = 25;
1036
1037 hOldFont = (HFONT)SelectObject(hDC, hFont);
1038 DrawText(hDC, szText, -1, &rect, DT_TOP | DT_CALCRECT | DT_WORDBREAK);
1039 SelectObject(hDC, hOldFont);
1040
1041 return (rect.bottom-rect.top + 14);
1042 }
1043
1044
1045 static LRESULT
OnCreate(HWND hWnd,WPARAM wParam,LPARAM lParam)1046 OnCreate(HWND hWnd, WPARAM wParam, LPARAM lParam)
1047 {
1048 UINT i;
1049 INT nLength;
1050 HDC ScreenDC;
1051 LOGFONT lf;
1052 DWORD dwTop;
1053 DWORD dwHeight = 0;
1054 TCHAR szText[80];
1055
1056 UNREFERENCED_PARAMETER(wParam);
1057 UNREFERENCED_PARAMETER(lParam);
1058
1059 hbrLightBlue = CreateSolidBrush(LIGHT_BLUE);
1060 hbrDarkBlue = CreateSolidBrush(DARK_BLUE);
1061
1062 ZeroMemory(&lf, sizeof(lf));
1063
1064 lf.lfEscapement = 0;
1065 lf.lfOrientation = 0; // TA_BASELINE;
1066 // lf.lfItalic = lf.lfUnderline = lf.lfStrikeOut = FALSE;
1067 lf.lfCharSet = ANSI_CHARSET;
1068 lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
1069 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
1070 lf.lfQuality = DEFAULT_QUALITY;
1071 lf.lfPitchAndFamily = FF_DONTCARE;
1072 if (LoadString(hInstance, IDS_FONTNAME, lf.lfFaceName, ARRAYSIZE(lf.lfFaceName)) == 0)
1073 StringCchCopy(lf.lfFaceName, ARRAYSIZE(lf.lfFaceName), TEXT("Tahoma"));
1074
1075 /* Topic title font */
1076 lf.lfHeight = -18;
1077 lf.lfWidth = 0;
1078 lf.lfWeight = FW_NORMAL;
1079 hFontTopicTitle = CreateFontIndirect(&lf);
1080
1081 /* Topic description font */
1082 lf.lfHeight = -11;
1083 lf.lfWidth = 0;
1084 lf.lfWeight = FW_THIN;
1085 hFontTopicDescription = CreateFontIndirect(&lf);
1086
1087 /* Topic button font */
1088 lf.lfHeight = -11;
1089 lf.lfWidth = 0;
1090 lf.lfWeight = FW_BOLD;
1091 hFontTopicButton = CreateFontIndirect(&lf);
1092
1093 /* Load title bitmap */
1094 if (hTitleBitmap)
1095 hTitleBitmap = LoadBitmap(hInstance, MAKEINTRESOURCE(IDB_TITLE_BITMAP));
1096
1097 /* Load topic bitmaps */
1098 hDefaultTopicBitmap = LoadBitmap(hInstance, MAKEINTRESOURCE(IDB_DEFAULT_TOPIC_BITMAP));
1099 for (i = 0; i < dwNumberTopics; i++)
1100 {
1101 // FIXME: Not implemented yet!
1102 // pTopics[i]->hBitmap = LoadBitmap(hInstance, MAKEINTRESOURCE(IDB_TOPIC_BITMAP0 + i));
1103 pTopics[i]->hBitmap = NULL;
1104 }
1105
1106 ScreenDC = GetWindowDC(hWnd);
1107 hdcMem = CreateCompatibleDC(ScreenDC);
1108 ReleaseDC(hWnd, ScreenDC);
1109
1110 /* Load and create the menu buttons */
1111 dwTop = rcLeftPanel.top;
1112 for (i = 0; i < dwNumberTopics; i++)
1113 {
1114 if (*pTopics[i]->szText)
1115 {
1116 dwHeight = GetButtonHeight(hdcMem,
1117 hFontTopicButton,
1118 pTopics[i]->szText,
1119 rcLeftPanel.right - rcLeftPanel.left);
1120
1121 pTopics[i]->hWndButton = CreateWindow(TEXT("BUTTON"),
1122 pTopics[i]->szText,
1123 (*pTopics[i]->szCommand ? 0 : WS_DISABLED) |
1124 WS_CHILDWINDOW | WS_VISIBLE | WS_TABSTOP |
1125 BS_MULTILINE | BS_OWNERDRAW,
1126 rcLeftPanel.left,
1127 dwTop,
1128 rcLeftPanel.right - rcLeftPanel.left,
1129 dwHeight,
1130 hWnd,
1131 (HMENU)IntToPtr(TOPIC_BTN_ID_BASE + i), // Similar to SetWindowLongPtr(GWLP_ID)
1132 hInstance,
1133 NULL);
1134 nDefaultTopic = i;
1135 SendMessage(pTopics[i]->hWndButton, WM_SETFONT, (WPARAM)hFontTopicButton, MAKELPARAM(TRUE, 0));
1136 fnOldBtn = (WNDPROC)SetWindowLongPtr(pTopics[i]->hWndButton, GWLP_WNDPROC, (DWORD_PTR)ButtonSubclassWndProc);
1137 }
1138 else
1139 {
1140 pTopics[i]->hWndButton = NULL;
1141 }
1142
1143 dwTop += dwHeight;
1144 }
1145
1146 /* Create the checkbox */
1147 if (bDisplayCheckBox)
1148 {
1149 nLength = LoadString(hInstance, IDS_CHECKTEXT, szText, ARRAYSIZE(szText));
1150 if (nLength > 0)
1151 {
1152 lf.lfHeight = -10;
1153 lf.lfWidth = 0;
1154 lf.lfWeight = FW_THIN;
1155 hFontCheckButton = CreateFontIndirect(&lf);
1156
1157 hWndCheckButton = CreateWindow(TEXT("BUTTON"),
1158 szText,
1159 WS_CHILDWINDOW | WS_VISIBLE | WS_TABSTOP |
1160 BS_AUTOCHECKBOX | BS_MULTILINE /**/| BS_FLAT/**/,
1161 rcLeftPanel.left + 8,
1162 rcLeftPanel.bottom - 8 - 13,
1163 rcLeftPanel.right - rcLeftPanel.left - 16,
1164 13,
1165 hWnd,
1166 (HMENU)IDC_CHECKBUTTON,
1167 hInstance,
1168 NULL);
1169 SendMessage(hWndCheckButton, WM_SETFONT, (WPARAM)hFontCheckButton, MAKELPARAM(TRUE, 0));
1170 }
1171 else
1172 {
1173 hFontCheckButton = NULL;
1174 hWndCheckButton = NULL;
1175 }
1176 }
1177
1178 /* Create the "Exit" button */
1179 if (bDisplayExitBtn)
1180 {
1181 nLength = LoadString(hInstance, IDS_CLOSETEXT, szText, ARRAYSIZE(szText));
1182 if (nLength > 0)
1183 {
1184 hWndCloseButton = CreateWindow(TEXT("BUTTON"),
1185 szText,
1186 WS_CHILDWINDOW | WS_VISIBLE | WS_TABSTOP | BS_FLAT,
1187 rcRightPanel.right - 8 - 57,
1188 rcRightPanel.bottom - 8 - 21,
1189 57,
1190 21,
1191 hWnd,
1192 (HMENU)IDC_CLOSEBUTTON,
1193 hInstance,
1194 NULL);
1195 nDefaultTopic = -1;
1196 SendMessage(hWndCloseButton, WM_SETFONT, (WPARAM)hFontTopicButton, MAKELPARAM(TRUE, 0));
1197 }
1198 else
1199 {
1200 hWndCloseButton = NULL;
1201 }
1202 }
1203
1204 return 0;
1205 }
1206
1207
1208 static LRESULT
OnCommand(HWND hWnd,WPARAM wParam,LPARAM lParam)1209 OnCommand(HWND hWnd, WPARAM wParam, LPARAM lParam)
1210 {
1211 UNREFERENCED_PARAMETER(lParam);
1212
1213 /* Retrieve the low-word from wParam */
1214 wParam = LOWORD(wParam);
1215
1216 /* Execute action */
1217 if (wParam == IDC_CLOSEBUTTON)
1218 {
1219 DestroyWindow(hWnd);
1220 }
1221 else if (wParam - TOPIC_BTN_ID_BASE < dwNumberTopics)
1222 {
1223 if (RunAction(wParam - TOPIC_BTN_ID_BASE) == FALSE)
1224 DestroyWindow(hWnd); // Corresponds to a <exit> action.
1225 }
1226
1227 return 0;
1228 }
1229
1230
1231 static VOID
PaintBanner(HDC hdc,LPRECT rcPanel)1232 PaintBanner(HDC hdc, LPRECT rcPanel)
1233 {
1234 HBITMAP hOldBitmap;
1235 HBRUSH hOldBrush;
1236
1237 /* Title bitmap */
1238 hOldBitmap = (HBITMAP)SelectObject(hdcMem, hTitleBitmap);
1239 BitBlt(hdc,
1240 rcPanel->left,
1241 rcPanel->top,
1242 rcPanel->right - rcPanel->left,
1243 rcPanel->bottom - 3,
1244 hdcMem, 0, 0, SRCCOPY);
1245 SelectObject(hdcMem, hOldBitmap);
1246
1247 /* Dark blue line */
1248 hOldBrush = (HBRUSH)SelectObject(hdc, hbrDarkBlue);
1249 PatBlt(hdc,
1250 rcPanel->left,
1251 rcPanel->bottom - 3,
1252 rcPanel->right - rcPanel->left,
1253 3,
1254 PATCOPY);
1255 SelectObject(hdc, hOldBrush);
1256 }
1257
1258
1259 static LRESULT
OnPaint(HWND hWnd,WPARAM wParam,LPARAM lParam)1260 OnPaint(HWND hWnd, WPARAM wParam, LPARAM lParam)
1261 {
1262 HPEN hPen;
1263 HPEN hOldPen;
1264 HDC hdc;
1265 PAINTSTRUCT ps;
1266 HBITMAP hOldBitmap = NULL;
1267 HBRUSH hOldBrush;
1268 HFONT hOldFont;
1269 RECT rcTitle, rcDescription;
1270 BITMAP bmpInfo;
1271 TCHAR szVersion[50];
1272 LPTSTR lpTitle = NULL, lpDesc = NULL;
1273
1274 UNREFERENCED_PARAMETER(wParam);
1275 UNREFERENCED_PARAMETER(lParam);
1276
1277 hdc = BeginPaint(hWnd, &ps);
1278
1279 /* Banner panel */
1280 PaintBanner(hdc, &rcTitlePanel);
1281
1282 /* Left panel */
1283 hOldBrush = (HBRUSH)SelectObject(hdc, hbrLightBlue);
1284 PatBlt(hdc,
1285 rcLeftPanel.left,
1286 rcLeftPanel.top,
1287 rcLeftPanel.right - rcLeftPanel.left,
1288 rcLeftPanel.bottom - rcLeftPanel.top,
1289 PATCOPY);
1290 SelectObject(hdc, hOldBrush);
1291
1292 /* Right panel */
1293 hOldBrush = (HBRUSH)SelectObject(hdc, GetStockObject(WHITE_BRUSH));
1294 PatBlt(hdc,
1295 rcRightPanel.left,
1296 rcRightPanel.top,
1297 rcRightPanel.right - rcRightPanel.left,
1298 rcRightPanel.bottom - rcRightPanel.top,
1299 PATCOPY);
1300 SelectObject(hdc, hOldBrush);
1301
1302 /* Draw dark vertical line */
1303 hPen = CreatePen(PS_SOLID, 0, DARK_BLUE);
1304 hOldPen = (HPEN)SelectObject(hdc, hPen);
1305 MoveToEx(hdc, rcRightPanel.left, rcRightPanel.top, NULL);
1306 LineTo(hdc, rcRightPanel.left, rcRightPanel.bottom);
1307 SelectObject(hdc, hOldPen);
1308 DeleteObject(hPen);
1309
1310 /* Draw topic bitmap */
1311 if ((nTopic == -1) && (hDefaultTopicBitmap))
1312 {
1313 GetObject(hDefaultTopicBitmap, sizeof(bmpInfo), &bmpInfo);
1314 hOldBitmap = (HBITMAP)SelectObject(hdcMem, hDefaultTopicBitmap);
1315 BitBlt(hdc,
1316 rcRightPanel.right - bmpInfo.bmWidth,
1317 rcRightPanel.bottom - bmpInfo.bmHeight,
1318 bmpInfo.bmWidth,
1319 bmpInfo.bmHeight,
1320 hdcMem,
1321 0,
1322 0,
1323 SRCCOPY);
1324 }
1325 else if ((nTopic != -1) && (pTopics[nTopic]->hBitmap))
1326 {
1327 GetObject(pTopics[nTopic]->hBitmap, sizeof(bmpInfo), &bmpInfo);
1328 hOldBitmap = (HBITMAP)SelectObject(hdcMem, pTopics[nTopic]->hBitmap);
1329 BitBlt(hdc,
1330 rcRightPanel.right - bmpInfo.bmWidth,
1331 rcRightPanel.bottom - bmpInfo.bmHeight,
1332 bmpInfo.bmWidth,
1333 bmpInfo.bmHeight,
1334 hdcMem,
1335 0,
1336 0,
1337 SRCCOPY);
1338 }
1339
1340 if (nTopic == -1)
1341 {
1342 lpTitle = szDefaultTitle;
1343 lpDesc = szDefaultDesc;
1344 }
1345 else
1346 {
1347 lpTitle = pTopics[nTopic]->szTitle;
1348 lpDesc = pTopics[nTopic]->szDesc;
1349 }
1350
1351 SetBkMode(hdc, TRANSPARENT);
1352
1353 /* Draw version information */
1354 StringCchCopy(szVersion, ARRAYSIZE(szVersion),
1355 TEXT("ReactOS ") TEXT(KERNEL_VERSION_STR));
1356
1357 /*
1358 * Compute the original rect (position & size) of the version info,
1359 * depending whether the checkbox is displayed (version info in the
1360 * right panel) or not (version info in the left panel).
1361 */
1362 if (bDisplayCheckBox)
1363 rcTitle = rcRightPanel;
1364 else
1365 rcTitle = rcLeftPanel;
1366
1367 rcTitle.left = rcTitle.left + 8;
1368 rcTitle.right = rcTitle.right - 5;
1369 rcTitle.top = rcTitle.bottom - 43;
1370 rcTitle.bottom = rcTitle.bottom - 8;
1371
1372 hOldFont = (HFONT)SelectObject(hdc, hFontTopicDescription);
1373 DrawText(hdc, szVersion, -1, &rcTitle, DT_BOTTOM | DT_CALCRECT | DT_SINGLELINE);
1374 SetTextColor(hdc, GetSysColor(COLOR_WINDOWTEXT));
1375 DrawText(hdc, szVersion, -1, &rcTitle, DT_BOTTOM | DT_SINGLELINE);
1376 SelectObject(hdc, hOldFont);
1377
1378 /* Draw topic title */
1379 rcTitle.left = rcRightPanel.left + 12;
1380 rcTitle.right = rcRightPanel.right - 8;
1381 rcTitle.top = rcRightPanel.top + 8;
1382 rcTitle.bottom = rcTitle.top + 57;
1383 hOldFont = (HFONT)SelectObject(hdc, hFontTopicTitle);
1384 DrawText(hdc, lpTitle, -1, &rcTitle, DT_TOP | DT_CALCRECT);
1385 SetTextColor(hdc, DARK_BLUE);
1386 DrawText(hdc, lpTitle, -1, &rcTitle, DT_TOP);
1387 SelectObject(hdc, hOldFont);
1388
1389 /* Draw topic description */
1390 rcDescription.left = rcRightPanel.left + 12;
1391 rcDescription.right = rcRightPanel.right - 8;
1392 rcDescription.top = rcTitle.bottom + 8;
1393 rcDescription.bottom = rcRightPanel.bottom - 20;
1394 hOldFont = (HFONT)SelectObject(hdc, hFontTopicDescription);
1395 SetTextColor(hdc, GetSysColor(COLOR_WINDOWTEXT));
1396 DrawText(hdc, lpDesc, -1, &rcDescription, DT_TOP | DT_WORDBREAK);
1397 SelectObject(hdc, hOldFont);
1398
1399 SetBkMode(hdc, OPAQUE);
1400
1401 SelectObject(hdcMem, hOldBrush);
1402 SelectObject(hdcMem, hOldBitmap);
1403
1404 EndPaint(hWnd, &ps);
1405
1406 return 0;
1407 }
1408
1409
1410 static LRESULT
OnDrawItem(HWND hWnd,WPARAM wParam,LPARAM lParam)1411 OnDrawItem(HWND hWnd, WPARAM wParam, LPARAM lParam)
1412 {
1413 LPDRAWITEMSTRUCT lpDis = (LPDRAWITEMSTRUCT)lParam;
1414 HPEN hPen, hOldPen;
1415 HBRUSH hOldBrush;
1416 INT iBkMode;
1417 TCHAR szText[80];
1418
1419 UNREFERENCED_PARAMETER(hWnd);
1420 UNREFERENCED_PARAMETER(wParam);
1421
1422 #if 0
1423 /* Neither the checkbox button nor the close button implement owner-drawing */
1424 if (lpDis->hwndItem == hWndCheckButton)
1425 return 0;
1426 if (lpDis->hwndItem == hWndCloseButton)
1427 {
1428 DrawFrameControl(lpDis->hDC,
1429 &lpDis->rcItem,
1430 DFC_BUTTON,
1431 DFCS_BUTTONPUSH | DFCS_FLAT);
1432 return TRUE;
1433 }
1434 #endif
1435
1436 if (lpDis->CtlID == (ULONG)(TOPIC_BTN_ID_BASE + nTopic))
1437 hOldBrush = (HBRUSH)SelectObject(lpDis->hDC, GetStockObject(WHITE_BRUSH));
1438 else
1439 hOldBrush = (HBRUSH)SelectObject(lpDis->hDC, hbrLightBlue);
1440
1441 PatBlt(lpDis->hDC,
1442 lpDis->rcItem.left,
1443 lpDis->rcItem.top,
1444 lpDis->rcItem.right,
1445 lpDis->rcItem.bottom,
1446 PATCOPY);
1447 SelectObject(lpDis->hDC, hOldBrush);
1448
1449 hPen = CreatePen(PS_SOLID, 0, DARK_BLUE);
1450 hOldPen = (HPEN)SelectObject(lpDis->hDC, hPen);
1451 MoveToEx(lpDis->hDC, lpDis->rcItem.left, lpDis->rcItem.bottom - 1, NULL);
1452 LineTo(lpDis->hDC, lpDis->rcItem.right, lpDis->rcItem.bottom - 1);
1453 SelectObject(lpDis->hDC, hOldPen);
1454 DeleteObject(hPen);
1455
1456 InflateRect(&lpDis->rcItem, -10, -4);
1457 OffsetRect(&lpDis->rcItem, 0, 1);
1458 GetWindowText(lpDis->hwndItem, szText, ARRAYSIZE(szText));
1459 SetTextColor(lpDis->hDC, GetSysColor(IsWindowEnabled(lpDis->hwndItem) ?
1460 COLOR_WINDOWTEXT : COLOR_GRAYTEXT));
1461 iBkMode = SetBkMode(lpDis->hDC, TRANSPARENT);
1462 DrawText(lpDis->hDC, szText, -1, &lpDis->rcItem, DT_TOP | DT_LEFT | DT_WORDBREAK);
1463 SetBkMode(lpDis->hDC, iBkMode);
1464
1465 return TRUE;
1466 }
1467
1468
1469 static LRESULT
OnMouseMove(HWND hWnd,WPARAM wParam,LPARAM lParam)1470 OnMouseMove(HWND hWnd, WPARAM wParam, LPARAM lParam)
1471 {
1472 static WPARAM wParamOld = 0;
1473 static LPARAM lParamOld = 0;
1474
1475 /* Ignore mouse-move messages on the same point */
1476 if ((wParam == wParamOld) && (lParam == lParamOld))
1477 return 0;
1478
1479 /*
1480 * If the user moves the mouse over the main window, outside of the
1481 * topic buttons, reset the current topic to the default one and
1482 * change the focus to some other default button (to keep keyboard
1483 * navigation possible).
1484 */
1485 if (nTopic != -1)
1486 {
1487 INT nOldTopic = nTopic;
1488 nTopic = -1;
1489 /* Also repaint the buttons, otherwise nothing repaints... */
1490 InvalidateRect(pTopics[nOldTopic]->hWndButton, NULL, TRUE);
1491
1492 /* Set the focus to some other default button */
1493 if (hWndCheckButton)
1494 SetFocus(hWndCheckButton);
1495 else if (hWndCloseButton)
1496 SetFocus(hWndCloseButton);
1497 // SetFocus(hWnd);
1498
1499 /* Repaint the description panel */
1500 InvalidateRect(hWndMain, &rcRightPanel, TRUE);
1501 }
1502
1503 wParamOld = wParam;
1504 lParamOld = lParam;
1505
1506 return 0;
1507 }
1508
1509
1510 static LRESULT
OnCtlColorStatic(HWND hWnd,WPARAM wParam,LPARAM lParam)1511 OnCtlColorStatic(HWND hWnd, WPARAM wParam, LPARAM lParam)
1512 {
1513 UNREFERENCED_PARAMETER(hWnd);
1514
1515 if ((HWND)lParam == hWndCheckButton)
1516 {
1517 SetBkMode((HDC)wParam, TRANSPARENT);
1518 return (LRESULT)hbrLightBlue;
1519 }
1520
1521 return 0;
1522 }
1523
1524
1525 static LRESULT
OnActivate(HWND hWnd,WPARAM wParam,LPARAM lParam)1526 OnActivate(HWND hWnd, WPARAM wParam, LPARAM lParam)
1527 {
1528 UNREFERENCED_PARAMETER(hWnd);
1529 UNREFERENCED_PARAMETER(lParam);
1530
1531 if (wParam != WA_INACTIVE)
1532 {
1533 /*
1534 * The main window is re-activated, set the focus back to
1535 * either the current topic or a default button.
1536 */
1537 if (nTopic != -1)
1538 SetFocus(pTopics[nTopic]->hWndButton);
1539 else if (hWndCheckButton)
1540 SetFocus(hWndCheckButton);
1541 else if (hWndCloseButton)
1542 SetFocus(hWndCloseButton);
1543
1544 // InvalidateRect(hWndMain, &rcRightPanel, TRUE);
1545 }
1546
1547 return 0;
1548 }
1549
1550
1551 static LRESULT
OnDestroy(HWND hWnd,WPARAM wParam,LPARAM lParam)1552 OnDestroy(HWND hWnd, WPARAM wParam, LPARAM lParam)
1553 {
1554 UINT i;
1555
1556 UNREFERENCED_PARAMETER(hWnd);
1557 UNREFERENCED_PARAMETER(wParam);
1558 UNREFERENCED_PARAMETER(lParam);
1559
1560 for (i = 0; i < dwNumberTopics; i++)
1561 {
1562 if (pTopics[i]->hWndButton)
1563 DestroyWindow(pTopics[i]->hWndButton);
1564 }
1565
1566 if (hWndCloseButton)
1567 DestroyWindow(hWndCloseButton);
1568
1569 if (hWndCheckButton)
1570 DestroyWindow(hWndCheckButton);
1571
1572 DeleteDC(hdcMem);
1573
1574 /* Delete bitmaps */
1575 DeleteObject(hDefaultTopicBitmap);
1576 DeleteObject(hTitleBitmap);
1577 for (i = 0; i < dwNumberTopics; i++)
1578 {
1579 if (pTopics[i]->hBitmap)
1580 DeleteObject(pTopics[i]->hBitmap);
1581 }
1582
1583 DeleteObject(hFontTopicTitle);
1584 DeleteObject(hFontTopicDescription);
1585 DeleteObject(hFontTopicButton);
1586
1587 if (hFontCheckButton)
1588 DeleteObject(hFontCheckButton);
1589
1590 DeleteObject(hbrLightBlue);
1591 DeleteObject(hbrDarkBlue);
1592
1593 return 0;
1594 }
1595
1596
1597 INT_PTR CALLBACK
MainWndProc(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam)1598 MainWndProc(HWND hWnd,
1599 UINT uMsg,
1600 WPARAM wParam,
1601 LPARAM lParam)
1602 {
1603 switch (uMsg)
1604 {
1605 case WM_CREATE:
1606 return OnCreate(hWnd, wParam, lParam);
1607
1608 case WM_COMMAND:
1609 return OnCommand(hWnd, wParam, lParam);
1610
1611 case WM_ACTIVATE:
1612 return OnActivate(hWnd, wParam, lParam);
1613
1614 case WM_PAINT:
1615 return OnPaint(hWnd, wParam, lParam);
1616
1617 case WM_DRAWITEM:
1618 return OnDrawItem(hWnd, wParam, lParam);
1619
1620 case WM_CTLCOLORSTATIC:
1621 return OnCtlColorStatic(hWnd, wParam, lParam);
1622
1623 case WM_MOUSEMOVE:
1624 return OnMouseMove(hWnd, wParam, lParam);
1625
1626 case WM_DESTROY:
1627 OnDestroy(hWnd, wParam, lParam);
1628 PostQuitMessage(0);
1629 return 0;
1630 }
1631
1632 return DefWindowProc(hWnd, uMsg, wParam, lParam);
1633 }
1634
1635 /* EOF */
1636