[WELCOME]: Addendum to r73656: The application title is also customizable!
[reactos.git] / reactos / 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/version.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 szFrameClass[] = TEXT("WelcomeWindowClass");
56 TCHAR szAppTitle[80];
57
58 HINSTANCE hInstance;
59
60 HWND hWndMain = NULL;
61 HWND hWndDefaultTopic = NULL;
62
63 HDC hdcMem = NULL;
64
65 ULONG ulInnerWidth = TITLE_WIDTH;
66 ULONG ulInnerHeight = (TITLE_WIDTH * 3) / 4;
67 ULONG ulTitleHeight = TITLE_HEIGHT + 3;
68
69 HBITMAP hTitleBitmap = NULL;
70 HBITMAP hDefaultTopicBitmap = NULL;
71 HWND hWndCloseButton = NULL;
72 HWND hWndCheckButton = NULL;
73
74 BOOL bDisplayCheckBox = FALSE; // FIXME: We should also repaint the OS version correctly!
75 BOOL bDisplayExitBtn = TRUE;
76
77 #define BUFFER_SIZE 1024
78
79 #define TOPIC_TITLE_LENGTH 80
80 #define TOPIC_DESC_LENGTH 1024
81
82 typedef struct _TOPIC
83 {
84 HBITMAP hBitmap;
85 HWND hWndButton;
86 TCHAR szText[80];
87 TCHAR szTitle[TOPIC_TITLE_LENGTH];
88 TCHAR szDesc[TOPIC_DESC_LENGTH];
89 TCHAR szAction[512];
90 } TOPIC, *PTOPIC;
91
92 DWORD dwNumberTopics = 0;
93 PTOPIC* pTopics = NULL;
94
95 TCHAR szDefaultTitle[TOPIC_TITLE_LENGTH];
96 TCHAR szDefaultDesc[TOPIC_DESC_LENGTH];
97
98 INT nTopic = -1; // Active (focused) topic
99 INT nDefaultTopic = -1; // Default selected topic
100
101 HFONT hFontTopicButton;
102 HFONT hFontTopicTitle;
103 HFONT hFontTopicDescription;
104 HFONT hFontCheckButton;
105
106 HBRUSH hbrLightBlue;
107 HBRUSH hbrDarkBlue;
108 HBRUSH hbrRightPanel;
109
110 RECT rcTitlePanel;
111 RECT rcLeftPanel;
112 RECT rcRightPanel;
113
114 WNDPROC fnOldBtn;
115
116
117 INT_PTR CALLBACK
118 MainWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
119
120
121 /* FUNCTIONS ****************************************************************/
122
123 INT GetLocaleName(IN LCID Locale, OUT LPTSTR lpLCData, IN SIZE_T cchData)
124 {
125 INT ret, ret2;
126
127 /* Try to retrieve the locale language name (LOCALE_SNAME is supported on Vista+) */
128 ret = GetLocaleInfo(Locale, LOCALE_SNAME, lpLCData, cchData);
129 if (ret || (GetLastError() != ERROR_INVALID_FLAGS))
130 return ret;
131
132 /*
133 * We failed because LOCALE_SNAME was unrecognized, so try to manually build
134 * a language name in the form xx-YY (WARNING: this method has its limitations).
135 */
136 ret = GetLocaleInfo(Locale, LOCALE_SISO639LANGNAME, lpLCData, cchData);
137 if (ret <= 1)
138 return ret;
139
140 lpLCData += (ret - 1);
141 cchData -= (ret - 1);
142 if (cchData <= 1)
143 return ret;
144
145 /* Try to get the second part; we add the '-' separator only if we succeed */
146 ret2 = GetLocaleInfo(Locale, LOCALE_SISO3166CTRYNAME, lpLCData + 1, cchData - 1);
147 if (ret2 <= 1)
148 return ret;
149 ret += ret2; // 'ret' already counts '-'.
150
151 *lpLCData = _T('-');
152
153 return ret;
154 }
155
156 VOID TranslateEscapes(IN OUT LPTSTR lpString)
157 {
158 LPTSTR pEscape = NULL; // Next backslash escape sequence.
159
160 while (lpString && *lpString)
161 {
162 /* Find the next backslash escape sequence */
163 pEscape = _tcschr(lpString, _T('\\'));
164 if (!pEscape)
165 break;
166
167 /* Go past the escape backslash */
168 lpString = pEscape + 1;
169
170 /* Find which sequence it is */
171 switch (*lpString)
172 {
173 case _T('\0'):
174 // *pEscape = _T('\0'); // Enable if one wants to convert \<NULL> into <NULL>.
175 // lpString = pEscape + 1; // Loop will stop at the next iteration.
176 break;
177
178 case _T('n'): case _T('r'):
179 // case _T('\\'): // others?
180 // So far we only need to deal with the newlines.
181 {
182 if (*lpString == _T('n'))
183 *pEscape = _T('\n');
184 else if (*lpString == _T('r'))
185 *pEscape = _T('\r');
186
187 memmove(lpString, lpString + 1, (_tcslen(lpString + 1) + 1) * sizeof(TCHAR));
188 break;
189 }
190
191 /* Unknown escape sequence, ignore it */
192 default:
193 lpString++;
194 break;
195 }
196 }
197 }
198
199 static BOOL
200 LoadLocalizedResourcesFromINI(LCID Locale, LPTSTR lpResPath)
201 {
202 DWORD dwRet;
203 DWORD dwSize;
204 TCHAR szBuffer[LOCALE_NAME_MAX_LENGTH];
205 TCHAR szIniPath[MAX_PATH];
206 LPTSTR lpszSections = NULL, lpszSection = NULL;
207 PTOPIC pTopic, *pTopicsTmp;
208
209 /* Retrieve the locale name (on which the INI file name is based) */
210 dwRet = (DWORD)GetLocaleName(Locale, szBuffer, ARRAYSIZE(szBuffer));
211 if (!dwRet)
212 {
213 /* Fall back to english (US) */
214 StringCchCopy(szBuffer, ARRAYSIZE(szBuffer), TEXT("en-US"));
215 }
216
217 /* Build the INI file name */
218 StringCchCopy(szIniPath, ARRAYSIZE(szIniPath), lpResPath);
219 StringCchCat(szIniPath, ARRAYSIZE(szIniPath), TEXT("\\"));
220 StringCchCat(szIniPath, ARRAYSIZE(szIniPath), szBuffer);
221 StringCchCat(szIniPath, ARRAYSIZE(szIniPath), TEXT(".ini"));
222
223 /* Verify that the file exists, otherwise fall back to english (US) */
224 if (GetFileAttributes(szIniPath) == INVALID_FILE_ATTRIBUTES)
225 {
226 StringCchCopy(szBuffer, ARRAYSIZE(szBuffer), TEXT("en-US"));
227
228 StringCchCopy(szIniPath, ARRAYSIZE(szIniPath), lpResPath);
229 StringCchCat(szIniPath, ARRAYSIZE(szIniPath), TEXT("\\"));
230 StringCchCat(szIniPath, ARRAYSIZE(szIniPath), szBuffer);
231 StringCchCat(szIniPath, ARRAYSIZE(szIniPath), TEXT(".ini"));
232 }
233
234 /* Verify that the file exists, otherwise fall back to internal (localized) resource */
235 if (GetFileAttributes(szIniPath) == INVALID_FILE_ATTRIBUTES)
236 return FALSE; // TODO: For localized resource, see the general function.
237
238 /* Try to load the default localized strings */
239 GetPrivateProfileString(TEXT("Defaults"), TEXT("AppTitle"), TEXT("ReactOS Welcome") /* default */,
240 szAppTitle, ARRAYSIZE(szAppTitle), szIniPath);
241 if (!GetPrivateProfileString(TEXT("Defaults"), TEXT("DefaultTopicTitle"), NULL /* default */,
242 szDefaultTitle, ARRAYSIZE(szDefaultTitle), szIniPath))
243 {
244 *szDefaultTitle = 0;
245 }
246 if (!GetPrivateProfileString(TEXT("Defaults"), TEXT("DefaultTopicDescription"), NULL /* default */,
247 szDefaultDesc, ARRAYSIZE(szDefaultDesc), szIniPath))
248 {
249 *szDefaultDesc = 0;
250 }
251 else
252 {
253 TranslateEscapes(szDefaultDesc);
254 }
255
256 /* Allocate a buffer big enough to hold all the section names */
257 for (dwSize = BUFFER_SIZE; ; dwSize += BUFFER_SIZE)
258 {
259 lpszSections = HeapAlloc(GetProcessHeap(), 0, dwSize * sizeof(TCHAR));
260 if (!lpszSections)
261 return TRUE; // FIXME!
262 dwRet = GetPrivateProfileSectionNames(lpszSections, dwSize, szIniPath);
263 if (dwRet < dwSize - 2)
264 break;
265 HeapFree(GetProcessHeap(), 0, lpszSections);
266 }
267
268 dwNumberTopics = 0;
269 pTopics = NULL;
270
271 /* Loop over the sections and load the topics */
272 lpszSection = lpszSections;
273 for (; lpszSection && *lpszSection; lpszSection += (_tcslen(lpszSection) + 1))
274 {
275 if (_tcsnicmp(lpszSection, TEXT("Topic"), 5) == 0)
276 {
277 /* Allocate (or reallocate) the list of topics */
278 if (!pTopics)
279 pTopicsTmp = HeapAlloc(GetProcessHeap(), 0, (dwNumberTopics + 1) * sizeof(*pTopics));
280 else
281 pTopicsTmp = HeapReAlloc(GetProcessHeap(), 0, pTopics, (dwNumberTopics + 1) * sizeof(*pTopics));
282 if (!pTopicsTmp)
283 break; // Cannot reallocate more
284 pTopics = pTopicsTmp;
285
286 /* Allocate a new topic */
287 pTopic = HeapAlloc(GetProcessHeap(), 0, sizeof(*pTopic));
288 if (!pTopic)
289 break; // Cannot reallocate more
290 pTopics[dwNumberTopics++] = pTopic;
291
292 /* Retrieve the information */
293 if (!GetPrivateProfileString(lpszSection, TEXT("Button"), NULL /* default */,
294 pTopic->szText, ARRAYSIZE(pTopic->szText), szIniPath))
295 {
296 *pTopic->szText = 0;
297 }
298 if (!GetPrivateProfileString(lpszSection, TEXT("Title"), NULL /* default */,
299 pTopic->szTitle, ARRAYSIZE(pTopic->szTitle), szIniPath))
300 {
301 *pTopic->szTitle = 0;
302 }
303 if (!GetPrivateProfileString(lpszSection, TEXT("Description"), NULL /* default */,
304 pTopic->szDesc, ARRAYSIZE(pTopic->szDesc), szIniPath))
305 {
306 *pTopic->szDesc = 0;
307 }
308 else
309 {
310 TranslateEscapes(pTopic->szDesc);
311 }
312 if (!GetPrivateProfileString(lpszSection, TEXT("Action"), NULL /* default */,
313 pTopic->szAction, ARRAYSIZE(pTopic->szAction), szIniPath))
314 {
315 *pTopic->szAction = 0;
316 }
317 else
318 {
319 TranslateEscapes(pTopic->szAction);
320 }
321 }
322 }
323
324 HeapFree(GetProcessHeap(), 0, lpszSections);
325
326 return TRUE;
327 }
328
329 static BOOL
330 LoadLocalizedResources(LPTSTR lpResPath)
331 {
332 #define MAX_NUMBER_INTERNAL_TOPICS 3
333
334 UINT i;
335 PTOPIC pTopic, *pTopicsTmp;
336
337 dwNumberTopics = 0;
338 pTopics = NULL;
339
340 /*
341 * First, try to load the default internal (localized) strings.
342 * They can be redefined by the localized INI files.
343 */
344 if (!LoadString(hInstance, IDS_APPTITLE, szAppTitle, ARRAYSIZE(szAppTitle)))
345 StringCchCopy(szAppTitle, ARRAYSIZE(szAppTitle), TEXT("ReactOS Welcome"));
346 if (!LoadString(hInstance, IDS_DEFAULTTOPICTITLE, szDefaultTitle, ARRAYSIZE(szDefaultTitle)))
347 *szDefaultTitle = 0;
348 if (!LoadString(hInstance, IDS_DEFAULTTOPICDESC, szDefaultDesc, ARRAYSIZE(szDefaultDesc)))
349 *szDefaultDesc = 0;
350
351 /* Try to load the resources from INI file */
352 if (*lpResPath && LoadLocalizedResourcesFromINI(LOCALE_USER_DEFAULT, lpResPath))
353 return TRUE;
354
355 /* We failed, fall back to internal (localized) resource */
356 for (i = 0; i < MAX_NUMBER_INTERNAL_TOPICS; ++i)
357 {
358 /* Allocate (or reallocate) the list of topics */
359 if (!pTopics)
360 pTopicsTmp = HeapAlloc(GetProcessHeap(), 0, (dwNumberTopics + 1) * sizeof(*pTopics));
361 else
362 pTopicsTmp = HeapReAlloc(GetProcessHeap(), 0, pTopics, (dwNumberTopics + 1) * sizeof(*pTopics));
363 if (!pTopicsTmp)
364 break; // Cannot reallocate more
365 pTopics = pTopicsTmp;
366
367 /* Allocate a new topic */
368 pTopic = HeapAlloc(GetProcessHeap(), 0, sizeof(*pTopic));
369 if (!pTopic)
370 break; // Cannot reallocate more
371 pTopics[dwNumberTopics++] = pTopic;
372
373 /* Retrieve the information */
374 if (!LoadString(hInstance, IDS_TOPICBUTTON0 + i, pTopic->szText, ARRAYSIZE(pTopic->szText)))
375 *pTopic->szText = 0;
376 if (!LoadString(hInstance, IDS_TOPICTITLE0 + i, pTopic->szTitle, ARRAYSIZE(pTopic->szTitle)))
377 *pTopic->szTitle = 0;
378 if (!LoadString(hInstance, IDS_TOPICDESC0 + i, pTopic->szDesc, ARRAYSIZE(pTopic->szDesc)))
379 *pTopic->szDesc = 0;
380 if (!LoadString(hInstance, IDS_TOPICACTION0 + i, pTopic->szAction, ARRAYSIZE(pTopic->szAction)))
381 *pTopic->szAction = 0;
382 }
383
384 return TRUE;
385 }
386
387 static VOID
388 FreeResources(VOID)
389 {
390 if (!pTopics)
391 return;
392
393 while (dwNumberTopics--)
394 {
395 if (pTopics[dwNumberTopics])
396 HeapFree(GetProcessHeap(), 0, pTopics[dwNumberTopics]);
397 }
398 HeapFree(GetProcessHeap(), 0, pTopics);
399 pTopics = NULL;
400 dwNumberTopics = 0;
401 }
402
403 static BOOL
404 LoadConfiguration(VOID)
405 {
406 TCHAR szAppPath[MAX_PATH];
407 TCHAR szIniPath[MAX_PATH];
408 TCHAR szResPath[MAX_PATH];
409
410 /* Retrieve the full path to this application */
411 GetModuleFileName(NULL, szAppPath, ARRAYSIZE(szAppPath));
412 if (*szAppPath)
413 {
414 LPTSTR lpFileName = _tcsrchr(szAppPath, _T('\\'));
415 if (lpFileName)
416 *lpFileName = 0;
417 else
418 *szAppPath = 0;
419 }
420
421 /* Build the full INI file path name */
422 StringCchCopy(szIniPath, ARRAYSIZE(szIniPath), szAppPath);
423 StringCchCat(szIniPath, ARRAYSIZE(szIniPath), TEXT("\\welcome.ini"));
424
425 /* Verify that the file exists, otherwise use the default configuration */
426 if (GetFileAttributes(szIniPath) == INVALID_FILE_ATTRIBUTES)
427 {
428 /* Use the default configuration and retrieve the default resources */
429 return LoadLocalizedResources(TEXT(""));
430 }
431
432 /* Load the settings from the INI configuration file */
433 bDisplayCheckBox = !!GetPrivateProfileInt(TEXT("Welcome"), TEXT("DisplayCheckBox"), FALSE /* default */, szIniPath);
434 bDisplayExitBtn = !!GetPrivateProfileInt(TEXT("Welcome"), TEXT("DisplayExitButton"), TRUE /* default */, szIniPath);
435
436 if (!GetPrivateProfileString(TEXT("Welcome"), TEXT("ResourceDir"), NULL /* default */,
437 szResPath, ARRAYSIZE(szResPath), szIniPath))
438 {
439 *szResPath = 0;
440 }
441
442 /* Set the current directory to the one of this application, and retrieve the resources */
443 SetCurrentDirectory(szAppPath);
444 return LoadLocalizedResources(szResPath);
445 }
446
447 #if 0
448 static VOID
449 ShowLastWin32Error(HWND hWnd)
450 {
451 LPTSTR lpMessageBuffer = NULL;
452 DWORD dwError = GetLastError();
453
454 if (dwError == ERROR_SUCCESS)
455 return;
456
457 if (!FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
458 FORMAT_MESSAGE_FROM_SYSTEM |
459 FORMAT_MESSAGE_IGNORE_INSERTS,
460 NULL,
461 dwError,
462 LANG_USER_DEFAULT,
463 (LPTSTR)&lpMessageBuffer,
464 0, NULL))
465 {
466 return;
467 }
468
469 MessageBox(hWnd, lpMessageBuffer, szAppTitle, MB_OK | MB_ICONERROR);
470 LocalFree(lpMessageBuffer);
471 }
472 #endif
473
474 int WINAPI
475 _tWinMain(HINSTANCE hInst,
476 HINSTANCE hPrevInstance,
477 LPTSTR lpszCmdLine,
478 int nCmdShow)
479 {
480 WNDCLASSEX wndclass;
481 MSG msg;
482 INT xPos, yPos;
483 INT xWidth, yHeight;
484 RECT rcWindow;
485 HICON hMainIcon;
486 HMENU hSystemMenu;
487 DWORD dwStyle = WS_OVERLAPPED | WS_CLIPCHILDREN | WS_CLIPSIBLINGS |
488 WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX;
489 BITMAP BitmapInfo;
490
491 UNREFERENCED_PARAMETER(hPrevInstance);
492 UNREFERENCED_PARAMETER(lpszCmdLine);
493
494 switch (GetUserDefaultUILanguage())
495 {
496 case MAKELANGID(LANG_HEBREW, SUBLANG_DEFAULT):
497 SetProcessDefaultLayout(LAYOUT_RTL);
498 break;
499
500 default:
501 break;
502 }
503
504 hInstance = hInst;
505
506 /* Load icons */
507 hMainIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_MAIN));
508
509 /* Register the window class */
510 wndclass.cbSize = sizeof(WNDCLASSEX);
511 wndclass.style = CS_HREDRAW | CS_VREDRAW;
512 wndclass.lpfnWndProc = (WNDPROC)MainWndProc;
513 wndclass.cbClsExtra = 0;
514 wndclass.cbWndExtra = 0;
515 wndclass.hInstance = hInstance;
516 wndclass.hIcon = hMainIcon;
517 wndclass.hIconSm = NULL;
518 wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
519 wndclass.hbrBackground = NULL;
520 wndclass.lpszMenuName = NULL;
521 wndclass.lpszClassName = szFrameClass;
522
523 RegisterClassEx(&wndclass);
524
525 hTitleBitmap = LoadBitmap(hInstance, MAKEINTRESOURCE(IDB_TITLEBITMAP));
526 if (hTitleBitmap != NULL)
527 {
528 GetObject(hTitleBitmap, sizeof(BITMAP), &BitmapInfo);
529 ulInnerWidth = BitmapInfo.bmWidth;
530 ulInnerHeight = (ulInnerWidth * 3) / 4;
531 ulTitleHeight = BitmapInfo.bmHeight + 3;
532 DeleteObject(hTitleBitmap);
533 }
534 ulInnerHeight -= GetSystemMetrics(SM_CYCAPTION);
535
536 rcWindow.top = 0;
537 rcWindow.bottom = ulInnerHeight - 1;
538 rcWindow.left = 0;
539 rcWindow.right = ulInnerWidth - 1;
540
541 AdjustWindowRect(&rcWindow, dwStyle, FALSE);
542 xWidth = rcWindow.right - rcWindow.left;
543 yHeight = rcWindow.bottom - rcWindow.top;
544
545 xPos = (GetSystemMetrics(SM_CXSCREEN) - xWidth) / 2;
546 yPos = (GetSystemMetrics(SM_CYSCREEN) - yHeight) / 2;
547
548 rcTitlePanel.top = 0;
549 rcTitlePanel.bottom = ulTitleHeight;
550 rcTitlePanel.left = 0;
551 rcTitlePanel.right = ulInnerWidth - 1;
552
553 rcLeftPanel.top = rcTitlePanel.bottom;
554 rcLeftPanel.bottom = ulInnerHeight - 1;
555 rcLeftPanel.left = 0;
556 rcLeftPanel.right = ulInnerWidth / 3;
557
558 rcRightPanel.top = rcLeftPanel.top;
559 rcRightPanel.bottom = rcLeftPanel.bottom;
560 rcRightPanel.left = rcLeftPanel.right;
561 rcRightPanel.right = ulInnerWidth - 1;
562
563 /* Load the configuration and the resources */
564 LoadConfiguration();
565
566 /* Create main window */
567 hWndMain = CreateWindow(szFrameClass,
568 szAppTitle,
569 dwStyle,
570 xPos,
571 yPos,
572 xWidth,
573 yHeight,
574 0,
575 0,
576 hInstance,
577 NULL);
578
579 hSystemMenu = GetSystemMenu(hWndMain, FALSE);
580 if (hSystemMenu)
581 {
582 RemoveMenu(hSystemMenu, SC_SIZE, MF_BYCOMMAND);
583 RemoveMenu(hSystemMenu, SC_MAXIMIZE, MF_BYCOMMAND);
584 }
585
586 ShowWindow(hWndMain, nCmdShow);
587 UpdateWindow(hWndMain);
588
589 while (GetMessage(&msg, NULL, 0, 0) != FALSE)
590 {
591 TranslateMessage(&msg);
592 DispatchMessage(&msg);
593 }
594
595 /* Cleanup */
596 FreeResources();
597
598 return msg.wParam;
599 }
600
601
602 INT_PTR CALLBACK
603 ButtonSubclassWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
604 {
605 LONG i;
606
607 if (uMsg == WM_MOUSEMOVE)
608 {
609 i = GetWindowLongPtr(hWnd, GWL_ID);
610 if (nTopic != i)
611 {
612 nTopic = i;
613 SetFocus(hWnd);
614 InvalidateRect(hWndMain, &rcRightPanel, TRUE);
615 }
616 }
617
618 return CallWindowProc(fnOldBtn, hWnd, uMsg, wParam, lParam);
619 }
620
621
622 static BOOL
623 RunAction(INT nTopic)
624 {
625 // TCHAR CurrentDir[MAX_PATH];
626 TCHAR Parameters[2];
627 TCHAR AppName[MAX_PATH];
628
629 InvalidateRect(hWndMain, NULL, TRUE);
630
631 if (nTopic < 0)
632 return TRUE;
633
634 // GetCurrentDirectory(ARRAYSIZE(CurrentDir), CurrentDir);
635
636 StringCchCopy(AppName, ARRAYSIZE(AppName), pTopics[nTopic]->szAction);
637 if (!*AppName)
638 return TRUE;
639
640 if (!_tcsicmp(AppName, TEXT("<exit>")))
641 return FALSE;
642
643 if (!_tcsnicmp(AppName, TEXT("<msg>"), 5))
644 {
645 MessageBox(hWndMain, AppName + 5, TEXT("ReactOS"), MB_OK | MB_TASKMODAL);
646 return TRUE;
647 }
648
649 if (_tcsicmp(AppName, TEXT("explorer.exe")) == 0)
650 {
651 // StringCchCat(AppName, ARRAYSIZE(AppName), TEXT(" "));
652 // StringCchCat(AppName, ARRAYSIZE(AppName), CurrentDir);
653 _tcscpy(Parameters, TEXT("\\"));
654 }
655 else
656 {
657 *Parameters = 0;
658 }
659
660 ShellExecute(NULL, NULL, AppName, Parameters, NULL, SW_SHOWDEFAULT);
661
662 return TRUE;
663 }
664
665
666 static VOID
667 SubclassButton(HWND hWnd)
668 {
669 fnOldBtn = (WNDPROC)SetWindowLongPtr(hWnd, GWL_WNDPROC, (DWORD_PTR)ButtonSubclassWndProc);
670 }
671
672
673 static DWORD
674 GetButtonHeight(HDC hDC,
675 HFONT hFont,
676 LPCTSTR szText,
677 DWORD dwWidth)
678 {
679 HFONT hOldFont;
680 RECT rect;
681
682 rect.left = 0;
683 rect.right = dwWidth - 20;
684 rect.top = 0;
685 rect.bottom = 25;
686
687 hOldFont = (HFONT)SelectObject(hDC, hFont);
688 DrawText(hDC, szText, -1, &rect, DT_TOP | DT_CALCRECT | DT_WORDBREAK);
689 SelectObject(hDC, hOldFont);
690
691 return (rect.bottom-rect.top + 14);
692 }
693
694
695 static LRESULT
696 OnCreate(HWND hWnd, WPARAM wParam, LPARAM lParam)
697 {
698 TCHAR szText[80];
699 UINT i;
700 INT nLength;
701 HDC ScreenDC;
702 DWORD dwTop;
703 DWORD dwHeight = 0;
704
705 UNREFERENCED_PARAMETER(wParam);
706 UNREFERENCED_PARAMETER(lParam);
707
708 hbrLightBlue = CreateSolidBrush(LIGHT_BLUE);
709 hbrDarkBlue = CreateSolidBrush(DARK_BLUE);
710 hbrRightPanel = CreateSolidBrush(RGB(255, 255, 255));
711
712 /* Topic title font */
713 hFontTopicTitle = CreateFont(-18, 0, 0, 0, FW_NORMAL,
714 FALSE, FALSE, FALSE,
715 ANSI_CHARSET,
716 OUT_DEFAULT_PRECIS,
717 CLIP_DEFAULT_PRECIS,
718 DEFAULT_QUALITY,
719 FF_DONTCARE,
720 TEXT("Arial"));
721
722 /* Topic description font */
723 hFontTopicDescription = CreateFont(-11, 0, 0, 0, FW_THIN,
724 FALSE, FALSE, FALSE,
725 ANSI_CHARSET,
726 OUT_DEFAULT_PRECIS,
727 CLIP_DEFAULT_PRECIS,
728 DEFAULT_QUALITY,
729 FF_DONTCARE,
730 TEXT("Arial"));
731
732 /* Topic button font */
733 hFontTopicButton = CreateFont(-11, 0, 0, 0, FW_BOLD,
734 FALSE, FALSE, FALSE,
735 ANSI_CHARSET,
736 OUT_DEFAULT_PRECIS,
737 CLIP_DEFAULT_PRECIS,
738 DEFAULT_QUALITY,
739 FF_DONTCARE,
740 TEXT("Arial"));
741
742 /* Load title bitmap */
743 if (hTitleBitmap != NULL)
744 hTitleBitmap = LoadBitmap(hInstance, MAKEINTRESOURCE(IDB_TITLEBITMAP));
745
746 /* Load topic bitmaps */
747 hDefaultTopicBitmap = LoadBitmap(hInstance, MAKEINTRESOURCE(IDB_DEFAULTTOPICBITMAP));
748 for (i = 0; i < dwNumberTopics; i++)
749 {
750 // FIXME: Not implemented yet!
751 // pTopics[i]->hBitmap = LoadBitmap(hInstance, MAKEINTRESOURCE(IDB_TOPICBITMAP0 + i));
752 pTopics[i]->hBitmap = NULL;
753 }
754
755 ScreenDC = GetWindowDC(hWnd);
756 hdcMem = CreateCompatibleDC(ScreenDC);
757 ReleaseDC(hWnd, ScreenDC);
758
759 /* Load and create buttons */
760 dwTop = rcLeftPanel.top;
761 for (i = 0; i < dwNumberTopics; i++)
762 {
763 if (*pTopics[i]->szText)
764 {
765 dwHeight = GetButtonHeight(hdcMem,
766 hFontTopicButton,
767 pTopics[i]->szText,
768 rcLeftPanel.right - rcLeftPanel.left);
769
770 pTopics[i]->hWndButton = CreateWindow(TEXT("BUTTON"),
771 pTopics[i]->szText,
772 WS_CHILDWINDOW | WS_VISIBLE | WS_TABSTOP | BS_MULTILINE | BS_OWNERDRAW,
773 rcLeftPanel.left,
774 dwTop,
775 rcLeftPanel.right - rcLeftPanel.left,
776 dwHeight,
777 hWnd,
778 (HMENU)IntToPtr(i),
779 hInstance,
780 NULL);
781 hWndDefaultTopic = pTopics[i]->hWndButton;
782 nDefaultTopic = i;
783 SubclassButton(pTopics[i]->hWndButton);
784 SendMessage(pTopics[i]->hWndButton, WM_SETFONT, (WPARAM)hFontTopicButton, MAKELPARAM(TRUE, 0));
785 }
786 else
787 {
788 pTopics[i]->hWndButton = NULL;
789 }
790
791 dwTop += dwHeight;
792 }
793
794 /* Create "Exit" button */
795 if (bDisplayExitBtn)
796 {
797 nLength = LoadString(hInstance, IDS_CLOSETEXT, szText, ARRAYSIZE(szText));
798 if (nLength > 0)
799 {
800 hWndCloseButton = CreateWindow(TEXT("BUTTON"),
801 szText,
802 WS_VISIBLE | WS_CHILD | BS_FLAT,
803 rcRightPanel.right - 10 - 57,
804 rcRightPanel.bottom - 10 - 21,
805 57,
806 21,
807 hWnd,
808 (HMENU)IDC_CLOSEBUTTON,
809 hInstance,
810 NULL);
811 hWndDefaultTopic = NULL;
812 nDefaultTopic = -1;
813 SendMessage(hWndCloseButton, WM_SETFONT, (WPARAM)hFontTopicButton, MAKELPARAM(TRUE, 0));
814 }
815 else
816 {
817 hWndCloseButton = NULL;
818 }
819 }
820
821 /* Create checkbox */
822 if (bDisplayCheckBox)
823 {
824 nLength = LoadString(hInstance, IDS_CHECKTEXT, szText, ARRAYSIZE(szText));
825 if (nLength > 0)
826 {
827 hFontCheckButton = CreateFont(-10, 0, 0, 0, FW_THIN,
828 FALSE, FALSE, FALSE,
829 ANSI_CHARSET,
830 OUT_DEFAULT_PRECIS,
831 CLIP_DEFAULT_PRECIS,
832 DEFAULT_QUALITY,
833 FF_DONTCARE,
834 TEXT("Tahoma"));
835
836 hWndCheckButton = CreateWindow(TEXT("BUTTON"),
837 szText,
838 WS_VISIBLE | WS_CHILD | BS_AUTOCHECKBOX /**/| BS_FLAT/**/,
839 rcLeftPanel.left + 8,
840 rcLeftPanel.bottom - 8 - 13,
841 rcLeftPanel.right - rcLeftPanel.left - 16,
842 13,
843 hWnd,
844 (HMENU)IDC_CHECKBUTTON,
845 hInstance,
846 NULL);
847 SendMessage(hWndCheckButton, WM_SETFONT, (WPARAM)hFontCheckButton, MAKELPARAM(TRUE, 0));
848 }
849 else
850 {
851 hFontCheckButton = NULL;
852 hWndCheckButton = NULL;
853 }
854 }
855
856 return 0;
857 }
858
859
860 static LRESULT
861 OnCommand(HWND hWnd, WPARAM wParam, LPARAM lParam)
862 {
863 UNREFERENCED_PARAMETER(lParam);
864
865 if (LOWORD(wParam) == IDC_CLOSEBUTTON)
866 {
867 DestroyWindow(hWnd);
868 }
869 else if ((LOWORD(wParam) < dwNumberTopics))
870 {
871 if (RunAction(LOWORD(wParam)) == FALSE)
872 DestroyWindow(hWnd); // Corresponds to a <exit> action.
873 }
874
875 return 0;
876 }
877
878
879 static VOID
880 PaintBanner(HDC hdc, LPRECT rcPanel)
881 {
882 HBITMAP hOldBitmap;
883 HBRUSH hOldBrush;
884
885 /* Title bitmap */
886 hOldBitmap = (HBITMAP)SelectObject(hdcMem, hTitleBitmap);
887 BitBlt(hdc,
888 rcPanel->left,
889 rcPanel->top,
890 rcPanel->right - rcPanel->left,
891 rcPanel->bottom - 3,
892 hdcMem, 0, 0, SRCCOPY);
893 SelectObject(hdcMem, hOldBitmap);
894
895 /* Dark blue line */
896 hOldBrush = (HBRUSH)SelectObject(hdc, hbrDarkBlue);
897 PatBlt(hdc,
898 rcPanel->left,
899 rcPanel->bottom - 3,
900 rcPanel->right - rcPanel->left,
901 3,
902 PATCOPY);
903
904 SelectObject(hdc, hOldBrush);
905 }
906
907
908 static LRESULT
909 OnPaint(HWND hWnd, WPARAM wParam, LPARAM lParam)
910 {
911 HPEN hPen;
912 HPEN hOldPen;
913 HDC hdc;
914 PAINTSTRUCT ps;
915 HBITMAP hOldBitmap = NULL;
916 HBRUSH hOldBrush;
917 HFONT hOldFont;
918 RECT rcTitle, rcDescription;
919 BITMAP bmpInfo;
920 TCHAR version[50];
921 LPTSTR lpTitle = NULL, lpDesc = NULL;
922
923 UNREFERENCED_PARAMETER(wParam);
924 UNREFERENCED_PARAMETER(lParam);
925
926 hdc = BeginPaint(hWnd, &ps);
927
928 /* Banner panel */
929 PaintBanner(hdc, &rcTitlePanel);
930
931 /* Left panel */
932 hOldBrush = (HBRUSH)SelectObject(hdc, hbrLightBlue);
933 PatBlt(hdc,
934 rcLeftPanel.left,
935 rcLeftPanel.top,
936 rcLeftPanel.right - rcLeftPanel.left,
937 rcLeftPanel.bottom - rcLeftPanel.top,
938 PATCOPY);
939 SelectObject(hdc, hOldBrush);
940
941 /* Right panel */
942 hOldBrush = (HBRUSH)SelectObject(hdc, WHITE_BRUSH);
943 PatBlt(hdc,
944 rcRightPanel.left,
945 rcRightPanel.top,
946 rcRightPanel.right - rcRightPanel.left,
947 rcRightPanel.bottom - rcRightPanel.top,
948 PATCOPY);
949 SelectObject(hdc, hOldBrush);
950
951 /* Draw dark vertical line */
952 hPen = CreatePen(PS_SOLID, 0, DARK_BLUE);
953 hOldPen = (HPEN)SelectObject(hdc, hPen);
954 MoveToEx(hdc, rcRightPanel.left, rcRightPanel.top, NULL);
955 LineTo(hdc, rcRightPanel.left, rcRightPanel.bottom);
956 SelectObject(hdc, hOldPen);
957 DeleteObject(hPen);
958
959 /* Draw topic bitmap */
960 if ((nTopic == -1) && (hDefaultTopicBitmap != NULL))
961 {
962 GetObject(hDefaultTopicBitmap, sizeof(BITMAP), &bmpInfo);
963 hOldBitmap = (HBITMAP)SelectObject(hdcMem, hDefaultTopicBitmap);
964 BitBlt(hdc,
965 rcRightPanel.right - bmpInfo.bmWidth,
966 rcRightPanel.bottom - bmpInfo.bmHeight,
967 bmpInfo.bmWidth,
968 bmpInfo.bmHeight,
969 hdcMem,
970 0,
971 0,
972 SRCCOPY);
973 }
974 else if ((nTopic != -1) && (pTopics[nTopic]->hBitmap != NULL))
975 {
976 GetObject(pTopics[nTopic]->hBitmap, sizeof(BITMAP), &bmpInfo);
977 hOldBitmap = (HBITMAP)SelectObject(hdcMem, pTopics[nTopic]->hBitmap);
978 BitBlt(hdc,
979 rcRightPanel.right - bmpInfo.bmWidth,
980 rcRightPanel.bottom - bmpInfo.bmHeight,
981 bmpInfo.bmWidth,
982 bmpInfo.bmHeight,
983 hdcMem,
984 0,
985 0,
986 SRCCOPY);
987 }
988
989 if (nTopic == -1)
990 {
991 lpTitle = szDefaultTitle;
992 lpDesc = szDefaultDesc;
993 }
994 else
995 {
996 lpTitle = pTopics[nTopic]->szTitle;
997 lpDesc = pTopics[nTopic]->szDesc;
998 }
999
1000 SetBkMode(hdc, TRANSPARENT);
1001
1002 /* Draw version information */
1003 _stprintf(version, TEXT("ReactOS %d.%d.%d"),
1004 KERNEL_VERSION_MAJOR,
1005 KERNEL_VERSION_MINOR,
1006 KERNEL_VERSION_PATCH_LEVEL);
1007
1008 rcTitle.left = rcLeftPanel.left + 8;
1009 rcTitle.right = rcLeftPanel.right - 5;
1010 rcTitle.top = rcLeftPanel.bottom - 40;
1011 rcTitle.bottom = rcLeftPanel.bottom - 5;
1012 hOldFont = (HFONT)SelectObject(hdc, hFontTopicDescription);
1013 DrawText(hdc, version, -1, &rcTitle, DT_BOTTOM | DT_CALCRECT | DT_SINGLELINE);
1014 DrawText(hdc, version, -1, &rcTitle, DT_BOTTOM | DT_SINGLELINE);
1015 SelectObject(hdc, hOldFont);
1016
1017 /* Draw topic title */
1018 rcTitle.left = rcRightPanel.left + 12;
1019 rcTitle.right = rcRightPanel.right - 8;
1020 rcTitle.top = rcRightPanel.top + 8;
1021 rcTitle.bottom = rcTitle.top + 57;
1022 hOldFont = (HFONT)SelectObject(hdc, hFontTopicTitle);
1023 DrawText(hdc, lpTitle, -1, &rcTitle, DT_TOP | DT_CALCRECT);
1024
1025 SetTextColor(hdc, DARK_BLUE);
1026 DrawText(hdc, lpTitle, -1, &rcTitle, DT_TOP);
1027
1028 /* Draw topic description */
1029 rcDescription.left = rcRightPanel.left + 12;
1030 rcDescription.right = rcRightPanel.right - 8;
1031 rcDescription.top = rcTitle.bottom + 8;
1032 rcDescription.bottom = rcRightPanel.bottom - 20;
1033
1034 SelectObject(hdc, hFontTopicDescription);
1035 SetTextColor(hdc, RGB(0, 0, 0));
1036 DrawText(hdc, lpDesc, -1, &rcDescription, DT_TOP | DT_WORDBREAK);
1037
1038 SetBkMode(hdc, OPAQUE);
1039 SelectObject(hdc, hOldFont);
1040
1041 SelectObject(hdcMem, hOldBrush);
1042 SelectObject(hdcMem, hOldBitmap);
1043
1044 EndPaint(hWnd, &ps);
1045
1046 return 0;
1047 }
1048
1049
1050 static LRESULT
1051 OnDrawItem(HWND hWnd, WPARAM wParam, LPARAM lParam)
1052 {
1053 LPDRAWITEMSTRUCT lpDis = (LPDRAWITEMSTRUCT)lParam;
1054 HPEN hPen, hOldPen;
1055 HBRUSH hOldBrush;
1056 INT iBkMode;
1057 TCHAR szText[80];
1058
1059 UNREFERENCED_PARAMETER(hWnd);
1060 UNREFERENCED_PARAMETER(wParam);
1061
1062 if (lpDis->hwndItem == hWndCloseButton)
1063 {
1064 DrawFrameControl(lpDis->hDC,
1065 &lpDis->rcItem,
1066 DFC_BUTTON,
1067 DFCS_BUTTONPUSH | DFCS_FLAT);
1068 }
1069 else
1070 {
1071 if (lpDis->CtlID == (ULONG)nTopic)
1072 hOldBrush = (HBRUSH)SelectObject(lpDis->hDC, hbrRightPanel);
1073 else
1074 hOldBrush = (HBRUSH)SelectObject(lpDis->hDC, hbrLightBlue);
1075
1076 PatBlt(lpDis->hDC,
1077 lpDis->rcItem.left,
1078 lpDis->rcItem.top,
1079 lpDis->rcItem.right,
1080 lpDis->rcItem.bottom,
1081 PATCOPY);
1082 SelectObject(lpDis->hDC, hOldBrush);
1083
1084 hPen = CreatePen(PS_SOLID, 0, DARK_BLUE);
1085 hOldPen = (HPEN)SelectObject(lpDis->hDC, hPen);
1086 MoveToEx(lpDis->hDC, lpDis->rcItem.left, lpDis->rcItem.bottom - 1, NULL);
1087 LineTo(lpDis->hDC, lpDis->rcItem.right, lpDis->rcItem.bottom - 1);
1088 SelectObject(lpDis->hDC, hOldPen);
1089 DeleteObject(hPen);
1090
1091 InflateRect(&lpDis->rcItem, -10, -4);
1092 OffsetRect(&lpDis->rcItem, 0, 1);
1093 GetWindowText(lpDis->hwndItem, szText, ARRAYSIZE(szText));
1094 SetTextColor(lpDis->hDC, RGB(0, 0, 0));
1095 iBkMode = SetBkMode(lpDis->hDC, TRANSPARENT);
1096 DrawText(lpDis->hDC, szText, -1, &lpDis->rcItem, DT_TOP | DT_LEFT | DT_WORDBREAK);
1097 SetBkMode(lpDis->hDC, iBkMode);
1098 }
1099
1100 return 0;
1101 }
1102
1103
1104 static LRESULT
1105 OnMouseMove(HWND hWnd, WPARAM wParam, LPARAM lParam)
1106 {
1107 UNREFERENCED_PARAMETER(wParam);
1108 UNREFERENCED_PARAMETER(lParam);
1109
1110 if (nTopic != -1)
1111 {
1112 nTopic = -1;
1113 SetFocus(hWnd);
1114 InvalidateRect(hWndMain, &rcRightPanel, TRUE);
1115 }
1116
1117 return 0;
1118 }
1119
1120
1121 static LRESULT
1122 OnCtlColorStatic(HWND hWnd, WPARAM wParam, LPARAM lParam)
1123 {
1124 UNREFERENCED_PARAMETER(hWnd);
1125
1126 if ((HWND)lParam == hWndCheckButton)
1127 {
1128 SetBkColor((HDC)wParam, LIGHT_BLUE);
1129 return (LRESULT)hbrLightBlue;
1130 }
1131
1132 return 0;
1133 }
1134
1135
1136 static LRESULT
1137 OnActivate(HWND hWnd, WPARAM wParam, LPARAM lParam)
1138 {
1139 UNREFERENCED_PARAMETER(hWnd);
1140 UNREFERENCED_PARAMETER(wParam);
1141 UNREFERENCED_PARAMETER(lParam);
1142
1143 nTopic = -1;
1144 InvalidateRect(hWndMain, &rcRightPanel, TRUE);
1145 return 0;
1146 }
1147
1148
1149 static LRESULT
1150 OnDestroy(HWND hWnd, WPARAM wParam, LPARAM lParam)
1151 {
1152 UINT i;
1153
1154 UNREFERENCED_PARAMETER(hWnd);
1155 UNREFERENCED_PARAMETER(wParam);
1156 UNREFERENCED_PARAMETER(lParam);
1157
1158 for (i = 0; i < dwNumberTopics; i++)
1159 {
1160 if (pTopics[i]->hWndButton != NULL)
1161 DestroyWindow(pTopics[i]->hWndButton);
1162 }
1163
1164 if (hWndCloseButton != NULL)
1165 DestroyWindow(hWndCloseButton);
1166
1167 if (hWndCheckButton != NULL)
1168 DestroyWindow(hWndCheckButton);
1169
1170 DeleteDC(hdcMem);
1171
1172 /* Delete bitmaps */
1173 DeleteObject(hDefaultTopicBitmap);
1174 DeleteObject(hTitleBitmap);
1175 for (i = 0; i < dwNumberTopics; i++)
1176 {
1177 if (pTopics[i]->hBitmap != NULL)
1178 DeleteObject(pTopics[i]->hBitmap);
1179 }
1180
1181 DeleteObject(hFontTopicTitle);
1182 DeleteObject(hFontTopicDescription);
1183 DeleteObject(hFontTopicButton);
1184
1185 if (hFontCheckButton != NULL)
1186 DeleteObject(hFontCheckButton);
1187
1188 DeleteObject(hbrLightBlue);
1189 DeleteObject(hbrDarkBlue);
1190 DeleteObject(hbrRightPanel);
1191
1192 return 0;
1193 }
1194
1195
1196 INT_PTR CALLBACK
1197 MainWndProc(HWND hWnd,
1198 UINT uMsg,
1199 WPARAM wParam,
1200 LPARAM lParam)
1201 {
1202 switch (uMsg)
1203 {
1204 case WM_CREATE:
1205 return OnCreate(hWnd, wParam, lParam);
1206
1207 case WM_COMMAND:
1208 return OnCommand(hWnd, wParam, lParam);
1209
1210 case WM_ACTIVATE:
1211 return OnActivate(hWnd, wParam, lParam);
1212
1213 case WM_PAINT:
1214 return OnPaint(hWnd, wParam, lParam);
1215
1216 case WM_DRAWITEM:
1217 return OnDrawItem(hWnd, wParam, lParam);
1218
1219 case WM_CTLCOLORSTATIC:
1220 return OnCtlColorStatic(hWnd, wParam, lParam);
1221
1222 case WM_MOUSEMOVE:
1223 return OnMouseMove(hWnd, wParam, lParam);
1224
1225 case WM_DESTROY:
1226 OnDestroy(hWnd, wParam, lParam);
1227 PostQuitMessage(0);
1228 return 0;
1229 }
1230
1231 return DefWindowProc(hWnd, uMsg, wParam, lParam);
1232 }
1233
1234 /* EOF */