1 /* 2 * PROJECT: ReactOS Applications Manager 3 * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) 4 * PURPOSE: GUI classes for RAPPS 5 * COPYRIGHT: Copyright 2015 David Quintana (gigaherz@gmail.com) 6 * Copyright 2017 Alexander Shaposhnikov (sanchaez@reactos.org) 7 * Copyright 2020 He Yang (1160386205@qq.com) 8 */ 9 10 #include "rapps.h" 11 #include "rosui.h" 12 #include "crichedit.h" 13 #include "appview.h" 14 #include "asyncinet.h" 15 #include "misc.h" 16 #include "gui.h" 17 #include "appview.h" 18 #include "winmain.h" 19 #include <shlobj_undoc.h> 20 #include <shlguid_undoc.h> 21 22 #include <atlbase.h> 23 #include <atlcom.h> 24 #include <atltypes.h> 25 #include <atlwin.h> 26 #include <wininet.h> 27 #include <shellutils.h> 28 #include <ui/rosctrls.h> 29 #include <gdiplus.h> 30 #include <math.h> 31 32 #define SEARCH_TIMER_ID 'SR' 33 #define TREEVIEW_ICON_SIZE 24 34 35 36 37 // **** CSideTreeView **** 38 39 CSideTreeView::CSideTreeView() : 40 CUiWindow(), 41 hImageTreeView(ImageList_Create(TREEVIEW_ICON_SIZE, TREEVIEW_ICON_SIZE, 42 GetSystemColorDepth() | ILC_MASK, 43 0, 1)) 44 { 45 } 46 47 HTREEITEM CSideTreeView::AddItem(HTREEITEM hParent, ATL::CStringW &Text, INT Image, INT SelectedImage, LPARAM lParam) 48 { 49 return CUiWindow<CTreeView>::AddItem(hParent, const_cast<LPWSTR>(Text.GetString()), Image, SelectedImage, lParam); 50 } 51 52 HTREEITEM CSideTreeView::AddCategory(HTREEITEM hRootItem, UINT TextIndex, UINT IconIndex) 53 { 54 ATL::CStringW szText; 55 INT Index = 0; 56 HICON hIcon; 57 58 hIcon = (HICON)LoadImageW(hInst, 59 MAKEINTRESOURCE(IconIndex), 60 IMAGE_ICON, 61 TREEVIEW_ICON_SIZE, 62 TREEVIEW_ICON_SIZE, 63 LR_CREATEDIBSECTION); 64 if (hIcon) 65 { 66 Index = ImageList_AddIcon(hImageTreeView, hIcon); 67 DestroyIcon(hIcon); 68 } 69 70 szText.LoadStringW(TextIndex); 71 return AddItem(hRootItem, szText, Index, Index, TextIndex); 72 } 73 74 HIMAGELIST CSideTreeView::SetImageList() 75 { 76 return CUiWindow<CTreeView>::SetImageList(hImageTreeView, TVSIL_NORMAL); 77 } 78 79 VOID CSideTreeView::DestroyImageList() 80 { 81 if (hImageTreeView) 82 ImageList_Destroy(hImageTreeView); 83 } 84 85 CSideTreeView::~CSideTreeView() 86 { 87 DestroyImageList(); 88 } 89 // **** CSideTreeView **** 90 91 92 93 // **** CMainWindow **** 94 95 CMainWindow::CMainWindow() : 96 m_ClientPanel(NULL), 97 SelectedEnumType(ENUM_ALL_INSTALLED) 98 { 99 } 100 101 CMainWindow::~CMainWindow() 102 { 103 LayoutCleanup(); 104 } 105 106 VOID CMainWindow::InitCategoriesList() 107 { 108 HTREEITEM hRootItemInstalled, hRootItemAvailable; 109 110 hRootItemInstalled = m_TreeView->AddCategory(TVI_ROOT, IDS_INSTALLED, IDI_CATEGORY); 111 m_TreeView->AddCategory(hRootItemInstalled, IDS_APPLICATIONS, IDI_APPS); 112 m_TreeView->AddCategory(hRootItemInstalled, IDS_UPDATES, IDI_APPUPD); 113 114 m_TreeView->AddCategory(TVI_ROOT, IDS_SELECTEDFORINST, IDI_SELECTEDFORINST); 115 116 hRootItemAvailable = m_TreeView->AddCategory(TVI_ROOT, IDS_AVAILABLEFORINST, IDI_CATEGORY); 117 m_TreeView->AddCategory(hRootItemAvailable, IDS_CAT_AUDIO, IDI_CAT_AUDIO); 118 m_TreeView->AddCategory(hRootItemAvailable, IDS_CAT_VIDEO, IDI_CAT_VIDEO); 119 m_TreeView->AddCategory(hRootItemAvailable, IDS_CAT_GRAPHICS, IDI_CAT_GRAPHICS); 120 m_TreeView->AddCategory(hRootItemAvailable, IDS_CAT_GAMES, IDI_CAT_GAMES); 121 m_TreeView->AddCategory(hRootItemAvailable, IDS_CAT_INTERNET, IDI_CAT_INTERNET); 122 m_TreeView->AddCategory(hRootItemAvailable, IDS_CAT_OFFICE, IDI_CAT_OFFICE); 123 m_TreeView->AddCategory(hRootItemAvailable, IDS_CAT_DEVEL, IDI_CAT_DEVEL); 124 m_TreeView->AddCategory(hRootItemAvailable, IDS_CAT_EDU, IDI_CAT_EDU); 125 m_TreeView->AddCategory(hRootItemAvailable, IDS_CAT_ENGINEER, IDI_CAT_ENGINEER); 126 m_TreeView->AddCategory(hRootItemAvailable, IDS_CAT_FINANCE, IDI_CAT_FINANCE); 127 m_TreeView->AddCategory(hRootItemAvailable, IDS_CAT_SCIENCE, IDI_CAT_SCIENCE); 128 m_TreeView->AddCategory(hRootItemAvailable, IDS_CAT_TOOLS, IDI_CAT_TOOLS); 129 m_TreeView->AddCategory(hRootItemAvailable, IDS_CAT_DRIVERS, IDI_CAT_DRIVERS); 130 m_TreeView->AddCategory(hRootItemAvailable, IDS_CAT_LIBS, IDI_CAT_LIBS); 131 m_TreeView->AddCategory(hRootItemAvailable, IDS_CAT_THEMES, IDI_CAT_THEMES); 132 m_TreeView->AddCategory(hRootItemAvailable, IDS_CAT_OTHER, IDI_CAT_OTHER); 133 134 m_TreeView->SetImageList(); 135 m_TreeView->Expand(hRootItemInstalled, TVE_EXPAND); 136 m_TreeView->Expand(hRootItemAvailable, TVE_EXPAND); 137 m_TreeView->SelectItem(hRootItemAvailable); 138 } 139 140 BOOL CMainWindow::CreateStatusBar() 141 { 142 m_StatusBar = new CUiWindow<CStatusBar>(); 143 m_StatusBar->m_VerticalAlignment = UiAlign_RightBtm; 144 m_StatusBar->m_HorizontalAlignment = UiAlign_Stretch; 145 m_ClientPanel->Children().Append(m_StatusBar); 146 147 return m_StatusBar->Create(m_hWnd, (HMENU)IDC_STATUSBAR) != NULL; 148 } 149 150 BOOL CMainWindow::CreateTreeView() 151 { 152 m_TreeView = new CSideTreeView(); 153 m_TreeView->m_VerticalAlignment = UiAlign_Stretch; 154 m_TreeView->m_HorizontalAlignment = UiAlign_Stretch; 155 m_VSplitter->First().Append(m_TreeView); 156 157 return m_TreeView->Create(m_hWnd) != NULL; 158 } 159 160 BOOL CMainWindow::CreateApplicationView() 161 { 162 m_ApplicationView = new CApplicationView(this); // pass this to ApplicationView for callback purpose 163 m_ApplicationView->m_VerticalAlignment = UiAlign_Stretch; 164 m_ApplicationView->m_HorizontalAlignment = UiAlign_Stretch; 165 m_VSplitter->Second().Append(m_ApplicationView); 166 167 return m_ApplicationView->Create(m_hWnd) != NULL; 168 } 169 170 BOOL CMainWindow::CreateVSplitter() 171 { 172 m_VSplitter = new CUiSplitPanel(); 173 m_VSplitter->m_VerticalAlignment = UiAlign_Stretch; 174 m_VSplitter->m_HorizontalAlignment = UiAlign_Stretch; 175 m_VSplitter->m_DynamicFirst = FALSE; 176 m_VSplitter->m_Horizontal = FALSE; 177 m_VSplitter->m_MinFirst = 0; 178 179 // TODO: m_MinSecond should be calculate dynamically instead of hard-coded 180 m_VSplitter->m_MinSecond = 480; 181 m_VSplitter->m_Pos = 240; 182 m_ClientPanel->Children().Append(m_VSplitter); 183 184 return m_VSplitter->Create(m_hWnd) != NULL; 185 } 186 187 BOOL CMainWindow::CreateLayout() 188 { 189 BOOL b = TRUE; 190 bUpdating = TRUE; 191 192 m_ClientPanel = new CUiPanel(); 193 m_ClientPanel->m_VerticalAlignment = UiAlign_Stretch; 194 m_ClientPanel->m_HorizontalAlignment = UiAlign_Stretch; 195 196 // Top level 197 b = b && CreateStatusBar(); 198 b = b && CreateVSplitter(); 199 200 // Inside V Splitter 201 b = b && CreateTreeView(); 202 b = b && CreateApplicationView(); 203 204 if (b) 205 { 206 RECT rBottom; 207 208 /* Size status bar */ 209 m_StatusBar->SendMessageW(WM_SIZE, 0, 0); 210 211 ::GetWindowRect(m_StatusBar->m_hWnd, &rBottom); 212 213 m_VSplitter->m_Margin.bottom = rBottom.bottom - rBottom.top; 214 } 215 216 bUpdating = FALSE; 217 return b; 218 } 219 220 VOID CMainWindow::LayoutCleanup() 221 { 222 delete m_TreeView; 223 delete m_ApplicationView; 224 delete m_VSplitter; 225 delete m_StatusBar; 226 return; 227 } 228 229 BOOL CMainWindow::InitControls() 230 { 231 if (CreateLayout()) 232 { 233 InitCategoriesList(); 234 235 UpdateStatusBarText(); 236 237 return TRUE; 238 } 239 240 return FALSE; 241 } 242 243 VOID CMainWindow::OnSize(HWND hwnd, WPARAM wParam, LPARAM lParam) 244 { 245 if (wParam == SIZE_MINIMIZED) 246 return; 247 248 /* Size status bar */ 249 m_StatusBar->SendMessage(WM_SIZE, 0, 0); 250 251 252 RECT r = { 0, 0, LOWORD(lParam), HIWORD(lParam) }; 253 HDWP hdwp = NULL; 254 INT count = m_ClientPanel->CountSizableChildren(); 255 256 hdwp = BeginDeferWindowPos(count); 257 if (hdwp) 258 { 259 hdwp = m_ClientPanel->OnParentSize(r, hdwp); 260 if (hdwp) 261 { 262 EndDeferWindowPos(hdwp); 263 } 264 } 265 } 266 267 BOOL CMainWindow::RemoveSelectedAppFromRegistry() 268 { 269 if (!IsInstalledEnum(SelectedEnumType)) 270 return FALSE; 271 272 ATL::CStringW szMsgText, szMsgTitle; 273 274 if (!szMsgText.LoadStringW(IDS_APP_REG_REMOVE) || 275 !szMsgTitle.LoadStringW(IDS_INFORMATION)) 276 return FALSE; 277 278 if (MessageBoxW(szMsgText, szMsgTitle, MB_YESNO | MB_ICONQUESTION) == IDYES) 279 { 280 CInstalledApplicationInfo *InstalledApp = (CInstalledApplicationInfo *)m_ApplicationView->GetFocusedItemData(); 281 if (!InstalledApp) 282 return FALSE; 283 284 LSTATUS Result = InstalledApp->RemoveFromRegistry(); 285 if (Result != ERROR_SUCCESS) 286 { 287 // TODO: popup a messagebox telling user it fails somehow 288 return FALSE; 289 } 290 291 // as it's already removed form registry, this will also remove it from the list 292 UpdateApplicationsList(-1); 293 return TRUE; 294 } 295 296 return FALSE; 297 } 298 299 BOOL CMainWindow::UninstallSelectedApp(BOOL bModify) 300 { 301 if (!IsInstalledEnum(SelectedEnumType)) 302 return FALSE; 303 304 CInstalledApplicationInfo *InstalledApp = (CInstalledApplicationInfo *)m_ApplicationView->GetFocusedItemData(); 305 if (!InstalledApp) 306 return FALSE; 307 308 return InstalledApp->UninstallApplication(bModify); 309 } 310 311 BOOL CMainWindow::ProcessWindowMessage(HWND hwnd, UINT Msg, WPARAM wParam, LPARAM lParam, LRESULT &theResult, DWORD dwMapId) 312 { 313 theResult = 0; 314 switch (Msg) 315 { 316 case WM_CREATE: 317 if (!InitControls()) 318 ::PostMessageW(hwnd, WM_CLOSE, 0, 0); 319 break; 320 321 case WM_DESTROY: 322 { 323 ShowWindow(SW_HIDE); 324 SaveSettings(hwnd, &SettingsInfo); 325 326 FreeLogs(); 327 m_AvailableApps.FreeCachedEntries(); 328 m_InstalledApps.FreeCachedEntries(); 329 330 delete m_ClientPanel; 331 332 PostQuitMessage(0); 333 return 0; 334 } 335 336 case WM_COMMAND: 337 OnCommand(wParam, lParam); 338 break; 339 340 case WM_NOTIFY: 341 { 342 LPNMHDR data = (LPNMHDR)lParam; 343 344 switch (data->code) 345 { 346 case TVN_SELCHANGED: 347 { 348 if (data->hwndFrom == m_TreeView->m_hWnd) 349 { 350 switch (((LPNMTREEVIEW)lParam)->itemNew.lParam) 351 { 352 case IDS_INSTALLED: 353 UpdateApplicationsList(ENUM_ALL_INSTALLED); 354 break; 355 356 case IDS_APPLICATIONS: 357 UpdateApplicationsList(ENUM_INSTALLED_APPLICATIONS); 358 break; 359 360 case IDS_UPDATES: 361 UpdateApplicationsList(ENUM_UPDATES); 362 break; 363 364 case IDS_AVAILABLEFORINST: 365 UpdateApplicationsList(ENUM_ALL_AVAILABLE); 366 break; 367 368 case IDS_CAT_AUDIO: 369 UpdateApplicationsList(ENUM_CAT_AUDIO); 370 break; 371 372 case IDS_CAT_DEVEL: 373 UpdateApplicationsList(ENUM_CAT_DEVEL); 374 break; 375 376 case IDS_CAT_DRIVERS: 377 UpdateApplicationsList(ENUM_CAT_DRIVERS); 378 break; 379 380 case IDS_CAT_EDU: 381 UpdateApplicationsList(ENUM_CAT_EDU); 382 break; 383 384 case IDS_CAT_ENGINEER: 385 UpdateApplicationsList(ENUM_CAT_ENGINEER); 386 break; 387 388 case IDS_CAT_FINANCE: 389 UpdateApplicationsList(ENUM_CAT_FINANCE); 390 break; 391 392 case IDS_CAT_GAMES: 393 UpdateApplicationsList(ENUM_CAT_GAMES); 394 break; 395 396 case IDS_CAT_GRAPHICS: 397 UpdateApplicationsList(ENUM_CAT_GRAPHICS); 398 break; 399 400 case IDS_CAT_INTERNET: 401 UpdateApplicationsList(ENUM_CAT_INTERNET); 402 break; 403 404 case IDS_CAT_LIBS: 405 UpdateApplicationsList(ENUM_CAT_LIBS); 406 break; 407 408 case IDS_CAT_OFFICE: 409 UpdateApplicationsList(ENUM_CAT_OFFICE); 410 break; 411 412 case IDS_CAT_OTHER: 413 UpdateApplicationsList(ENUM_CAT_OTHER); 414 break; 415 416 case IDS_CAT_SCIENCE: 417 UpdateApplicationsList(ENUM_CAT_SCIENCE); 418 break; 419 420 case IDS_CAT_TOOLS: 421 UpdateApplicationsList(ENUM_CAT_TOOLS); 422 break; 423 424 case IDS_CAT_VIDEO: 425 UpdateApplicationsList(ENUM_CAT_VIDEO); 426 break; 427 428 case IDS_CAT_THEMES: 429 UpdateApplicationsList(ENUM_CAT_THEMES); 430 break; 431 432 case IDS_SELECTEDFORINST: 433 UpdateApplicationsList(ENUM_CAT_SELECTED); 434 break; 435 } 436 } 437 438 HMENU mainMenu = ::GetMenu(hwnd); 439 440 /* Disable/enable items based on treeview selection */ 441 if (IsSelectedNodeInstalled()) 442 { 443 EnableMenuItem(mainMenu, ID_REGREMOVE, MF_ENABLED); 444 EnableMenuItem(mainMenu, ID_INSTALL, MF_GRAYED); 445 EnableMenuItem(mainMenu, ID_UNINSTALL, MF_ENABLED); 446 EnableMenuItem(mainMenu, ID_MODIFY, MF_ENABLED); 447 } 448 else 449 { 450 EnableMenuItem(mainMenu, ID_REGREMOVE, MF_GRAYED); 451 EnableMenuItem(mainMenu, ID_INSTALL, MF_ENABLED); 452 EnableMenuItem(mainMenu, ID_UNINSTALL, MF_GRAYED); 453 EnableMenuItem(mainMenu, ID_MODIFY, MF_GRAYED); 454 } 455 } 456 break; 457 458 } 459 } 460 break; 461 462 case WM_SIZE: 463 OnSize(hwnd, wParam, lParam); 464 break; 465 466 case WM_SIZING: 467 { 468 LPRECT pRect = (LPRECT)lParam; 469 470 if (pRect->right - pRect->left < 565) 471 pRect->right = pRect->left + 565; 472 473 if (pRect->bottom - pRect->top < 300) 474 pRect->bottom = pRect->top + 300; 475 476 return TRUE; 477 } 478 479 case WM_SYSCOLORCHANGE: 480 { 481 /* Forward WM_SYSCOLORCHANGE to common controls */ 482 m_ApplicationView->SendMessageW(WM_SYSCOLORCHANGE, wParam, lParam); 483 m_TreeView->SendMessageW(WM_SYSCOLORCHANGE, wParam, lParam); 484 } 485 break; 486 487 case WM_TIMER: 488 if (wParam == SEARCH_TIMER_ID) 489 { 490 ::KillTimer(hwnd, SEARCH_TIMER_ID); 491 492 UpdateApplicationsList(-1); 493 } 494 break; 495 } 496 497 return FALSE; 498 } 499 500 BOOL CMainWindow::IsSelectedNodeInstalled() 501 { 502 HTREEITEM hSelectedItem = m_TreeView->GetSelection(); 503 TV_ITEM tItem; 504 505 tItem.mask = TVIF_PARAM | TVIF_HANDLE; 506 tItem.hItem = hSelectedItem; 507 m_TreeView->GetItem(&tItem); 508 switch (tItem.lParam) 509 { 510 case IDS_INSTALLED: 511 case IDS_APPLICATIONS: 512 case IDS_UPDATES: 513 return TRUE; 514 default: 515 return FALSE; 516 } 517 } 518 519 VOID CMainWindow::ShowAboutDlg() 520 { 521 ATL::CStringW szApp; 522 ATL::CStringW szAuthors; 523 HICON hIcon; 524 525 szApp.LoadStringW(IDS_APPTITLE); 526 szAuthors.LoadStringW(IDS_APP_AUTHORS); 527 hIcon = LoadIconW(hInst, MAKEINTRESOURCEW(IDI_MAIN)); 528 ShellAboutW(m_hWnd, szApp, szAuthors, hIcon); 529 DestroyIcon(hIcon); 530 } 531 532 VOID CMainWindow::OnCommand(WPARAM wParam, LPARAM lParam) 533 { 534 WORD wCommand = LOWORD(wParam); 535 536 if (!lParam) 537 { 538 switch (wCommand) 539 { 540 case ID_SETTINGS: 541 CreateSettingsDlg(m_hWnd); 542 break; 543 544 case ID_EXIT: 545 PostMessageW(WM_CLOSE, 0, 0); 546 break; 547 548 case ID_SEARCH: 549 m_ApplicationView->SetFocusOnSearchBar(); 550 break; 551 552 case ID_INSTALL: 553 if (IsAvailableEnum(SelectedEnumType)) 554 { 555 ATL::CSimpleArray<CAvailableApplicationInfo> AppsList; 556 557 // enum all selected apps 558 m_AvailableApps.Enum(ENUM_CAT_SELECTED, s_EnumSelectedAppForDownloadProc, (PVOID)&AppsList); 559 560 if (AppsList.GetSize()) 561 { 562 if (DownloadListOfApplications(AppsList, FALSE)) 563 { 564 m_AvailableApps.RemoveAllSelected(); 565 UpdateApplicationsList(-1); 566 } 567 } 568 else 569 { 570 // use the currently focused item in application-view 571 CAvailableApplicationInfo *FocusedApps = (CAvailableApplicationInfo *)m_ApplicationView->GetFocusedItemData(); 572 if (FocusedApps) 573 { 574 if (DownloadApplication(FocusedApps)) 575 { 576 UpdateApplicationsList(-1); 577 } 578 } 579 else 580 { 581 // TODO: in this case, Install button in toolbar (and all other places) should be disabled 582 // or at least popup a messagebox telling user to select/check some app first 583 } 584 } 585 } 586 break; 587 588 case ID_UNINSTALL: 589 if (UninstallSelectedApp(FALSE)) 590 UpdateApplicationsList(-1); 591 break; 592 593 case ID_MODIFY: 594 if (UninstallSelectedApp(TRUE)) 595 UpdateApplicationsList(-1); 596 break; 597 598 case ID_REGREMOVE: 599 RemoveSelectedAppFromRegistry(); 600 break; 601 602 case ID_REFRESH: 603 UpdateApplicationsList(-1); 604 break; 605 606 case ID_RESETDB: 607 CAvailableApps::ForceUpdateAppsDB(); 608 UpdateApplicationsList(-1); 609 break; 610 611 case ID_HELP: 612 MessageBoxW(L"Help not implemented yet", NULL, MB_OK); 613 break; 614 615 case ID_ABOUT: 616 ShowAboutDlg(); 617 break; 618 619 case ID_CHECK_ALL: 620 m_ApplicationView->CheckAll(); 621 break; 622 } 623 } 624 } 625 626 BOOL CALLBACK CMainWindow::EnumInstalledAppProc(CInstalledApplicationInfo *Info) 627 { 628 if (!SearchPatternMatch(Info->szDisplayName.GetString(), szSearchPattern)) 629 { 630 return TRUE; 631 } 632 return m_ApplicationView->AddInstalledApplication(Info, Info); // currently, the callback param is Info itself 633 } 634 635 BOOL CALLBACK CMainWindow::EnumAvailableAppProc(CAvailableApplicationInfo *Info, BOOL bInitialCheckState) 636 { 637 if (!SearchPatternMatch(Info->m_szName.GetString(), szSearchPattern) && 638 !SearchPatternMatch(Info->m_szDesc.GetString(), szSearchPattern)) 639 { 640 return TRUE; 641 } 642 return m_ApplicationView->AddAvailableApplication(Info, bInitialCheckState, Info); // currently, the callback param is Info itself 643 } 644 645 BOOL CALLBACK CMainWindow::s_EnumInstalledAppProc(CInstalledApplicationInfo *Info, PVOID param) 646 { 647 CMainWindow *pThis = (CMainWindow *)param; 648 return pThis->EnumInstalledAppProc(Info); 649 } 650 651 BOOL CALLBACK CMainWindow::s_EnumAvailableAppProc(CAvailableApplicationInfo *Info, BOOL bInitialCheckState, PVOID param) 652 { 653 CMainWindow *pThis = (CMainWindow *)param; 654 return pThis->EnumAvailableAppProc(Info, bInitialCheckState); 655 } 656 657 BOOL CALLBACK CMainWindow::s_EnumSelectedAppForDownloadProc(CAvailableApplicationInfo *Info, BOOL bInitialCheckState, PVOID param) 658 { 659 ATL::CSimpleArray<CAvailableApplicationInfo> *pAppList = (ATL::CSimpleArray<CAvailableApplicationInfo> *)param; 660 pAppList->Add(*Info); 661 return TRUE; 662 } 663 664 VOID CMainWindow::UpdateStatusBarText() 665 { 666 if (m_StatusBar) 667 { 668 ATL::CStringW szBuffer; 669 670 szBuffer.Format(IDS_APPS_COUNT, m_ApplicationView->GetItemCount(), m_AvailableApps.GetSelectedCount()); 671 m_StatusBar->SetText(szBuffer); 672 } 673 } 674 675 VOID CMainWindow::UpdateApplicationsList(INT EnumType) 676 { 677 bUpdating = TRUE; 678 679 if (EnumType == -1) 680 { 681 // keep the old enum type 682 EnumType = SelectedEnumType; 683 } 684 else 685 { 686 SelectedEnumType = EnumType; 687 } 688 689 m_ApplicationView->SetRedraw(FALSE); 690 if (IsInstalledEnum(EnumType)) 691 { 692 // set the display type of application-view. this will remove all the item in application-view too. 693 m_ApplicationView->SetDisplayAppType(AppViewTypeInstalledApps); 694 695 // enum installed softwares 696 m_InstalledApps.Enum(EnumType, s_EnumInstalledAppProc, this); 697 } 698 else if (IsAvailableEnum(EnumType)) 699 { 700 // set the display type of application-view. this will remove all the item in application-view too. 701 m_ApplicationView->SetDisplayAppType(AppViewTypeAvailableApps); 702 703 // enum available softwares 704 m_AvailableApps.Enum(EnumType, s_EnumAvailableAppProc, this); 705 } 706 m_ApplicationView->SetRedraw(TRUE); 707 m_ApplicationView->RedrawWindow(0, 0, RDW_INVALIDATE | RDW_ALLCHILDREN); // force the child window to repaint 708 UpdateStatusBarText(); 709 710 CStringW text; 711 if (m_ApplicationView->GetItemCount() == 0 && !szSearchPattern.IsEmpty()) 712 { 713 text.LoadString(IDS_NO_SEARCH_RESULTS); 714 } 715 m_ApplicationView->SetWatermark(text); 716 717 bUpdating = FALSE; 718 } 719 720 ATL::CWndClassInfo &CMainWindow::GetWndClassInfo() 721 { 722 DWORD csStyle = CS_VREDRAW | CS_HREDRAW; 723 static ATL::CWndClassInfo wc = 724 { 725 { 726 sizeof(WNDCLASSEX), 727 csStyle, 728 StartWindowProc, 729 0, 730 0, 731 NULL, 732 LoadIconW(_AtlBaseModule.GetModuleInstance(), MAKEINTRESOURCEW(IDI_MAIN)), 733 LoadCursorW(NULL, IDC_ARROW), 734 (HBRUSH)(COLOR_BTNFACE + 1), 735 MAKEINTRESOURCEW(IDR_MAINMENU), 736 szWindowClass, 737 NULL 738 }, 739 NULL, NULL, IDC_ARROW, TRUE, 0, _T("") 740 }; 741 return wc; 742 } 743 744 HWND CMainWindow::Create() 745 { 746 ATL::CStringW szWindowName; 747 szWindowName.LoadStringW(IDS_APPTITLE); 748 749 RECT r = { 750 (SettingsInfo.bSaveWndPos ? SettingsInfo.Left : CW_USEDEFAULT), 751 (SettingsInfo.bSaveWndPos ? SettingsInfo.Top : CW_USEDEFAULT), 752 (SettingsInfo.bSaveWndPos ? SettingsInfo.Width : 680), 753 (SettingsInfo.bSaveWndPos ? SettingsInfo.Height : 450) 754 }; 755 r.right += r.left; 756 r.bottom += r.top; 757 758 return CWindowImpl::Create(NULL, r, szWindowName.GetString(), WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, WS_EX_WINDOWEDGE); 759 } 760 761 // this function is called when a item of application-view is checked/unchecked 762 // CallbackParam is the param passed to application-view when adding the item (the one getting focus now). 763 BOOL CMainWindow::ItemCheckStateChanged(BOOL bChecked, LPVOID CallbackParam) 764 { 765 if (!bUpdating) 766 { 767 if (bChecked) 768 { 769 if (!m_AvailableApps.AddSelected((CAvailableApplicationInfo *)CallbackParam)) 770 { 771 return FALSE; 772 } 773 } 774 else 775 { 776 if (!m_AvailableApps.RemoveSelected((CAvailableApplicationInfo *)CallbackParam)) 777 { 778 return FALSE; 779 } 780 } 781 782 UpdateStatusBarText(); 783 return TRUE; 784 } 785 else 786 { 787 return TRUE; 788 } 789 } 790 791 // this function is called when one or more application(s) should be installed install 792 // if Info is not zero, this app should be installed. otherwise those checked apps should be installed 793 BOOL CMainWindow::InstallApplication(CAvailableApplicationInfo *Info) 794 { 795 if (Info) 796 { 797 if (DownloadApplication(Info)) 798 { 799 UpdateApplicationsList(-1); 800 return TRUE; 801 } 802 } 803 else 804 { 805 ATL::CSimpleArray<CAvailableApplicationInfo> AppsList; 806 807 // enum all selected apps 808 m_AvailableApps.Enum(ENUM_CAT_SELECTED, s_EnumSelectedAppForDownloadProc, (PVOID)&AppsList); 809 810 if (AppsList.GetSize()) 811 { 812 if (DownloadListOfApplications(AppsList, FALSE)) 813 { 814 m_AvailableApps.RemoveAllSelected(); 815 UpdateApplicationsList(-1); 816 return TRUE; 817 } 818 } 819 } 820 821 return FALSE; 822 } 823 824 BOOL CMainWindow::SearchTextChanged(ATL::CStringW &SearchText) 825 { 826 if (szSearchPattern == SearchText) 827 { 828 return FALSE; 829 } 830 831 szSearchPattern = SearchText; 832 833 DWORD dwDelay; 834 SystemParametersInfoW(SPI_GETMENUSHOWDELAY, 0, &dwDelay, 0); 835 SetTimer(SEARCH_TIMER_ID, dwDelay); 836 837 return TRUE; 838 } 839 840 void CMainWindow::HandleTabOrder(int direction) 841 { 842 ATL::CSimpleArray<HWND> TabOrderHwndList; 843 844 m_TreeView->AppendTabOrderWindow(direction, TabOrderHwndList); 845 m_ApplicationView->AppendTabOrderWindow(direction, TabOrderHwndList); 846 847 848 if (TabOrderHwndList.GetSize() == 0) 849 { 850 // in case the list is empty 851 return; 852 } 853 854 int FocusIndex; 855 856 if ((FocusIndex = TabOrderHwndList.Find(GetFocus())) == -1) 857 { 858 FocusIndex = 0; // focus the first window in the list 859 } 860 else 861 { 862 FocusIndex += direction; 863 FocusIndex += TabOrderHwndList.GetSize(); // FocusIndex might be negative. we don't want to mod a negative number 864 FocusIndex %= TabOrderHwndList.GetSize(); 865 } 866 867 ::SetFocus(TabOrderHwndList[FocusIndex]); 868 return; 869 } 870 // **** CMainWindow **** 871 872 873 874 VOID MainWindowLoop(INT nShowCmd) 875 { 876 HACCEL KeyBrd; 877 MSG Msg; 878 879 CMainWindow* wnd = new CMainWindow(); 880 if (!wnd) 881 return; 882 883 hMainWnd = wnd->Create(); 884 if (!hMainWnd) 885 return; 886 887 /* Maximize it if we must */ 888 wnd->ShowWindow((SettingsInfo.bSaveWndPos && SettingsInfo.Maximized) ? SW_MAXIMIZE : nShowCmd); 889 wnd->UpdateWindow(); 890 891 /* Load the menu hotkeys */ 892 KeyBrd = LoadAcceleratorsW(NULL, MAKEINTRESOURCEW(HOTKEYS)); 893 894 /* Message Loop */ 895 while (GetMessageW(&Msg, NULL, 0, 0)) 896 { 897 if (!TranslateAcceleratorW(hMainWnd, KeyBrd, &Msg)) 898 { 899 if (Msg.message == WM_CHAR && 900 Msg.wParam == VK_TAB) 901 { 902 // Move backwards if shift is held down 903 int direction = (GetKeyState(VK_SHIFT) & 0x8000) ? -1 : 1; 904 905 wnd->HandleTabOrder(direction); 906 continue; 907 } 908 909 TranslateMessage(&Msg); 910 DispatchMessageW(&Msg); 911 } 912 } 913 914 delete wnd; 915 } 916