3 * Copyright (C) 2001, 2002, 2003 ReactOS Team
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.
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.
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.
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
28 * This utility can be customized by using localized INI configuration files.
29 * The default strings are stored in the utility's resources.
43 #include <reactos/buildno.h>
47 #define LIGHT_BLUE RGB(214, 239, 247)
48 #define DARK_BLUE RGB(107, 123, 140)
50 #define TITLE_WIDTH 480
51 #define TITLE_HEIGHT 93
53 /* GLOBALS ******************************************************************/
55 TCHAR szWindowClass
[] = TEXT("WelcomeWindowClass");
62 HWND hWndCheckButton
= NULL
;
63 HWND hWndCloseButton
= NULL
;
65 BOOL bDisplayCheckBox
= FALSE
;
66 BOOL bDisplayExitBtn
= TRUE
;
68 #define BUFFER_SIZE 1024
70 #define TOPIC_TITLE_LENGTH 80
71 #define TOPIC_DESC_LENGTH 1024
79 * TRUE : szCommand contains a command (e.g. executable to run);
80 * FALSE: szCommand contains a custom "Welcome"/AutoRun action.
85 TCHAR szTitle
[TOPIC_TITLE_LENGTH
];
86 TCHAR szDesc
[TOPIC_DESC_LENGTH
];
91 DWORD dwNumberTopics
= 0;
92 PTOPIC
* pTopics
= NULL
;
96 TCHAR szDefaultTitle
[TOPIC_TITLE_LENGTH
];
97 TCHAR szDefaultDesc
[TOPIC_DESC_LENGTH
];
99 #define TOPIC_BTN_ID_BASE 100
101 INT nTopic
= -1; // Active (focused) topic
102 INT nDefaultTopic
= -1; // Default selected topic
105 HBITMAP hTitleBitmap
= NULL
;
106 HBITMAP hDefaultTopicBitmap
= NULL
;
108 HFONT hFontTopicButton
;
109 HFONT hFontTopicTitle
;
110 HFONT hFontTopicDescription
;
111 HFONT hFontCheckButton
;
122 MainWndProc(HWND hWnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
);
125 /* FUNCTIONS ****************************************************************/
127 INT
GetLocaleName(IN LCID Locale
, OUT LPTSTR lpLCData
, IN SIZE_T cchData
)
131 /* Try to retrieve the locale language name (LOCALE_SNAME is supported on Vista+) */
132 cchRet
= GetLocaleInfo(Locale
, LOCALE_SNAME
, lpLCData
, cchData
);
133 if (cchRet
|| (GetLastError() != ERROR_INVALID_FLAGS
))
137 * We failed because LOCALE_SNAME was unrecognized, so try to manually build
138 * a language name in the form xx-YY (WARNING: this method has its limitations).
140 cchRet
= GetLocaleInfo(Locale
, LOCALE_SISO639LANGNAME
, lpLCData
, cchData
);
144 lpLCData
+= (cchRet
- 1);
145 cchData
-= (cchRet
- 1);
149 /* Try to get the second part; we add the '-' separator only if we succeed */
150 cchRet2
= GetLocaleInfo(Locale
, LOCALE_SISO3166CTRYNAME
, lpLCData
+ 1, cchData
- 1);
153 cchRet
+= cchRet2
; // 'cchRet' already counts '-'.
160 VOID
TranslateEscapes(IN OUT LPTSTR lpString
)
162 LPTSTR pEscape
= NULL
; // Next backslash escape sequence.
164 while (lpString
&& *lpString
)
166 /* Find the next backslash escape sequence */
167 pEscape
= _tcschr(lpString
, _T('\\'));
171 /* Go past the escape backslash */
172 lpString
= pEscape
+ 1;
174 /* Find which sequence it is */
178 // *pEscape = _T('\0'); // Enable if one wants to convert \<NULL> into <NULL>.
179 // lpString = pEscape + 1; // Loop will stop at the next iteration.
182 /* New-line and carriage return */
183 case _T('n'): case _T('r'):
184 // case _T('\\'): // others?
185 // So far we only need to deal with the newlines.
187 if (*lpString
== _T('n'))
189 else if (*lpString
== _T('r'))
192 memmove(lpString
, lpString
+ 1, (_tcslen(lpString
+ 1) + 1) * sizeof(TCHAR
));
196 /* \xhhhh hexadecimal character specification */
200 *pEscape
= (WCHAR
)_tcstoul(lpString
+ 1, &lpStringNew
, 16);
201 memmove(lpString
, lpStringNew
, (_tcslen(lpStringNew
) + 1) * sizeof(TCHAR
));
205 /* Unknown escape sequence, ignore it */
213 VOID
InitializeTopicList(VOID
)
219 PTOPIC
AddNewTopic(VOID
)
221 PTOPIC pTopic
, *pTopicsTmp
;
223 /* Allocate (or reallocate) the list of topics */
225 pTopicsTmp
= HeapAlloc(GetProcessHeap(), 0, (dwNumberTopics
+ 1) * sizeof(*pTopics
));
227 pTopicsTmp
= HeapReAlloc(GetProcessHeap(), 0, pTopics
, (dwNumberTopics
+ 1) * sizeof(*pTopics
));
229 return NULL
; // Cannot reallocate more
230 pTopics
= pTopicsTmp
;
232 /* Allocate a new topic entry */
233 pTopic
= HeapAlloc(GetProcessHeap(), 0, sizeof(*pTopic
));
235 return NULL
; // Cannot reallocate more
236 pTopics
[dwNumberTopics
++] = pTopic
;
238 /* Return the allocated topic entry */
242 PTOPIC
AddNewTopicEx(
243 IN LPTSTR szText OPTIONAL
,
244 IN LPTSTR szTitle OPTIONAL
,
245 IN LPTSTR szDesc OPTIONAL
,
246 IN LPTSTR szCommand OPTIONAL
,
247 IN LPTSTR szArgs OPTIONAL
,
248 IN LPTSTR szAction OPTIONAL
)
250 PTOPIC pTopic
= AddNewTopic();
254 if (szText
&& *szText
)
255 StringCchCopy(pTopic
->szText
, ARRAYSIZE(pTopic
->szText
), szText
);
259 if (szTitle
&& *szTitle
)
260 StringCchCopy(pTopic
->szTitle
, ARRAYSIZE(pTopic
->szTitle
), szTitle
);
262 *pTopic
->szTitle
= 0;
264 if (szDesc
&& *szDesc
)
266 StringCchCopy(pTopic
->szDesc
, ARRAYSIZE(pTopic
->szDesc
), szDesc
);
267 TranslateEscapes(pTopic
->szDesc
);
274 if (szCommand
&& *szCommand
)
276 pTopic
->bIsCommand
= TRUE
;
277 StringCchCopy(pTopic
->szCommand
, ARRAYSIZE(pTopic
->szCommand
), szCommand
);
281 pTopic
->bIsCommand
= FALSE
;
282 *pTopic
->szCommand
= 0;
285 /* Only care about command arguments if we actually have a command */
286 if (*pTopic
->szCommand
)
288 if (szArgs
&& *szArgs
)
290 StringCchCopy(pTopic
->szArgs
, ARRAYSIZE(pTopic
->szArgs
), szArgs
);
294 /* Check for special applications: ReactOS Shell */
295 if (/* pTopic->szCommand && */ *pTopic
->szCommand
&&
296 _tcsicmp(pTopic
->szCommand
, TEXT("explorer.exe")) == 0)
299 TCHAR CurrentDir
[MAX_PATH
];
300 GetCurrentDirectory(ARRAYSIZE(CurrentDir
), CurrentDir
);
302 StringCchCopy(pTopic
->szArgs
, ARRAYSIZE(pTopic
->szArgs
), TEXT("\\"));
315 /* Only care about custom actions if we actually don't have a command */
316 if (!*pTopic
->szCommand
&& szAction
&& *szAction
)
319 * Re-use the pTopic->szCommand member. We distinguish with respect to
320 * a regular command by using the pTopic->bIsCommand flag.
322 pTopic
->bIsCommand
= FALSE
;
323 StringCchCopy(pTopic
->szCommand
, ARRAYSIZE(pTopic
->szCommand
), szAction
);
324 TranslateEscapes(pTopic
->szCommand
);
331 LoadLocalizedResourcesInternal(VOID
)
333 #define MAX_NUMBER_INTERNAL_TOPICS 3
336 LPTSTR lpszCommand
, lpszAction
;
337 TOPIC newTopic
, *pTopic
;
339 for (i
= 0; i
< MAX_NUMBER_INTERNAL_TOPICS
; ++i
)
341 lpszCommand
= NULL
, lpszAction
= NULL
;
343 /* Retrieve the information */
344 if (!LoadString(hInstance
, IDS_TOPIC_BUTTON0
+ i
, newTopic
.szText
, ARRAYSIZE(newTopic
.szText
)))
345 *newTopic
.szText
= 0;
346 if (!LoadString(hInstance
, IDS_TOPIC_TITLE0
+ i
, newTopic
.szTitle
, ARRAYSIZE(newTopic
.szTitle
)))
347 *newTopic
.szTitle
= 0;
348 if (!LoadString(hInstance
, IDS_TOPIC_DESC0
+ i
, newTopic
.szDesc
, ARRAYSIZE(newTopic
.szDesc
)))
349 *newTopic
.szDesc
= 0;
351 if (!LoadString(hInstance
, IDS_TOPIC_COMMAND0
+ i
, newTopic
.szCommand
, ARRAYSIZE(newTopic
.szCommand
)))
352 *newTopic
.szCommand
= 0;
354 /* Only care about command arguments if we actually have a command */
355 if (*newTopic
.szCommand
)
357 lpszCommand
= newTopic
.szCommand
;
358 if (!LoadString(hInstance
, IDS_TOPIC_CMD_ARGS0
+ i
, newTopic
.szArgs
, ARRAYSIZE(newTopic
.szArgs
)))
359 *newTopic
.szArgs
= 0;
361 /* Only care about custom actions if we actually don't have a command */
362 else // if (!*newTopic.szCommand)
364 lpszAction
= newTopic
.szCommand
;
365 if (!LoadString(hInstance
, IDS_TOPIC_ACTION0
+ i
, newTopic
.szCommand
, ARRAYSIZE(newTopic
.szCommand
)))
366 *newTopic
.szCommand
= 0;
369 /* Allocate a new topic */
370 pTopic
= AddNewTopicEx(newTopic
.szText
,
377 break; // Cannot reallocate more
382 LoadLocalizedResourcesFromINI(LCID Locale
, LPTSTR lpResPath
)
386 TCHAR szBuffer
[LOCALE_NAME_MAX_LENGTH
];
387 TCHAR szIniPath
[MAX_PATH
];
388 LPTSTR lpszSections
= NULL
, lpszSection
= NULL
;
389 LPTSTR lpszCommand
, lpszAction
;
390 TOPIC newTopic
, *pTopic
;
392 /* Retrieve the locale name (on which the INI file name is based) */
393 dwRet
= (DWORD
)GetLocaleName(Locale
, szBuffer
, ARRAYSIZE(szBuffer
));
396 /* Fall back to english (US) */
397 StringCchCopy(szBuffer
, ARRAYSIZE(szBuffer
), TEXT("en-US"));
400 /* Build the INI file name */
401 StringCchPrintf(szIniPath
, ARRAYSIZE(szIniPath
),
402 TEXT("%s\\%s.ini"), lpResPath
, szBuffer
);
404 /* Verify that the file exists, otherwise fall back to english (US) */
405 if (GetFileAttributes(szIniPath
) == INVALID_FILE_ATTRIBUTES
)
407 StringCchCopy(szBuffer
, ARRAYSIZE(szBuffer
), TEXT("en-US"));
409 StringCchPrintf(szIniPath
, ARRAYSIZE(szIniPath
),
410 TEXT("%s\\%s.ini"), lpResPath
, szBuffer
);
413 /* Verify that the file exists, otherwise fall back to internal (localized) resource */
414 if (GetFileAttributes(szIniPath
) == INVALID_FILE_ATTRIBUTES
)
415 return FALSE
; // For localized resources, see the general function.
417 /* Try to load the default localized strings */
418 GetPrivateProfileString(TEXT("Defaults"), TEXT("AppTitle"), TEXT("ReactOS - Welcome") /* default */,
419 szAppTitle
, ARRAYSIZE(szAppTitle
), szIniPath
);
420 GetPrivateProfileString(TEXT("Defaults"), TEXT("DefaultTopicTitle"), TEXT("") /* default */,
421 szDefaultTitle
, ARRAYSIZE(szDefaultTitle
), szIniPath
);
422 if (!GetPrivateProfileString(TEXT("Defaults"), TEXT("DefaultTopicDescription"), TEXT("") /* default */,
423 szDefaultDesc
, ARRAYSIZE(szDefaultDesc
), szIniPath
))
429 TranslateEscapes(szDefaultDesc
);
432 /* Allocate a buffer big enough to hold all the section names */
433 for (dwSize
= BUFFER_SIZE
; ; dwSize
+= BUFFER_SIZE
)
435 lpszSections
= HeapAlloc(GetProcessHeap(), 0, dwSize
* sizeof(TCHAR
));
437 return TRUE
; // FIXME!
438 dwRet
= GetPrivateProfileSectionNames(lpszSections
, dwSize
, szIniPath
);
439 if (dwRet
< dwSize
- 2)
441 HeapFree(GetProcessHeap(), 0, lpszSections
);
444 /* Loop over the sections and load the topics */
445 lpszSection
= lpszSections
;
446 for (; lpszSection
&& *lpszSection
; lpszSection
+= (_tcslen(lpszSection
) + 1))
448 /* Ignore everything that is not a topic */
449 if (_tcsnicmp(lpszSection
, TEXT("Topic"), 5) != 0)
452 lpszCommand
= NULL
, lpszAction
= NULL
;
454 /* Retrieve the information */
455 GetPrivateProfileString(lpszSection
, TEXT("MenuText"), TEXT("") /* default */,
456 newTopic
.szText
, ARRAYSIZE(newTopic
.szText
), szIniPath
);
457 GetPrivateProfileString(lpszSection
, TEXT("Title"), TEXT("") /* default */,
458 newTopic
.szTitle
, ARRAYSIZE(newTopic
.szTitle
), szIniPath
);
459 GetPrivateProfileString(lpszSection
, TEXT("Description"), TEXT("") /* default */,
460 newTopic
.szDesc
, ARRAYSIZE(newTopic
.szDesc
), szIniPath
);
462 GetPrivateProfileString(lpszSection
, TEXT("ConfigCommand"), TEXT("") /* default */,
463 newTopic
.szCommand
, ARRAYSIZE(newTopic
.szCommand
), szIniPath
);
465 /* Only care about command arguments if we actually have a command */
466 if (*newTopic
.szCommand
)
468 lpszCommand
= newTopic
.szCommand
;
469 GetPrivateProfileString(lpszSection
, TEXT("ConfigArgs"), TEXT("") /* default */,
470 newTopic
.szArgs
, ARRAYSIZE(newTopic
.szArgs
), szIniPath
);
472 /* Only care about custom actions if we actually don't have a command */
473 else // if (!*newTopic.szCommand)
475 lpszAction
= newTopic
.szCommand
;
476 GetPrivateProfileString(lpszSection
, TEXT("Action"), TEXT("") /* default */,
477 newTopic
.szCommand
, ARRAYSIZE(newTopic
.szCommand
), szIniPath
);
480 /* Allocate a new topic */
481 pTopic
= AddNewTopicEx(newTopic
.szText
,
488 break; // Cannot reallocate more
491 HeapFree(GetProcessHeap(), 0, lpszSections
);
497 LoadConfiguration(VOID
)
499 BOOL bLoadDefaultResources
;
500 TCHAR szAppPath
[MAX_PATH
];
501 TCHAR szIniPath
[MAX_PATH
];
502 TCHAR szResPath
[MAX_PATH
];
504 /* Initialize the topic list */
505 InitializeTopicList();
508 * First, try to load the default internal (localized) strings.
509 * They can be redefined by the localized INI files.
511 if (!LoadString(hInstance
, IDS_APPTITLE
, szAppTitle
, ARRAYSIZE(szAppTitle
)))
512 StringCchCopy(szAppTitle
, ARRAYSIZE(szAppTitle
), TEXT("ReactOS - Welcome"));
513 if (!LoadString(hInstance
, IDS_DEFAULT_TOPIC_TITLE
, szDefaultTitle
, ARRAYSIZE(szDefaultTitle
)))
515 if (!LoadString(hInstance
, IDS_DEFAULT_TOPIC_DESC
, szDefaultDesc
, ARRAYSIZE(szDefaultDesc
)))
518 /* Retrieve the full path to this application */
519 GetModuleFileName(NULL
, szAppPath
, ARRAYSIZE(szAppPath
));
522 LPTSTR lpFileName
= _tcsrchr(szAppPath
, _T('\\'));
529 /* Build the full INI file path name */
530 StringCchPrintf(szIniPath
, ARRAYSIZE(szIniPath
), TEXT("%s\\welcome.ini"), szAppPath
);
532 /* Verify that the file exists, otherwise use the default configuration */
533 if (GetFileAttributes(szIniPath
) == INVALID_FILE_ATTRIBUTES
)
535 /* Use the default internal (localized) resources */
536 LoadLocalizedResourcesInternal();
540 /* Load the settings from the INI configuration file */
541 bDisplayCheckBox
= !!GetPrivateProfileInt(TEXT("Welcome"), TEXT("DisplayCheckBox"), FALSE
/* default */, szIniPath
);
542 bDisplayExitBtn
= !!GetPrivateProfileInt(TEXT("Welcome"), TEXT("DisplayExitButton"), TRUE
/* default */, szIniPath
);
544 /* Load the default internal (localized) resources if needed */
545 bLoadDefaultResources
= !!GetPrivateProfileInt(TEXT("Welcome"), TEXT("LoadDefaultResources"), FALSE
/* default */, szIniPath
);
546 if (bLoadDefaultResources
)
547 LoadLocalizedResourcesInternal();
549 GetPrivateProfileString(TEXT("Welcome"), TEXT("ResourceDir"), TEXT("") /* default */,
550 szResPath
, ARRAYSIZE(szResPath
), szIniPath
);
552 /* Set the current directory to the one of this application, and retrieve the resources */
553 SetCurrentDirectory(szAppPath
);
554 if (!LoadLocalizedResourcesFromINI(LOCALE_USER_DEFAULT
, szResPath
))
557 * Loading localized resources from INI file failed, try to load the
558 * internal resources only if they were not already loaded earlier.
560 if (!bLoadDefaultResources
)
561 LoadLocalizedResourcesInternal();
571 while (dwNumberTopics
--)
573 if (pTopics
[dwNumberTopics
])
574 HeapFree(GetProcessHeap(), 0, pTopics
[dwNumberTopics
]);
576 HeapFree(GetProcessHeap(), 0, pTopics
);
583 ShowLastWin32Error(HWND hWnd
)
585 LPTSTR lpMessageBuffer
= NULL
;
586 DWORD dwError
= GetLastError();
588 if (dwError
== ERROR_SUCCESS
)
591 if (!FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER
|
592 FORMAT_MESSAGE_FROM_SYSTEM
|
593 FORMAT_MESSAGE_IGNORE_INSERTS
,
597 (LPTSTR
)&lpMessageBuffer
,
603 MessageBox(hWnd
, lpMessageBuffer
, szAppTitle
, MB_OK
| MB_ICONERROR
);
604 LocalFree(lpMessageBuffer
);
609 _tWinMain(HINSTANCE hInst
,
610 HINSTANCE hPrevInstance
,
614 HANDLE hMutex
= NULL
;
623 DWORD dwStyle
= WS_OVERLAPPED
| WS_CLIPCHILDREN
| WS_CLIPSIBLINGS
|
624 WS_CAPTION
| WS_SYSMENU
| WS_MINIMIZEBOX
;
627 ULONG ulInnerWidth
= TITLE_WIDTH
;
628 ULONG ulInnerHeight
= (TITLE_WIDTH
* 3) / 4;
629 ULONG ulTitleHeight
= TITLE_HEIGHT
+ 3;
631 UNREFERENCED_PARAMETER(hPrevInstance
);
632 UNREFERENCED_PARAMETER(lpszCmdLine
);
634 /* Ensure only one instance is running */
635 hMutex
= CreateMutex(NULL
, FALSE
, szWindowClass
);
636 if (hMutex
&& (GetLastError() == ERROR_ALREADY_EXISTS
))
638 /* If already started, find its window */
639 hWndMain
= FindWindow(szWindowClass
, NULL
);
641 /* Activate window */
642 ShowWindow(hWndMain
, SW_SHOWNORMAL
);
643 SetForegroundWindow(hWndMain
);
645 /* Close the mutex handle and quit */
650 switch (GetUserDefaultUILanguage())
652 case MAKELANGID(LANG_HEBREW
, SUBLANG_DEFAULT
):
653 SetProcessDefaultLayout(LAYOUT_RTL
);
663 hMainIcon
= LoadIcon(hInstance
, MAKEINTRESOURCE(IDI_MAIN
));
665 /* Register the window class */
666 wndclass
.cbSize
= sizeof(wndclass
);
667 wndclass
.style
= CS_HREDRAW
| CS_VREDRAW
;
668 wndclass
.lpfnWndProc
= (WNDPROC
)MainWndProc
;
669 wndclass
.cbClsExtra
= 0;
670 wndclass
.cbWndExtra
= 0;
671 wndclass
.hInstance
= hInstance
;
672 wndclass
.hIcon
= hMainIcon
;
673 wndclass
.hIconSm
= NULL
;
674 wndclass
.hCursor
= LoadCursor(NULL
, IDC_ARROW
);
675 wndclass
.hbrBackground
= NULL
;
676 wndclass
.lpszMenuName
= NULL
;
677 wndclass
.lpszClassName
= szWindowClass
;
679 RegisterClassEx(&wndclass
);
681 /* Load the banner bitmap, and compute the window dimensions */
682 hTitleBitmap
= LoadBitmap(hInstance
, MAKEINTRESOURCE(IDB_TITLE_BITMAP
));
685 GetObject(hTitleBitmap
, sizeof(BitmapInfo
), &BitmapInfo
);
686 ulInnerWidth
= BitmapInfo
.bmWidth
;
687 ulInnerHeight
= (ulInnerWidth
* 3) / 4;
688 ulTitleHeight
= BitmapInfo
.bmHeight
+ 3;
689 DeleteObject(hTitleBitmap
);
691 ulInnerHeight
-= GetSystemMetrics(SM_CYCAPTION
);
694 rcWindow
.bottom
= ulInnerHeight
- 1;
696 rcWindow
.right
= ulInnerWidth
- 1;
698 AdjustWindowRect(&rcWindow
, dwStyle
, FALSE
);
699 xWidth
= rcWindow
.right
- rcWindow
.left
;
700 yHeight
= rcWindow
.bottom
- rcWindow
.top
;
702 /* Compute the window position */
703 xPos
= (GetSystemMetrics(SM_CXSCREEN
) - xWidth
) / 2;
704 yPos
= (GetSystemMetrics(SM_CYSCREEN
) - yHeight
) / 2;
706 rcTitlePanel
.top
= 0;
707 rcTitlePanel
.bottom
= ulTitleHeight
;
708 rcTitlePanel
.left
= 0;
709 rcTitlePanel
.right
= ulInnerWidth
- 1;
711 rcLeftPanel
.top
= rcTitlePanel
.bottom
;
712 rcLeftPanel
.bottom
= ulInnerHeight
- 1;
713 rcLeftPanel
.left
= 0;
714 rcLeftPanel
.right
= ulInnerWidth
/ 3;
716 rcRightPanel
.top
= rcLeftPanel
.top
;
717 rcRightPanel
.bottom
= rcLeftPanel
.bottom
;
718 rcRightPanel
.left
= rcLeftPanel
.right
;
719 rcRightPanel
.right
= ulInnerWidth
- 1;
721 /* Load the configuration and the resources */
724 /* Create main window */
725 hWndMain
= CreateWindow(szWindowClass
,
737 hSystemMenu
= GetSystemMenu(hWndMain
, FALSE
);
740 RemoveMenu(hSystemMenu
, SC_SIZE
, MF_BYCOMMAND
);
741 RemoveMenu(hSystemMenu
, SC_MAXIMIZE
, MF_BYCOMMAND
);
744 ShowWindow(hWndMain
, nCmdShow
);
745 UpdateWindow(hWndMain
);
747 while (GetMessage(&msg
, NULL
, 0, 0) != FALSE
)
749 /* Check for ENTER key presses */
750 if (msg
.message
== WM_KEYDOWN
&& msg
.wParam
== VK_RETURN
)
753 * The user pressed the ENTER key. Retrieve the handle to the
754 * child window that has the keyboard focus, and send it a
755 * WM_COMMAND message.
757 hWndFocus
= GetFocus();
760 SendMessage(hWndMain
, WM_COMMAND
,
761 (WPARAM
)GetDlgCtrlID(hWndFocus
), (LPARAM
)hWndFocus
);
764 /* Allow using keyboard navigation */
765 else if (!IsDialogMessage(hWndMain
, &msg
))
767 TranslateMessage(&msg
);
768 DispatchMessage(&msg
);
775 /* Close the mutex handle and quit */
782 ButtonSubclassWndProc(HWND hWnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
784 static WPARAM wParamOld
= 0;
785 static LPARAM lParamOld
= 0;
789 if (uMsg
== WM_MOUSEMOVE
)
791 /* Ignore mouse-move messages on the same point */
792 if ((wParam
== wParamOld
) && (lParam
== lParamOld
))
795 /* Retrieve the topic index of this button */
796 i
= GetWindowLongPtr(hWnd
, GWLP_ID
) - TOPIC_BTN_ID_BASE
;
799 * Change the focus to this button if the current topic index differs
800 * (we will receive WM_SETFOCUS afterwards).
808 else if (uMsg
== WM_SETFOCUS
)
810 /* Retrieve the topic index of this button */
811 i
= GetWindowLongPtr(hWnd
, GWLP_ID
) - TOPIC_BTN_ID_BASE
;
813 /* Change the current topic index and repaint the description panel */
817 InvalidateRect(hWndMain
, &rcRightPanel
, TRUE
);
820 else if (uMsg
== WM_KILLFOCUS
)
823 * We lost focus, either because the user changed button focus,
824 * or because the main window to which we belong went inactivated.
825 * If we are in the latter case, we ignore the focus change.
826 * If we are in the former case, we reset to the default topic.
828 if (GetParent(hWnd
) == GetForegroundWindow())
831 InvalidateRect(hWndMain
, &rcRightPanel
, TRUE
);
835 return CallWindowProc(fnOldBtn
, hWnd
, uMsg
, wParam
, lParam
);
840 RunAction(INT nTopic
)
842 PCWSTR Command
= NULL
, Args
= NULL
;
847 Command
= pTopics
[nTopic
]->szCommand
;
848 if (/* !Command && */ !*Command
)
851 /* Check for known actions */
852 if (!pTopics
[nTopic
]->bIsCommand
)
854 if (!_tcsicmp(Command
, TEXT("<exit>")))
857 if (!_tcsnicmp(Command
, TEXT("<msg>"), 5))
859 MessageBox(hWndMain
, Command
+ 5, TEXT("ReactOS"), MB_OK
| MB_TASKMODAL
);
864 /* Run the command */
866 Args
= pTopics
[nTopic
]->szArgs
;
867 if (!*Args
) Args
= NULL
;
868 ShellExecute(NULL
, NULL
,
870 NULL
, SW_SHOWDEFAULT
);
878 GetButtonHeight(HDC hDC
,
887 rect
.right
= dwWidth
- 20;
891 hOldFont
= (HFONT
)SelectObject(hDC
, hFont
);
892 DrawText(hDC
, szText
, -1, &rect
, DT_TOP
| DT_CALCRECT
| DT_WORDBREAK
);
893 SelectObject(hDC
, hOldFont
);
895 return (rect
.bottom
-rect
.top
+ 14);
900 OnCreate(HWND hWnd
, WPARAM wParam
, LPARAM lParam
)
910 UNREFERENCED_PARAMETER(wParam
);
911 UNREFERENCED_PARAMETER(lParam
);
913 hbrLightBlue
= CreateSolidBrush(LIGHT_BLUE
);
914 hbrDarkBlue
= CreateSolidBrush(DARK_BLUE
);
916 ZeroMemory(&lf
, sizeof(lf
));
919 lf
.lfOrientation
= 0; // TA_BASELINE;
920 // lf.lfItalic = lf.lfUnderline = lf.lfStrikeOut = FALSE;
921 lf
.lfCharSet
= ANSI_CHARSET
;
922 lf
.lfOutPrecision
= OUT_DEFAULT_PRECIS
;
923 lf
.lfClipPrecision
= CLIP_DEFAULT_PRECIS
;
924 lf
.lfQuality
= DEFAULT_QUALITY
;
925 lf
.lfPitchAndFamily
= FF_DONTCARE
;
926 if (LoadString(hInstance
, IDS_FONTNAME
, lf
.lfFaceName
, ARRAYSIZE(lf
.lfFaceName
)) == 0)
927 StringCchCopy(lf
.lfFaceName
, ARRAYSIZE(lf
.lfFaceName
), TEXT("Tahoma"));
929 /* Topic title font */
932 lf
.lfWeight
= FW_NORMAL
;
933 hFontTopicTitle
= CreateFontIndirect(&lf
);
935 /* Topic description font */
938 lf
.lfWeight
= FW_THIN
;
939 hFontTopicDescription
= CreateFontIndirect(&lf
);
941 /* Topic button font */
944 lf
.lfWeight
= FW_BOLD
;
945 hFontTopicButton
= CreateFontIndirect(&lf
);
947 /* Load title bitmap */
949 hTitleBitmap
= LoadBitmap(hInstance
, MAKEINTRESOURCE(IDB_TITLE_BITMAP
));
951 /* Load topic bitmaps */
952 hDefaultTopicBitmap
= LoadBitmap(hInstance
, MAKEINTRESOURCE(IDB_DEFAULT_TOPIC_BITMAP
));
953 for (i
= 0; i
< dwNumberTopics
; i
++)
955 // FIXME: Not implemented yet!
956 // pTopics[i]->hBitmap = LoadBitmap(hInstance, MAKEINTRESOURCE(IDB_TOPIC_BITMAP0 + i));
957 pTopics
[i
]->hBitmap
= NULL
;
960 ScreenDC
= GetWindowDC(hWnd
);
961 hdcMem
= CreateCompatibleDC(ScreenDC
);
962 ReleaseDC(hWnd
, ScreenDC
);
964 /* Load and create the menu buttons */
965 dwTop
= rcLeftPanel
.top
;
966 for (i
= 0; i
< dwNumberTopics
; i
++)
968 if (*pTopics
[i
]->szText
)
970 dwHeight
= GetButtonHeight(hdcMem
,
973 rcLeftPanel
.right
- rcLeftPanel
.left
);
975 pTopics
[i
]->hWndButton
= CreateWindow(TEXT("BUTTON"),
977 WS_CHILDWINDOW
| WS_VISIBLE
| WS_TABSTOP
| BS_MULTILINE
| BS_OWNERDRAW
,
980 rcLeftPanel
.right
- rcLeftPanel
.left
,
983 (HMENU
)IntToPtr(TOPIC_BTN_ID_BASE
+ i
), // Similar to SetWindowLongPtr(GWLP_ID)
987 SendMessage(pTopics
[i
]->hWndButton
, WM_SETFONT
, (WPARAM
)hFontTopicButton
, MAKELPARAM(TRUE
, 0));
988 fnOldBtn
= (WNDPROC
)SetWindowLongPtr(pTopics
[i
]->hWndButton
, GWLP_WNDPROC
, (DWORD_PTR
)ButtonSubclassWndProc
);
992 pTopics
[i
]->hWndButton
= NULL
;
998 /* Create the checkbox */
999 if (bDisplayCheckBox
)
1001 nLength
= LoadString(hInstance
, IDS_CHECKTEXT
, szText
, ARRAYSIZE(szText
));
1006 lf
.lfWeight
= FW_THIN
;
1007 hFontCheckButton
= CreateFontIndirect(&lf
);
1009 hWndCheckButton
= CreateWindow(TEXT("BUTTON"),
1011 WS_CHILDWINDOW
| WS_VISIBLE
| WS_TABSTOP
| BS_AUTOCHECKBOX
| BS_MULTILINE
/**/| BS_FLAT
/**/,
1012 rcLeftPanel
.left
+ 8,
1013 rcLeftPanel
.bottom
- 8 - 13,
1014 rcLeftPanel
.right
- rcLeftPanel
.left
- 16,
1017 (HMENU
)IDC_CHECKBUTTON
,
1020 SendMessage(hWndCheckButton
, WM_SETFONT
, (WPARAM
)hFontCheckButton
, MAKELPARAM(TRUE
, 0));
1024 hFontCheckButton
= NULL
;
1025 hWndCheckButton
= NULL
;
1029 /* Create the "Exit" button */
1030 if (bDisplayExitBtn
)
1032 nLength
= LoadString(hInstance
, IDS_CLOSETEXT
, szText
, ARRAYSIZE(szText
));
1035 hWndCloseButton
= CreateWindow(TEXT("BUTTON"),
1037 WS_CHILDWINDOW
| WS_VISIBLE
| WS_TABSTOP
| BS_FLAT
,
1038 rcRightPanel
.right
- 8 - 57,
1039 rcRightPanel
.bottom
- 8 - 21,
1043 (HMENU
)IDC_CLOSEBUTTON
,
1047 SendMessage(hWndCloseButton
, WM_SETFONT
, (WPARAM
)hFontTopicButton
, MAKELPARAM(TRUE
, 0));
1051 hWndCloseButton
= NULL
;
1060 OnCommand(HWND hWnd
, WPARAM wParam
, LPARAM lParam
)
1062 UNREFERENCED_PARAMETER(lParam
);
1064 /* Retrieve the low-word from wParam */
1065 wParam
= LOWORD(wParam
);
1067 /* Execute action */
1068 if (wParam
== IDC_CLOSEBUTTON
)
1070 DestroyWindow(hWnd
);
1072 else if (wParam
- TOPIC_BTN_ID_BASE
< dwNumberTopics
)
1074 if (RunAction(wParam
- TOPIC_BTN_ID_BASE
) == FALSE
)
1075 DestroyWindow(hWnd
); // Corresponds to a <exit> action.
1083 PaintBanner(HDC hdc
, LPRECT rcPanel
)
1089 hOldBitmap
= (HBITMAP
)SelectObject(hdcMem
, hTitleBitmap
);
1093 rcPanel
->right
- rcPanel
->left
,
1094 rcPanel
->bottom
- 3,
1095 hdcMem
, 0, 0, SRCCOPY
);
1096 SelectObject(hdcMem
, hOldBitmap
);
1098 /* Dark blue line */
1099 hOldBrush
= (HBRUSH
)SelectObject(hdc
, hbrDarkBlue
);
1102 rcPanel
->bottom
- 3,
1103 rcPanel
->right
- rcPanel
->left
,
1106 SelectObject(hdc
, hOldBrush
);
1111 OnPaint(HWND hWnd
, WPARAM wParam
, LPARAM lParam
)
1117 HBITMAP hOldBitmap
= NULL
;
1120 RECT rcTitle
, rcDescription
;
1122 TCHAR szVersion
[50];
1123 LPTSTR lpTitle
= NULL
, lpDesc
= NULL
;
1125 UNREFERENCED_PARAMETER(wParam
);
1126 UNREFERENCED_PARAMETER(lParam
);
1128 hdc
= BeginPaint(hWnd
, &ps
);
1131 PaintBanner(hdc
, &rcTitlePanel
);
1134 hOldBrush
= (HBRUSH
)SelectObject(hdc
, hbrLightBlue
);
1138 rcLeftPanel
.right
- rcLeftPanel
.left
,
1139 rcLeftPanel
.bottom
- rcLeftPanel
.top
,
1141 SelectObject(hdc
, hOldBrush
);
1144 hOldBrush
= (HBRUSH
)SelectObject(hdc
, GetStockObject(WHITE_BRUSH
));
1148 rcRightPanel
.right
- rcRightPanel
.left
,
1149 rcRightPanel
.bottom
- rcRightPanel
.top
,
1151 SelectObject(hdc
, hOldBrush
);
1153 /* Draw dark vertical line */
1154 hPen
= CreatePen(PS_SOLID
, 0, DARK_BLUE
);
1155 hOldPen
= (HPEN
)SelectObject(hdc
, hPen
);
1156 MoveToEx(hdc
, rcRightPanel
.left
, rcRightPanel
.top
, NULL
);
1157 LineTo(hdc
, rcRightPanel
.left
, rcRightPanel
.bottom
);
1158 SelectObject(hdc
, hOldPen
);
1161 /* Draw topic bitmap */
1162 if ((nTopic
== -1) && (hDefaultTopicBitmap
))
1164 GetObject(hDefaultTopicBitmap
, sizeof(bmpInfo
), &bmpInfo
);
1165 hOldBitmap
= (HBITMAP
)SelectObject(hdcMem
, hDefaultTopicBitmap
);
1167 rcRightPanel
.right
- bmpInfo
.bmWidth
,
1168 rcRightPanel
.bottom
- bmpInfo
.bmHeight
,
1176 else if ((nTopic
!= -1) && (pTopics
[nTopic
]->hBitmap
))
1178 GetObject(pTopics
[nTopic
]->hBitmap
, sizeof(bmpInfo
), &bmpInfo
);
1179 hOldBitmap
= (HBITMAP
)SelectObject(hdcMem
, pTopics
[nTopic
]->hBitmap
);
1181 rcRightPanel
.right
- bmpInfo
.bmWidth
,
1182 rcRightPanel
.bottom
- bmpInfo
.bmHeight
,
1193 lpTitle
= szDefaultTitle
;
1194 lpDesc
= szDefaultDesc
;
1198 lpTitle
= pTopics
[nTopic
]->szTitle
;
1199 lpDesc
= pTopics
[nTopic
]->szDesc
;
1202 SetBkMode(hdc
, TRANSPARENT
);
1204 /* Draw version information */
1205 StringCchCopy(szVersion
, ARRAYSIZE(szVersion
),
1206 TEXT("ReactOS ") TEXT(KERNEL_VERSION_STR
));
1209 * Compute the original rect (position & size) of the version info,
1210 * depending whether the checkbox is displayed (version info in the
1211 * right panel) or not (version info in the left panel).
1213 if (bDisplayCheckBox
)
1214 rcTitle
= rcRightPanel
;
1216 rcTitle
= rcLeftPanel
;
1218 rcTitle
.left
= rcTitle
.left
+ 8;
1219 rcTitle
.right
= rcTitle
.right
- 5;
1220 rcTitle
.top
= rcTitle
.bottom
- 43;
1221 rcTitle
.bottom
= rcTitle
.bottom
- 8;
1223 hOldFont
= (HFONT
)SelectObject(hdc
, hFontTopicDescription
);
1224 DrawText(hdc
, szVersion
, -1, &rcTitle
, DT_BOTTOM
| DT_CALCRECT
| DT_SINGLELINE
);
1225 SetTextColor(hdc
, GetSysColor(COLOR_WINDOWTEXT
));
1226 DrawText(hdc
, szVersion
, -1, &rcTitle
, DT_BOTTOM
| DT_SINGLELINE
);
1227 SelectObject(hdc
, hOldFont
);
1229 /* Draw topic title */
1230 rcTitle
.left
= rcRightPanel
.left
+ 12;
1231 rcTitle
.right
= rcRightPanel
.right
- 8;
1232 rcTitle
.top
= rcRightPanel
.top
+ 8;
1233 rcTitle
.bottom
= rcTitle
.top
+ 57;
1234 hOldFont
= (HFONT
)SelectObject(hdc
, hFontTopicTitle
);
1235 DrawText(hdc
, lpTitle
, -1, &rcTitle
, DT_TOP
| DT_CALCRECT
);
1236 SetTextColor(hdc
, DARK_BLUE
);
1237 DrawText(hdc
, lpTitle
, -1, &rcTitle
, DT_TOP
);
1238 SelectObject(hdc
, hOldFont
);
1240 /* Draw topic description */
1241 rcDescription
.left
= rcRightPanel
.left
+ 12;
1242 rcDescription
.right
= rcRightPanel
.right
- 8;
1243 rcDescription
.top
= rcTitle
.bottom
+ 8;
1244 rcDescription
.bottom
= rcRightPanel
.bottom
- 20;
1245 hOldFont
= (HFONT
)SelectObject(hdc
, hFontTopicDescription
);
1246 SetTextColor(hdc
, GetSysColor(COLOR_WINDOWTEXT
));
1247 DrawText(hdc
, lpDesc
, -1, &rcDescription
, DT_TOP
| DT_WORDBREAK
);
1248 SelectObject(hdc
, hOldFont
);
1250 SetBkMode(hdc
, OPAQUE
);
1252 SelectObject(hdcMem
, hOldBrush
);
1253 SelectObject(hdcMem
, hOldBitmap
);
1255 EndPaint(hWnd
, &ps
);
1262 OnDrawItem(HWND hWnd
, WPARAM wParam
, LPARAM lParam
)
1264 LPDRAWITEMSTRUCT lpDis
= (LPDRAWITEMSTRUCT
)lParam
;
1270 UNREFERENCED_PARAMETER(hWnd
);
1271 UNREFERENCED_PARAMETER(wParam
);
1274 /* Neither the checkbox button nor the close button implement owner-drawing */
1275 if (lpDis
->hwndItem
== hWndCheckButton
)
1277 if (lpDis
->hwndItem
== hWndCloseButton
)
1279 DrawFrameControl(lpDis
->hDC
,
1282 DFCS_BUTTONPUSH
| DFCS_FLAT
);
1287 if (lpDis
->CtlID
== (ULONG
)(TOPIC_BTN_ID_BASE
+ nTopic
))
1288 hOldBrush
= (HBRUSH
)SelectObject(lpDis
->hDC
, GetStockObject(WHITE_BRUSH
));
1290 hOldBrush
= (HBRUSH
)SelectObject(lpDis
->hDC
, hbrLightBlue
);
1295 lpDis
->rcItem
.right
,
1296 lpDis
->rcItem
.bottom
,
1298 SelectObject(lpDis
->hDC
, hOldBrush
);
1300 hPen
= CreatePen(PS_SOLID
, 0, DARK_BLUE
);
1301 hOldPen
= (HPEN
)SelectObject(lpDis
->hDC
, hPen
);
1302 MoveToEx(lpDis
->hDC
, lpDis
->rcItem
.left
, lpDis
->rcItem
.bottom
- 1, NULL
);
1303 LineTo(lpDis
->hDC
, lpDis
->rcItem
.right
, lpDis
->rcItem
.bottom
- 1);
1304 SelectObject(lpDis
->hDC
, hOldPen
);
1307 InflateRect(&lpDis
->rcItem
, -10, -4);
1308 OffsetRect(&lpDis
->rcItem
, 0, 1);
1309 GetWindowText(lpDis
->hwndItem
, szText
, ARRAYSIZE(szText
));
1310 SetTextColor(lpDis
->hDC
, GetSysColor(COLOR_WINDOWTEXT
));
1311 iBkMode
= SetBkMode(lpDis
->hDC
, TRANSPARENT
);
1312 DrawText(lpDis
->hDC
, szText
, -1, &lpDis
->rcItem
, DT_TOP
| DT_LEFT
| DT_WORDBREAK
);
1313 SetBkMode(lpDis
->hDC
, iBkMode
);
1320 OnMouseMove(HWND hWnd
, WPARAM wParam
, LPARAM lParam
)
1322 static WPARAM wParamOld
= 0;
1323 static LPARAM lParamOld
= 0;
1325 /* Ignore mouse-move messages on the same point */
1326 if ((wParam
== wParamOld
) && (lParam
== lParamOld
))
1330 * If the user moves the mouse over the main window, outside of the
1331 * topic buttons, reset the current topic to the default one and
1332 * change the focus to some other default button (to keep keyboard
1333 * navigation possible).
1337 INT nOldTopic
= nTopic
;
1339 /* Also repaint the buttons, otherwise nothing repaints... */
1340 InvalidateRect(pTopics
[nOldTopic
]->hWndButton
, NULL
, TRUE
);
1342 /* Set the focus to some other default button */
1343 if (hWndCheckButton
)
1344 SetFocus(hWndCheckButton
);
1345 else if (hWndCloseButton
)
1346 SetFocus(hWndCloseButton
);
1349 /* Repaint the description panel */
1350 InvalidateRect(hWndMain
, &rcRightPanel
, TRUE
);
1361 OnCtlColorStatic(HWND hWnd
, WPARAM wParam
, LPARAM lParam
)
1363 UNREFERENCED_PARAMETER(hWnd
);
1365 if ((HWND
)lParam
== hWndCheckButton
)
1367 SetBkMode((HDC
)wParam
, TRANSPARENT
);
1368 return (LRESULT
)hbrLightBlue
;
1376 OnActivate(HWND hWnd
, WPARAM wParam
, LPARAM lParam
)
1378 UNREFERENCED_PARAMETER(hWnd
);
1379 UNREFERENCED_PARAMETER(lParam
);
1381 if (wParam
!= WA_INACTIVE
)
1384 * The main window is re-activated, set the focus back to
1385 * either the current topic or a default button.
1388 SetFocus(pTopics
[nTopic
]->hWndButton
);
1389 else if (hWndCheckButton
)
1390 SetFocus(hWndCheckButton
);
1391 else if (hWndCloseButton
)
1392 SetFocus(hWndCloseButton
);
1394 // InvalidateRect(hWndMain, &rcRightPanel, TRUE);
1402 OnDestroy(HWND hWnd
, WPARAM wParam
, LPARAM lParam
)
1406 UNREFERENCED_PARAMETER(hWnd
);
1407 UNREFERENCED_PARAMETER(wParam
);
1408 UNREFERENCED_PARAMETER(lParam
);
1410 for (i
= 0; i
< dwNumberTopics
; i
++)
1412 if (pTopics
[i
]->hWndButton
)
1413 DestroyWindow(pTopics
[i
]->hWndButton
);
1416 if (hWndCloseButton
)
1417 DestroyWindow(hWndCloseButton
);
1419 if (hWndCheckButton
)
1420 DestroyWindow(hWndCheckButton
);
1424 /* Delete bitmaps */
1425 DeleteObject(hDefaultTopicBitmap
);
1426 DeleteObject(hTitleBitmap
);
1427 for (i
= 0; i
< dwNumberTopics
; i
++)
1429 if (pTopics
[i
]->hBitmap
)
1430 DeleteObject(pTopics
[i
]->hBitmap
);
1433 DeleteObject(hFontTopicTitle
);
1434 DeleteObject(hFontTopicDescription
);
1435 DeleteObject(hFontTopicButton
);
1437 if (hFontCheckButton
)
1438 DeleteObject(hFontCheckButton
);
1440 DeleteObject(hbrLightBlue
);
1441 DeleteObject(hbrDarkBlue
);
1448 MainWndProc(HWND hWnd
,
1456 return OnCreate(hWnd
, wParam
, lParam
);
1459 return OnCommand(hWnd
, wParam
, lParam
);
1462 return OnActivate(hWnd
, wParam
, lParam
);
1465 return OnPaint(hWnd
, wParam
, lParam
);
1468 return OnDrawItem(hWnd
, wParam
, lParam
);
1470 case WM_CTLCOLORSTATIC
:
1471 return OnCtlColorStatic(hWnd
, wParam
, lParam
);
1474 return OnMouseMove(hWnd
, wParam
, lParam
);
1477 OnDestroy(hWnd
, wParam
, lParam
);
1482 return DefWindowProc(hWnd
, uMsg
, wParam
, lParam
);