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