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