1 /* 2 * PROJECT: ReactOS Applications Manager 3 * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+) 4 * FILE: base/applications/rapps/gui.cpp 5 * PURPOSE: GUI classes for RAPPS 6 * COPYRIGHT: Copyright 2015 David Quintana (gigaherz@gmail.com) 7 * Copyright 2017 Alexander Shaposhnikov (sanchaez@reactos.org) 8 */ 9 #include "rapps.h" 10 11 #include "rapps.h" 12 #include "rosui.h" 13 #include "crichedit.h" 14 15 #include <shlobj_undoc.h> 16 #include <shlguid_undoc.h> 17 18 #include <atlbase.h> 19 #include <atlcom.h> 20 #include <atlwin.h> 21 #include <wininet.h> 22 #include <shellutils.h> 23 #include <rosctrls.h> 24 25 #define SEARCH_TIMER_ID 'SR' 26 #define LISTVIEW_ICON_SIZE 24 27 #define TREEVIEW_ICON_SIZE 24 28 29 INT GetSystemColorDepth() 30 { 31 DEVMODEW pDevMode; 32 INT ColorDepth; 33 34 pDevMode.dmSize = sizeof(pDevMode); 35 pDevMode.dmDriverExtra = 0; 36 37 if (!EnumDisplaySettingsW(NULL, ENUM_CURRENT_SETTINGS, &pDevMode)) 38 { 39 /* TODO: Error message */ 40 return ILC_COLOR; 41 } 42 43 switch (pDevMode.dmBitsPerPel) 44 { 45 case 32: ColorDepth = ILC_COLOR32; break; 46 case 24: ColorDepth = ILC_COLOR24; break; 47 case 16: ColorDepth = ILC_COLOR16; break; 48 case 8: ColorDepth = ILC_COLOR8; break; 49 case 4: ColorDepth = ILC_COLOR4; break; 50 default: ColorDepth = ILC_COLOR; break; 51 } 52 53 return ColorDepth; 54 } 55 56 class CAppRichEdit: 57 public CUiWindow<CRichEdit> 58 { 59 private: 60 VOID LoadAndInsertText(UINT uStringID, 61 const ATL::CStringW& szText, 62 DWORD StringFlags, 63 DWORD TextFlags) 64 { 65 ATL::CStringW szLoadedText; 66 if (!szText.IsEmpty() && szLoadedText.LoadStringW(uStringID)) 67 { 68 InsertText(szLoadedText, StringFlags); 69 InsertText(szText, TextFlags); 70 } 71 } 72 73 VOID LoadAndInsertText(UINT uStringID, 74 DWORD StringFlags) 75 { 76 ATL::CStringW szLoadedText; 77 if (szLoadedText.LoadStringW(uStringID)) 78 { 79 InsertText(L"\n", 0); 80 InsertText(szLoadedText, StringFlags); 81 InsertText(L"\n", 0); 82 } 83 } 84 85 VOID InsertVersionInfo(CAvailableApplicationInfo* Info) 86 { 87 if (Info->IsInstalled()) 88 { 89 if (Info->HasInstalledVersion()) 90 { 91 if (Info->HasUpdate()) 92 LoadAndInsertText(IDS_STATUS_UPDATE_AVAILABLE, CFE_ITALIC); 93 else 94 LoadAndInsertText(IDS_STATUS_INSTALLED, CFE_ITALIC); 95 96 LoadAndInsertText(IDS_AINFO_VERSION, Info->m_szInstalledVersion, CFE_BOLD, 0); 97 } 98 else 99 { 100 LoadAndInsertText(IDS_STATUS_INSTALLED, CFE_ITALIC); 101 } 102 } 103 else 104 { 105 LoadAndInsertText(IDS_STATUS_NOTINSTALLED, CFE_ITALIC); 106 } 107 108 LoadAndInsertText(IDS_AINFO_AVAILABLEVERSION, Info->m_szVersion, CFE_BOLD, 0); 109 } 110 111 VOID InsertLicenseInfo(CAvailableApplicationInfo* Info) 112 { 113 ATL::CStringW szLicense; 114 switch (Info->m_LicenseType) 115 { 116 case LICENSE_OPENSOURCE: 117 szLicense.LoadStringW(IDS_LICENSE_OPENSOURCE); 118 break; 119 case LICENSE_FREEWARE: 120 szLicense.LoadStringW(IDS_LICENSE_FREEWARE); 121 break; 122 case LICENSE_TRIAL: 123 szLicense.LoadStringW(IDS_LICENSE_TRIAL); 124 break; 125 default: 126 LoadAndInsertText(IDS_AINFO_LICENSE, Info->m_szLicense, CFE_BOLD, 0); 127 return; 128 } 129 130 szLicense += L" (" + Info->m_szLicense + L")"; 131 LoadAndInsertText(IDS_AINFO_LICENSE, szLicense, CFE_BOLD, 0); 132 } 133 134 VOID InsertLanguageInfo(CAvailableApplicationInfo* Info) 135 { 136 if (!Info->HasLanguageInfo()) 137 { 138 return; 139 } 140 141 const INT nTranslations = Info->m_LanguageLCIDs.GetSize(); 142 ATL::CStringW szLangInfo; 143 ATL::CStringW szLoadedTextAvailability; 144 ATL::CStringW szLoadedAInfoText; 145 146 szLoadedAInfoText.LoadStringW(IDS_AINFO_LANGUAGES); 147 148 if (Info->HasNativeLanguage()) 149 { 150 szLoadedTextAvailability.LoadStringW(IDS_LANGUAGE_AVAILABLE_TRANSLATION); 151 if (nTranslations > 1) 152 { 153 ATL::CStringW buf; 154 buf.LoadStringW(IDS_LANGUAGE_MORE_PLACEHOLDER); 155 szLangInfo.Format(buf, nTranslations - 1); 156 } 157 else 158 { 159 szLangInfo.LoadStringW(IDS_LANGUAGE_SINGLE); 160 szLangInfo = L" (" + szLangInfo + L")"; 161 } 162 } 163 else if (Info->HasEnglishLanguage()) 164 { 165 szLoadedTextAvailability.LoadStringW(IDS_LANGUAGE_ENGLISH_TRANSLATION); 166 if (nTranslations > 1) 167 { 168 ATL::CStringW buf; 169 buf.LoadStringW(IDS_LANGUAGE_AVAILABLE_PLACEHOLDER); 170 szLangInfo.Format(buf, nTranslations - 1); 171 } 172 else 173 { 174 szLangInfo.LoadStringW(IDS_LANGUAGE_SINGLE); 175 szLangInfo = L" (" + szLangInfo + L")"; 176 } 177 } 178 else 179 { 180 szLoadedTextAvailability.LoadStringW(IDS_LANGUAGE_NO_TRANSLATION); 181 } 182 183 InsertText(szLoadedAInfoText, CFE_BOLD); 184 InsertText(szLoadedTextAvailability, NULL); 185 InsertText(szLangInfo, CFE_ITALIC); 186 } 187 188 public: 189 BOOL ShowAvailableAppInfo(CAvailableApplicationInfo* Info) 190 { 191 if (!Info) return FALSE; 192 193 SetText(Info->m_szName, CFE_BOLD); 194 InsertVersionInfo(Info); 195 InsertLicenseInfo(Info); 196 InsertLanguageInfo(Info); 197 198 LoadAndInsertText(IDS_AINFO_SIZE, Info->m_szSize, CFE_BOLD, 0); 199 LoadAndInsertText(IDS_AINFO_URLSITE, Info->m_szUrlSite, CFE_BOLD, CFE_LINK); 200 LoadAndInsertText(IDS_AINFO_DESCRIPTION, Info->m_szDesc, CFE_BOLD, 0); 201 LoadAndInsertText(IDS_AINFO_URLDOWNLOAD, Info->m_szUrlDownload, CFE_BOLD, CFE_LINK); 202 203 return TRUE; 204 } 205 206 BOOL ShowInstalledAppInfo(PINSTALLED_INFO Info) 207 { 208 ATL::CStringW szText; 209 ATL::CStringW szInfo; 210 211 if (!Info || !Info->hSubKey) 212 return FALSE; 213 214 Info->GetApplicationString(L"DisplayName", szText); 215 SetText(szText, CFE_BOLD); 216 InsertText(L"\n", 0); 217 218 #define GET_INFO(a, b, c, d) \ 219 if (Info->GetApplicationString(a, szInfo)) \ 220 { \ 221 LoadAndInsertText(b, szInfo, c, d); \ 222 } 223 224 GET_INFO(L"DisplayVersion", IDS_INFO_VERSION, CFE_BOLD, 0); 225 GET_INFO(L"Publisher", IDS_INFO_PUBLISHER, CFE_BOLD, 0); 226 GET_INFO(L"RegOwner", IDS_INFO_REGOWNER, CFE_BOLD, 0); 227 GET_INFO(L"ProductID", IDS_INFO_PRODUCTID, CFE_BOLD, 0); 228 GET_INFO(L"HelpLink", IDS_INFO_HELPLINK, CFE_BOLD, CFM_LINK); 229 GET_INFO(L"HelpTelephone", IDS_INFO_HELPPHONE, CFE_BOLD, 0); 230 GET_INFO(L"Readme", IDS_INFO_README, CFE_BOLD, 0); 231 GET_INFO(L"Contact", IDS_INFO_CONTACT, CFE_BOLD, 0); 232 GET_INFO(L"URLUpdateInfo", IDS_INFO_UPDATEINFO, CFE_BOLD, CFM_LINK); 233 GET_INFO(L"URLInfoAbout", IDS_INFO_INFOABOUT, CFE_BOLD, CFM_LINK); 234 GET_INFO(L"Comments", IDS_INFO_COMMENTS, CFE_BOLD, 0); 235 GET_INFO(L"InstallDate", IDS_INFO_INSTALLDATE, CFE_BOLD, 0); 236 GET_INFO(L"InstallLocation", IDS_INFO_INSTLOCATION, CFE_BOLD, 0); 237 GET_INFO(L"InstallSource", IDS_INFO_INSTALLSRC, CFE_BOLD, 0); 238 GET_INFO(L"UninstallString", IDS_INFO_UNINSTALLSTR, CFE_BOLD, 0); 239 GET_INFO(L"InstallSource", IDS_INFO_INSTALLSRC, CFE_BOLD, 0); 240 GET_INFO(L"ModifyPath", IDS_INFO_MODIFYPATH, CFE_BOLD, 0); 241 242 return TRUE; 243 } 244 245 VOID SetWelcomeText() 246 { 247 ATL::CStringW szText; 248 249 szText.LoadStringW(IDS_WELCOME_TITLE); 250 SetText(szText, CFE_BOLD); 251 252 szText.LoadStringW(IDS_WELCOME_TEXT); 253 InsertText(szText, 0); 254 255 szText.LoadStringW(IDS_WELCOME_URL); 256 InsertText(szText, CFM_LINK); 257 } 258 }; 259 260 class CMainToolbar : 261 public CUiWindow< CToolbar<> > 262 { 263 const INT m_iToolbarHeight; 264 DWORD m_dButtonsWidthMax; 265 266 WCHAR szInstallBtn[MAX_STR_LEN]; 267 WCHAR szUninstallBtn[MAX_STR_LEN]; 268 WCHAR szModifyBtn[MAX_STR_LEN]; 269 WCHAR szSelectAll[MAX_STR_LEN]; 270 271 VOID AddImageToImageList(HIMAGELIST hImageList, UINT ImageIndex) 272 { 273 HICON hImage; 274 275 if (!(hImage = (HICON) LoadImageW(hInst, 276 MAKEINTRESOURCE(ImageIndex), 277 IMAGE_ICON, 278 m_iToolbarHeight, 279 m_iToolbarHeight, 280 0))) 281 { 282 /* TODO: Error message */ 283 } 284 285 ImageList_AddIcon(hImageList, hImage); 286 DeleteObject(hImage); 287 } 288 289 HIMAGELIST InitImageList() 290 { 291 HIMAGELIST hImageList; 292 293 /* Create the toolbar icon image list */ 294 hImageList = ImageList_Create(m_iToolbarHeight,//GetSystemMetrics(SM_CXSMICON), 295 m_iToolbarHeight,//GetSystemMetrics(SM_CYSMICON), 296 ILC_MASK | GetSystemColorDepth(), 297 1, 1); 298 if (!hImageList) 299 { 300 /* TODO: Error message */ 301 return NULL; 302 } 303 304 AddImageToImageList(hImageList, IDI_INSTALL); 305 AddImageToImageList(hImageList, IDI_UNINSTALL); 306 AddImageToImageList(hImageList, IDI_MODIFY); 307 AddImageToImageList(hImageList, IDI_CHECK_ALL); 308 AddImageToImageList(hImageList, IDI_REFRESH); 309 AddImageToImageList(hImageList, IDI_UPDATE_DB); 310 AddImageToImageList(hImageList, IDI_SETTINGS); 311 AddImageToImageList(hImageList, IDI_EXIT); 312 313 return hImageList; 314 } 315 316 public: 317 CMainToolbar() : m_iToolbarHeight(24) 318 { 319 } 320 321 VOID OnGetDispInfo(LPTOOLTIPTEXT lpttt) 322 { 323 UINT idButton = (UINT) lpttt->hdr.idFrom; 324 325 switch (idButton) 326 { 327 case ID_EXIT: 328 lpttt->lpszText = MAKEINTRESOURCEW(IDS_TOOLTIP_EXIT); 329 break; 330 331 case ID_INSTALL: 332 lpttt->lpszText = MAKEINTRESOURCEW(IDS_TOOLTIP_INSTALL); 333 break; 334 335 case ID_UNINSTALL: 336 lpttt->lpszText = MAKEINTRESOURCEW(IDS_TOOLTIP_UNINSTALL); 337 break; 338 339 case ID_MODIFY: 340 lpttt->lpszText = MAKEINTRESOURCEW(IDS_TOOLTIP_MODIFY); 341 break; 342 343 case ID_SETTINGS: 344 lpttt->lpszText = MAKEINTRESOURCEW(IDS_TOOLTIP_SETTINGS); 345 break; 346 347 case ID_REFRESH: 348 lpttt->lpszText = MAKEINTRESOURCEW(IDS_TOOLTIP_REFRESH); 349 break; 350 351 case ID_RESETDB: 352 lpttt->lpszText = MAKEINTRESOURCEW(IDS_TOOLTIP_UPDATE_DB); 353 break; 354 } 355 } 356 357 HWND Create(HWND hwndParent) 358 { 359 /* Create buttons */ 360 TBBUTTON Buttons[] = 361 { /* iBitmap, idCommand, fsState, fsStyle, bReserved[2], dwData, iString */ 362 { 0, ID_INSTALL, TBSTATE_ENABLED, BTNS_BUTTON | BTNS_AUTOSIZE, { 0 }, 0, (INT_PTR) szInstallBtn }, 363 { 1, ID_UNINSTALL, TBSTATE_ENABLED, BTNS_BUTTON | BTNS_AUTOSIZE, { 0 }, 0, (INT_PTR) szUninstallBtn }, 364 { 2, ID_MODIFY, TBSTATE_ENABLED, BTNS_BUTTON | BTNS_AUTOSIZE, { 0 }, 0, (INT_PTR) szModifyBtn }, 365 { 3, ID_CHECK_ALL, TBSTATE_ENABLED, BTNS_BUTTON | BTNS_AUTOSIZE, { 0 }, 0, (INT_PTR) szSelectAll }, 366 { -1, 0, TBSTATE_ENABLED, BTNS_SEP, { 0 }, 0, 0 }, 367 { 4, ID_REFRESH, TBSTATE_ENABLED, BTNS_BUTTON | BTNS_AUTOSIZE, { 0 }, 0, 0 }, 368 { 5, ID_RESETDB, TBSTATE_ENABLED, BTNS_BUTTON | BTNS_AUTOSIZE, { 0 }, 0, 0 }, 369 { -1, 0, TBSTATE_ENABLED, BTNS_SEP, { 0 }, 0, 0 }, 370 { 6, ID_SETTINGS, TBSTATE_ENABLED, BTNS_BUTTON | BTNS_AUTOSIZE, { 0 }, 0, 0 }, 371 { 7, ID_EXIT, TBSTATE_ENABLED, BTNS_BUTTON | BTNS_AUTOSIZE, { 0 }, 0, 0 }, 372 }; 373 374 LoadStringW(hInst, IDS_INSTALL, szInstallBtn, _countof(szInstallBtn)); 375 LoadStringW(hInst, IDS_UNINSTALL, szUninstallBtn, _countof(szUninstallBtn)); 376 LoadStringW(hInst, IDS_MODIFY, szModifyBtn, _countof(szModifyBtn)); 377 LoadStringW(hInst, IDS_SELECT_ALL, szSelectAll, _countof(szSelectAll)); 378 379 m_hWnd = CreateWindowExW(0, TOOLBARCLASSNAMEW, NULL, 380 WS_CHILD | WS_VISIBLE | TBSTYLE_FLAT | TBSTYLE_TOOLTIPS | TBSTYLE_LIST, 381 0, 0, 0, 0, 382 hwndParent, 383 0, hInst, NULL); 384 385 if (!m_hWnd) 386 { 387 /* TODO: Show error message */ 388 return FALSE; 389 } 390 391 SendMessageW(TB_SETEXTENDEDSTYLE, 0, TBSTYLE_EX_HIDECLIPPEDBUTTONS); 392 SetButtonStructSize(); 393 394 /* Set image list */ 395 HIMAGELIST hImageList = InitImageList(); 396 397 if (!hImageList) 398 { 399 /* TODO: Show error message */ 400 return FALSE; 401 } 402 403 ImageList_Destroy(SetImageList(hImageList)); 404 405 AddButtons(_countof(Buttons), Buttons); 406 407 /* Remember ideal width to use as a max width of buttons */ 408 SIZE size; 409 GetIdealSize(FALSE, &size); 410 m_dButtonsWidthMax = size.cx; 411 412 return m_hWnd; 413 } 414 415 VOID HideButtonCaption() 416 { 417 DWORD dCurrentExStyle = (DWORD) SendMessageW(TB_GETEXTENDEDSTYLE, 0, 0); 418 SendMessageW(TB_SETEXTENDEDSTYLE, 0, dCurrentExStyle | TBSTYLE_EX_MIXEDBUTTONS); 419 } 420 421 VOID ShowButtonCaption() 422 { 423 DWORD dCurrentExStyle = (DWORD) SendMessageW(TB_GETEXTENDEDSTYLE, 0, 0); 424 SendMessageW(TB_SETEXTENDEDSTYLE, 0, dCurrentExStyle & ~TBSTYLE_EX_MIXEDBUTTONS); 425 } 426 427 DWORD GetMaxButtonsWidth() const 428 { 429 return m_dButtonsWidthMax; 430 } 431 }; 432 433 class CAppsListView : 434 public CUiWindow<CListView> 435 { 436 struct SortContext 437 { 438 CAppsListView * lvw; 439 INT iSubItem; 440 }; 441 442 BOOL bHasAllChecked; 443 BOOL bIsAscending; 444 BOOL bHasCheckboxes; 445 446 INT nLastHeaderID; 447 448 public: 449 CAppsListView() : 450 bHasAllChecked(FALSE), 451 bIsAscending(TRUE), 452 bHasCheckboxes(FALSE), 453 nLastHeaderID(-1) 454 { 455 } 456 457 VOID SetCheckboxesVisible(BOOL bIsVisible) 458 { 459 if (bIsVisible) 460 { 461 SetExtendedListViewStyle(LVS_EX_CHECKBOXES | LVS_EX_FULLROWSELECT); 462 } 463 else 464 { 465 SetExtendedListViewStyle(LVS_EX_FULLROWSELECT); 466 } 467 468 bHasCheckboxes = bIsVisible; 469 } 470 471 VOID ColumnClick(LPNMLISTVIEW pnmv) 472 { 473 HWND hHeader; 474 HDITEMW hColumn; 475 INT nHeaderID = pnmv->iSubItem; 476 477 if ((GetWindowLongPtr(GWL_STYLE) & ~LVS_NOSORTHEADER) == 0) 478 return; 479 480 hHeader = (HWND) SendMessage(LVM_GETHEADER, 0, 0); 481 ZeroMemory(&hColumn, sizeof(hColumn)); 482 483 /* If the sorting column changed, remove the sorting style from the old column */ 484 if ((nLastHeaderID != -1) && (nLastHeaderID != nHeaderID)) 485 { 486 hColumn.mask = HDI_FORMAT; 487 Header_GetItem(hHeader, nLastHeaderID, &hColumn); 488 hColumn.fmt &= ~(HDF_SORTUP | HDF_SORTDOWN); 489 Header_SetItem(hHeader, nLastHeaderID, &hColumn); 490 } 491 492 /* Set the sorting style to the new column */ 493 hColumn.mask = HDI_FORMAT; 494 Header_GetItem(hHeader, nHeaderID, &hColumn); 495 496 hColumn.fmt &= (bIsAscending ? ~HDF_SORTDOWN : ~HDF_SORTUP); 497 hColumn.fmt |= (bIsAscending ? HDF_SORTUP : HDF_SORTDOWN); 498 Header_SetItem(hHeader, nHeaderID, &hColumn); 499 500 /* Sort the list, using the current values of nHeaderID and bIsAscending */ 501 SortContext ctx = {this, nHeaderID}; 502 SortItems(s_CompareFunc, &ctx); 503 504 /* Save new values */ 505 nLastHeaderID = nHeaderID; 506 bIsAscending = !bIsAscending; 507 } 508 509 BOOL AddColumn(INT Index, ATL::CStringW& Text, INT Width, INT Format) 510 { 511 return AddColumn(Index, const_cast<LPWSTR>(Text.GetString()), Width, Format); 512 } 513 514 BOOL AddColumn(INT Index, LPWSTR lpText, INT Width, INT Format) 515 { 516 LVCOLUMNW Column; 517 518 ZeroMemory(&Column, sizeof(Column)); 519 520 Column.mask = LVCF_FMT | LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM; 521 Column.iSubItem = Index; 522 Column.pszText = lpText; 523 Column.cx = Width; 524 Column.fmt = Format; 525 526 return (InsertColumn(Index, &Column) == -1) ? FALSE : TRUE; 527 } 528 529 INT AddItem(INT ItemIndex, INT IconIndex, LPCWSTR lpText, LPARAM lParam) 530 { 531 LVITEMW Item; 532 533 ZeroMemory(&Item, sizeof(Item)); 534 535 Item.mask = LVIF_TEXT | LVIF_PARAM | LVIF_STATE | LVIF_IMAGE; 536 Item.pszText = const_cast<LPWSTR>(lpText); 537 Item.lParam = lParam; 538 Item.iItem = ItemIndex; 539 Item.iImage = IconIndex; 540 541 return InsertItem(&Item); 542 } 543 544 static INT CALLBACK s_CompareFunc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort) 545 { 546 SortContext * ctx = ((SortContext*) lParamSort); 547 return ctx->lvw->CompareFunc(lParam1, lParam2, ctx->iSubItem); 548 } 549 550 INT CompareFunc(LPARAM lParam1, LPARAM lParam2, INT iSubItem) 551 { 552 ATL::CStringW Item1, Item2; 553 LVFINDINFOW IndexInfo; 554 INT Index; 555 556 IndexInfo.flags = LVFI_PARAM; 557 558 IndexInfo.lParam = lParam1; 559 Index = FindItem(-1, &IndexInfo); 560 GetItemText(Index, iSubItem, Item1.GetBuffer(MAX_STR_LEN), MAX_STR_LEN); 561 Item1.ReleaseBuffer(); 562 563 IndexInfo.lParam = lParam2; 564 Index = FindItem(-1, &IndexInfo); 565 GetItemText(Index, iSubItem, Item2.GetBuffer(MAX_STR_LEN), MAX_STR_LEN); 566 Item2.ReleaseBuffer(); 567 568 return bIsAscending ? Item1.Compare(Item2) : Item2.Compare(Item1); 569 } 570 571 HWND Create(HWND hwndParent) 572 { 573 RECT r = {205, 28, 465, 250}; 574 DWORD style = WS_CHILD | WS_VISIBLE | LVS_SORTASCENDING | LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS; 575 HMENU menu = GetSubMenu(LoadMenuW(hInst, MAKEINTRESOURCEW(IDR_APPLICATIONMENU)), 0); 576 577 HWND hwnd = CListView::Create(hwndParent, r, NULL, style, WS_EX_CLIENTEDGE, menu); 578 579 if (hwnd) 580 { 581 SetCheckboxesVisible(FALSE); 582 } 583 584 return hwnd; 585 } 586 587 BOOL GetCheckState(INT item) 588 { 589 return (BOOL) (GetItemState(item, LVIS_STATEIMAGEMASK) >> 12) - 1; 590 } 591 592 VOID SetCheckState(INT item, BOOL fCheck) 593 { 594 if (bHasCheckboxes) 595 { 596 SetItemState(item, INDEXTOSTATEIMAGEMASK((fCheck) ? 2 : 1), LVIS_STATEIMAGEMASK); 597 SetSelected(item, fCheck); 598 } 599 } 600 601 VOID SetSelected(INT item, BOOL value) 602 { 603 if (item < 0) 604 { 605 for (INT i = 0; i >= 0; i = GetNextItem(i, LVNI_ALL)) 606 { 607 CAvailableApplicationInfo* pAppInfo = (CAvailableApplicationInfo*) GetItemData(i); 608 if (pAppInfo) 609 { 610 pAppInfo->m_IsSelected = value; 611 } 612 } 613 } 614 else 615 { 616 CAvailableApplicationInfo* pAppInfo = (CAvailableApplicationInfo*) GetItemData(item); 617 if (pAppInfo) 618 { 619 pAppInfo->m_IsSelected = value; 620 } 621 } 622 } 623 624 VOID CheckAll() 625 { 626 if (bHasCheckboxes) 627 { 628 bHasAllChecked = !bHasAllChecked; 629 SetCheckState(-1, bHasAllChecked); 630 } 631 } 632 633 ATL::CSimpleArray<CAvailableApplicationInfo> GetCheckedItems() 634 { 635 if (!bHasCheckboxes) 636 { 637 return ATL::CSimpleArray<CAvailableApplicationInfo>(); 638 } 639 640 ATL::CSimpleArray<CAvailableApplicationInfo> list; 641 for (INT i = 0; i >= 0; i = GetNextItem(i, LVNI_ALL)) 642 { 643 if (GetCheckState(i) != FALSE) 644 { 645 CAvailableApplicationInfo* pAppInfo = (CAvailableApplicationInfo*) GetItemData(i); 646 list.Add(*pAppInfo); 647 } 648 } 649 return list; 650 } 651 652 CAvailableApplicationInfo* GetSelectedData() 653 { 654 INT item = GetSelectionMark(); 655 return (CAvailableApplicationInfo*) GetItemData(item); 656 } 657 }; 658 659 class CSideTreeView : 660 public CUiWindow<CTreeView> 661 { 662 HIMAGELIST hImageTreeView; 663 664 public: 665 CSideTreeView() : 666 CUiWindow(), 667 hImageTreeView(ImageList_Create(TREEVIEW_ICON_SIZE, TREEVIEW_ICON_SIZE, 668 GetSystemColorDepth() | ILC_MASK, 669 0, 1)) 670 { 671 } 672 673 HTREEITEM AddItem(HTREEITEM hParent, ATL::CStringW &Text, INT Image, INT SelectedImage, LPARAM lParam) 674 { 675 return CUiWindow<CTreeView>::AddItem(hParent, const_cast<LPWSTR>(Text.GetString()), Image, SelectedImage, lParam); 676 } 677 678 HTREEITEM AddCategory(HTREEITEM hRootItem, UINT TextIndex, UINT IconIndex) 679 { 680 ATL::CStringW szText; 681 INT Index; 682 HICON hIcon; 683 684 hIcon = (HICON) LoadImageW(hInst, 685 MAKEINTRESOURCE(IconIndex), 686 IMAGE_ICON, 687 TREEVIEW_ICON_SIZE, 688 TREEVIEW_ICON_SIZE, 689 LR_CREATEDIBSECTION); 690 if (hIcon) 691 { 692 Index = ImageList_AddIcon(hImageTreeView, hIcon); 693 DestroyIcon(hIcon); 694 } 695 696 szText.LoadStringW(TextIndex); 697 return AddItem(hRootItem, szText, Index, Index, TextIndex); 698 } 699 700 HIMAGELIST SetImageList() 701 { 702 return CUiWindow<CTreeView>::SetImageList(hImageTreeView, TVSIL_NORMAL); 703 } 704 705 VOID DestroyImageList() 706 { 707 if (hImageTreeView) 708 ImageList_Destroy(hImageTreeView); 709 } 710 711 ~CSideTreeView() 712 { 713 DestroyImageList(); 714 } 715 }; 716 717 class CSearchBar : 718 public CWindow 719 { 720 public: 721 const INT m_Width; 722 const INT m_Height; 723 724 CSearchBar() : m_Width(200), m_Height(22) 725 { 726 } 727 728 VOID SetText(LPCWSTR lpszText) 729 { 730 SendMessageW(SB_SETTEXT, SBT_NOBORDERS, (LPARAM) lpszText); 731 } 732 733 HWND Create(HWND hwndParent) 734 { 735 ATL::CStringW szBuf; 736 m_hWnd = CreateWindowExW(WS_EX_CLIENTEDGE, L"Edit", NULL, 737 WS_CHILD | WS_VISIBLE | ES_LEFT | ES_AUTOHSCROLL, 738 0, 0, m_Width, m_Height, 739 hwndParent, (HMENU) NULL, 740 hInst, 0); 741 742 SendMessageW(WM_SETFONT, (WPARAM) GetStockObject(DEFAULT_GUI_FONT), 0); 743 szBuf.LoadStringW(IDS_SEARCH_TEXT); 744 SetWindowTextW(szBuf); 745 return m_hWnd; 746 } 747 748 }; 749 750 class CMainWindow : 751 public CWindowImpl<CMainWindow, CWindow, CFrameWinTraits> 752 { 753 CUiPanel* m_ClientPanel; 754 CUiSplitPanel* m_VSplitter; 755 CUiSplitPanel* m_HSplitter; 756 757 CMainToolbar* m_Toolbar; 758 CAppsListView* m_ListView; 759 760 CSideTreeView* m_TreeView; 761 CUiWindow<CStatusBar>* m_StatusBar; 762 CAppRichEdit* m_RichEdit; 763 764 CUiWindow<CSearchBar>* m_SearchBar; 765 CAvailableApps m_AvailableApps; 766 767 LPWSTR pLink; 768 769 INT nSelectedApps; 770 771 BOOL bSearchEnabled; 772 BOOL bUpdating; 773 774 ATL::CStringW szSearchPattern; 775 INT SelectedEnumType; 776 777 public: 778 CMainWindow() : 779 m_ClientPanel(NULL), 780 pLink(NULL), 781 bSearchEnabled(FALSE), 782 SelectedEnumType(ENUM_ALL_INSTALLED) 783 { 784 } 785 786 private: 787 VOID InitApplicationsList() 788 { 789 ATL::CStringW szText; 790 791 /* Add columns to ListView */ 792 szText.LoadStringW(IDS_APP_NAME); 793 m_ListView->AddColumn(0, szText, 250, LVCFMT_LEFT); 794 795 szText.LoadStringW(IDS_APP_INST_VERSION); 796 m_ListView->AddColumn(1, szText, 90, LVCFMT_RIGHT); 797 798 szText.LoadStringW(IDS_APP_DESCRIPTION); 799 m_ListView->AddColumn(3, szText, 300, LVCFMT_LEFT); 800 801 // Unnesesary since the list updates on every TreeView selection 802 // UpdateApplicationsList(ENUM_ALL_COMPONENTS); 803 } 804 805 VOID InitCategoriesList() 806 { 807 HTREEITEM hRootItemInstalled, hRootItemAvailable; 808 809 hRootItemInstalled = m_TreeView->AddCategory(TVI_ROOT, IDS_INSTALLED, IDI_CATEGORY); 810 m_TreeView->AddCategory(hRootItemInstalled, IDS_APPLICATIONS, IDI_APPS); 811 m_TreeView->AddCategory(hRootItemInstalled, IDS_UPDATES, IDI_APPUPD); 812 813 m_TreeView->AddCategory(TVI_ROOT, IDS_SELECTEDFORINST, IDI_SELECTEDFORINST); 814 815 hRootItemAvailable = m_TreeView->AddCategory(TVI_ROOT, IDS_AVAILABLEFORINST, IDI_CATEGORY); 816 m_TreeView->AddCategory(hRootItemAvailable, IDS_CAT_AUDIO, IDI_CAT_AUDIO); 817 m_TreeView->AddCategory(hRootItemAvailable, IDS_CAT_VIDEO, IDI_CAT_VIDEO); 818 m_TreeView->AddCategory(hRootItemAvailable, IDS_CAT_GRAPHICS, IDI_CAT_GRAPHICS); 819 m_TreeView->AddCategory(hRootItemAvailable, IDS_CAT_GAMES, IDI_CAT_GAMES); 820 m_TreeView->AddCategory(hRootItemAvailable, IDS_CAT_INTERNET, IDI_CAT_INTERNET); 821 m_TreeView->AddCategory(hRootItemAvailable, IDS_CAT_OFFICE, IDI_CAT_OFFICE); 822 m_TreeView->AddCategory(hRootItemAvailable, IDS_CAT_DEVEL, IDI_CAT_DEVEL); 823 m_TreeView->AddCategory(hRootItemAvailable, IDS_CAT_EDU, IDI_CAT_EDU); 824 m_TreeView->AddCategory(hRootItemAvailable, IDS_CAT_ENGINEER, IDI_CAT_ENGINEER); 825 m_TreeView->AddCategory(hRootItemAvailable, IDS_CAT_FINANCE, IDI_CAT_FINANCE); 826 m_TreeView->AddCategory(hRootItemAvailable, IDS_CAT_SCIENCE, IDI_CAT_SCIENCE); 827 m_TreeView->AddCategory(hRootItemAvailable, IDS_CAT_TOOLS, IDI_CAT_TOOLS); 828 m_TreeView->AddCategory(hRootItemAvailable, IDS_CAT_DRIVERS, IDI_CAT_DRIVERS); 829 m_TreeView->AddCategory(hRootItemAvailable, IDS_CAT_LIBS, IDI_CAT_LIBS); 830 m_TreeView->AddCategory(hRootItemAvailable, IDS_CAT_THEMES, IDI_CAT_THEMES); 831 m_TreeView->AddCategory(hRootItemAvailable, IDS_CAT_OTHER, IDI_CAT_OTHER); 832 833 m_TreeView->SetImageList(); 834 m_TreeView->Expand(hRootItemInstalled, TVE_EXPAND); 835 m_TreeView->Expand(hRootItemAvailable, TVE_EXPAND); 836 m_TreeView->SelectItem(hRootItemAvailable); 837 } 838 839 BOOL CreateStatusBar() 840 { 841 m_StatusBar = new CUiWindow<CStatusBar>(); 842 m_StatusBar->m_VerticalAlignment = UiAlign_RightBtm; 843 m_StatusBar->m_HorizontalAlignment = UiAlign_Stretch; 844 m_ClientPanel->Children().Append(m_StatusBar); 845 846 return m_StatusBar->Create(m_hWnd, (HMENU) IDC_STATUSBAR) != NULL; 847 } 848 849 BOOL CreateToolbar() 850 { 851 m_Toolbar = new CMainToolbar(); 852 m_Toolbar->m_VerticalAlignment = UiAlign_LeftTop; 853 m_Toolbar->m_HorizontalAlignment = UiAlign_Stretch; 854 m_ClientPanel->Children().Append(m_Toolbar); 855 856 return m_Toolbar->Create(m_hWnd) != NULL; 857 } 858 859 BOOL CreateTreeView() 860 { 861 m_TreeView = new CSideTreeView(); 862 m_TreeView->m_VerticalAlignment = UiAlign_Stretch; 863 m_TreeView->m_HorizontalAlignment = UiAlign_Stretch; 864 m_VSplitter->First().Append(m_TreeView); 865 866 return m_TreeView->Create(m_hWnd) != NULL; 867 } 868 869 BOOL CreateListView() 870 { 871 m_ListView = new CAppsListView(); 872 m_ListView->m_VerticalAlignment = UiAlign_Stretch; 873 m_ListView->m_HorizontalAlignment = UiAlign_Stretch; 874 m_HSplitter->First().Append(m_ListView); 875 876 return m_ListView->Create(m_hWnd) != NULL; 877 } 878 879 BOOL CreateRichEdit() 880 { 881 m_RichEdit = new CAppRichEdit(); 882 m_RichEdit->m_VerticalAlignment = UiAlign_Stretch; 883 m_RichEdit->m_HorizontalAlignment = UiAlign_Stretch; 884 m_HSplitter->Second().Append(m_RichEdit); 885 886 return m_RichEdit->Create(m_hWnd) != NULL; 887 } 888 889 BOOL CreateVSplitter() 890 { 891 m_VSplitter = new CUiSplitPanel(); 892 m_VSplitter->m_VerticalAlignment = UiAlign_Stretch; 893 m_VSplitter->m_HorizontalAlignment = UiAlign_Stretch; 894 m_VSplitter->m_DynamicFirst = FALSE; 895 m_VSplitter->m_Horizontal = FALSE; 896 m_VSplitter->m_MinFirst = 0; 897 m_VSplitter->m_MinSecond = 320; 898 m_VSplitter->m_Pos = 240; 899 m_ClientPanel->Children().Append(m_VSplitter); 900 901 return m_VSplitter->Create(m_hWnd) != NULL; 902 } 903 904 BOOL CreateHSplitter() 905 { 906 m_HSplitter = new CUiSplitPanel(); 907 m_HSplitter->m_VerticalAlignment = UiAlign_Stretch; 908 m_HSplitter->m_HorizontalAlignment = UiAlign_Stretch; 909 m_HSplitter->m_DynamicFirst = TRUE; 910 m_HSplitter->m_Horizontal = TRUE; 911 m_HSplitter->m_Pos = INT_MAX; //set INT_MAX to use lowest possible position (m_MinSecond) 912 m_HSplitter->m_MinFirst = 10; 913 m_HSplitter->m_MinSecond = 140; 914 m_VSplitter->Second().Append(m_HSplitter); 915 916 return m_HSplitter->Create(m_hWnd) != NULL; 917 } 918 919 BOOL CreateSearchBar() 920 { 921 m_SearchBar = new CUiWindow<CSearchBar>(); 922 m_SearchBar->m_VerticalAlignment = UiAlign_LeftTop; 923 m_SearchBar->m_HorizontalAlignment = UiAlign_RightBtm; 924 m_SearchBar->m_Margin.top = 4; 925 m_SearchBar->m_Margin.right = 6; 926 927 return m_SearchBar->Create(m_Toolbar->m_hWnd) != NULL; 928 } 929 930 BOOL CreateLayout() 931 { 932 BOOL b = TRUE; 933 bUpdating = TRUE; 934 935 m_ClientPanel = new CUiPanel(); 936 m_ClientPanel->m_VerticalAlignment = UiAlign_Stretch; 937 m_ClientPanel->m_HorizontalAlignment = UiAlign_Stretch; 938 939 // Top level 940 b = b && CreateStatusBar(); 941 b = b && CreateToolbar(); 942 b = b && CreateSearchBar(); 943 b = b && CreateVSplitter(); 944 945 // Inside V Splitter 946 b = b && CreateHSplitter(); 947 b = b && CreateTreeView(); 948 949 // Inside H Splitter 950 b = b && CreateListView(); 951 b = b && CreateRichEdit(); 952 953 if (b) 954 { 955 RECT rTop; 956 RECT rBottom; 957 958 /* Size status bar */ 959 m_StatusBar->SendMessageW(WM_SIZE, 0, 0); 960 961 /* Size tool bar */ 962 m_Toolbar->AutoSize(); 963 964 ::GetWindowRect(m_Toolbar->m_hWnd, &rTop); 965 ::GetWindowRect(m_StatusBar->m_hWnd, &rBottom); 966 967 m_VSplitter->m_Margin.top = rTop.bottom - rTop.top; 968 m_VSplitter->m_Margin.bottom = rBottom.bottom - rBottom.top; 969 } 970 971 bUpdating = FALSE; 972 return b; 973 } 974 975 BOOL InitControls() 976 { 977 if (CreateLayout()) 978 { 979 980 InitApplicationsList(); 981 InitCategoriesList(); 982 983 nSelectedApps = 0; 984 UpdateStatusBarText(); 985 986 return TRUE; 987 } 988 989 return FALSE; 990 } 991 992 VOID ShowAppInfo(INT Index) 993 { 994 if (IsInstalledEnum(SelectedEnumType)) 995 { 996 if (Index == -1) 997 Index = m_ListView->GetSelectionMark(); 998 999 PINSTALLED_INFO Info = (PINSTALLED_INFO) m_ListView->GetItemData(Index); 1000 1001 m_RichEdit->ShowInstalledAppInfo(Info); 1002 } 1003 else if (IsAvailableEnum(SelectedEnumType)) 1004 { 1005 if (Index == -1) 1006 return; 1007 1008 CAvailableApplicationInfo* Info = (CAvailableApplicationInfo*) m_ListView->GetItemData(Index); 1009 1010 m_RichEdit->ShowAvailableAppInfo(Info); 1011 } 1012 } 1013 1014 VOID OnSize(HWND hwnd, WPARAM wParam, LPARAM lParam) 1015 { 1016 if (wParam == SIZE_MINIMIZED) 1017 return; 1018 1019 /* Size status bar */ 1020 m_StatusBar->SendMessage(WM_SIZE, 0, 0); 1021 1022 /* Size tool bar */ 1023 m_Toolbar->AutoSize(); 1024 1025 /* Automatically hide captions */ 1026 DWORD dToolbarTreshold = m_Toolbar->GetMaxButtonsWidth(); 1027 DWORD dSearchbarMargin = (LOWORD(lParam) - m_SearchBar->m_Width); 1028 1029 if (dSearchbarMargin > dToolbarTreshold) 1030 { 1031 m_Toolbar->ShowButtonCaption(); 1032 } 1033 else if (dSearchbarMargin < dToolbarTreshold) 1034 { 1035 m_Toolbar->HideButtonCaption(); 1036 } 1037 1038 RECT r = {0, 0, LOWORD(lParam), HIWORD(lParam)}; 1039 HDWP hdwp = NULL; 1040 INT count = m_ClientPanel->CountSizableChildren(); 1041 1042 hdwp = BeginDeferWindowPos(count); 1043 if (hdwp) 1044 { 1045 hdwp = m_ClientPanel->OnParentSize(r, hdwp); 1046 if (hdwp) 1047 { 1048 EndDeferWindowPos(hdwp); 1049 } 1050 1051 } 1052 1053 // TODO: Sub-layouts for children of children 1054 count = m_SearchBar->CountSizableChildren(); 1055 hdwp = BeginDeferWindowPos(count); 1056 if (hdwp) 1057 { 1058 hdwp = m_SearchBar->OnParentSize(r, hdwp); 1059 if (hdwp) 1060 { 1061 EndDeferWindowPos(hdwp); 1062 } 1063 } 1064 1065 } 1066 1067 VOID RemoveSelectedAppFromRegistry() 1068 { 1069 PINSTALLED_INFO Info; 1070 WCHAR szFullName[MAX_PATH] = L"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\"; 1071 ATL::CStringW szMsgText, szMsgTitle; 1072 INT ItemIndex = m_ListView->GetNextItem(-1, LVNI_FOCUSED); 1073 1074 if (!IsInstalledEnum(SelectedEnumType)) 1075 return; 1076 1077 Info = reinterpret_cast<PINSTALLED_INFO>(m_ListView->GetItemData(ItemIndex)); 1078 if (!Info || !Info->hSubKey || (ItemIndex == -1)) 1079 return; 1080 1081 if (!szMsgText.LoadStringW(IDS_APP_REG_REMOVE) || 1082 !szMsgTitle.LoadStringW(IDS_INFORMATION)) 1083 return; 1084 1085 if (MessageBoxW(szMsgText, szMsgTitle, MB_YESNO | MB_ICONQUESTION) == IDYES) 1086 { 1087 ATL::CStringW::CopyChars(szFullName, 1088 MAX_PATH, 1089 Info->szKeyName.GetString(), 1090 MAX_PATH - wcslen(szFullName)); 1091 1092 if (RegDeleteKeyW(Info->hRootKey, szFullName) == ERROR_SUCCESS) 1093 { 1094 m_ListView->DeleteItem(ItemIndex); 1095 return; 1096 } 1097 1098 if (!szMsgText.LoadStringW(IDS_UNABLE_TO_REMOVE)) 1099 return; 1100 1101 MessageBoxW(szMsgText.GetString(), NULL, MB_OK | MB_ICONERROR); 1102 } 1103 } 1104 1105 BOOL UninstallSelectedApp(BOOL bModify) 1106 { 1107 WCHAR szAppName[MAX_STR_LEN]; 1108 1109 if (!IsInstalledEnum(SelectedEnumType)) 1110 return FALSE; 1111 1112 INT ItemIndex = m_ListView->GetNextItem(-1, LVNI_FOCUSED); 1113 if (ItemIndex == -1) 1114 return FALSE; 1115 1116 m_ListView->GetItemText(ItemIndex, 0, szAppName, _countof(szAppName)); 1117 WriteLogMessage(EVENTLOG_SUCCESS, MSG_SUCCESS_REMOVE, szAppName); 1118 1119 PINSTALLED_INFO ItemInfo = (PINSTALLED_INFO)m_ListView->GetItemData(ItemIndex); 1120 return UninstallApplication(ItemInfo, bModify); 1121 } 1122 BOOL ProcessWindowMessage(HWND hwnd, UINT Msg, WPARAM wParam, LPARAM lParam, LRESULT& theResult, DWORD dwMapId) 1123 { 1124 theResult = 0; 1125 switch (Msg) 1126 { 1127 case WM_CREATE: 1128 if (!InitControls()) 1129 ::PostMessageW(hwnd, WM_CLOSE, 0, 0); 1130 break; 1131 1132 case WM_DESTROY: 1133 { 1134 ShowWindow(SW_HIDE); 1135 SaveSettings(hwnd); 1136 1137 FreeLogs(); 1138 m_AvailableApps.FreeCachedEntries(); 1139 1140 if (IsInstalledEnum(SelectedEnumType)) 1141 FreeInstalledAppList(); 1142 1143 delete m_ClientPanel; 1144 1145 PostQuitMessage(0); 1146 return 0; 1147 } 1148 1149 case WM_COMMAND: 1150 OnCommand(wParam, lParam); 1151 break; 1152 1153 case WM_NOTIFY: 1154 { 1155 LPNMHDR data = (LPNMHDR) lParam; 1156 1157 switch (data->code) 1158 { 1159 case TVN_SELCHANGED: 1160 { 1161 if (data->hwndFrom == m_TreeView->m_hWnd) 1162 { 1163 switch (((LPNMTREEVIEW) lParam)->itemNew.lParam) 1164 { 1165 case IDS_INSTALLED: 1166 UpdateApplicationsList(ENUM_ALL_INSTALLED); 1167 break; 1168 1169 case IDS_APPLICATIONS: 1170 UpdateApplicationsList(ENUM_INSTALLED_APPLICATIONS); 1171 break; 1172 1173 case IDS_UPDATES: 1174 UpdateApplicationsList(ENUM_UPDATES); 1175 break; 1176 1177 case IDS_AVAILABLEFORINST: 1178 UpdateApplicationsList(ENUM_ALL_AVAILABLE); 1179 break; 1180 1181 case IDS_CAT_AUDIO: 1182 UpdateApplicationsList(ENUM_CAT_AUDIO); 1183 break; 1184 1185 case IDS_CAT_DEVEL: 1186 UpdateApplicationsList(ENUM_CAT_DEVEL); 1187 break; 1188 1189 case IDS_CAT_DRIVERS: 1190 UpdateApplicationsList(ENUM_CAT_DRIVERS); 1191 break; 1192 1193 case IDS_CAT_EDU: 1194 UpdateApplicationsList(ENUM_CAT_EDU); 1195 break; 1196 1197 case IDS_CAT_ENGINEER: 1198 UpdateApplicationsList(ENUM_CAT_ENGINEER); 1199 break; 1200 1201 case IDS_CAT_FINANCE: 1202 UpdateApplicationsList(ENUM_CAT_FINANCE); 1203 break; 1204 1205 case IDS_CAT_GAMES: 1206 UpdateApplicationsList(ENUM_CAT_GAMES); 1207 break; 1208 1209 case IDS_CAT_GRAPHICS: 1210 UpdateApplicationsList(ENUM_CAT_GRAPHICS); 1211 break; 1212 1213 case IDS_CAT_INTERNET: 1214 UpdateApplicationsList(ENUM_CAT_INTERNET); 1215 break; 1216 1217 case IDS_CAT_LIBS: 1218 UpdateApplicationsList(ENUM_CAT_LIBS); 1219 break; 1220 1221 case IDS_CAT_OFFICE: 1222 UpdateApplicationsList(ENUM_CAT_OFFICE); 1223 break; 1224 1225 case IDS_CAT_OTHER: 1226 UpdateApplicationsList(ENUM_CAT_OTHER); 1227 break; 1228 1229 case IDS_CAT_SCIENCE: 1230 UpdateApplicationsList(ENUM_CAT_SCIENCE); 1231 break; 1232 1233 case IDS_CAT_TOOLS: 1234 UpdateApplicationsList(ENUM_CAT_TOOLS); 1235 break; 1236 1237 case IDS_CAT_VIDEO: 1238 UpdateApplicationsList(ENUM_CAT_VIDEO); 1239 break; 1240 1241 case IDS_CAT_THEMES: 1242 UpdateApplicationsList(ENUM_CAT_THEMES); 1243 break; 1244 1245 case IDS_SELECTEDFORINST: 1246 UpdateApplicationsList(ENUM_CAT_SELECTED); 1247 break; 1248 } 1249 } 1250 1251 HMENU mainMenu = ::GetMenu(hwnd); 1252 HMENU lvwMenu = ::GetMenu(m_ListView->m_hWnd); 1253 1254 /* Disable/enable items based on treeview selection */ 1255 if (IsSelectedNodeInstalled()) 1256 { 1257 EnableMenuItem(mainMenu, ID_REGREMOVE, MF_ENABLED); 1258 EnableMenuItem(mainMenu, ID_INSTALL, MF_GRAYED); 1259 EnableMenuItem(mainMenu, ID_UNINSTALL, MF_ENABLED); 1260 EnableMenuItem(mainMenu, ID_MODIFY, MF_ENABLED); 1261 1262 EnableMenuItem(lvwMenu, ID_REGREMOVE, MF_ENABLED); 1263 EnableMenuItem(lvwMenu, ID_INSTALL, MF_GRAYED); 1264 EnableMenuItem(lvwMenu, ID_UNINSTALL, MF_ENABLED); 1265 EnableMenuItem(lvwMenu, ID_MODIFY, MF_ENABLED); 1266 1267 m_Toolbar->SendMessageW(TB_ENABLEBUTTON, ID_REGREMOVE, TRUE); 1268 m_Toolbar->SendMessageW(TB_ENABLEBUTTON, ID_INSTALL, FALSE); 1269 m_Toolbar->SendMessageW(TB_ENABLEBUTTON, ID_UNINSTALL, TRUE); 1270 m_Toolbar->SendMessageW(TB_ENABLEBUTTON, ID_MODIFY, TRUE); 1271 } 1272 else 1273 { 1274 EnableMenuItem(mainMenu, ID_REGREMOVE, MF_GRAYED); 1275 EnableMenuItem(mainMenu, ID_INSTALL, MF_ENABLED); 1276 EnableMenuItem(mainMenu, ID_UNINSTALL, MF_GRAYED); 1277 EnableMenuItem(mainMenu, ID_MODIFY, MF_GRAYED); 1278 1279 EnableMenuItem(lvwMenu, ID_REGREMOVE, MF_GRAYED); 1280 EnableMenuItem(lvwMenu, ID_INSTALL, MF_ENABLED); 1281 EnableMenuItem(lvwMenu, ID_UNINSTALL, MF_GRAYED); 1282 EnableMenuItem(lvwMenu, ID_MODIFY, MF_GRAYED); 1283 1284 m_Toolbar->SendMessageW(TB_ENABLEBUTTON, ID_REGREMOVE, FALSE); 1285 m_Toolbar->SendMessageW(TB_ENABLEBUTTON, ID_INSTALL, TRUE); 1286 m_Toolbar->SendMessageW(TB_ENABLEBUTTON, ID_UNINSTALL, FALSE); 1287 m_Toolbar->SendMessageW(TB_ENABLEBUTTON, ID_MODIFY, FALSE); 1288 } 1289 } 1290 break; 1291 1292 case LVN_ITEMCHANGED: 1293 { 1294 LPNMLISTVIEW pnic = (LPNMLISTVIEW) lParam; 1295 1296 if (pnic->hdr.hwndFrom == m_ListView->m_hWnd) 1297 { 1298 /* Check if this is a valid item 1299 * (technically, it can be also an unselect) */ 1300 INT ItemIndex = pnic->iItem; 1301 if (ItemIndex == -1 || 1302 ItemIndex >= ListView_GetItemCount(pnic->hdr.hwndFrom)) 1303 { 1304 break; 1305 } 1306 1307 /* Check if the focus has been moved to another item */ 1308 if ((pnic->uChanged & LVIF_STATE) && 1309 (pnic->uNewState & LVIS_FOCUSED) && 1310 !(pnic->uOldState & LVIS_FOCUSED)) 1311 { 1312 ShowAppInfo(ItemIndex); 1313 } 1314 /* Check if the item is checked */ 1315 if ((pnic->uNewState & LVIS_STATEIMAGEMASK) && !bUpdating) 1316 { 1317 BOOL checked = m_ListView->GetCheckState(pnic->iItem); 1318 /* FIXME: HAX! 1319 - preventing decremention below zero as a safeguard for ReactOS 1320 In ReactOS this action is triggered whenever user changes *selection*, but should be only when *checkbox* state toggled 1321 Maybe LVIS_STATEIMAGEMASK is set incorrectly 1322 */ 1323 nSelectedApps += 1324 (checked) 1325 ? 1 1326 : ((nSelectedApps > 0) 1327 ? -1 1328 : 0); 1329 1330 /* Update item's selection status */ 1331 m_ListView->SetSelected(pnic->iItem, checked); 1332 1333 UpdateStatusBarText(); 1334 } 1335 } 1336 } 1337 break; 1338 1339 case LVN_COLUMNCLICK: 1340 { 1341 LPNMLISTVIEW pnmv = (LPNMLISTVIEW) lParam; 1342 1343 m_ListView->ColumnClick(pnmv); 1344 } 1345 break; 1346 1347 case NM_CLICK: 1348 { 1349 if (data->hwndFrom == m_ListView->m_hWnd && ((LPNMLISTVIEW) lParam)->iItem != -1) 1350 { 1351 ShowAppInfo(-1); 1352 } 1353 } 1354 break; 1355 1356 case NM_DBLCLK: 1357 { 1358 if (data->hwndFrom == m_ListView->m_hWnd && ((LPNMLISTVIEW) lParam)->iItem != -1) 1359 { 1360 /* this won't do anything if the program is already installed */ 1361 SendMessageW(hwnd, WM_COMMAND, ID_INSTALL, 0); 1362 } 1363 } 1364 break; 1365 1366 case NM_RCLICK: 1367 { 1368 if (data->hwndFrom == m_ListView->m_hWnd && ((LPNMLISTVIEW) lParam)->iItem != -1) 1369 { 1370 ShowPopupMenu(m_ListView->m_hWnd, 0, ID_INSTALL); 1371 } 1372 } 1373 break; 1374 1375 case EN_LINK: 1376 OnLink((ENLINK*) lParam); 1377 break; 1378 1379 case TTN_GETDISPINFO: 1380 m_Toolbar->OnGetDispInfo((LPTOOLTIPTEXT) lParam); 1381 break; 1382 } 1383 } 1384 break; 1385 1386 case WM_SIZE: 1387 OnSize(hwnd, wParam, lParam); 1388 break; 1389 1390 case WM_SIZING: 1391 { 1392 LPRECT pRect = (LPRECT) lParam; 1393 1394 if (pRect->right - pRect->left < 565) 1395 pRect->right = pRect->left + 565; 1396 1397 if (pRect->bottom - pRect->top < 300) 1398 pRect->bottom = pRect->top + 300; 1399 1400 return TRUE; 1401 } 1402 1403 case WM_SYSCOLORCHANGE: 1404 { 1405 /* Forward WM_SYSCOLORCHANGE to common controls */ 1406 m_ListView->SendMessageW(WM_SYSCOLORCHANGE, 0, 0); 1407 m_TreeView->SendMessageW(WM_SYSCOLORCHANGE, 0, 0); 1408 m_Toolbar->SendMessageW(WM_SYSCOLORCHANGE, 0, 0); 1409 m_ListView->SendMessageW(EM_SETBKGNDCOLOR, 0, GetSysColor(COLOR_BTNFACE)); 1410 } 1411 break; 1412 1413 case WM_TIMER: 1414 if (wParam == SEARCH_TIMER_ID) 1415 { 1416 ::KillTimer(hwnd, SEARCH_TIMER_ID); 1417 if (bSearchEnabled) 1418 UpdateApplicationsList(-1); 1419 } 1420 break; 1421 } 1422 1423 return FALSE; 1424 } 1425 1426 virtual VOID OnLink(ENLINK *Link) 1427 { 1428 switch (Link->msg) 1429 { 1430 case WM_LBUTTONUP: 1431 case WM_RBUTTONUP: 1432 { 1433 if (pLink) HeapFree(GetProcessHeap(), 0, pLink); 1434 1435 pLink = (LPWSTR) HeapAlloc(GetProcessHeap(), 0, 1436 (max(Link->chrg.cpMin, Link->chrg.cpMax) - 1437 min(Link->chrg.cpMin, Link->chrg.cpMax) + 1) * sizeof(WCHAR)); 1438 if (!pLink) 1439 { 1440 /* TODO: Error message */ 1441 return; 1442 } 1443 1444 m_RichEdit->SendMessageW(EM_SETSEL, Link->chrg.cpMin, Link->chrg.cpMax); 1445 m_RichEdit->SendMessageW(EM_GETSELTEXT, 0, (LPARAM) pLink); 1446 1447 ShowPopupMenu(m_RichEdit->m_hWnd, IDR_LINKMENU, -1); 1448 } 1449 break; 1450 } 1451 } 1452 1453 BOOL IsSelectedNodeInstalled() 1454 { 1455 HTREEITEM hSelectedItem = m_TreeView->GetSelection(); 1456 TV_ITEM tItem; 1457 1458 tItem.mask = TVIF_PARAM | TVIF_HANDLE; 1459 tItem.hItem = hSelectedItem; 1460 m_TreeView->GetItem(&tItem); 1461 switch (tItem.lParam) 1462 { 1463 case IDS_INSTALLED: 1464 case IDS_APPLICATIONS: 1465 case IDS_UPDATES: 1466 return TRUE; 1467 default: 1468 return FALSE; 1469 } 1470 } 1471 1472 VOID ShowAboutDlg() 1473 { 1474 ATL::CStringW szApp; 1475 ATL::CStringW szAuthors; 1476 HICON hIcon; 1477 1478 szApp.LoadStringW(IDS_APPTITLE); 1479 szAuthors.LoadStringW(IDS_APP_AUTHORS); 1480 hIcon = LoadIconW(hInst, MAKEINTRESOURCEW(IDI_MAIN)); 1481 ShellAboutW(m_hWnd, szApp, szAuthors, hIcon); 1482 DestroyIcon(hIcon); 1483 } 1484 1485 VOID OnCommand(WPARAM wParam, LPARAM lParam) 1486 { 1487 WORD wCommand = LOWORD(wParam); 1488 1489 if (lParam == (LPARAM) m_SearchBar->m_hWnd) 1490 { 1491 ATL::CStringW szBuf; 1492 1493 switch (HIWORD(wParam)) 1494 { 1495 case EN_SETFOCUS: 1496 { 1497 ATL::CStringW szWndText; 1498 1499 szBuf.LoadStringW(IDS_SEARCH_TEXT); 1500 m_SearchBar->GetWindowTextW(szWndText); 1501 if (szBuf == szWndText) 1502 { 1503 bSearchEnabled = FALSE; 1504 m_SearchBar->SetWindowTextW(L""); 1505 } 1506 } 1507 break; 1508 1509 case EN_KILLFOCUS: 1510 { 1511 m_SearchBar->GetWindowTextW(szBuf); 1512 if (szBuf.IsEmpty()) 1513 { 1514 szBuf.LoadStringW(IDS_SEARCH_TEXT); 1515 bSearchEnabled = FALSE; 1516 m_SearchBar->SetWindowTextW(szBuf.GetString()); 1517 } 1518 } 1519 break; 1520 1521 case EN_CHANGE: 1522 { 1523 ATL::CStringW szWndText; 1524 1525 if (!bSearchEnabled) 1526 { 1527 bSearchEnabled = TRUE; 1528 break; 1529 } 1530 1531 szBuf.LoadStringW(IDS_SEARCH_TEXT); 1532 m_SearchBar->GetWindowTextW(szWndText); 1533 if (szBuf == szWndText) 1534 { 1535 szSearchPattern.Empty(); 1536 } 1537 else 1538 { 1539 szSearchPattern = szWndText; 1540 } 1541 1542 DWORD dwDelay; 1543 SystemParametersInfoW(SPI_GETMENUSHOWDELAY, 0, &dwDelay, 0); 1544 SetTimer(SEARCH_TIMER_ID, dwDelay); 1545 } 1546 break; 1547 } 1548 1549 return; 1550 } 1551 1552 switch (wCommand) 1553 { 1554 case ID_OPEN_LINK: 1555 ShellExecuteW(m_hWnd, L"open", pLink, NULL, NULL, SW_SHOWNOACTIVATE); 1556 HeapFree(GetProcessHeap(), 0, pLink); 1557 break; 1558 1559 case ID_COPY_LINK: 1560 CopyTextToClipboard(pLink); 1561 HeapFree(GetProcessHeap(), 0, pLink); 1562 break; 1563 1564 case ID_SETTINGS: 1565 CreateSettingsDlg(m_hWnd); 1566 break; 1567 1568 case ID_EXIT: 1569 PostMessageW(WM_CLOSE, 0, 0); 1570 break; 1571 1572 case ID_SEARCH: 1573 m_SearchBar->SetFocus(); 1574 break; 1575 1576 case ID_INSTALL: 1577 if (IsAvailableEnum(SelectedEnumType)) 1578 { 1579 if (nSelectedApps > 0) 1580 { 1581 DownloadListOfApplications(m_AvailableApps.GetSelected(), FALSE); 1582 UpdateApplicationsList(-1); 1583 m_ListView->SetSelected(-1, FALSE); 1584 } 1585 else if (DownloadApplication(m_ListView->GetSelectedData(), FALSE)) 1586 { 1587 UpdateApplicationsList(-1); 1588 } 1589 1590 } 1591 break; 1592 1593 case ID_UNINSTALL: 1594 if (UninstallSelectedApp(FALSE)) 1595 UpdateApplicationsList(-1); 1596 break; 1597 1598 case ID_MODIFY: 1599 if (UninstallSelectedApp(TRUE)) 1600 UpdateApplicationsList(-1); 1601 break; 1602 1603 case ID_REGREMOVE: 1604 RemoveSelectedAppFromRegistry(); 1605 break; 1606 1607 case ID_REFRESH: 1608 UpdateApplicationsList(-1); 1609 break; 1610 1611 case ID_RESETDB: 1612 CAvailableApps::ForceUpdateAppsDB(); 1613 UpdateApplicationsList(-1); 1614 break; 1615 1616 case ID_HELP: 1617 MessageBoxW(L"Help not implemented yet", NULL, MB_OK); 1618 break; 1619 1620 case ID_ABOUT: 1621 ShowAboutDlg(); 1622 break; 1623 1624 case ID_CHECK_ALL: 1625 m_ListView->CheckAll(); 1626 break; 1627 } 1628 } 1629 1630 VOID FreeInstalledAppList() 1631 { 1632 INT Count = m_ListView->GetItemCount() - 1; 1633 PINSTALLED_INFO Info; 1634 1635 while (Count >= 0) 1636 { 1637 Info = (PINSTALLED_INFO) m_ListView->GetItemData(Count); 1638 if (Info) 1639 { 1640 RegCloseKey(Info->hSubKey); 1641 delete Info; 1642 } 1643 Count--; 1644 } 1645 } 1646 1647 static BOOL SearchPatternMatch(LPCWSTR szHaystack, LPCWSTR szNeedle) 1648 { 1649 if (!*szNeedle) 1650 return TRUE; 1651 /* TODO: Improve pattern search beyond a simple case-insensitive substring search. */ 1652 return StrStrIW(szHaystack, szNeedle) != NULL; 1653 } 1654 1655 BOOL CALLBACK EnumInstalledAppProc(INT ItemIndex, ATL::CStringW &m_szName, PINSTALLED_INFO Info) 1656 { 1657 PINSTALLED_INFO ItemInfo; 1658 ATL::CStringW szText; 1659 INT Index; 1660 1661 if (!SearchPatternMatch(m_szName.GetString(), szSearchPattern)) 1662 { 1663 RegCloseKey(Info->hSubKey); 1664 return TRUE; 1665 } 1666 1667 ItemInfo = new INSTALLED_INFO(*Info); 1668 if (!ItemInfo) 1669 { 1670 RegCloseKey(Info->hSubKey); 1671 return FALSE; 1672 } 1673 1674 Index = m_ListView->AddItem(ItemIndex, 0, m_szName.GetString(), (LPARAM) ItemInfo); 1675 1676 /* Get version info */ 1677 ItemInfo->GetApplicationString(L"DisplayVersion", szText); 1678 m_ListView->SetItemText(Index, 1, szText.GetString()); 1679 1680 /* Get comments */ 1681 ItemInfo->GetApplicationString(L"Comments", szText); 1682 m_ListView->SetItemText(Index, 2, szText.GetString()); 1683 1684 return TRUE; 1685 } 1686 1687 BOOL EnumAvailableAppProc(CAvailableApplicationInfo* Info, LPCWSTR szFolderPath) 1688 { 1689 INT Index; 1690 HICON hIcon = NULL; 1691 1692 HIMAGELIST hImageListView = (HIMAGELIST)m_ListView->SendMessage(LVM_GETIMAGELIST, LVSIL_SMALL, 0); 1693 1694 if (!SearchPatternMatch(Info->m_szName.GetString(), szSearchPattern) && 1695 !SearchPatternMatch(Info->m_szDesc.GetString(), szSearchPattern)) 1696 { 1697 return TRUE; 1698 } 1699 1700 /* Load icon from file */ 1701 ATL::CStringW szIconPath; 1702 szIconPath.Format(L"%lsicons\\%ls.ico", szFolderPath, Info->m_szName.GetString()); 1703 hIcon = (HICON) LoadImageW(NULL, 1704 szIconPath.GetString(), 1705 IMAGE_ICON, 1706 LISTVIEW_ICON_SIZE, 1707 LISTVIEW_ICON_SIZE, 1708 LR_LOADFROMFILE); 1709 1710 if (!hIcon || GetLastError() != ERROR_SUCCESS) 1711 { 1712 /* Load default icon */ 1713 hIcon = (HICON) LoadIconW(hInst, MAKEINTRESOURCEW(IDI_MAIN)); 1714 } 1715 1716 Index = ImageList_AddIcon(hImageListView, hIcon); 1717 DestroyIcon(hIcon); 1718 1719 Index = m_ListView->AddItem(Info->m_Category, Index, Info->m_szName.GetString(), (LPARAM) Info); 1720 m_ListView->SetImageList(hImageListView, LVSIL_SMALL); 1721 m_ListView->SetItemText(Index, 1, Info->m_szVersion.GetString()); 1722 m_ListView->SetItemText(Index, 2, Info->m_szDesc.GetString()); 1723 m_ListView->SetCheckState(Index, Info->m_IsSelected); 1724 1725 return TRUE; 1726 } 1727 1728 static BOOL CALLBACK s_EnumInstalledAppProc(INT ItemIndex, ATL::CStringW &m_szName, PINSTALLED_INFO Info, PVOID param) 1729 { 1730 CMainWindow* pThis = (CMainWindow*)param; 1731 return pThis->EnumInstalledAppProc(ItemIndex, m_szName, Info); 1732 } 1733 1734 static BOOL CALLBACK s_EnumAvailableAppProc(CAvailableApplicationInfo* Info, LPCWSTR szFolderPath, PVOID param) 1735 { 1736 CMainWindow* pThis = (CMainWindow*)param; 1737 return pThis->EnumAvailableAppProc(Info, szFolderPath); 1738 } 1739 1740 VOID UpdateStatusBarText() 1741 { 1742 if (m_StatusBar) 1743 { 1744 ATL::CStringW szBuffer; 1745 1746 szBuffer.Format(IDS_APPS_COUNT, m_ListView->GetItemCount(), nSelectedApps); 1747 m_StatusBar->SetText(szBuffer); 1748 } 1749 } 1750 1751 VOID UpdateApplicationsList(INT EnumType) 1752 { 1753 ATL::CStringW szBuffer1, szBuffer2; 1754 HIMAGELIST hImageListView; 1755 BOOL bWasInInstalled = IsInstalledEnum(SelectedEnumType); 1756 1757 bUpdating = TRUE; 1758 m_ListView->SetRedraw(FALSE); 1759 1760 if (EnumType < 0) 1761 { 1762 EnumType = SelectedEnumType; 1763 } 1764 1765 //if previous one was INSTALLED purge the list 1766 //TODO: make the Installed category a separate class to avoid doing this 1767 if (bWasInInstalled) 1768 { 1769 FreeInstalledAppList(); 1770 } 1771 1772 m_ListView->DeleteAllItems(); 1773 1774 // Create new ImageList 1775 hImageListView = ImageList_Create(LISTVIEW_ICON_SIZE, 1776 LISTVIEW_ICON_SIZE, 1777 GetSystemColorDepth() | ILC_MASK, 1778 0, 1); 1779 HIMAGELIST hImageListBuf = m_ListView->SetImageList(hImageListView, LVSIL_SMALL); 1780 if (hImageListBuf) 1781 { 1782 ImageList_Destroy(hImageListBuf); 1783 } 1784 1785 if (IsInstalledEnum(EnumType)) 1786 { 1787 if (!bWasInInstalled) 1788 { 1789 m_ListView->SetCheckboxesVisible(FALSE); 1790 } 1791 1792 HICON hIcon = (HICON) LoadIconW(hInst, MAKEINTRESOURCEW(IDI_MAIN)); 1793 ImageList_AddIcon(hImageListView, hIcon); 1794 DestroyIcon(hIcon); 1795 1796 // Enum installed applications and updates 1797 EnumInstalledApplications(EnumType, TRUE, s_EnumInstalledAppProc, this); 1798 EnumInstalledApplications(EnumType, FALSE, s_EnumInstalledAppProc, this); 1799 } 1800 else if (IsAvailableEnum(EnumType)) 1801 { 1802 if (bWasInInstalled) 1803 { 1804 m_ListView->SetCheckboxesVisible(TRUE); 1805 } 1806 1807 // Enum available applications 1808 m_AvailableApps.Enum(EnumType, s_EnumAvailableAppProc, this); 1809 } 1810 1811 SelectedEnumType = EnumType; 1812 UpdateStatusBarText(); 1813 m_RichEdit->SetWelcomeText(); 1814 1815 // Set automatic column width for program names if the list is not empty 1816 if (m_ListView->GetItemCount() > 0) 1817 { 1818 ListView_SetColumnWidth(m_ListView->GetWindow(), 0, LVSCW_AUTOSIZE); 1819 } 1820 1821 bUpdating = FALSE; 1822 m_ListView->SetRedraw(TRUE); 1823 } 1824 1825 public: 1826 static ATL::CWndClassInfo& GetWndClassInfo() 1827 { 1828 DWORD csStyle = CS_VREDRAW | CS_HREDRAW; 1829 static ATL::CWndClassInfo wc = 1830 { 1831 { 1832 sizeof(WNDCLASSEX), 1833 csStyle, 1834 StartWindowProc, 1835 0, 1836 0, 1837 NULL, 1838 LoadIconW(_AtlBaseModule.GetModuleInstance(), MAKEINTRESOURCEW(IDI_MAIN)), 1839 LoadCursorW(NULL, IDC_ARROW), 1840 (HBRUSH) (COLOR_BTNFACE + 1), 1841 MAKEINTRESOURCEW(IDR_MAINMENU), 1842 L"RAppsWnd", 1843 NULL 1844 }, 1845 NULL, NULL, IDC_ARROW, TRUE, 0, _T("") 1846 }; 1847 return wc; 1848 } 1849 1850 HWND Create() 1851 { 1852 ATL::CStringW szWindowName; 1853 szWindowName.LoadStringW(IDS_APPTITLE); 1854 1855 RECT r = { 1856 (SettingsInfo.bSaveWndPos ? SettingsInfo.Left : CW_USEDEFAULT), 1857 (SettingsInfo.bSaveWndPos ? SettingsInfo.Top : CW_USEDEFAULT), 1858 (SettingsInfo.bSaveWndPos ? SettingsInfo.Width : 680), 1859 (SettingsInfo.bSaveWndPos ? SettingsInfo.Height : 450) 1860 }; 1861 r.right += r.left; 1862 r.bottom += r.top; 1863 1864 return CWindowImpl::Create(NULL, r, szWindowName.GetString(), WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, WS_EX_WINDOWEDGE); 1865 } 1866 1867 void HandleTabOrder(int direction) 1868 { 1869 HWND Controls[] = { m_Toolbar->m_hWnd, m_SearchBar->m_hWnd, m_TreeView->m_hWnd, m_ListView->m_hWnd, m_RichEdit->m_hWnd }; 1870 // When there is no control found, go to the first or last (depending on tab vs shift-tab) 1871 int current = direction > 0 ? 0 : (_countof(Controls) - 1); 1872 HWND hActive = ::GetFocus(); 1873 for (size_t n = 0; n < _countof(Controls); ++n) 1874 { 1875 if (hActive == Controls[n]) 1876 { 1877 current = n + direction; 1878 break; 1879 } 1880 } 1881 1882 if (current < 0) 1883 current = (_countof(Controls) - 1); 1884 else if ((UINT)current >= _countof(Controls)) 1885 current = 0; 1886 1887 ::SetFocus(Controls[current]); 1888 } 1889 }; 1890 1891 VOID ShowMainWindow(INT nShowCmd) 1892 { 1893 HACCEL KeyBrd; 1894 MSG Msg; 1895 1896 CMainWindow* wnd = new CMainWindow(); 1897 if (!wnd) 1898 return; 1899 1900 hMainWnd = wnd->Create(); 1901 if (!hMainWnd) 1902 return; 1903 1904 /* Maximize it if we must */ 1905 wnd->ShowWindow((SettingsInfo.bSaveWndPos && SettingsInfo.Maximized) ? SW_MAXIMIZE : nShowCmd); 1906 wnd->UpdateWindow(); 1907 1908 /* Load the menu hotkeys */ 1909 KeyBrd = LoadAcceleratorsW(NULL, MAKEINTRESOURCEW(HOTKEYS)); 1910 1911 /* Message Loop */ 1912 while (GetMessageW(&Msg, NULL, 0, 0)) 1913 { 1914 if (!TranslateAcceleratorW(hMainWnd, KeyBrd, &Msg)) 1915 { 1916 if (Msg.message == WM_CHAR && 1917 Msg.wParam == VK_TAB) 1918 { 1919 // Move backwards if shift is held down 1920 int direction = (GetKeyState(VK_SHIFT) & 0x8000) ? -1 : 1; 1921 1922 wnd->HandleTabOrder(direction); 1923 continue; 1924 } 1925 1926 TranslateMessage(&Msg); 1927 DispatchMessageW(&Msg); 1928 } 1929 } 1930 1931 delete wnd; 1932 } 1933