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