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(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_SEARCH: 1460 ::SetFocus(m_SearchBar->m_hWnd); 1461 break; 1462 1463 case ID_INSTALL: 1464 if (IsAvailableEnum(SelectedEnumType)) 1465 { 1466 if (nSelectedApps > 0) 1467 { 1468 CDownloadManager::DownloadListOfApplications(m_AvailableApps.GetSelected()); 1469 UpdateApplicationsList(-1); 1470 m_ListView->SetSelected(-1, FALSE); 1471 } 1472 else if (CDownloadManager::DownloadApplication(m_ListView->GetSelectedData())) 1473 { 1474 UpdateApplicationsList(-1); 1475 } 1476 1477 } 1478 break; 1479 1480 case ID_UNINSTALL: 1481 if (UninstallApplication(-1, FALSE)) 1482 UpdateApplicationsList(-1); 1483 break; 1484 1485 case ID_MODIFY: 1486 if (UninstallApplication(-1, TRUE)) 1487 UpdateApplicationsList(-1); 1488 break; 1489 1490 case ID_REGREMOVE: 1491 RemoveAppFromRegistry(-1); 1492 break; 1493 1494 case ID_REFRESH: 1495 UpdateApplicationsList(-1); 1496 break; 1497 1498 case ID_RESETDB: 1499 CAvailableApps::ForceUpdateAppsDB(); 1500 UpdateApplicationsList(-1); 1501 break; 1502 1503 case ID_HELP: 1504 MessageBoxW(L"Help not implemented yet", NULL, MB_OK); 1505 break; 1506 1507 case ID_ABOUT: 1508 ShowAboutDialog(); 1509 break; 1510 1511 case ID_CHECK_ALL: 1512 m_ListView->CheckAll(); 1513 break; 1514 } 1515 } 1516 1517 VOID FreeInstalledAppList() 1518 { 1519 INT Count = m_ListView->GetItemCount() - 1; 1520 PINSTALLED_INFO Info; 1521 1522 while (Count >= 0) 1523 { 1524 Info = (PINSTALLED_INFO) ListViewGetlParam(Count); 1525 if (Info) 1526 { 1527 RegCloseKey(Info->hSubKey); 1528 delete Info; 1529 } 1530 Count--; 1531 } 1532 } 1533 1534 static BOOL SearchPatternMatch(LPCWSTR szHaystack, LPCWSTR szNeedle) 1535 { 1536 if (!*szNeedle) 1537 return TRUE; 1538 /* TODO: Improve pattern search beyond a simple case-insensitive substring search. */ 1539 return StrStrIW(szHaystack, szNeedle) != NULL; 1540 } 1541 1542 static BOOL CALLBACK s_EnumInstalledAppProc(INT ItemIndex, ATL::CStringW &m_szName, PINSTALLED_INFO Info) 1543 { 1544 PINSTALLED_INFO ItemInfo; 1545 ATL::CStringW szText; 1546 INT Index; 1547 1548 if (!SearchPatternMatch(m_szName.GetString(), szSearchPattern)) 1549 { 1550 RegCloseKey(Info->hSubKey); 1551 return TRUE; 1552 } 1553 1554 ItemInfo = new INSTALLED_INFO(*Info); 1555 if (!ItemInfo) 1556 { 1557 RegCloseKey(Info->hSubKey); 1558 return FALSE; 1559 } 1560 1561 Index = ListViewAddItem(ItemIndex, 0, m_szName, (LPARAM) ItemInfo); 1562 1563 /* Get version info */ 1564 GetApplicationString(ItemInfo->hSubKey, L"DisplayVersion", szText); 1565 ListView_SetItemText(hListView, Index, 1, const_cast<LPWSTR>(szText.GetString())); 1566 1567 /* Get comments */ 1568 GetApplicationString(ItemInfo->hSubKey, L"Comments", szText); 1569 ListView_SetItemText(hListView, Index, 2, const_cast<LPWSTR>(szText.GetString())); 1570 1571 return TRUE; 1572 } 1573 1574 static BOOL CALLBACK s_EnumAvailableAppProc(CAvailableApplicationInfo* Info, LPCWSTR szFolderPath) 1575 { 1576 INT Index; 1577 HICON hIcon = NULL; 1578 1579 HIMAGELIST hImageListView = ListView_GetImageList(hListView, LVSIL_SMALL); 1580 1581 if (!SearchPatternMatch(Info->m_szName.GetString(), szSearchPattern) && 1582 !SearchPatternMatch(Info->m_szDesc.GetString(), szSearchPattern)) 1583 { 1584 return TRUE; 1585 } 1586 1587 /* Load icon from file */ 1588 ATL::CStringW szIconPath; 1589 szIconPath.Format(L"%lsicons\\%ls.ico", szFolderPath, Info->m_szName.GetString()); 1590 hIcon = (HICON) LoadImageW(NULL, 1591 szIconPath.GetString(), 1592 IMAGE_ICON, 1593 LISTVIEW_ICON_SIZE, 1594 LISTVIEW_ICON_SIZE, 1595 LR_LOADFROMFILE); 1596 1597 if (!hIcon || GetLastError() != ERROR_SUCCESS) 1598 { 1599 /* Load default icon */ 1600 hIcon = (HICON) LoadIconW(hInst, MAKEINTRESOURCEW(IDI_MAIN)); 1601 } 1602 1603 Index = ImageList_AddIcon(hImageListView, hIcon); 1604 DestroyIcon(hIcon); 1605 1606 Index = ListViewAddItem(Info->m_Category, Index, Info->m_szName.GetString(), (LPARAM) Info); 1607 ListView_SetImageList(hListView, hImageListView, LVSIL_SMALL); 1608 1609 ListView_SetItemText(hListView, Index, 1, const_cast<LPWSTR>(Info->m_szVersion.GetString())); 1610 ListView_SetItemText(hListView, Index, 2, const_cast<LPWSTR>(Info->m_szDesc.GetString())); 1611 ListView_SetCheckState(hListView, Index, Info->m_IsSelected); 1612 1613 return TRUE; 1614 } 1615 1616 VOID UpdateStatusBarText() 1617 { 1618 if (m_StatusBar) 1619 { 1620 ATL::CStringW szBuffer; 1621 1622 szBuffer.Format(IDS_APPS_COUNT, m_ListView->GetItemCount(), nSelectedApps); 1623 m_StatusBar->SetText(szBuffer); 1624 } 1625 } 1626 1627 VOID UpdateApplicationsList(INT EnumType) 1628 { 1629 ATL::CStringW szBuffer1, szBuffer2; 1630 HIMAGELIST hImageListView; 1631 BOOL bWasInInstalled = IsInstalledEnum(SelectedEnumType); 1632 1633 bUpdating = TRUE; 1634 m_ListView->SetRedraw(FALSE); 1635 1636 if (EnumType < 0) 1637 { 1638 EnumType = SelectedEnumType; 1639 } 1640 1641 //if previous one was INSTALLED purge the list 1642 //TODO: make the Installed category a separate class to avoid doing this 1643 if (bWasInInstalled) 1644 { 1645 FreeInstalledAppList(); 1646 } 1647 1648 m_ListView->DeleteAllItems(); 1649 1650 // Create new ImageList 1651 hImageListView = ImageList_Create(LISTVIEW_ICON_SIZE, 1652 LISTVIEW_ICON_SIZE, 1653 GetSystemColorDepth() | ILC_MASK, 1654 0, 1); 1655 HIMAGELIST hImageListBuf = m_ListView->SetImageList(hImageListView, LVSIL_SMALL); 1656 if (hImageListBuf) 1657 { 1658 ImageList_Destroy(hImageListBuf); 1659 } 1660 1661 if (IsInstalledEnum(EnumType)) 1662 { 1663 if (!bWasInInstalled) 1664 { 1665 m_ListView->SetCheckboxesVisible(FALSE); 1666 } 1667 1668 HICON hIcon = (HICON) LoadIconW(hInst, MAKEINTRESOURCEW(IDI_MAIN)); 1669 ImageList_AddIcon(hImageListView, hIcon); 1670 DestroyIcon(hIcon); 1671 1672 // Enum installed applications and updates 1673 EnumInstalledApplications(EnumType, TRUE, s_EnumInstalledAppProc); 1674 EnumInstalledApplications(EnumType, FALSE, s_EnumInstalledAppProc); 1675 } 1676 else if (IsAvailableEnum(EnumType)) 1677 { 1678 if (bWasInInstalled) 1679 { 1680 m_ListView->SetCheckboxesVisible(TRUE); 1681 } 1682 1683 // Enum available applications 1684 m_AvailableApps.Enum(EnumType, s_EnumAvailableAppProc); 1685 } 1686 1687 SelectedEnumType = EnumType; 1688 UpdateStatusBarText(); 1689 SetWelcomeText(); 1690 1691 // Set automatic column width for program names if the list is not empty 1692 if (m_ListView->GetItemCount() > 0) 1693 { 1694 ListView_SetColumnWidth(m_ListView->GetWindow(), 0, LVSCW_AUTOSIZE); 1695 } 1696 1697 bUpdating = FALSE; 1698 m_ListView->SetRedraw(TRUE); 1699 } 1700 1701 public: 1702 static ATL::CWndClassInfo& GetWndClassInfo() 1703 { 1704 DWORD csStyle = CS_VREDRAW | CS_HREDRAW; 1705 static ATL::CWndClassInfo wc = 1706 { 1707 { 1708 sizeof(WNDCLASSEX), 1709 csStyle, 1710 StartWindowProc, 1711 0, 1712 0, 1713 NULL, 1714 LoadIconW(_AtlBaseModule.GetModuleInstance(), MAKEINTRESOURCEW(IDI_MAIN)), 1715 LoadCursorW(NULL, IDC_ARROW), 1716 (HBRUSH) (COLOR_BTNFACE + 1), 1717 MAKEINTRESOURCEW(IDR_MAINMENU), 1718 L"RAppsWnd", 1719 NULL 1720 }, 1721 NULL, NULL, IDC_ARROW, TRUE, 0, _T("") 1722 }; 1723 return wc; 1724 } 1725 1726 HWND Create() 1727 { 1728 ATL::CStringW szWindowName; 1729 szWindowName.LoadStringW(IDS_APPTITLE); 1730 1731 RECT r = { 1732 (SettingsInfo.bSaveWndPos ? SettingsInfo.Left : CW_USEDEFAULT), 1733 (SettingsInfo.bSaveWndPos ? SettingsInfo.Top : CW_USEDEFAULT), 1734 (SettingsInfo.bSaveWndPos ? SettingsInfo.Width : 680), 1735 (SettingsInfo.bSaveWndPos ? SettingsInfo.Height : 450) 1736 }; 1737 r.right += r.left; 1738 r.bottom += r.top; 1739 1740 return CWindowImpl::Create(NULL, r, szWindowName.GetString(), WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, WS_EX_WINDOWEDGE); 1741 } 1742 1743 CStatusBar * GetStatusBar() 1744 { 1745 return m_StatusBar; 1746 } 1747 1748 CAppsListView * GetListView() 1749 { 1750 return m_ListView; 1751 } 1752 1753 CRichEdit * GetRichEdit() 1754 { 1755 return m_RichEdit; 1756 } 1757 1758 CAvailableApps * GetAvailableApps() 1759 { 1760 return &m_AvailableApps; 1761 } 1762 }; 1763 1764 // global interface 1765 CMainWindow * g_MainWindow; 1766 1767 HWND CreateMainWindow() 1768 { 1769 g_MainWindow = new CMainWindow(); 1770 return g_MainWindow->Create(); 1771 } 1772 1773 DWORD_PTR ListViewGetlParam(INT item) 1774 { 1775 if (item < 0) 1776 { 1777 item = g_MainWindow->GetListView()->GetSelectionMark(); 1778 } 1779 return g_MainWindow->GetListView()->GetItemData(item); 1780 } 1781 1782 VOID SetStatusBarText(LPCWSTR szText) 1783 { 1784 g_MainWindow->GetStatusBar()->SetText(szText); 1785 } 1786 1787 INT ListViewAddItem(INT ItemIndex, INT IconIndex, LPWSTR lpName, LPARAM lParam) 1788 { 1789 return g_MainWindow->GetListView()->AddItem(ItemIndex, IconIndex, lpName, lParam); 1790 } 1791 1792 VOID NewRichEditText(LPCWSTR szText, DWORD flags) 1793 { 1794 g_MainWindow->GetRichEdit()->SetText(szText, flags); 1795 } 1796 1797 VOID InsertRichEditText(LPCWSTR szText, DWORD flags) 1798 { 1799 g_MainWindow->GetRichEdit()->InsertText(szText, flags); 1800 } 1801 1802 CAvailableApps* GetAvailableApps() 1803 { 1804 return g_MainWindow->GetAvailableApps(); 1805 } 1806 1807 // ATL version of functions above 1808 VOID SetStatusBarText(const ATL::CStringW& szText) 1809 { 1810 SetStatusBarText(szText.GetString()); 1811 } 1812 1813 INT ListViewAddItem(INT ItemIndex, INT IconIndex, const ATL::CStringW& Name, LPARAM lParam) 1814 { 1815 return ListViewAddItem(ItemIndex, IconIndex, const_cast<LPWSTR>(Name.GetString()), lParam); 1816 } 1817 1818 VOID NewRichEditText(const ATL::CStringW& szText, DWORD flags) 1819 { 1820 NewRichEditText(szText.GetString(), flags); 1821 } 1822 1823 VOID InsertRichEditText(const ATL::CStringW& szText, DWORD flags) 1824 { 1825 InsertRichEditText(szText.GetString(), flags); 1826 } 1827