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