1 /* 2 * ReactOS applications 3 * Copyright (C) 2001, 2002, 2003 ReactOS Team 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation; either version 2 of the License, or 8 * (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License along 16 * with this program; if not, write to the Free Software Foundation, Inc., 17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 */ 19 /* 20 * COPYRIGHT: See COPYING in the top level directory 21 * PROJECT: ReactOS "Welcome"/AutoRun application 22 * FILE: base/setup/welcome/welcome.c 23 * PROGRAMMERS: Eric Kohl 24 * Casper S. Hornstrup (chorns@users.sourceforge.net) 25 * Hermes Belusca-Maito 26 * 27 * NOTE: 28 * This utility can be customized by using localized INI configuration files. 29 * The default strings are stored in the utility's resources. 30 */ 31 32 #include <stdarg.h> 33 #include <tchar.h> 34 35 #include <windef.h> 36 #include <winbase.h> 37 #include <wingdi.h> 38 #include <winnls.h> 39 #include <winuser.h> 40 #include <shellapi.h> 41 #include <strsafe.h> 42 43 #include <reactos/buildno.h> 44 45 #include "resource.h" 46 47 #define LIGHT_BLUE RGB(214, 239, 247) 48 #define DARK_BLUE RGB(107, 123, 140) 49 50 #define TITLE_WIDTH 480 51 #define TITLE_HEIGHT 93 52 53 /* GLOBALS ******************************************************************/ 54 55 TCHAR szWindowClass[] = TEXT("WelcomeWindowClass"); 56 TCHAR szAppTitle[80]; 57 58 HINSTANCE hInstance; 59 60 HWND hWndMain = NULL; 61 62 HWND hWndCheckButton = NULL; 63 HWND hWndCloseButton = NULL; 64 65 BOOL bDisplayCheckBox = FALSE; 66 BOOL bDisplayExitBtn = TRUE; 67 68 #define BUFFER_SIZE 1024 69 70 #define TOPIC_TITLE_LENGTH 80 71 #define TOPIC_DESC_LENGTH 1024 72 73 typedef struct _TOPIC 74 { 75 HBITMAP hBitmap; 76 HWND hWndButton; 77 78 /* 79 * TRUE : szCommand contains a command (e.g. executable to run); 80 * FALSE: szCommand contains a custom "Welcome"/AutoRun action. 81 */ 82 BOOL bIsCommand; 83 84 TCHAR szText[80]; 85 TCHAR szTitle[TOPIC_TITLE_LENGTH]; 86 TCHAR szDesc[TOPIC_DESC_LENGTH]; 87 TCHAR szCommand[512]; 88 TCHAR szArgs[512]; 89 } TOPIC, *PTOPIC; 90 91 DWORD dwNumberTopics = 0; 92 PTOPIC* pTopics = NULL; 93 94 WNDPROC fnOldBtn; 95 96 TCHAR szDefaultTitle[TOPIC_TITLE_LENGTH]; 97 TCHAR szDefaultDesc[TOPIC_DESC_LENGTH]; 98 99 #define TOPIC_BTN_ID_BASE 100 100 101 INT nTopic = -1; // Active (focused) topic 102 INT nDefaultTopic = -1; // Default selected topic 103 104 HDC hdcMem = NULL; 105 HBITMAP hTitleBitmap = NULL; 106 HBITMAP hDefaultTopicBitmap = NULL; 107 108 HFONT hFontTopicButton; 109 HFONT hFontTopicTitle; 110 HFONT hFontTopicDescription; 111 HFONT hFontCheckButton; 112 113 HBRUSH hbrLightBlue; 114 HBRUSH hbrDarkBlue; 115 116 RECT rcTitlePanel; 117 RECT rcLeftPanel; 118 RECT rcRightPanel; 119 120 121 INT_PTR CALLBACK 122 MainWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); 123 124 125 /* FUNCTIONS ****************************************************************/ 126 127 INT GetLocaleName(IN LCID Locale, OUT LPTSTR lpLCData, IN SIZE_T cchData) 128 { 129 INT cchRet, cchRet2; 130 131 /* Try to retrieve the locale language name (LOCALE_SNAME is supported on Vista+) */ 132 cchRet = GetLocaleInfo(Locale, LOCALE_SNAME, lpLCData, cchData); 133 if (cchRet || (GetLastError() != ERROR_INVALID_FLAGS)) 134 return cchRet; 135 136 /* 137 * We failed because LOCALE_SNAME was unrecognized, so try to manually build 138 * a language name in the form xx-YY (WARNING: this method has its limitations). 139 */ 140 cchRet = GetLocaleInfo(Locale, LOCALE_SISO639LANGNAME, lpLCData, cchData); 141 if (cchRet <= 1) 142 return cchRet; 143 144 lpLCData += (cchRet - 1); 145 cchData -= (cchRet - 1); 146 if (cchData <= 1) 147 return cchRet; 148 149 /* Try to get the second part; we add the '-' separator only if we succeed */ 150 cchRet2 = GetLocaleInfo(Locale, LOCALE_SISO3166CTRYNAME, lpLCData + 1, cchData - 1); 151 if (cchRet2 <= 1) 152 return cchRet; 153 cchRet += cchRet2; // 'cchRet' already counts '-'. 154 155 *lpLCData = _T('-'); 156 157 return cchRet; 158 } 159 160 VOID TranslateEscapes(IN OUT LPTSTR lpString) 161 { 162 LPTSTR pEscape = NULL; // Next backslash escape sequence. 163 164 while (lpString && *lpString) 165 { 166 /* Find the next backslash escape sequence */ 167 pEscape = _tcschr(lpString, _T('\\')); 168 if (!pEscape) 169 break; 170 171 /* Go past the escape backslash */ 172 lpString = pEscape + 1; 173 174 /* Find which sequence it is */ 175 switch (*lpString) 176 { 177 case _T('\0'): 178 // *pEscape = _T('\0'); // Enable if one wants to convert \<NULL> into <NULL>. 179 // lpString = pEscape + 1; // Loop will stop at the next iteration. 180 break; 181 182 /* New-line and carriage return */ 183 case _T('n'): case _T('r'): 184 // case _T('\\'): // others? 185 // So far we only need to deal with the newlines. 186 { 187 if (*lpString == _T('n')) 188 *pEscape = _T('\n'); 189 else if (*lpString == _T('r')) 190 *pEscape = _T('\r'); 191 192 memmove(lpString, lpString + 1, (_tcslen(lpString + 1) + 1) * sizeof(TCHAR)); 193 break; 194 } 195 196 /* \xhhhh hexadecimal character specification */ 197 case _T('x'): 198 { 199 LPTSTR lpStringNew; 200 *pEscape = (WCHAR)_tcstoul(lpString + 1, &lpStringNew, 16); 201 memmove(lpString, lpStringNew, (_tcslen(lpStringNew) + 1) * sizeof(TCHAR)); 202 break; 203 } 204 205 /* Unknown escape sequence, ignore it */ 206 default: 207 lpString++; 208 break; 209 } 210 } 211 } 212 213 /* 214 * Expands the path for the ReactOS Installer "reactos.exe". 215 * See also base/system/userinit/userinit.c!StartInstaller() 216 */ 217 BOOL 218 ExpandInstallerPath( 219 IN LPCTSTR lpInstallerName, 220 OUT LPTSTR lpInstallerPath, 221 IN SIZE_T PathSize) 222 { 223 SYSTEM_INFO SystemInfo; 224 SIZE_T cchInstallerNameLen; 225 PTSTR ptr; 226 DWORD dwAttribs; 227 228 cchInstallerNameLen = _tcslen(lpInstallerName); 229 if (PathSize < cchInstallerNameLen) 230 { 231 /* The buffer is not large enough to contain the installer file name */ 232 *lpInstallerPath = 0; 233 return FALSE; 234 } 235 236 /* 237 * First, try to find the installer using the default drive, under 238 * the directory whose name corresponds to the currently-running 239 * CPU architecture. 240 */ 241 GetSystemInfo(&SystemInfo); 242 243 *lpInstallerPath = 0; 244 GetModuleFileName(NULL, lpInstallerPath, PathSize - cchInstallerNameLen - 1); 245 ptr = _tcschr(lpInstallerPath, _T('\\')); 246 if (ptr) 247 *++ptr = 0; 248 else 249 *lpInstallerPath = 0; 250 251 /* Append the corresponding CPU architecture */ 252 switch (SystemInfo.wProcessorArchitecture) 253 { 254 case PROCESSOR_ARCHITECTURE_INTEL: 255 StringCchCat(lpInstallerPath, PathSize, TEXT("I386")); 256 break; 257 258 case PROCESSOR_ARCHITECTURE_MIPS: 259 StringCchCat(lpInstallerPath, PathSize, TEXT("MIPS")); 260 break; 261 262 case PROCESSOR_ARCHITECTURE_ALPHA: 263 StringCchCat(lpInstallerPath, PathSize, TEXT("ALPHA")); 264 break; 265 266 case PROCESSOR_ARCHITECTURE_PPC: 267 StringCchCat(lpInstallerPath, PathSize, TEXT("PPC")); 268 break; 269 270 case PROCESSOR_ARCHITECTURE_SHX: 271 StringCchCat(lpInstallerPath, PathSize, TEXT("SHX")); 272 break; 273 274 case PROCESSOR_ARCHITECTURE_ARM: 275 StringCchCat(lpInstallerPath, PathSize, TEXT("ARM")); 276 break; 277 278 case PROCESSOR_ARCHITECTURE_IA64: 279 StringCchCat(lpInstallerPath, PathSize, TEXT("IA64")); 280 break; 281 282 case PROCESSOR_ARCHITECTURE_ALPHA64: 283 StringCchCat(lpInstallerPath, PathSize, TEXT("ALPHA64")); 284 break; 285 286 case PROCESSOR_ARCHITECTURE_AMD64: 287 StringCchCat(lpInstallerPath, PathSize, TEXT("AMD64")); 288 break; 289 290 // case PROCESSOR_ARCHITECTURE_MSIL: /* .NET CPU-independent code */ 291 case PROCESSOR_ARCHITECTURE_UNKNOWN: 292 default: 293 SystemInfo.wProcessorArchitecture = PROCESSOR_ARCHITECTURE_UNKNOWN; 294 break; 295 } 296 297 if (SystemInfo.wProcessorArchitecture != PROCESSOR_ARCHITECTURE_UNKNOWN) 298 StringCchCat(lpInstallerPath, PathSize, TEXT("\\")); 299 StringCchCat(lpInstallerPath, PathSize, lpInstallerName); 300 301 dwAttribs = GetFileAttributes(lpInstallerPath); 302 if ((dwAttribs != INVALID_FILE_ATTRIBUTES) && 303 !(dwAttribs & FILE_ATTRIBUTE_DIRECTORY)) 304 { 305 /* We have found the installer */ 306 return TRUE; 307 } 308 309 /* 310 * We failed. Try to find the installer from either the current 311 * ReactOS installation directory, or from our current directory. 312 */ 313 *lpInstallerPath = 0; 314 if (GetWindowsDirectory(lpInstallerPath, PathSize - cchInstallerNameLen - 1)) 315 StringCchCat(lpInstallerPath, PathSize, TEXT("\\")); 316 StringCchCat(lpInstallerPath, PathSize, lpInstallerName); 317 318 dwAttribs = GetFileAttributes(lpInstallerPath); 319 if ((dwAttribs != INVALID_FILE_ATTRIBUTES) && 320 !(dwAttribs & FILE_ATTRIBUTE_DIRECTORY)) 321 { 322 /* We have found the installer */ 323 return TRUE; 324 } 325 326 /* Installer not found */ 327 *lpInstallerPath = 0; 328 return FALSE; 329 } 330 331 VOID InitializeTopicList(VOID) 332 { 333 dwNumberTopics = 0; 334 pTopics = NULL; 335 } 336 337 PTOPIC AddNewTopic(VOID) 338 { 339 PTOPIC pTopic, *pTopicsTmp; 340 341 /* Allocate (or reallocate) the list of topics */ 342 if (!pTopics) 343 pTopicsTmp = HeapAlloc(GetProcessHeap(), 0, (dwNumberTopics + 1) * sizeof(*pTopics)); 344 else 345 pTopicsTmp = HeapReAlloc(GetProcessHeap(), 0, pTopics, (dwNumberTopics + 1) * sizeof(*pTopics)); 346 if (!pTopicsTmp) 347 return NULL; // Cannot reallocate more 348 pTopics = pTopicsTmp; 349 350 /* Allocate a new topic entry */ 351 pTopic = HeapAlloc(GetProcessHeap(), 0, sizeof(*pTopic)); 352 if (!pTopic) 353 return NULL; // Cannot reallocate more 354 pTopics[dwNumberTopics++] = pTopic; 355 356 /* Return the allocated topic entry */ 357 return pTopic; 358 } 359 360 PTOPIC 361 AddNewTopicEx( 362 IN LPTSTR szText OPTIONAL, 363 IN LPTSTR szTitle OPTIONAL, 364 IN LPTSTR szDesc OPTIONAL, 365 IN LPTSTR szCommand OPTIONAL, 366 IN LPTSTR szArgs OPTIONAL, 367 IN LPTSTR szAction OPTIONAL) 368 { 369 PTOPIC pTopic = AddNewTopic(); 370 if (!pTopic) 371 return NULL; 372 373 if (szText && *szText) 374 StringCchCopy(pTopic->szText, ARRAYSIZE(pTopic->szText), szText); 375 else 376 *pTopic->szText = 0; 377 378 if (szTitle && *szTitle) 379 StringCchCopy(pTopic->szTitle, ARRAYSIZE(pTopic->szTitle), szTitle); 380 else 381 *pTopic->szTitle = 0; 382 383 if (szDesc && *szDesc) 384 { 385 StringCchCopy(pTopic->szDesc, ARRAYSIZE(pTopic->szDesc), szDesc); 386 TranslateEscapes(pTopic->szDesc); 387 } 388 else 389 { 390 *pTopic->szDesc = 0; 391 } 392 393 if (szCommand && *szCommand) 394 { 395 pTopic->bIsCommand = TRUE; 396 397 /* Check for special applications: ReactOS Installer */ 398 if (_tcsicmp(szCommand, TEXT("reactos.exe")) == 0) 399 { 400 ExpandInstallerPath(szCommand, pTopic->szCommand, ARRAYSIZE(pTopic->szCommand)); 401 } 402 else 403 { 404 /* Expand any environment string in the command line */ 405 DWORD dwSize = ExpandEnvironmentStringsW(szCommand, NULL, 0); 406 if (dwSize <= ARRAYSIZE(pTopic->szCommand)) 407 ExpandEnvironmentStringsW(szCommand, pTopic->szCommand, ARRAYSIZE(pTopic->szCommand)); 408 else 409 StringCchCopy(pTopic->szCommand, ARRAYSIZE(pTopic->szCommand), szCommand); 410 } 411 } 412 else 413 { 414 pTopic->bIsCommand = FALSE; 415 *pTopic->szCommand = 0; 416 } 417 418 /* Only care about command arguments if we actually have a command */ 419 if (*pTopic->szCommand) 420 { 421 if (szArgs && *szArgs) 422 { 423 StringCchCopy(pTopic->szArgs, ARRAYSIZE(pTopic->szArgs), szArgs); 424 } 425 else 426 { 427 /* Check for special applications: ReactOS Shell */ 428 if (/* pTopic->szCommand && */ *pTopic->szCommand && 429 _tcsicmp(pTopic->szCommand, TEXT("explorer.exe")) == 0) 430 { 431 #if 0 432 TCHAR CurrentDir[MAX_PATH]; 433 GetCurrentDirectory(ARRAYSIZE(CurrentDir), CurrentDir); 434 #endif 435 StringCchCopy(pTopic->szArgs, ARRAYSIZE(pTopic->szArgs), TEXT("\\")); 436 } 437 else 438 { 439 *pTopic->szArgs = 0; 440 } 441 } 442 } 443 else 444 { 445 *pTopic->szArgs = 0; 446 } 447 448 /* Only care about custom actions if we actually don't have a command */ 449 if (!*pTopic->szCommand && szAction && *szAction) 450 { 451 /* 452 * Re-use the pTopic->szCommand member. We distinguish with respect to 453 * a regular command by using the pTopic->bIsCommand flag. 454 */ 455 pTopic->bIsCommand = FALSE; 456 StringCchCopy(pTopic->szCommand, ARRAYSIZE(pTopic->szCommand), szAction); 457 TranslateEscapes(pTopic->szCommand); 458 } 459 460 return pTopic; 461 } 462 463 static VOID 464 LoadLocalizedResourcesInternal(VOID) 465 { 466 #define MAX_NUMBER_INTERNAL_TOPICS 3 467 468 UINT i; 469 LPTSTR lpszCommand, lpszAction; 470 TOPIC newTopic, *pTopic; 471 472 for (i = 0; i < MAX_NUMBER_INTERNAL_TOPICS; ++i) 473 { 474 lpszCommand = NULL, lpszAction = NULL; 475 476 /* Retrieve the information */ 477 if (!LoadString(hInstance, IDS_TOPIC_BUTTON0 + i, newTopic.szText, ARRAYSIZE(newTopic.szText))) 478 *newTopic.szText = 0; 479 if (!LoadString(hInstance, IDS_TOPIC_TITLE0 + i, newTopic.szTitle, ARRAYSIZE(newTopic.szTitle))) 480 *newTopic.szTitle = 0; 481 if (!LoadString(hInstance, IDS_TOPIC_DESC0 + i, newTopic.szDesc, ARRAYSIZE(newTopic.szDesc))) 482 *newTopic.szDesc = 0; 483 484 if (!LoadString(hInstance, IDS_TOPIC_COMMAND0 + i, newTopic.szCommand, ARRAYSIZE(newTopic.szCommand))) 485 *newTopic.szCommand = 0; 486 487 /* Only care about command arguments if we actually have a command */ 488 if (*newTopic.szCommand) 489 { 490 lpszCommand = newTopic.szCommand; 491 if (!LoadString(hInstance, IDS_TOPIC_CMD_ARGS0 + i, newTopic.szArgs, ARRAYSIZE(newTopic.szArgs))) 492 *newTopic.szArgs = 0; 493 } 494 /* Only care about custom actions if we actually don't have a command */ 495 else // if (!*newTopic.szCommand) 496 { 497 lpszAction = newTopic.szCommand; 498 if (!LoadString(hInstance, IDS_TOPIC_ACTION0 + i, newTopic.szCommand, ARRAYSIZE(newTopic.szCommand))) 499 *newTopic.szCommand = 0; 500 } 501 502 /* Allocate a new topic */ 503 pTopic = AddNewTopicEx(newTopic.szText, 504 newTopic.szTitle, 505 newTopic.szDesc, 506 lpszCommand, 507 newTopic.szArgs, 508 lpszAction); 509 if (!pTopic) 510 break; // Cannot reallocate more 511 } 512 } 513 514 static BOOL 515 LoadLocalizedResourcesFromINI(LCID Locale, LPTSTR lpResPath) 516 { 517 DWORD dwRet; 518 DWORD dwAttribs; 519 DWORD dwSize; 520 TCHAR szBuffer[LOCALE_NAME_MAX_LENGTH]; 521 TCHAR szIniPath[MAX_PATH]; 522 LPTSTR lpszSections = NULL, lpszSection = NULL; 523 LPTSTR lpszCommand, lpszAction; 524 TOPIC newTopic, *pTopic; 525 526 /* Retrieve the locale name (on which the INI file name is based) */ 527 dwRet = (DWORD)GetLocaleName(Locale, szBuffer, ARRAYSIZE(szBuffer)); 528 if (!dwRet) 529 { 530 /* Fall back to english (US) */ 531 StringCchCopy(szBuffer, ARRAYSIZE(szBuffer), TEXT("en-US")); 532 } 533 534 /* Build the INI file name */ 535 StringCchPrintf(szIniPath, ARRAYSIZE(szIniPath), 536 TEXT("%s\\%s.ini"), lpResPath, szBuffer); 537 538 /* Verify that the file exists, otherwise fall back to english (US) */ 539 dwAttribs = GetFileAttributes(szIniPath); 540 if ((dwAttribs == INVALID_FILE_ATTRIBUTES) || 541 (dwAttribs & FILE_ATTRIBUTE_DIRECTORY)) 542 { 543 StringCchCopy(szBuffer, ARRAYSIZE(szBuffer), TEXT("en-US")); 544 545 StringCchPrintf(szIniPath, ARRAYSIZE(szIniPath), 546 TEXT("%s\\%s.ini"), lpResPath, szBuffer); 547 } 548 549 /* Verify that the file exists, otherwise fall back to internal (localized) resource */ 550 dwAttribs = GetFileAttributes(szIniPath); 551 if ((dwAttribs == INVALID_FILE_ATTRIBUTES) || 552 (dwAttribs & FILE_ATTRIBUTE_DIRECTORY)) 553 { 554 return FALSE; // For localized resources, see the general function. 555 } 556 557 /* Try to load the default localized strings */ 558 GetPrivateProfileString(TEXT("Defaults"), TEXT("AppTitle"), TEXT("ReactOS - Welcome") /* default */, 559 szAppTitle, ARRAYSIZE(szAppTitle), szIniPath); 560 GetPrivateProfileString(TEXT("Defaults"), TEXT("DefaultTopicTitle"), TEXT("") /* default */, 561 szDefaultTitle, ARRAYSIZE(szDefaultTitle), szIniPath); 562 if (!GetPrivateProfileString(TEXT("Defaults"), TEXT("DefaultTopicDescription"), TEXT("") /* default */, 563 szDefaultDesc, ARRAYSIZE(szDefaultDesc), szIniPath)) 564 { 565 *szDefaultDesc = 0; 566 } 567 else 568 { 569 TranslateEscapes(szDefaultDesc); 570 } 571 572 /* Allocate a buffer big enough to hold all the section names */ 573 for (dwSize = BUFFER_SIZE; ; dwSize += BUFFER_SIZE) 574 { 575 lpszSections = HeapAlloc(GetProcessHeap(), 0, dwSize * sizeof(TCHAR)); 576 if (!lpszSections) 577 return TRUE; // FIXME! 578 dwRet = GetPrivateProfileSectionNames(lpszSections, dwSize, szIniPath); 579 if (dwRet < dwSize - 2) 580 break; 581 HeapFree(GetProcessHeap(), 0, lpszSections); 582 } 583 584 /* Loop over the sections and load the topics */ 585 lpszSection = lpszSections; 586 for (; lpszSection && *lpszSection; lpszSection += (_tcslen(lpszSection) + 1)) 587 { 588 /* Ignore everything that is not a topic */ 589 if (_tcsnicmp(lpszSection, TEXT("Topic"), 5) != 0) 590 continue; 591 592 lpszCommand = NULL, lpszAction = NULL; 593 594 /* Retrieve the information */ 595 GetPrivateProfileString(lpszSection, TEXT("MenuText"), TEXT("") /* default */, 596 newTopic.szText, ARRAYSIZE(newTopic.szText), szIniPath); 597 GetPrivateProfileString(lpszSection, TEXT("Title"), TEXT("") /* default */, 598 newTopic.szTitle, ARRAYSIZE(newTopic.szTitle), szIniPath); 599 GetPrivateProfileString(lpszSection, TEXT("Description"), TEXT("") /* default */, 600 newTopic.szDesc, ARRAYSIZE(newTopic.szDesc), szIniPath); 601 602 GetPrivateProfileString(lpszSection, TEXT("ConfigCommand"), TEXT("") /* default */, 603 newTopic.szCommand, ARRAYSIZE(newTopic.szCommand), szIniPath); 604 605 /* Only care about command arguments if we actually have a command */ 606 if (*newTopic.szCommand) 607 { 608 lpszCommand = newTopic.szCommand; 609 GetPrivateProfileString(lpszSection, TEXT("ConfigArgs"), TEXT("") /* default */, 610 newTopic.szArgs, ARRAYSIZE(newTopic.szArgs), szIniPath); 611 } 612 /* Only care about custom actions if we actually don't have a command */ 613 else // if (!*newTopic.szCommand) 614 { 615 lpszAction = newTopic.szCommand; 616 GetPrivateProfileString(lpszSection, TEXT("Action"), TEXT("") /* default */, 617 newTopic.szCommand, ARRAYSIZE(newTopic.szCommand), szIniPath); 618 } 619 620 /* Allocate a new topic */ 621 pTopic = AddNewTopicEx(newTopic.szText, 622 newTopic.szTitle, 623 newTopic.szDesc, 624 lpszCommand, 625 newTopic.szArgs, 626 lpszAction); 627 if (!pTopic) 628 break; // Cannot reallocate more 629 } 630 631 HeapFree(GetProcessHeap(), 0, lpszSections); 632 633 return TRUE; 634 } 635 636 static VOID 637 LoadConfiguration(VOID) 638 { 639 DWORD dwAttribs; 640 BOOL bLoadDefaultResources; 641 TCHAR szAppPath[MAX_PATH]; 642 TCHAR szIniPath[MAX_PATH]; 643 TCHAR szResPath[MAX_PATH]; 644 645 /* Initialize the topic list */ 646 InitializeTopicList(); 647 648 /* 649 * First, try to load the default internal (localized) strings. 650 * They can be redefined by the localized INI files. 651 */ 652 if (!LoadString(hInstance, IDS_APPTITLE, szAppTitle, ARRAYSIZE(szAppTitle))) 653 StringCchCopy(szAppTitle, ARRAYSIZE(szAppTitle), TEXT("ReactOS - Welcome")); 654 if (!LoadString(hInstance, IDS_DEFAULT_TOPIC_TITLE, szDefaultTitle, ARRAYSIZE(szDefaultTitle))) 655 *szDefaultTitle = 0; 656 if (!LoadString(hInstance, IDS_DEFAULT_TOPIC_DESC, szDefaultDesc, ARRAYSIZE(szDefaultDesc))) 657 *szDefaultDesc = 0; 658 659 /* Retrieve the full path to this application */ 660 GetModuleFileName(NULL, szAppPath, ARRAYSIZE(szAppPath)); 661 if (*szAppPath) 662 { 663 LPTSTR lpFileName = _tcsrchr(szAppPath, _T('\\')); 664 if (lpFileName) 665 *lpFileName = 0; 666 else 667 *szAppPath = 0; 668 } 669 670 /* Build the full INI file path name */ 671 StringCchPrintf(szIniPath, ARRAYSIZE(szIniPath), TEXT("%s\\welcome.ini"), szAppPath); 672 673 /* Verify that the file exists, otherwise use the default configuration */ 674 dwAttribs = GetFileAttributes(szIniPath); 675 if ((dwAttribs == INVALID_FILE_ATTRIBUTES) || 676 (dwAttribs & FILE_ATTRIBUTE_DIRECTORY)) 677 { 678 /* Use the default internal (localized) resources */ 679 LoadLocalizedResourcesInternal(); 680 return; 681 } 682 683 /* Load the settings from the INI configuration file */ 684 bDisplayCheckBox = !!GetPrivateProfileInt(TEXT("Welcome"), TEXT("DisplayCheckBox"), FALSE /* default */, szIniPath); 685 bDisplayExitBtn = !!GetPrivateProfileInt(TEXT("Welcome"), TEXT("DisplayExitButton"), TRUE /* default */, szIniPath); 686 687 /* Load the default internal (localized) resources if needed */ 688 bLoadDefaultResources = !!GetPrivateProfileInt(TEXT("Welcome"), TEXT("LoadDefaultResources"), FALSE /* default */, szIniPath); 689 if (bLoadDefaultResources) 690 LoadLocalizedResourcesInternal(); 691 692 GetPrivateProfileString(TEXT("Welcome"), TEXT("ResourceDir"), TEXT("") /* default */, 693 szResPath, ARRAYSIZE(szResPath), szIniPath); 694 695 /* Set the current directory to the one of this application, and retrieve the resources */ 696 SetCurrentDirectory(szAppPath); 697 if (!LoadLocalizedResourcesFromINI(LOCALE_USER_DEFAULT, szResPath)) 698 { 699 /* 700 * Loading localized resources from INI file failed, try to load the 701 * internal resources only if they were not already loaded earlier. 702 */ 703 if (!bLoadDefaultResources) 704 LoadLocalizedResourcesInternal(); 705 } 706 } 707 708 static VOID 709 FreeResources(VOID) 710 { 711 if (!pTopics) 712 return; 713 714 while (dwNumberTopics--) 715 { 716 if (pTopics[dwNumberTopics]) 717 HeapFree(GetProcessHeap(), 0, pTopics[dwNumberTopics]); 718 } 719 HeapFree(GetProcessHeap(), 0, pTopics); 720 pTopics = NULL; 721 dwNumberTopics = 0; 722 } 723 724 #if 0 725 static VOID 726 ShowLastWin32Error(HWND hWnd) 727 { 728 LPTSTR lpMessageBuffer = NULL; 729 DWORD dwError = GetLastError(); 730 731 if (dwError == ERROR_SUCCESS) 732 return; 733 734 if (!FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | 735 FORMAT_MESSAGE_FROM_SYSTEM | 736 FORMAT_MESSAGE_IGNORE_INSERTS, 737 NULL, 738 dwError, 739 LANG_USER_DEFAULT, 740 (LPTSTR)&lpMessageBuffer, 741 0, NULL)) 742 { 743 return; 744 } 745 746 MessageBox(hWnd, lpMessageBuffer, szAppTitle, MB_OK | MB_ICONERROR); 747 LocalFree(lpMessageBuffer); 748 } 749 #endif 750 751 int WINAPI 752 _tWinMain(HINSTANCE hInst, 753 HINSTANCE hPrevInstance, 754 LPTSTR lpszCmdLine, 755 int nCmdShow) 756 { 757 HANDLE hMutex = NULL; 758 WNDCLASSEX wndclass; 759 MSG msg; 760 HWND hWndFocus; 761 INT xPos, yPos; 762 INT xWidth, yHeight; 763 RECT rcWindow; 764 HICON hMainIcon; 765 HMENU hSystemMenu; 766 DWORD dwStyle = WS_OVERLAPPED | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | 767 WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX; 768 769 BITMAP BitmapInfo; 770 ULONG ulInnerWidth = TITLE_WIDTH; 771 ULONG ulInnerHeight = (TITLE_WIDTH * 3) / 4; 772 ULONG ulTitleHeight = TITLE_HEIGHT + 3; 773 774 UNREFERENCED_PARAMETER(hPrevInstance); 775 UNREFERENCED_PARAMETER(lpszCmdLine); 776 777 /* Ensure only one instance is running */ 778 hMutex = CreateMutex(NULL, FALSE, szWindowClass); 779 if (hMutex && (GetLastError() == ERROR_ALREADY_EXISTS)) 780 { 781 /* If already started, find its window */ 782 hWndMain = FindWindow(szWindowClass, NULL); 783 784 /* Activate window */ 785 ShowWindow(hWndMain, SW_SHOWNORMAL); 786 SetForegroundWindow(hWndMain); 787 788 /* Close the mutex handle and quit */ 789 CloseHandle(hMutex); 790 return 0; 791 } 792 793 #if 0 794 /* Mirroring is enabled from within the resources */ 795 switch (GetUserDefaultUILanguage()) 796 { 797 case MAKELANGID(LANG_HEBREW, SUBLANG_DEFAULT): 798 SetProcessDefaultLayout(LAYOUT_RTL); 799 break; 800 801 default: 802 break; 803 } 804 #endif 805 806 hInstance = hInst; 807 808 /* Load icons */ 809 hMainIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_MAIN)); 810 811 /* Register the window class */ 812 wndclass.cbSize = sizeof(wndclass); 813 wndclass.style = CS_HREDRAW | CS_VREDRAW; 814 wndclass.lpfnWndProc = (WNDPROC)MainWndProc; 815 wndclass.cbClsExtra = 0; 816 wndclass.cbWndExtra = 0; 817 wndclass.hInstance = hInstance; 818 wndclass.hIcon = hMainIcon; 819 wndclass.hIconSm = NULL; 820 wndclass.hCursor = LoadCursor(NULL, IDC_ARROW); 821 wndclass.hbrBackground = NULL; 822 wndclass.lpszMenuName = NULL; 823 wndclass.lpszClassName = szWindowClass; 824 825 RegisterClassEx(&wndclass); 826 827 /* Load the banner bitmap, and compute the window dimensions */ 828 hTitleBitmap = LoadBitmap(hInstance, MAKEINTRESOURCE(IDB_TITLE_BITMAP)); 829 if (hTitleBitmap) 830 { 831 GetObject(hTitleBitmap, sizeof(BitmapInfo), &BitmapInfo); 832 ulInnerWidth = BitmapInfo.bmWidth; 833 ulInnerHeight = (ulInnerWidth * 3) / 4; 834 ulTitleHeight = BitmapInfo.bmHeight + 3; 835 DeleteObject(hTitleBitmap); 836 } 837 ulInnerHeight -= GetSystemMetrics(SM_CYCAPTION); 838 839 rcWindow.top = 0; 840 rcWindow.bottom = ulInnerHeight - 1; 841 rcWindow.left = 0; 842 rcWindow.right = ulInnerWidth - 1; 843 844 AdjustWindowRect(&rcWindow, dwStyle, FALSE); 845 xWidth = rcWindow.right - rcWindow.left; 846 yHeight = rcWindow.bottom - rcWindow.top; 847 848 /* Compute the window position */ 849 xPos = (GetSystemMetrics(SM_CXSCREEN) - xWidth) / 2; 850 yPos = (GetSystemMetrics(SM_CYSCREEN) - yHeight) / 2; 851 852 rcTitlePanel.top = 0; 853 rcTitlePanel.bottom = ulTitleHeight; 854 rcTitlePanel.left = 0; 855 rcTitlePanel.right = ulInnerWidth - 1; 856 857 rcLeftPanel.top = rcTitlePanel.bottom; 858 rcLeftPanel.bottom = ulInnerHeight - 1; 859 rcLeftPanel.left = 0; 860 rcLeftPanel.right = ulInnerWidth / 3; 861 862 rcRightPanel.top = rcLeftPanel.top; 863 rcRightPanel.bottom = rcLeftPanel.bottom; 864 rcRightPanel.left = rcLeftPanel.right; 865 rcRightPanel.right = ulInnerWidth - 1; 866 867 /* Load the configuration and the resources */ 868 LoadConfiguration(); 869 870 /* Create main window */ 871 hWndMain = CreateWindow(szWindowClass, 872 szAppTitle, 873 dwStyle, 874 xPos, 875 yPos, 876 xWidth, 877 yHeight, 878 0, 879 0, 880 hInstance, 881 NULL); 882 883 hSystemMenu = GetSystemMenu(hWndMain, FALSE); 884 if (hSystemMenu) 885 { 886 RemoveMenu(hSystemMenu, SC_SIZE, MF_BYCOMMAND); 887 RemoveMenu(hSystemMenu, SC_MAXIMIZE, MF_BYCOMMAND); 888 } 889 890 ShowWindow(hWndMain, nCmdShow); 891 UpdateWindow(hWndMain); 892 893 while (GetMessage(&msg, NULL, 0, 0) != FALSE) 894 { 895 /* Check for ENTER key presses */ 896 if (msg.message == WM_KEYDOWN && msg.wParam == VK_RETURN) 897 { 898 /* 899 * The user pressed the ENTER key. Retrieve the handle to the 900 * child window that has the keyboard focus, and send it a 901 * WM_COMMAND message. 902 */ 903 hWndFocus = GetFocus(); 904 if (hWndFocus) 905 { 906 SendMessage(hWndMain, WM_COMMAND, 907 (WPARAM)GetDlgCtrlID(hWndFocus), (LPARAM)hWndFocus); 908 } 909 } 910 /* Allow using keyboard navigation */ 911 else if (!IsDialogMessage(hWndMain, &msg)) 912 { 913 TranslateMessage(&msg); 914 DispatchMessage(&msg); 915 } 916 } 917 918 /* Cleanup */ 919 FreeResources(); 920 921 /* Close the mutex handle and quit */ 922 CloseHandle(hMutex); 923 return msg.wParam; 924 } 925 926 927 INT_PTR CALLBACK 928 ButtonSubclassWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 929 { 930 static WPARAM wParamOld = 0; 931 static LPARAM lParamOld = 0; 932 933 LONG i; 934 935 if (uMsg == WM_MOUSEMOVE) 936 { 937 /* Ignore mouse-move messages on the same point */ 938 if ((wParam == wParamOld) && (lParam == lParamOld)) 939 return 0; 940 941 /* Retrieve the topic index of this button */ 942 i = GetWindowLongPtr(hWnd, GWLP_ID) - TOPIC_BTN_ID_BASE; 943 944 /* 945 * Change the focus to this button if the current topic index differs 946 * (we will receive WM_SETFOCUS afterwards). 947 */ 948 if (nTopic != i) 949 SetFocus(hWnd); 950 951 wParamOld = wParam; 952 lParamOld = lParam; 953 } 954 else if (uMsg == WM_SETFOCUS) 955 { 956 /* Retrieve the topic index of this button */ 957 i = GetWindowLongPtr(hWnd, GWLP_ID) - TOPIC_BTN_ID_BASE; 958 959 /* Change the current topic index and repaint the description panel */ 960 if (nTopic != i) 961 { 962 nTopic = i; 963 InvalidateRect(hWndMain, &rcRightPanel, TRUE); 964 } 965 } 966 else if (uMsg == WM_KILLFOCUS) 967 { 968 /* 969 * We lost focus, either because the user changed button focus, 970 * or because the main window to which we belong went inactivated. 971 * If we are in the latter case, we ignore the focus change. 972 * If we are in the former case, we reset to the default topic. 973 */ 974 if (GetParent(hWnd) == GetForegroundWindow()) 975 { 976 nTopic = -1; 977 InvalidateRect(hWndMain, &rcRightPanel, TRUE); 978 } 979 } 980 981 return CallWindowProc(fnOldBtn, hWnd, uMsg, wParam, lParam); 982 } 983 984 985 static BOOL 986 RunAction(INT nTopic) 987 { 988 PCWSTR Command = NULL, Args = NULL; 989 990 if (nTopic < 0) 991 return TRUE; 992 993 Command = pTopics[nTopic]->szCommand; 994 if (/* !Command && */ !*Command) 995 return TRUE; 996 997 /* Check for known actions */ 998 if (!pTopics[nTopic]->bIsCommand) 999 { 1000 if (!_tcsicmp(Command, TEXT("<exit>"))) 1001 return FALSE; 1002 1003 if (!_tcsnicmp(Command, TEXT("<msg>"), 5)) 1004 { 1005 MessageBox(hWndMain, Command + 5, TEXT("ReactOS"), MB_OK | MB_TASKMODAL); 1006 return TRUE; 1007 } 1008 } 1009 else 1010 /* Run the command */ 1011 { 1012 Args = pTopics[nTopic]->szArgs; 1013 if (!*Args) Args = NULL; 1014 ShellExecute(NULL, NULL, 1015 Command, Args, 1016 NULL, SW_SHOWDEFAULT); 1017 } 1018 1019 return TRUE; 1020 } 1021 1022 1023 static DWORD 1024 GetButtonHeight(HDC hDC, 1025 HFONT hFont, 1026 LPCTSTR szText, 1027 DWORD dwWidth) 1028 { 1029 HFONT hOldFont; 1030 RECT rect; 1031 1032 rect.left = 0; 1033 rect.right = dwWidth - 20; 1034 rect.top = 0; 1035 rect.bottom = 25; 1036 1037 hOldFont = (HFONT)SelectObject(hDC, hFont); 1038 DrawText(hDC, szText, -1, &rect, DT_TOP | DT_CALCRECT | DT_WORDBREAK); 1039 SelectObject(hDC, hOldFont); 1040 1041 return (rect.bottom-rect.top + 14); 1042 } 1043 1044 1045 static LRESULT 1046 OnCreate(HWND hWnd, WPARAM wParam, LPARAM lParam) 1047 { 1048 UINT i; 1049 INT nLength; 1050 HDC ScreenDC; 1051 LOGFONT lf; 1052 DWORD dwTop; 1053 DWORD dwHeight = 0; 1054 TCHAR szText[80]; 1055 1056 UNREFERENCED_PARAMETER(wParam); 1057 UNREFERENCED_PARAMETER(lParam); 1058 1059 hbrLightBlue = CreateSolidBrush(LIGHT_BLUE); 1060 hbrDarkBlue = CreateSolidBrush(DARK_BLUE); 1061 1062 ZeroMemory(&lf, sizeof(lf)); 1063 1064 lf.lfEscapement = 0; 1065 lf.lfOrientation = 0; // TA_BASELINE; 1066 // lf.lfItalic = lf.lfUnderline = lf.lfStrikeOut = FALSE; 1067 lf.lfCharSet = ANSI_CHARSET; 1068 lf.lfOutPrecision = OUT_DEFAULT_PRECIS; 1069 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS; 1070 lf.lfQuality = DEFAULT_QUALITY; 1071 lf.lfPitchAndFamily = FF_DONTCARE; 1072 if (LoadString(hInstance, IDS_FONTNAME, lf.lfFaceName, ARRAYSIZE(lf.lfFaceName)) == 0) 1073 StringCchCopy(lf.lfFaceName, ARRAYSIZE(lf.lfFaceName), TEXT("Tahoma")); 1074 1075 /* Topic title font */ 1076 lf.lfHeight = -18; 1077 lf.lfWidth = 0; 1078 lf.lfWeight = FW_NORMAL; 1079 hFontTopicTitle = CreateFontIndirect(&lf); 1080 1081 /* Topic description font */ 1082 lf.lfHeight = -11; 1083 lf.lfWidth = 0; 1084 lf.lfWeight = FW_THIN; 1085 hFontTopicDescription = CreateFontIndirect(&lf); 1086 1087 /* Topic button font */ 1088 lf.lfHeight = -11; 1089 lf.lfWidth = 0; 1090 lf.lfWeight = FW_BOLD; 1091 hFontTopicButton = CreateFontIndirect(&lf); 1092 1093 /* Load title bitmap */ 1094 if (hTitleBitmap) 1095 hTitleBitmap = LoadBitmap(hInstance, MAKEINTRESOURCE(IDB_TITLE_BITMAP)); 1096 1097 /* Load topic bitmaps */ 1098 hDefaultTopicBitmap = LoadBitmap(hInstance, MAKEINTRESOURCE(IDB_DEFAULT_TOPIC_BITMAP)); 1099 for (i = 0; i < dwNumberTopics; i++) 1100 { 1101 // FIXME: Not implemented yet! 1102 // pTopics[i]->hBitmap = LoadBitmap(hInstance, MAKEINTRESOURCE(IDB_TOPIC_BITMAP0 + i)); 1103 pTopics[i]->hBitmap = NULL; 1104 } 1105 1106 ScreenDC = GetWindowDC(hWnd); 1107 hdcMem = CreateCompatibleDC(ScreenDC); 1108 ReleaseDC(hWnd, ScreenDC); 1109 1110 /* Load and create the menu buttons */ 1111 dwTop = rcLeftPanel.top; 1112 for (i = 0; i < dwNumberTopics; i++) 1113 { 1114 if (*pTopics[i]->szText) 1115 { 1116 dwHeight = GetButtonHeight(hdcMem, 1117 hFontTopicButton, 1118 pTopics[i]->szText, 1119 rcLeftPanel.right - rcLeftPanel.left); 1120 1121 pTopics[i]->hWndButton = CreateWindow(TEXT("BUTTON"), 1122 pTopics[i]->szText, 1123 (*pTopics[i]->szCommand ? 0 : WS_DISABLED) | 1124 WS_CHILDWINDOW | WS_VISIBLE | WS_TABSTOP | 1125 BS_MULTILINE | BS_OWNERDRAW, 1126 rcLeftPanel.left, 1127 dwTop, 1128 rcLeftPanel.right - rcLeftPanel.left, 1129 dwHeight, 1130 hWnd, 1131 (HMENU)IntToPtr(TOPIC_BTN_ID_BASE + i), // Similar to SetWindowLongPtr(GWLP_ID) 1132 hInstance, 1133 NULL); 1134 nDefaultTopic = i; 1135 SendMessage(pTopics[i]->hWndButton, WM_SETFONT, (WPARAM)hFontTopicButton, MAKELPARAM(TRUE, 0)); 1136 fnOldBtn = (WNDPROC)SetWindowLongPtr(pTopics[i]->hWndButton, GWLP_WNDPROC, (DWORD_PTR)ButtonSubclassWndProc); 1137 } 1138 else 1139 { 1140 pTopics[i]->hWndButton = NULL; 1141 } 1142 1143 dwTop += dwHeight; 1144 } 1145 1146 /* Create the checkbox */ 1147 if (bDisplayCheckBox) 1148 { 1149 nLength = LoadString(hInstance, IDS_CHECKTEXT, szText, ARRAYSIZE(szText)); 1150 if (nLength > 0) 1151 { 1152 lf.lfHeight = -10; 1153 lf.lfWidth = 0; 1154 lf.lfWeight = FW_THIN; 1155 hFontCheckButton = CreateFontIndirect(&lf); 1156 1157 hWndCheckButton = CreateWindow(TEXT("BUTTON"), 1158 szText, 1159 WS_CHILDWINDOW | WS_VISIBLE | WS_TABSTOP | 1160 BS_AUTOCHECKBOX | BS_MULTILINE /**/| BS_FLAT/**/, 1161 rcLeftPanel.left + 8, 1162 rcLeftPanel.bottom - 8 - 13, 1163 rcLeftPanel.right - rcLeftPanel.left - 16, 1164 13, 1165 hWnd, 1166 (HMENU)IDC_CHECKBUTTON, 1167 hInstance, 1168 NULL); 1169 SendMessage(hWndCheckButton, WM_SETFONT, (WPARAM)hFontCheckButton, MAKELPARAM(TRUE, 0)); 1170 } 1171 else 1172 { 1173 hFontCheckButton = NULL; 1174 hWndCheckButton = NULL; 1175 } 1176 } 1177 1178 /* Create the "Exit" button */ 1179 if (bDisplayExitBtn) 1180 { 1181 nLength = LoadString(hInstance, IDS_CLOSETEXT, szText, ARRAYSIZE(szText)); 1182 if (nLength > 0) 1183 { 1184 hWndCloseButton = CreateWindow(TEXT("BUTTON"), 1185 szText, 1186 WS_CHILDWINDOW | WS_VISIBLE | WS_TABSTOP | BS_FLAT, 1187 rcRightPanel.right - 8 - 57, 1188 rcRightPanel.bottom - 8 - 21, 1189 57, 1190 21, 1191 hWnd, 1192 (HMENU)IDC_CLOSEBUTTON, 1193 hInstance, 1194 NULL); 1195 nDefaultTopic = -1; 1196 SendMessage(hWndCloseButton, WM_SETFONT, (WPARAM)hFontTopicButton, MAKELPARAM(TRUE, 0)); 1197 } 1198 else 1199 { 1200 hWndCloseButton = NULL; 1201 } 1202 } 1203 1204 return 0; 1205 } 1206 1207 1208 static LRESULT 1209 OnCommand(HWND hWnd, WPARAM wParam, LPARAM lParam) 1210 { 1211 UNREFERENCED_PARAMETER(lParam); 1212 1213 /* Retrieve the low-word from wParam */ 1214 wParam = LOWORD(wParam); 1215 1216 /* Execute action */ 1217 if (wParam == IDC_CLOSEBUTTON) 1218 { 1219 DestroyWindow(hWnd); 1220 } 1221 else if (wParam - TOPIC_BTN_ID_BASE < dwNumberTopics) 1222 { 1223 if (RunAction(wParam - TOPIC_BTN_ID_BASE) == FALSE) 1224 DestroyWindow(hWnd); // Corresponds to a <exit> action. 1225 } 1226 1227 return 0; 1228 } 1229 1230 1231 static VOID 1232 PaintBanner(HDC hdc, LPRECT rcPanel) 1233 { 1234 HBITMAP hOldBitmap; 1235 HBRUSH hOldBrush; 1236 1237 /* Title bitmap */ 1238 hOldBitmap = (HBITMAP)SelectObject(hdcMem, hTitleBitmap); 1239 BitBlt(hdc, 1240 rcPanel->left, 1241 rcPanel->top, 1242 rcPanel->right - rcPanel->left, 1243 rcPanel->bottom - 3, 1244 hdcMem, 0, 0, SRCCOPY); 1245 SelectObject(hdcMem, hOldBitmap); 1246 1247 /* Dark blue line */ 1248 hOldBrush = (HBRUSH)SelectObject(hdc, hbrDarkBlue); 1249 PatBlt(hdc, 1250 rcPanel->left, 1251 rcPanel->bottom - 3, 1252 rcPanel->right - rcPanel->left, 1253 3, 1254 PATCOPY); 1255 SelectObject(hdc, hOldBrush); 1256 } 1257 1258 1259 static LRESULT 1260 OnPaint(HWND hWnd, WPARAM wParam, LPARAM lParam) 1261 { 1262 HPEN hPen; 1263 HPEN hOldPen; 1264 HDC hdc; 1265 PAINTSTRUCT ps; 1266 HBITMAP hOldBitmap = NULL; 1267 HBRUSH hOldBrush; 1268 HFONT hOldFont; 1269 RECT rcTitle, rcDescription; 1270 BITMAP bmpInfo; 1271 TCHAR szVersion[50]; 1272 LPTSTR lpTitle = NULL, lpDesc = NULL; 1273 1274 UNREFERENCED_PARAMETER(wParam); 1275 UNREFERENCED_PARAMETER(lParam); 1276 1277 hdc = BeginPaint(hWnd, &ps); 1278 1279 /* Banner panel */ 1280 PaintBanner(hdc, &rcTitlePanel); 1281 1282 /* Left panel */ 1283 hOldBrush = (HBRUSH)SelectObject(hdc, hbrLightBlue); 1284 PatBlt(hdc, 1285 rcLeftPanel.left, 1286 rcLeftPanel.top, 1287 rcLeftPanel.right - rcLeftPanel.left, 1288 rcLeftPanel.bottom - rcLeftPanel.top, 1289 PATCOPY); 1290 SelectObject(hdc, hOldBrush); 1291 1292 /* Right panel */ 1293 hOldBrush = (HBRUSH)SelectObject(hdc, GetStockObject(WHITE_BRUSH)); 1294 PatBlt(hdc, 1295 rcRightPanel.left, 1296 rcRightPanel.top, 1297 rcRightPanel.right - rcRightPanel.left, 1298 rcRightPanel.bottom - rcRightPanel.top, 1299 PATCOPY); 1300 SelectObject(hdc, hOldBrush); 1301 1302 /* Draw dark vertical line */ 1303 hPen = CreatePen(PS_SOLID, 0, DARK_BLUE); 1304 hOldPen = (HPEN)SelectObject(hdc, hPen); 1305 MoveToEx(hdc, rcRightPanel.left, rcRightPanel.top, NULL); 1306 LineTo(hdc, rcRightPanel.left, rcRightPanel.bottom); 1307 SelectObject(hdc, hOldPen); 1308 DeleteObject(hPen); 1309 1310 /* Draw topic bitmap */ 1311 if ((nTopic == -1) && (hDefaultTopicBitmap)) 1312 { 1313 GetObject(hDefaultTopicBitmap, sizeof(bmpInfo), &bmpInfo); 1314 hOldBitmap = (HBITMAP)SelectObject(hdcMem, hDefaultTopicBitmap); 1315 BitBlt(hdc, 1316 rcRightPanel.right - bmpInfo.bmWidth, 1317 rcRightPanel.bottom - bmpInfo.bmHeight, 1318 bmpInfo.bmWidth, 1319 bmpInfo.bmHeight, 1320 hdcMem, 1321 0, 1322 0, 1323 SRCCOPY); 1324 } 1325 else if ((nTopic != -1) && (pTopics[nTopic]->hBitmap)) 1326 { 1327 GetObject(pTopics[nTopic]->hBitmap, sizeof(bmpInfo), &bmpInfo); 1328 hOldBitmap = (HBITMAP)SelectObject(hdcMem, pTopics[nTopic]->hBitmap); 1329 BitBlt(hdc, 1330 rcRightPanel.right - bmpInfo.bmWidth, 1331 rcRightPanel.bottom - bmpInfo.bmHeight, 1332 bmpInfo.bmWidth, 1333 bmpInfo.bmHeight, 1334 hdcMem, 1335 0, 1336 0, 1337 SRCCOPY); 1338 } 1339 1340 if (nTopic == -1) 1341 { 1342 lpTitle = szDefaultTitle; 1343 lpDesc = szDefaultDesc; 1344 } 1345 else 1346 { 1347 lpTitle = pTopics[nTopic]->szTitle; 1348 lpDesc = pTopics[nTopic]->szDesc; 1349 } 1350 1351 SetBkMode(hdc, TRANSPARENT); 1352 1353 /* Draw version information */ 1354 StringCchCopy(szVersion, ARRAYSIZE(szVersion), 1355 TEXT("ReactOS ") TEXT(KERNEL_VERSION_STR)); 1356 1357 /* 1358 * Compute the original rect (position & size) of the version info, 1359 * depending whether the checkbox is displayed (version info in the 1360 * right panel) or not (version info in the left panel). 1361 */ 1362 if (bDisplayCheckBox) 1363 rcTitle = rcRightPanel; 1364 else 1365 rcTitle = rcLeftPanel; 1366 1367 rcTitle.left = rcTitle.left + 8; 1368 rcTitle.right = rcTitle.right - 5; 1369 rcTitle.top = rcTitle.bottom - 43; 1370 rcTitle.bottom = rcTitle.bottom - 8; 1371 1372 hOldFont = (HFONT)SelectObject(hdc, hFontTopicDescription); 1373 DrawText(hdc, szVersion, -1, &rcTitle, DT_BOTTOM | DT_CALCRECT | DT_SINGLELINE); 1374 SetTextColor(hdc, GetSysColor(COLOR_WINDOWTEXT)); 1375 DrawText(hdc, szVersion, -1, &rcTitle, DT_BOTTOM | DT_SINGLELINE); 1376 SelectObject(hdc, hOldFont); 1377 1378 /* Draw topic title */ 1379 rcTitle.left = rcRightPanel.left + 12; 1380 rcTitle.right = rcRightPanel.right - 8; 1381 rcTitle.top = rcRightPanel.top + 8; 1382 rcTitle.bottom = rcTitle.top + 57; 1383 hOldFont = (HFONT)SelectObject(hdc, hFontTopicTitle); 1384 DrawText(hdc, lpTitle, -1, &rcTitle, DT_TOP | DT_CALCRECT); 1385 SetTextColor(hdc, DARK_BLUE); 1386 DrawText(hdc, lpTitle, -1, &rcTitle, DT_TOP); 1387 SelectObject(hdc, hOldFont); 1388 1389 /* Draw topic description */ 1390 rcDescription.left = rcRightPanel.left + 12; 1391 rcDescription.right = rcRightPanel.right - 8; 1392 rcDescription.top = rcTitle.bottom + 8; 1393 rcDescription.bottom = rcRightPanel.bottom - 20; 1394 hOldFont = (HFONT)SelectObject(hdc, hFontTopicDescription); 1395 SetTextColor(hdc, GetSysColor(COLOR_WINDOWTEXT)); 1396 DrawText(hdc, lpDesc, -1, &rcDescription, DT_TOP | DT_WORDBREAK); 1397 SelectObject(hdc, hOldFont); 1398 1399 SetBkMode(hdc, OPAQUE); 1400 1401 SelectObject(hdcMem, hOldBrush); 1402 SelectObject(hdcMem, hOldBitmap); 1403 1404 EndPaint(hWnd, &ps); 1405 1406 return 0; 1407 } 1408 1409 1410 static LRESULT 1411 OnDrawItem(HWND hWnd, WPARAM wParam, LPARAM lParam) 1412 { 1413 LPDRAWITEMSTRUCT lpDis = (LPDRAWITEMSTRUCT)lParam; 1414 HPEN hPen, hOldPen; 1415 HBRUSH hOldBrush; 1416 INT iBkMode; 1417 TCHAR szText[80]; 1418 1419 UNREFERENCED_PARAMETER(hWnd); 1420 UNREFERENCED_PARAMETER(wParam); 1421 1422 #if 0 1423 /* Neither the checkbox button nor the close button implement owner-drawing */ 1424 if (lpDis->hwndItem == hWndCheckButton) 1425 return 0; 1426 if (lpDis->hwndItem == hWndCloseButton) 1427 { 1428 DrawFrameControl(lpDis->hDC, 1429 &lpDis->rcItem, 1430 DFC_BUTTON, 1431 DFCS_BUTTONPUSH | DFCS_FLAT); 1432 return TRUE; 1433 } 1434 #endif 1435 1436 if (lpDis->CtlID == (ULONG)(TOPIC_BTN_ID_BASE + nTopic)) 1437 hOldBrush = (HBRUSH)SelectObject(lpDis->hDC, GetStockObject(WHITE_BRUSH)); 1438 else 1439 hOldBrush = (HBRUSH)SelectObject(lpDis->hDC, hbrLightBlue); 1440 1441 PatBlt(lpDis->hDC, 1442 lpDis->rcItem.left, 1443 lpDis->rcItem.top, 1444 lpDis->rcItem.right, 1445 lpDis->rcItem.bottom, 1446 PATCOPY); 1447 SelectObject(lpDis->hDC, hOldBrush); 1448 1449 hPen = CreatePen(PS_SOLID, 0, DARK_BLUE); 1450 hOldPen = (HPEN)SelectObject(lpDis->hDC, hPen); 1451 MoveToEx(lpDis->hDC, lpDis->rcItem.left, lpDis->rcItem.bottom - 1, NULL); 1452 LineTo(lpDis->hDC, lpDis->rcItem.right, lpDis->rcItem.bottom - 1); 1453 SelectObject(lpDis->hDC, hOldPen); 1454 DeleteObject(hPen); 1455 1456 InflateRect(&lpDis->rcItem, -10, -4); 1457 OffsetRect(&lpDis->rcItem, 0, 1); 1458 GetWindowText(lpDis->hwndItem, szText, ARRAYSIZE(szText)); 1459 SetTextColor(lpDis->hDC, GetSysColor(IsWindowEnabled(lpDis->hwndItem) ? 1460 COLOR_WINDOWTEXT : COLOR_GRAYTEXT)); 1461 iBkMode = SetBkMode(lpDis->hDC, TRANSPARENT); 1462 DrawText(lpDis->hDC, szText, -1, &lpDis->rcItem, DT_TOP | DT_LEFT | DT_WORDBREAK); 1463 SetBkMode(lpDis->hDC, iBkMode); 1464 1465 return TRUE; 1466 } 1467 1468 1469 static LRESULT 1470 OnMouseMove(HWND hWnd, WPARAM wParam, LPARAM lParam) 1471 { 1472 static WPARAM wParamOld = 0; 1473 static LPARAM lParamOld = 0; 1474 1475 /* Ignore mouse-move messages on the same point */ 1476 if ((wParam == wParamOld) && (lParam == lParamOld)) 1477 return 0; 1478 1479 /* 1480 * If the user moves the mouse over the main window, outside of the 1481 * topic buttons, reset the current topic to the default one and 1482 * change the focus to some other default button (to keep keyboard 1483 * navigation possible). 1484 */ 1485 if (nTopic != -1) 1486 { 1487 INT nOldTopic = nTopic; 1488 nTopic = -1; 1489 /* Also repaint the buttons, otherwise nothing repaints... */ 1490 InvalidateRect(pTopics[nOldTopic]->hWndButton, NULL, TRUE); 1491 1492 /* Set the focus to some other default button */ 1493 if (hWndCheckButton) 1494 SetFocus(hWndCheckButton); 1495 else if (hWndCloseButton) 1496 SetFocus(hWndCloseButton); 1497 // SetFocus(hWnd); 1498 1499 /* Repaint the description panel */ 1500 InvalidateRect(hWndMain, &rcRightPanel, TRUE); 1501 } 1502 1503 wParamOld = wParam; 1504 lParamOld = lParam; 1505 1506 return 0; 1507 } 1508 1509 1510 static LRESULT 1511 OnCtlColorStatic(HWND hWnd, WPARAM wParam, LPARAM lParam) 1512 { 1513 UNREFERENCED_PARAMETER(hWnd); 1514 1515 if ((HWND)lParam == hWndCheckButton) 1516 { 1517 SetBkMode((HDC)wParam, TRANSPARENT); 1518 return (LRESULT)hbrLightBlue; 1519 } 1520 1521 return 0; 1522 } 1523 1524 1525 static LRESULT 1526 OnActivate(HWND hWnd, WPARAM wParam, LPARAM lParam) 1527 { 1528 UNREFERENCED_PARAMETER(hWnd); 1529 UNREFERENCED_PARAMETER(lParam); 1530 1531 if (wParam != WA_INACTIVE) 1532 { 1533 /* 1534 * The main window is re-activated, set the focus back to 1535 * either the current topic or a default button. 1536 */ 1537 if (nTopic != -1) 1538 SetFocus(pTopics[nTopic]->hWndButton); 1539 else if (hWndCheckButton) 1540 SetFocus(hWndCheckButton); 1541 else if (hWndCloseButton) 1542 SetFocus(hWndCloseButton); 1543 1544 // InvalidateRect(hWndMain, &rcRightPanel, TRUE); 1545 } 1546 1547 return 0; 1548 } 1549 1550 1551 static LRESULT 1552 OnDestroy(HWND hWnd, WPARAM wParam, LPARAM lParam) 1553 { 1554 UINT i; 1555 1556 UNREFERENCED_PARAMETER(hWnd); 1557 UNREFERENCED_PARAMETER(wParam); 1558 UNREFERENCED_PARAMETER(lParam); 1559 1560 for (i = 0; i < dwNumberTopics; i++) 1561 { 1562 if (pTopics[i]->hWndButton) 1563 DestroyWindow(pTopics[i]->hWndButton); 1564 } 1565 1566 if (hWndCloseButton) 1567 DestroyWindow(hWndCloseButton); 1568 1569 if (hWndCheckButton) 1570 DestroyWindow(hWndCheckButton); 1571 1572 DeleteDC(hdcMem); 1573 1574 /* Delete bitmaps */ 1575 DeleteObject(hDefaultTopicBitmap); 1576 DeleteObject(hTitleBitmap); 1577 for (i = 0; i < dwNumberTopics; i++) 1578 { 1579 if (pTopics[i]->hBitmap) 1580 DeleteObject(pTopics[i]->hBitmap); 1581 } 1582 1583 DeleteObject(hFontTopicTitle); 1584 DeleteObject(hFontTopicDescription); 1585 DeleteObject(hFontTopicButton); 1586 1587 if (hFontCheckButton) 1588 DeleteObject(hFontCheckButton); 1589 1590 DeleteObject(hbrLightBlue); 1591 DeleteObject(hbrDarkBlue); 1592 1593 return 0; 1594 } 1595 1596 1597 INT_PTR CALLBACK 1598 MainWndProc(HWND hWnd, 1599 UINT uMsg, 1600 WPARAM wParam, 1601 LPARAM lParam) 1602 { 1603 switch (uMsg) 1604 { 1605 case WM_CREATE: 1606 return OnCreate(hWnd, wParam, lParam); 1607 1608 case WM_COMMAND: 1609 return OnCommand(hWnd, wParam, lParam); 1610 1611 case WM_ACTIVATE: 1612 return OnActivate(hWnd, wParam, lParam); 1613 1614 case WM_PAINT: 1615 return OnPaint(hWnd, wParam, lParam); 1616 1617 case WM_DRAWITEM: 1618 return OnDrawItem(hWnd, wParam, lParam); 1619 1620 case WM_CTLCOLORSTATIC: 1621 return OnCtlColorStatic(hWnd, wParam, lParam); 1622 1623 case WM_MOUSEMOVE: 1624 return OnMouseMove(hWnd, wParam, lParam); 1625 1626 case WM_DESTROY: 1627 OnDestroy(hWnd, wParam, lParam); 1628 PostQuitMessage(0); 1629 return 0; 1630 } 1631 1632 return DefWindowProc(hWnd, uMsg, wParam, lParam); 1633 } 1634 1635 /* EOF */ 1636