01a63b1612c54e7442d29c960c09786ec2babd39
[reactos.git] / base / setup / welcome / welcome.c
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
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
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 VOID InitializeTopicList(VOID)
214 {
215 dwNumberTopics = 0;
216 pTopics = NULL;
217 }
218
219 PTOPIC AddNewTopic(VOID)
220 {
221 PTOPIC pTopic, *pTopicsTmp;
222
223 /* Allocate (or reallocate) the list of topics */
224 if (!pTopics)
225 pTopicsTmp = HeapAlloc(GetProcessHeap(), 0, (dwNumberTopics + 1) * sizeof(*pTopics));
226 else
227 pTopicsTmp = HeapReAlloc(GetProcessHeap(), 0, pTopics, (dwNumberTopics + 1) * sizeof(*pTopics));
228 if (!pTopicsTmp)
229 return NULL; // Cannot reallocate more
230 pTopics = pTopicsTmp;
231
232 /* Allocate a new topic entry */
233 pTopic = HeapAlloc(GetProcessHeap(), 0, sizeof(*pTopic));
234 if (!pTopic)
235 return NULL; // Cannot reallocate more
236 pTopics[dwNumberTopics++] = pTopic;
237
238 /* Return the allocated topic entry */
239 return pTopic;
240 }
241
242 PTOPIC AddNewTopicEx(
243 IN LPTSTR szText OPTIONAL,
244 IN LPTSTR szTitle OPTIONAL,
245 IN LPTSTR szDesc OPTIONAL,
246 IN LPTSTR szCommand OPTIONAL,
247 IN LPTSTR szArgs OPTIONAL,
248 IN LPTSTR szAction OPTIONAL)
249 {
250 PTOPIC pTopic = AddNewTopic();
251 if (!pTopic)
252 return NULL;
253
254 if (szText && *szText)
255 StringCchCopy(pTopic->szText, ARRAYSIZE(pTopic->szText), szText);
256 else
257 *pTopic->szText = 0;
258
259 if (szTitle && *szTitle)
260 StringCchCopy(pTopic->szTitle, ARRAYSIZE(pTopic->szTitle), szTitle);
261 else
262 *pTopic->szTitle = 0;
263
264 if (szDesc && *szDesc)
265 {
266 StringCchCopy(pTopic->szDesc, ARRAYSIZE(pTopic->szDesc), szDesc);
267 TranslateEscapes(pTopic->szDesc);
268 }
269 else
270 {
271 *pTopic->szDesc = 0;
272 }
273
274 if (szCommand && *szCommand)
275 {
276 pTopic->bIsCommand = TRUE;
277 StringCchCopy(pTopic->szCommand, ARRAYSIZE(pTopic->szCommand), szCommand);
278 }
279 else
280 {
281 pTopic->bIsCommand = FALSE;
282 *pTopic->szCommand = 0;
283 }
284
285 /* Only care about command arguments if we actually have a command */
286 if (*pTopic->szCommand)
287 {
288 if (szArgs && *szArgs)
289 {
290 StringCchCopy(pTopic->szArgs, ARRAYSIZE(pTopic->szArgs), szArgs);
291 }
292 else
293 {
294 /* Check for special applications: ReactOS Shell */
295 if (/* pTopic->szCommand && */ *pTopic->szCommand &&
296 _tcsicmp(pTopic->szCommand, TEXT("explorer.exe")) == 0)
297 {
298 #if 0
299 TCHAR CurrentDir[MAX_PATH];
300 GetCurrentDirectory(ARRAYSIZE(CurrentDir), CurrentDir);
301 #endif
302 StringCchCopy(pTopic->szArgs, ARRAYSIZE(pTopic->szArgs), TEXT("\\"));
303 }
304 else
305 {
306 *pTopic->szArgs = 0;
307 }
308 }
309 }
310 else
311 {
312 *pTopic->szArgs = 0;
313 }
314
315 /* Only care about custom actions if we actually don't have a command */
316 if (!*pTopic->szCommand && szAction && *szAction)
317 {
318 /*
319 * Re-use the pTopic->szCommand member. We distinguish with respect to
320 * a regular command by using the pTopic->bIsCommand flag.
321 */
322 pTopic->bIsCommand = FALSE;
323 StringCchCopy(pTopic->szCommand, ARRAYSIZE(pTopic->szCommand), szAction);
324 TranslateEscapes(pTopic->szCommand);
325 }
326
327 return pTopic;
328 }
329
330 static VOID
331 LoadLocalizedResourcesInternal(VOID)
332 {
333 #define MAX_NUMBER_INTERNAL_TOPICS 3
334
335 UINT i;
336 LPTSTR lpszCommand, lpszAction;
337 TOPIC newTopic, *pTopic;
338
339 for (i = 0; i < MAX_NUMBER_INTERNAL_TOPICS; ++i)
340 {
341 lpszCommand = NULL, lpszAction = NULL;
342
343 /* Retrieve the information */
344 if (!LoadString(hInstance, IDS_TOPIC_BUTTON0 + i, newTopic.szText, ARRAYSIZE(newTopic.szText)))
345 *newTopic.szText = 0;
346 if (!LoadString(hInstance, IDS_TOPIC_TITLE0 + i, newTopic.szTitle, ARRAYSIZE(newTopic.szTitle)))
347 *newTopic.szTitle = 0;
348 if (!LoadString(hInstance, IDS_TOPIC_DESC0 + i, newTopic.szDesc, ARRAYSIZE(newTopic.szDesc)))
349 *newTopic.szDesc = 0;
350
351 if (!LoadString(hInstance, IDS_TOPIC_COMMAND0 + i, newTopic.szCommand, ARRAYSIZE(newTopic.szCommand)))
352 *newTopic.szCommand = 0;
353
354 /* Only care about command arguments if we actually have a command */
355 if (*newTopic.szCommand)
356 {
357 lpszCommand = newTopic.szCommand;
358 if (!LoadString(hInstance, IDS_TOPIC_CMD_ARGS0 + i, newTopic.szArgs, ARRAYSIZE(newTopic.szArgs)))
359 *newTopic.szArgs = 0;
360 }
361 /* Only care about custom actions if we actually don't have a command */
362 else // if (!*newTopic.szCommand)
363 {
364 lpszAction = newTopic.szCommand;
365 if (!LoadString(hInstance, IDS_TOPIC_ACTION0 + i, newTopic.szCommand, ARRAYSIZE(newTopic.szCommand)))
366 *newTopic.szCommand = 0;
367 }
368
369 /* Allocate a new topic */
370 pTopic = AddNewTopicEx(newTopic.szText,
371 newTopic.szTitle,
372 newTopic.szDesc,
373 lpszCommand,
374 newTopic.szArgs,
375 lpszAction);
376 if (!pTopic)
377 break; // Cannot reallocate more
378 }
379 }
380
381 static BOOL
382 LoadLocalizedResourcesFromINI(LCID Locale, LPTSTR lpResPath)
383 {
384 DWORD dwRet;
385 DWORD dwSize;
386 TCHAR szBuffer[LOCALE_NAME_MAX_LENGTH];
387 TCHAR szIniPath[MAX_PATH];
388 LPTSTR lpszSections = NULL, lpszSection = NULL;
389 LPTSTR lpszCommand, lpszAction;
390 TOPIC newTopic, *pTopic;
391
392 /* Retrieve the locale name (on which the INI file name is based) */
393 dwRet = (DWORD)GetLocaleName(Locale, szBuffer, ARRAYSIZE(szBuffer));
394 if (!dwRet)
395 {
396 /* Fall back to english (US) */
397 StringCchCopy(szBuffer, ARRAYSIZE(szBuffer), TEXT("en-US"));
398 }
399
400 /* Build the INI file name */
401 StringCchPrintf(szIniPath, ARRAYSIZE(szIniPath),
402 TEXT("%s\\%s.ini"), lpResPath, szBuffer);
403
404 /* Verify that the file exists, otherwise fall back to english (US) */
405 if (GetFileAttributes(szIniPath) == INVALID_FILE_ATTRIBUTES)
406 {
407 StringCchCopy(szBuffer, ARRAYSIZE(szBuffer), TEXT("en-US"));
408
409 StringCchPrintf(szIniPath, ARRAYSIZE(szIniPath),
410 TEXT("%s\\%s.ini"), lpResPath, szBuffer);
411 }
412
413 /* Verify that the file exists, otherwise fall back to internal (localized) resource */
414 if (GetFileAttributes(szIniPath) == INVALID_FILE_ATTRIBUTES)
415 return FALSE; // For localized resources, see the general function.
416
417 /* Try to load the default localized strings */
418 GetPrivateProfileString(TEXT("Defaults"), TEXT("AppTitle"), TEXT("ReactOS - Welcome") /* default */,
419 szAppTitle, ARRAYSIZE(szAppTitle), szIniPath);
420 GetPrivateProfileString(TEXT("Defaults"), TEXT("DefaultTopicTitle"), TEXT("") /* default */,
421 szDefaultTitle, ARRAYSIZE(szDefaultTitle), szIniPath);
422 if (!GetPrivateProfileString(TEXT("Defaults"), TEXT("DefaultTopicDescription"), TEXT("") /* default */,
423 szDefaultDesc, ARRAYSIZE(szDefaultDesc), szIniPath))
424 {
425 *szDefaultDesc = 0;
426 }
427 else
428 {
429 TranslateEscapes(szDefaultDesc);
430 }
431
432 /* Allocate a buffer big enough to hold all the section names */
433 for (dwSize = BUFFER_SIZE; ; dwSize += BUFFER_SIZE)
434 {
435 lpszSections = HeapAlloc(GetProcessHeap(), 0, dwSize * sizeof(TCHAR));
436 if (!lpszSections)
437 return TRUE; // FIXME!
438 dwRet = GetPrivateProfileSectionNames(lpszSections, dwSize, szIniPath);
439 if (dwRet < dwSize - 2)
440 break;
441 HeapFree(GetProcessHeap(), 0, lpszSections);
442 }
443
444 /* Loop over the sections and load the topics */
445 lpszSection = lpszSections;
446 for (; lpszSection && *lpszSection; lpszSection += (_tcslen(lpszSection) + 1))
447 {
448 /* Ignore everything that is not a topic */
449 if (_tcsnicmp(lpszSection, TEXT("Topic"), 5) != 0)
450 continue;
451
452 lpszCommand = NULL, lpszAction = NULL;
453
454 /* Retrieve the information */
455 GetPrivateProfileString(lpszSection, TEXT("MenuText"), TEXT("") /* default */,
456 newTopic.szText, ARRAYSIZE(newTopic.szText), szIniPath);
457 GetPrivateProfileString(lpszSection, TEXT("Title"), TEXT("") /* default */,
458 newTopic.szTitle, ARRAYSIZE(newTopic.szTitle), szIniPath);
459 GetPrivateProfileString(lpszSection, TEXT("Description"), TEXT("") /* default */,
460 newTopic.szDesc, ARRAYSIZE(newTopic.szDesc), szIniPath);
461
462 GetPrivateProfileString(lpszSection, TEXT("ConfigCommand"), TEXT("") /* default */,
463 newTopic.szCommand, ARRAYSIZE(newTopic.szCommand), szIniPath);
464
465 /* Only care about command arguments if we actually have a command */
466 if (*newTopic.szCommand)
467 {
468 lpszCommand = newTopic.szCommand;
469 GetPrivateProfileString(lpszSection, TEXT("ConfigArgs"), TEXT("") /* default */,
470 newTopic.szArgs, ARRAYSIZE(newTopic.szArgs), szIniPath);
471 }
472 /* Only care about custom actions if we actually don't have a command */
473 else // if (!*newTopic.szCommand)
474 {
475 lpszAction = newTopic.szCommand;
476 GetPrivateProfileString(lpszSection, TEXT("Action"), TEXT("") /* default */,
477 newTopic.szCommand, ARRAYSIZE(newTopic.szCommand), szIniPath);
478 }
479
480 /* Allocate a new topic */
481 pTopic = AddNewTopicEx(newTopic.szText,
482 newTopic.szTitle,
483 newTopic.szDesc,
484 lpszCommand,
485 newTopic.szArgs,
486 lpszAction);
487 if (!pTopic)
488 break; // Cannot reallocate more
489 }
490
491 HeapFree(GetProcessHeap(), 0, lpszSections);
492
493 return TRUE;
494 }
495
496 static VOID
497 LoadConfiguration(VOID)
498 {
499 BOOL bLoadDefaultResources;
500 TCHAR szAppPath[MAX_PATH];
501 TCHAR szIniPath[MAX_PATH];
502 TCHAR szResPath[MAX_PATH];
503
504 /* Initialize the topic list */
505 InitializeTopicList();
506
507 /*
508 * First, try to load the default internal (localized) strings.
509 * They can be redefined by the localized INI files.
510 */
511 if (!LoadString(hInstance, IDS_APPTITLE, szAppTitle, ARRAYSIZE(szAppTitle)))
512 StringCchCopy(szAppTitle, ARRAYSIZE(szAppTitle), TEXT("ReactOS - Welcome"));
513 if (!LoadString(hInstance, IDS_DEFAULT_TOPIC_TITLE, szDefaultTitle, ARRAYSIZE(szDefaultTitle)))
514 *szDefaultTitle = 0;
515 if (!LoadString(hInstance, IDS_DEFAULT_TOPIC_DESC, szDefaultDesc, ARRAYSIZE(szDefaultDesc)))
516 *szDefaultDesc = 0;
517
518 /* Retrieve the full path to this application */
519 GetModuleFileName(NULL, szAppPath, ARRAYSIZE(szAppPath));
520 if (*szAppPath)
521 {
522 LPTSTR lpFileName = _tcsrchr(szAppPath, _T('\\'));
523 if (lpFileName)
524 *lpFileName = 0;
525 else
526 *szAppPath = 0;
527 }
528
529 /* Build the full INI file path name */
530 StringCchPrintf(szIniPath, ARRAYSIZE(szIniPath), TEXT("%s\\welcome.ini"), szAppPath);
531
532 /* Verify that the file exists, otherwise use the default configuration */
533 if (GetFileAttributes(szIniPath) == INVALID_FILE_ATTRIBUTES)
534 {
535 /* Use the default internal (localized) resources */
536 LoadLocalizedResourcesInternal();
537 return;
538 }
539
540 /* Load the settings from the INI configuration file */
541 bDisplayCheckBox = !!GetPrivateProfileInt(TEXT("Welcome"), TEXT("DisplayCheckBox"), FALSE /* default */, szIniPath);
542 bDisplayExitBtn = !!GetPrivateProfileInt(TEXT("Welcome"), TEXT("DisplayExitButton"), TRUE /* default */, szIniPath);
543
544 /* Load the default internal (localized) resources if needed */
545 bLoadDefaultResources = !!GetPrivateProfileInt(TEXT("Welcome"), TEXT("LoadDefaultResources"), FALSE /* default */, szIniPath);
546 if (bLoadDefaultResources)
547 LoadLocalizedResourcesInternal();
548
549 GetPrivateProfileString(TEXT("Welcome"), TEXT("ResourceDir"), TEXT("") /* default */,
550 szResPath, ARRAYSIZE(szResPath), szIniPath);
551
552 /* Set the current directory to the one of this application, and retrieve the resources */
553 SetCurrentDirectory(szAppPath);
554 if (!LoadLocalizedResourcesFromINI(LOCALE_USER_DEFAULT, szResPath))
555 {
556 /*
557 * Loading localized resources from INI file failed, try to load the
558 * internal resources only if they were not already loaded earlier.
559 */
560 if (!bLoadDefaultResources)
561 LoadLocalizedResourcesInternal();
562 }
563 }
564
565 static VOID
566 FreeResources(VOID)
567 {
568 if (!pTopics)
569 return;
570
571 while (dwNumberTopics--)
572 {
573 if (pTopics[dwNumberTopics])
574 HeapFree(GetProcessHeap(), 0, pTopics[dwNumberTopics]);
575 }
576 HeapFree(GetProcessHeap(), 0, pTopics);
577 pTopics = NULL;
578 dwNumberTopics = 0;
579 }
580
581 #if 0
582 static VOID
583 ShowLastWin32Error(HWND hWnd)
584 {
585 LPTSTR lpMessageBuffer = NULL;
586 DWORD dwError = GetLastError();
587
588 if (dwError == ERROR_SUCCESS)
589 return;
590
591 if (!FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
592 FORMAT_MESSAGE_FROM_SYSTEM |
593 FORMAT_MESSAGE_IGNORE_INSERTS,
594 NULL,
595 dwError,
596 LANG_USER_DEFAULT,
597 (LPTSTR)&lpMessageBuffer,
598 0, NULL))
599 {
600 return;
601 }
602
603 MessageBox(hWnd, lpMessageBuffer, szAppTitle, MB_OK | MB_ICONERROR);
604 LocalFree(lpMessageBuffer);
605 }
606 #endif
607
608 int WINAPI
609 _tWinMain(HINSTANCE hInst,
610 HINSTANCE hPrevInstance,
611 LPTSTR lpszCmdLine,
612 int nCmdShow)
613 {
614 HANDLE hMutex = NULL;
615 WNDCLASSEX wndclass;
616 MSG msg;
617 HWND hWndFocus;
618 INT xPos, yPos;
619 INT xWidth, yHeight;
620 RECT rcWindow;
621 HICON hMainIcon;
622 HMENU hSystemMenu;
623 DWORD dwStyle = WS_OVERLAPPED | WS_CLIPCHILDREN | WS_CLIPSIBLINGS |
624 WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX;
625
626 BITMAP BitmapInfo;
627 ULONG ulInnerWidth = TITLE_WIDTH;
628 ULONG ulInnerHeight = (TITLE_WIDTH * 3) / 4;
629 ULONG ulTitleHeight = TITLE_HEIGHT + 3;
630
631 UNREFERENCED_PARAMETER(hPrevInstance);
632 UNREFERENCED_PARAMETER(lpszCmdLine);
633
634 /* Ensure only one instance is running */
635 hMutex = CreateMutex(NULL, FALSE, szWindowClass);
636 if (hMutex && (GetLastError() == ERROR_ALREADY_EXISTS))
637 {
638 /* If already started, find its window */
639 hWndMain = FindWindow(szWindowClass, NULL);
640
641 /* Activate window */
642 ShowWindow(hWndMain, SW_SHOWNORMAL);
643 SetForegroundWindow(hWndMain);
644
645 /* Close the mutex handle and quit */
646 CloseHandle(hMutex);
647 return 0;
648 }
649
650 switch (GetUserDefaultUILanguage())
651 {
652 case MAKELANGID(LANG_HEBREW, SUBLANG_DEFAULT):
653 SetProcessDefaultLayout(LAYOUT_RTL);
654 break;
655
656 default:
657 break;
658 }
659
660 hInstance = hInst;
661
662 /* Load icons */
663 hMainIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_MAIN));
664
665 /* Register the window class */
666 wndclass.cbSize = sizeof(wndclass);
667 wndclass.style = CS_HREDRAW | CS_VREDRAW;
668 wndclass.lpfnWndProc = (WNDPROC)MainWndProc;
669 wndclass.cbClsExtra = 0;
670 wndclass.cbWndExtra = 0;
671 wndclass.hInstance = hInstance;
672 wndclass.hIcon = hMainIcon;
673 wndclass.hIconSm = NULL;
674 wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
675 wndclass.hbrBackground = NULL;
676 wndclass.lpszMenuName = NULL;
677 wndclass.lpszClassName = szWindowClass;
678
679 RegisterClassEx(&wndclass);
680
681 /* Load the banner bitmap, and compute the window dimensions */
682 hTitleBitmap = LoadBitmap(hInstance, MAKEINTRESOURCE(IDB_TITLE_BITMAP));
683 if (hTitleBitmap)
684 {
685 GetObject(hTitleBitmap, sizeof(BitmapInfo), &BitmapInfo);
686 ulInnerWidth = BitmapInfo.bmWidth;
687 ulInnerHeight = (ulInnerWidth * 3) / 4;
688 ulTitleHeight = BitmapInfo.bmHeight + 3;
689 DeleteObject(hTitleBitmap);
690 }
691 ulInnerHeight -= GetSystemMetrics(SM_CYCAPTION);
692
693 rcWindow.top = 0;
694 rcWindow.bottom = ulInnerHeight - 1;
695 rcWindow.left = 0;
696 rcWindow.right = ulInnerWidth - 1;
697
698 AdjustWindowRect(&rcWindow, dwStyle, FALSE);
699 xWidth = rcWindow.right - rcWindow.left;
700 yHeight = rcWindow.bottom - rcWindow.top;
701
702 /* Compute the window position */
703 xPos = (GetSystemMetrics(SM_CXSCREEN) - xWidth) / 2;
704 yPos = (GetSystemMetrics(SM_CYSCREEN) - yHeight) / 2;
705
706 rcTitlePanel.top = 0;
707 rcTitlePanel.bottom = ulTitleHeight;
708 rcTitlePanel.left = 0;
709 rcTitlePanel.right = ulInnerWidth - 1;
710
711 rcLeftPanel.top = rcTitlePanel.bottom;
712 rcLeftPanel.bottom = ulInnerHeight - 1;
713 rcLeftPanel.left = 0;
714 rcLeftPanel.right = ulInnerWidth / 3;
715
716 rcRightPanel.top = rcLeftPanel.top;
717 rcRightPanel.bottom = rcLeftPanel.bottom;
718 rcRightPanel.left = rcLeftPanel.right;
719 rcRightPanel.right = ulInnerWidth - 1;
720
721 /* Load the configuration and the resources */
722 LoadConfiguration();
723
724 /* Create main window */
725 hWndMain = CreateWindow(szWindowClass,
726 szAppTitle,
727 dwStyle,
728 xPos,
729 yPos,
730 xWidth,
731 yHeight,
732 0,
733 0,
734 hInstance,
735 NULL);
736
737 hSystemMenu = GetSystemMenu(hWndMain, FALSE);
738 if (hSystemMenu)
739 {
740 RemoveMenu(hSystemMenu, SC_SIZE, MF_BYCOMMAND);
741 RemoveMenu(hSystemMenu, SC_MAXIMIZE, MF_BYCOMMAND);
742 }
743
744 ShowWindow(hWndMain, nCmdShow);
745 UpdateWindow(hWndMain);
746
747 while (GetMessage(&msg, NULL, 0, 0) != FALSE)
748 {
749 /* Check for ENTER key presses */
750 if (msg.message == WM_KEYDOWN && msg.wParam == VK_RETURN)
751 {
752 /*
753 * The user pressed the ENTER key. Retrieve the handle to the
754 * child window that has the keyboard focus, and send it a
755 * WM_COMMAND message.
756 */
757 hWndFocus = GetFocus();
758 if (hWndFocus)
759 {
760 SendMessage(hWndMain, WM_COMMAND,
761 (WPARAM)GetDlgCtrlID(hWndFocus), (LPARAM)hWndFocus);
762 }
763 }
764 /* Allow using keyboard navigation */
765 else if (!IsDialogMessage(hWndMain, &msg))
766 {
767 TranslateMessage(&msg);
768 DispatchMessage(&msg);
769 }
770 }
771
772 /* Cleanup */
773 FreeResources();
774
775 /* Close the mutex handle and quit */
776 CloseHandle(hMutex);
777 return msg.wParam;
778 }
779
780
781 INT_PTR CALLBACK
782 ButtonSubclassWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
783 {
784 static WPARAM wParamOld = 0;
785 static LPARAM lParamOld = 0;
786
787 LONG i;
788
789 if (uMsg == WM_MOUSEMOVE)
790 {
791 /* Ignore mouse-move messages on the same point */
792 if ((wParam == wParamOld) && (lParam == lParamOld))
793 return 0;
794
795 /* Retrieve the topic index of this button */
796 i = GetWindowLongPtr(hWnd, GWLP_ID) - TOPIC_BTN_ID_BASE;
797
798 /*
799 * Change the focus to this button if the current topic index differs
800 * (we will receive WM_SETFOCUS afterwards).
801 */
802 if (nTopic != i)
803 SetFocus(hWnd);
804
805 wParamOld = wParam;
806 lParamOld = lParam;
807 }
808 else if (uMsg == WM_SETFOCUS)
809 {
810 /* Retrieve the topic index of this button */
811 i = GetWindowLongPtr(hWnd, GWLP_ID) - TOPIC_BTN_ID_BASE;
812
813 /* Change the current topic index and repaint the description panel */
814 if (nTopic != i)
815 {
816 nTopic = i;
817 InvalidateRect(hWndMain, &rcRightPanel, TRUE);
818 }
819 }
820 else if (uMsg == WM_KILLFOCUS)
821 {
822 /*
823 * We lost focus, either because the user changed button focus,
824 * or because the main window to which we belong went inactivated.
825 * If we are in the latter case, we ignore the focus change.
826 * If we are in the former case, we reset to the default topic.
827 */
828 if (GetParent(hWnd) == GetForegroundWindow())
829 {
830 nTopic = -1;
831 InvalidateRect(hWndMain, &rcRightPanel, TRUE);
832 }
833 }
834
835 return CallWindowProc(fnOldBtn, hWnd, uMsg, wParam, lParam);
836 }
837
838
839 static BOOL
840 RunAction(INT nTopic)
841 {
842 PCWSTR Command = NULL, Args = NULL;
843
844 if (nTopic < 0)
845 return TRUE;
846
847 Command = pTopics[nTopic]->szCommand;
848 if (/* !Command && */ !*Command)
849 return TRUE;
850
851 /* Check for known actions */
852 if (!pTopics[nTopic]->bIsCommand)
853 {
854 if (!_tcsicmp(Command, TEXT("<exit>")))
855 return FALSE;
856
857 if (!_tcsnicmp(Command, TEXT("<msg>"), 5))
858 {
859 MessageBox(hWndMain, Command + 5, TEXT("ReactOS"), MB_OK | MB_TASKMODAL);
860 return TRUE;
861 }
862 }
863 else
864 /* Run the command */
865 {
866 Args = pTopics[nTopic]->szArgs;
867 if (!*Args) Args = NULL;
868 ShellExecute(NULL, NULL,
869 Command, Args,
870 NULL, SW_SHOWDEFAULT);
871 }
872
873 return TRUE;
874 }
875
876
877 static DWORD
878 GetButtonHeight(HDC hDC,
879 HFONT hFont,
880 LPCTSTR szText,
881 DWORD dwWidth)
882 {
883 HFONT hOldFont;
884 RECT rect;
885
886 rect.left = 0;
887 rect.right = dwWidth - 20;
888 rect.top = 0;
889 rect.bottom = 25;
890
891 hOldFont = (HFONT)SelectObject(hDC, hFont);
892 DrawText(hDC, szText, -1, &rect, DT_TOP | DT_CALCRECT | DT_WORDBREAK);
893 SelectObject(hDC, hOldFont);
894
895 return (rect.bottom-rect.top + 14);
896 }
897
898
899 static LRESULT
900 OnCreate(HWND hWnd, WPARAM wParam, LPARAM lParam)
901 {
902 UINT i;
903 INT nLength;
904 HDC ScreenDC;
905 LOGFONT lf;
906 DWORD dwTop;
907 DWORD dwHeight = 0;
908 TCHAR szText[80];
909
910 UNREFERENCED_PARAMETER(wParam);
911 UNREFERENCED_PARAMETER(lParam);
912
913 hbrLightBlue = CreateSolidBrush(LIGHT_BLUE);
914 hbrDarkBlue = CreateSolidBrush(DARK_BLUE);
915
916 ZeroMemory(&lf, sizeof(lf));
917
918 lf.lfEscapement = 0;
919 lf.lfOrientation = 0; // TA_BASELINE;
920 // lf.lfItalic = lf.lfUnderline = lf.lfStrikeOut = FALSE;
921 lf.lfCharSet = ANSI_CHARSET;
922 lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
923 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
924 lf.lfQuality = DEFAULT_QUALITY;
925 lf.lfPitchAndFamily = FF_DONTCARE;
926 if (LoadString(hInstance, IDS_FONTNAME, lf.lfFaceName, ARRAYSIZE(lf.lfFaceName)) == 0)
927 StringCchCopy(lf.lfFaceName, ARRAYSIZE(lf.lfFaceName), TEXT("Tahoma"));
928
929 /* Topic title font */
930 lf.lfHeight = -18;
931 lf.lfWidth = 0;
932 lf.lfWeight = FW_NORMAL;
933 hFontTopicTitle = CreateFontIndirect(&lf);
934
935 /* Topic description font */
936 lf.lfHeight = -11;
937 lf.lfWidth = 0;
938 lf.lfWeight = FW_THIN;
939 hFontTopicDescription = CreateFontIndirect(&lf);
940
941 /* Topic button font */
942 lf.lfHeight = -11;
943 lf.lfWidth = 0;
944 lf.lfWeight = FW_BOLD;
945 hFontTopicButton = CreateFontIndirect(&lf);
946
947 /* Load title bitmap */
948 if (hTitleBitmap)
949 hTitleBitmap = LoadBitmap(hInstance, MAKEINTRESOURCE(IDB_TITLE_BITMAP));
950
951 /* Load topic bitmaps */
952 hDefaultTopicBitmap = LoadBitmap(hInstance, MAKEINTRESOURCE(IDB_DEFAULT_TOPIC_BITMAP));
953 for (i = 0; i < dwNumberTopics; i++)
954 {
955 // FIXME: Not implemented yet!
956 // pTopics[i]->hBitmap = LoadBitmap(hInstance, MAKEINTRESOURCE(IDB_TOPIC_BITMAP0 + i));
957 pTopics[i]->hBitmap = NULL;
958 }
959
960 ScreenDC = GetWindowDC(hWnd);
961 hdcMem = CreateCompatibleDC(ScreenDC);
962 ReleaseDC(hWnd, ScreenDC);
963
964 /* Load and create the menu buttons */
965 dwTop = rcLeftPanel.top;
966 for (i = 0; i < dwNumberTopics; i++)
967 {
968 if (*pTopics[i]->szText)
969 {
970 dwHeight = GetButtonHeight(hdcMem,
971 hFontTopicButton,
972 pTopics[i]->szText,
973 rcLeftPanel.right - rcLeftPanel.left);
974
975 pTopics[i]->hWndButton = CreateWindow(TEXT("BUTTON"),
976 pTopics[i]->szText,
977 WS_CHILDWINDOW | WS_VISIBLE | WS_TABSTOP | BS_MULTILINE | BS_OWNERDRAW,
978 rcLeftPanel.left,
979 dwTop,
980 rcLeftPanel.right - rcLeftPanel.left,
981 dwHeight,
982 hWnd,
983 (HMENU)IntToPtr(TOPIC_BTN_ID_BASE + i), // Similar to SetWindowLongPtr(GWLP_ID)
984 hInstance,
985 NULL);
986 nDefaultTopic = i;
987 SendMessage(pTopics[i]->hWndButton, WM_SETFONT, (WPARAM)hFontTopicButton, MAKELPARAM(TRUE, 0));
988 fnOldBtn = (WNDPROC)SetWindowLongPtr(pTopics[i]->hWndButton, GWLP_WNDPROC, (DWORD_PTR)ButtonSubclassWndProc);
989 }
990 else
991 {
992 pTopics[i]->hWndButton = NULL;
993 }
994
995 dwTop += dwHeight;
996 }
997
998 /* Create the checkbox */
999 if (bDisplayCheckBox)
1000 {
1001 nLength = LoadString(hInstance, IDS_CHECKTEXT, szText, ARRAYSIZE(szText));
1002 if (nLength > 0)
1003 {
1004 lf.lfHeight = -10;
1005 lf.lfWidth = 0;
1006 lf.lfWeight = FW_THIN;
1007 hFontCheckButton = CreateFontIndirect(&lf);
1008
1009 hWndCheckButton = CreateWindow(TEXT("BUTTON"),
1010 szText,
1011 WS_CHILDWINDOW | WS_VISIBLE | WS_TABSTOP | BS_AUTOCHECKBOX | BS_MULTILINE /**/| BS_FLAT/**/,
1012 rcLeftPanel.left + 8,
1013 rcLeftPanel.bottom - 8 - 13,
1014 rcLeftPanel.right - rcLeftPanel.left - 16,
1015 13,
1016 hWnd,
1017 (HMENU)IDC_CHECKBUTTON,
1018 hInstance,
1019 NULL);
1020 SendMessage(hWndCheckButton, WM_SETFONT, (WPARAM)hFontCheckButton, MAKELPARAM(TRUE, 0));
1021 }
1022 else
1023 {
1024 hFontCheckButton = NULL;
1025 hWndCheckButton = NULL;
1026 }
1027 }
1028
1029 /* Create the "Exit" button */
1030 if (bDisplayExitBtn)
1031 {
1032 nLength = LoadString(hInstance, IDS_CLOSETEXT, szText, ARRAYSIZE(szText));
1033 if (nLength > 0)
1034 {
1035 hWndCloseButton = CreateWindow(TEXT("BUTTON"),
1036 szText,
1037 WS_CHILDWINDOW | WS_VISIBLE | WS_TABSTOP | BS_FLAT,
1038 rcRightPanel.right - 8 - 57,
1039 rcRightPanel.bottom - 8 - 21,
1040 57,
1041 21,
1042 hWnd,
1043 (HMENU)IDC_CLOSEBUTTON,
1044 hInstance,
1045 NULL);
1046 nDefaultTopic = -1;
1047 SendMessage(hWndCloseButton, WM_SETFONT, (WPARAM)hFontTopicButton, MAKELPARAM(TRUE, 0));
1048 }
1049 else
1050 {
1051 hWndCloseButton = NULL;
1052 }
1053 }
1054
1055 return 0;
1056 }
1057
1058
1059 static LRESULT
1060 OnCommand(HWND hWnd, WPARAM wParam, LPARAM lParam)
1061 {
1062 UNREFERENCED_PARAMETER(lParam);
1063
1064 /* Retrieve the low-word from wParam */
1065 wParam = LOWORD(wParam);
1066
1067 /* Execute action */
1068 if (wParam == IDC_CLOSEBUTTON)
1069 {
1070 DestroyWindow(hWnd);
1071 }
1072 else if (wParam - TOPIC_BTN_ID_BASE < dwNumberTopics)
1073 {
1074 if (RunAction(wParam - TOPIC_BTN_ID_BASE) == FALSE)
1075 DestroyWindow(hWnd); // Corresponds to a <exit> action.
1076 }
1077
1078 return 0;
1079 }
1080
1081
1082 static VOID
1083 PaintBanner(HDC hdc, LPRECT rcPanel)
1084 {
1085 HBITMAP hOldBitmap;
1086 HBRUSH hOldBrush;
1087
1088 /* Title bitmap */
1089 hOldBitmap = (HBITMAP)SelectObject(hdcMem, hTitleBitmap);
1090 BitBlt(hdc,
1091 rcPanel->left,
1092 rcPanel->top,
1093 rcPanel->right - rcPanel->left,
1094 rcPanel->bottom - 3,
1095 hdcMem, 0, 0, SRCCOPY);
1096 SelectObject(hdcMem, hOldBitmap);
1097
1098 /* Dark blue line */
1099 hOldBrush = (HBRUSH)SelectObject(hdc, hbrDarkBlue);
1100 PatBlt(hdc,
1101 rcPanel->left,
1102 rcPanel->bottom - 3,
1103 rcPanel->right - rcPanel->left,
1104 3,
1105 PATCOPY);
1106 SelectObject(hdc, hOldBrush);
1107 }
1108
1109
1110 static LRESULT
1111 OnPaint(HWND hWnd, WPARAM wParam, LPARAM lParam)
1112 {
1113 HPEN hPen;
1114 HPEN hOldPen;
1115 HDC hdc;
1116 PAINTSTRUCT ps;
1117 HBITMAP hOldBitmap = NULL;
1118 HBRUSH hOldBrush;
1119 HFONT hOldFont;
1120 RECT rcTitle, rcDescription;
1121 BITMAP bmpInfo;
1122 TCHAR szVersion[50];
1123 LPTSTR lpTitle = NULL, lpDesc = NULL;
1124
1125 UNREFERENCED_PARAMETER(wParam);
1126 UNREFERENCED_PARAMETER(lParam);
1127
1128 hdc = BeginPaint(hWnd, &ps);
1129
1130 /* Banner panel */
1131 PaintBanner(hdc, &rcTitlePanel);
1132
1133 /* Left panel */
1134 hOldBrush = (HBRUSH)SelectObject(hdc, hbrLightBlue);
1135 PatBlt(hdc,
1136 rcLeftPanel.left,
1137 rcLeftPanel.top,
1138 rcLeftPanel.right - rcLeftPanel.left,
1139 rcLeftPanel.bottom - rcLeftPanel.top,
1140 PATCOPY);
1141 SelectObject(hdc, hOldBrush);
1142
1143 /* Right panel */
1144 hOldBrush = (HBRUSH)SelectObject(hdc, GetStockObject(WHITE_BRUSH));
1145 PatBlt(hdc,
1146 rcRightPanel.left,
1147 rcRightPanel.top,
1148 rcRightPanel.right - rcRightPanel.left,
1149 rcRightPanel.bottom - rcRightPanel.top,
1150 PATCOPY);
1151 SelectObject(hdc, hOldBrush);
1152
1153 /* Draw dark vertical line */
1154 hPen = CreatePen(PS_SOLID, 0, DARK_BLUE);
1155 hOldPen = (HPEN)SelectObject(hdc, hPen);
1156 MoveToEx(hdc, rcRightPanel.left, rcRightPanel.top, NULL);
1157 LineTo(hdc, rcRightPanel.left, rcRightPanel.bottom);
1158 SelectObject(hdc, hOldPen);
1159 DeleteObject(hPen);
1160
1161 /* Draw topic bitmap */
1162 if ((nTopic == -1) && (hDefaultTopicBitmap))
1163 {
1164 GetObject(hDefaultTopicBitmap, sizeof(bmpInfo), &bmpInfo);
1165 hOldBitmap = (HBITMAP)SelectObject(hdcMem, hDefaultTopicBitmap);
1166 BitBlt(hdc,
1167 rcRightPanel.right - bmpInfo.bmWidth,
1168 rcRightPanel.bottom - bmpInfo.bmHeight,
1169 bmpInfo.bmWidth,
1170 bmpInfo.bmHeight,
1171 hdcMem,
1172 0,
1173 0,
1174 SRCCOPY);
1175 }
1176 else if ((nTopic != -1) && (pTopics[nTopic]->hBitmap))
1177 {
1178 GetObject(pTopics[nTopic]->hBitmap, sizeof(bmpInfo), &bmpInfo);
1179 hOldBitmap = (HBITMAP)SelectObject(hdcMem, pTopics[nTopic]->hBitmap);
1180 BitBlt(hdc,
1181 rcRightPanel.right - bmpInfo.bmWidth,
1182 rcRightPanel.bottom - bmpInfo.bmHeight,
1183 bmpInfo.bmWidth,
1184 bmpInfo.bmHeight,
1185 hdcMem,
1186 0,
1187 0,
1188 SRCCOPY);
1189 }
1190
1191 if (nTopic == -1)
1192 {
1193 lpTitle = szDefaultTitle;
1194 lpDesc = szDefaultDesc;
1195 }
1196 else
1197 {
1198 lpTitle = pTopics[nTopic]->szTitle;
1199 lpDesc = pTopics[nTopic]->szDesc;
1200 }
1201
1202 SetBkMode(hdc, TRANSPARENT);
1203
1204 /* Draw version information */
1205 StringCchCopy(szVersion, ARRAYSIZE(szVersion),
1206 TEXT("ReactOS ") TEXT(KERNEL_VERSION_STR));
1207
1208 /*
1209 * Compute the original rect (position & size) of the version info,
1210 * depending whether the checkbox is displayed (version info in the
1211 * right panel) or not (version info in the left panel).
1212 */
1213 if (bDisplayCheckBox)
1214 rcTitle = rcRightPanel;
1215 else
1216 rcTitle = rcLeftPanel;
1217
1218 rcTitle.left = rcTitle.left + 8;
1219 rcTitle.right = rcTitle.right - 5;
1220 rcTitle.top = rcTitle.bottom - 43;
1221 rcTitle.bottom = rcTitle.bottom - 8;
1222
1223 hOldFont = (HFONT)SelectObject(hdc, hFontTopicDescription);
1224 DrawText(hdc, szVersion, -1, &rcTitle, DT_BOTTOM | DT_CALCRECT | DT_SINGLELINE);
1225 SetTextColor(hdc, GetSysColor(COLOR_WINDOWTEXT));
1226 DrawText(hdc, szVersion, -1, &rcTitle, DT_BOTTOM | DT_SINGLELINE);
1227 SelectObject(hdc, hOldFont);
1228
1229 /* Draw topic title */
1230 rcTitle.left = rcRightPanel.left + 12;
1231 rcTitle.right = rcRightPanel.right - 8;
1232 rcTitle.top = rcRightPanel.top + 8;
1233 rcTitle.bottom = rcTitle.top + 57;
1234 hOldFont = (HFONT)SelectObject(hdc, hFontTopicTitle);
1235 DrawText(hdc, lpTitle, -1, &rcTitle, DT_TOP | DT_CALCRECT);
1236 SetTextColor(hdc, DARK_BLUE);
1237 DrawText(hdc, lpTitle, -1, &rcTitle, DT_TOP);
1238 SelectObject(hdc, hOldFont);
1239
1240 /* Draw topic description */
1241 rcDescription.left = rcRightPanel.left + 12;
1242 rcDescription.right = rcRightPanel.right - 8;
1243 rcDescription.top = rcTitle.bottom + 8;
1244 rcDescription.bottom = rcRightPanel.bottom - 20;
1245 hOldFont = (HFONT)SelectObject(hdc, hFontTopicDescription);
1246 SetTextColor(hdc, GetSysColor(COLOR_WINDOWTEXT));
1247 DrawText(hdc, lpDesc, -1, &rcDescription, DT_TOP | DT_WORDBREAK);
1248 SelectObject(hdc, hOldFont);
1249
1250 SetBkMode(hdc, OPAQUE);
1251
1252 SelectObject(hdcMem, hOldBrush);
1253 SelectObject(hdcMem, hOldBitmap);
1254
1255 EndPaint(hWnd, &ps);
1256
1257 return 0;
1258 }
1259
1260
1261 static LRESULT
1262 OnDrawItem(HWND hWnd, WPARAM wParam, LPARAM lParam)
1263 {
1264 LPDRAWITEMSTRUCT lpDis = (LPDRAWITEMSTRUCT)lParam;
1265 HPEN hPen, hOldPen;
1266 HBRUSH hOldBrush;
1267 INT iBkMode;
1268 TCHAR szText[80];
1269
1270 UNREFERENCED_PARAMETER(hWnd);
1271 UNREFERENCED_PARAMETER(wParam);
1272
1273 #if 0
1274 /* Neither the checkbox button nor the close button implement owner-drawing */
1275 if (lpDis->hwndItem == hWndCheckButton)
1276 return 0;
1277 if (lpDis->hwndItem == hWndCloseButton)
1278 {
1279 DrawFrameControl(lpDis->hDC,
1280 &lpDis->rcItem,
1281 DFC_BUTTON,
1282 DFCS_BUTTONPUSH | DFCS_FLAT);
1283 return TRUE;
1284 }
1285 #endif
1286
1287 if (lpDis->CtlID == (ULONG)(TOPIC_BTN_ID_BASE + nTopic))
1288 hOldBrush = (HBRUSH)SelectObject(lpDis->hDC, GetStockObject(WHITE_BRUSH));
1289 else
1290 hOldBrush = (HBRUSH)SelectObject(lpDis->hDC, hbrLightBlue);
1291
1292 PatBlt(lpDis->hDC,
1293 lpDis->rcItem.left,
1294 lpDis->rcItem.top,
1295 lpDis->rcItem.right,
1296 lpDis->rcItem.bottom,
1297 PATCOPY);
1298 SelectObject(lpDis->hDC, hOldBrush);
1299
1300 hPen = CreatePen(PS_SOLID, 0, DARK_BLUE);
1301 hOldPen = (HPEN)SelectObject(lpDis->hDC, hPen);
1302 MoveToEx(lpDis->hDC, lpDis->rcItem.left, lpDis->rcItem.bottom - 1, NULL);
1303 LineTo(lpDis->hDC, lpDis->rcItem.right, lpDis->rcItem.bottom - 1);
1304 SelectObject(lpDis->hDC, hOldPen);
1305 DeleteObject(hPen);
1306
1307 InflateRect(&lpDis->rcItem, -10, -4);
1308 OffsetRect(&lpDis->rcItem, 0, 1);
1309 GetWindowText(lpDis->hwndItem, szText, ARRAYSIZE(szText));
1310 SetTextColor(lpDis->hDC, GetSysColor(COLOR_WINDOWTEXT));
1311 iBkMode = SetBkMode(lpDis->hDC, TRANSPARENT);
1312 DrawText(lpDis->hDC, szText, -1, &lpDis->rcItem, DT_TOP | DT_LEFT | DT_WORDBREAK);
1313 SetBkMode(lpDis->hDC, iBkMode);
1314
1315 return TRUE;
1316 }
1317
1318
1319 static LRESULT
1320 OnMouseMove(HWND hWnd, WPARAM wParam, LPARAM lParam)
1321 {
1322 static WPARAM wParamOld = 0;
1323 static LPARAM lParamOld = 0;
1324
1325 /* Ignore mouse-move messages on the same point */
1326 if ((wParam == wParamOld) && (lParam == lParamOld))
1327 return 0;
1328
1329 /*
1330 * If the user moves the mouse over the main window, outside of the
1331 * topic buttons, reset the current topic to the default one and
1332 * change the focus to some other default button (to keep keyboard
1333 * navigation possible).
1334 */
1335 if (nTopic != -1)
1336 {
1337 INT nOldTopic = nTopic;
1338 nTopic = -1;
1339 /* Also repaint the buttons, otherwise nothing repaints... */
1340 InvalidateRect(pTopics[nOldTopic]->hWndButton, NULL, TRUE);
1341
1342 /* Set the focus to some other default button */
1343 if (hWndCheckButton)
1344 SetFocus(hWndCheckButton);
1345 else if (hWndCloseButton)
1346 SetFocus(hWndCloseButton);
1347 // SetFocus(hWnd);
1348
1349 /* Repaint the description panel */
1350 InvalidateRect(hWndMain, &rcRightPanel, TRUE);
1351 }
1352
1353 wParamOld = wParam;
1354 lParamOld = lParam;
1355
1356 return 0;
1357 }
1358
1359
1360 static LRESULT
1361 OnCtlColorStatic(HWND hWnd, WPARAM wParam, LPARAM lParam)
1362 {
1363 UNREFERENCED_PARAMETER(hWnd);
1364
1365 if ((HWND)lParam == hWndCheckButton)
1366 {
1367 SetBkMode((HDC)wParam, TRANSPARENT);
1368 return (LRESULT)hbrLightBlue;
1369 }
1370
1371 return 0;
1372 }
1373
1374
1375 static LRESULT
1376 OnActivate(HWND hWnd, WPARAM wParam, LPARAM lParam)
1377 {
1378 UNREFERENCED_PARAMETER(hWnd);
1379 UNREFERENCED_PARAMETER(lParam);
1380
1381 if (wParam != WA_INACTIVE)
1382 {
1383 /*
1384 * The main window is re-activated, set the focus back to
1385 * either the current topic or a default button.
1386 */
1387 if (nTopic != -1)
1388 SetFocus(pTopics[nTopic]->hWndButton);
1389 else if (hWndCheckButton)
1390 SetFocus(hWndCheckButton);
1391 else if (hWndCloseButton)
1392 SetFocus(hWndCloseButton);
1393
1394 // InvalidateRect(hWndMain, &rcRightPanel, TRUE);
1395 }
1396
1397 return 0;
1398 }
1399
1400
1401 static LRESULT
1402 OnDestroy(HWND hWnd, WPARAM wParam, LPARAM lParam)
1403 {
1404 UINT i;
1405
1406 UNREFERENCED_PARAMETER(hWnd);
1407 UNREFERENCED_PARAMETER(wParam);
1408 UNREFERENCED_PARAMETER(lParam);
1409
1410 for (i = 0; i < dwNumberTopics; i++)
1411 {
1412 if (pTopics[i]->hWndButton)
1413 DestroyWindow(pTopics[i]->hWndButton);
1414 }
1415
1416 if (hWndCloseButton)
1417 DestroyWindow(hWndCloseButton);
1418
1419 if (hWndCheckButton)
1420 DestroyWindow(hWndCheckButton);
1421
1422 DeleteDC(hdcMem);
1423
1424 /* Delete bitmaps */
1425 DeleteObject(hDefaultTopicBitmap);
1426 DeleteObject(hTitleBitmap);
1427 for (i = 0; i < dwNumberTopics; i++)
1428 {
1429 if (pTopics[i]->hBitmap)
1430 DeleteObject(pTopics[i]->hBitmap);
1431 }
1432
1433 DeleteObject(hFontTopicTitle);
1434 DeleteObject(hFontTopicDescription);
1435 DeleteObject(hFontTopicButton);
1436
1437 if (hFontCheckButton)
1438 DeleteObject(hFontCheckButton);
1439
1440 DeleteObject(hbrLightBlue);
1441 DeleteObject(hbrDarkBlue);
1442
1443 return 0;
1444 }
1445
1446
1447 INT_PTR CALLBACK
1448 MainWndProc(HWND hWnd,
1449 UINT uMsg,
1450 WPARAM wParam,
1451 LPARAM lParam)
1452 {
1453 switch (uMsg)
1454 {
1455 case WM_CREATE:
1456 return OnCreate(hWnd, wParam, lParam);
1457
1458 case WM_COMMAND:
1459 return OnCommand(hWnd, wParam, lParam);
1460
1461 case WM_ACTIVATE:
1462 return OnActivate(hWnd, wParam, lParam);
1463
1464 case WM_PAINT:
1465 return OnPaint(hWnd, wParam, lParam);
1466
1467 case WM_DRAWITEM:
1468 return OnDrawItem(hWnd, wParam, lParam);
1469
1470 case WM_CTLCOLORSTATIC:
1471 return OnCtlColorStatic(hWnd, wParam, lParam);
1472
1473 case WM_MOUSEMOVE:
1474 return OnMouseMove(hWnd, wParam, lParam);
1475
1476 case WM_DESTROY:
1477 OnDestroy(hWnd, wParam, lParam);
1478 PostQuitMessage(0);
1479 return 0;
1480 }
1481
1482 return DefWindowProc(hWnd, uMsg, wParam, lParam);
1483 }
1484
1485 /* EOF */