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 "asyncinet.h" 14 #include "gui.h" 15 #include <shlobj_undoc.h> 16 #include <shlguid_undoc.h> 17 18 #include <atlbase.h> 19 #include <atlcom.h> 20 #include <atltypes.h> 21 #include <atlwin.h> 22 #include <wininet.h> 23 #include <shellutils.h> 24 #include <ui/rosctrls.h> 25 #include <gdiplus.h> 26 #include <math.h> 27 28 #define SEARCH_TIMER_ID 'SR' 29 #define TREEVIEW_ICON_SIZE 24 30 31 // **** CSideTreeView **** 32 33 CSideTreeView::CSideTreeView() 34 : CUiWindow(), 35 hImageTreeView(ImageList_Create(TREEVIEW_ICON_SIZE, TREEVIEW_ICON_SIZE, GetSystemColorDepth() | ILC_MASK, 0, 1)) 36 { 37 } 38 39 HTREEITEM 40 CSideTreeView::AddItem(HTREEITEM hParent, CStringW &Text, INT Image, INT SelectedImage, LPARAM lParam) 41 { 42 return CUiWindow<CTreeView>::AddItem(hParent, const_cast<LPWSTR>(Text.GetString()), Image, SelectedImage, lParam); 43 } 44 45 HTREEITEM 46 CSideTreeView::AddCategory(HTREEITEM hRootItem, UINT TextIndex, UINT IconIndex) 47 { 48 CStringW szText; 49 INT Index = 0; 50 HICON hIcon; 51 52 hIcon = (HICON)LoadImageW( 53 hInst, MAKEINTRESOURCE(IconIndex), IMAGE_ICON, TREEVIEW_ICON_SIZE, TREEVIEW_ICON_SIZE, LR_CREATEDIBSECTION); 54 if (hIcon) 55 { 56 Index = ImageList_AddIcon(hImageTreeView, hIcon); 57 DestroyIcon(hIcon); 58 } 59 60 szText.LoadStringW(TextIndex); 61 return AddItem(hRootItem, szText, Index, Index, TextIndex); 62 } 63 64 HIMAGELIST 65 CSideTreeView::SetImageList() 66 { 67 return CUiWindow<CTreeView>::SetImageList(hImageTreeView, TVSIL_NORMAL); 68 } 69 70 VOID 71 CSideTreeView::DestroyImageList() 72 { 73 if (hImageTreeView) 74 ImageList_Destroy(hImageTreeView); 75 } 76 77 CSideTreeView::~CSideTreeView() 78 { 79 DestroyImageList(); 80 } 81 // **** CSideTreeView **** 82 83 // **** CMainWindow **** 84 85 CMainWindow::CMainWindow(CAppDB *db, BOOL bAppwiz) : m_ClientPanel(NULL), m_Db(db), m_bAppwizMode(bAppwiz), SelectedEnumType(ENUM_ALL_INSTALLED) 86 { 87 } 88 89 CMainWindow::~CMainWindow() 90 { 91 LayoutCleanup(); 92 } 93 94 VOID 95 CMainWindow::InitCategoriesList() 96 { 97 HTREEITEM hRootItemAvailable; 98 99 hRootItemInstalled = m_TreeView->AddCategory(TVI_ROOT, IDS_INSTALLED, IDI_CATEGORY); 100 m_TreeView->AddCategory(hRootItemInstalled, IDS_APPLICATIONS, IDI_APPS); 101 m_TreeView->AddCategory(hRootItemInstalled, IDS_UPDATES, IDI_APPUPD); 102 103 // Do not show any other categories in APPWIZ-mode. 104 if (m_bAppwizMode) 105 goto Finish; 106 107 m_TreeView->AddCategory(TVI_ROOT, IDS_SELECTEDFORINST, IDI_SELECTEDFORINST); 108 109 hRootItemAvailable = m_TreeView->AddCategory(TVI_ROOT, IDS_AVAILABLEFORINST, IDI_CATEGORY); 110 m_TreeView->AddCategory(hRootItemAvailable, IDS_CAT_AUDIO, IDI_CAT_AUDIO); 111 m_TreeView->AddCategory(hRootItemAvailable, IDS_CAT_VIDEO, IDI_CAT_VIDEO); 112 m_TreeView->AddCategory(hRootItemAvailable, IDS_CAT_GRAPHICS, IDI_CAT_GRAPHICS); 113 m_TreeView->AddCategory(hRootItemAvailable, IDS_CAT_GAMES, IDI_CAT_GAMES); 114 m_TreeView->AddCategory(hRootItemAvailable, IDS_CAT_INTERNET, IDI_CAT_INTERNET); 115 m_TreeView->AddCategory(hRootItemAvailable, IDS_CAT_OFFICE, IDI_CAT_OFFICE); 116 m_TreeView->AddCategory(hRootItemAvailable, IDS_CAT_DEVEL, IDI_CAT_DEVEL); 117 m_TreeView->AddCategory(hRootItemAvailable, IDS_CAT_EDU, IDI_CAT_EDU); 118 m_TreeView->AddCategory(hRootItemAvailable, IDS_CAT_ENGINEER, IDI_CAT_ENGINEER); 119 m_TreeView->AddCategory(hRootItemAvailable, IDS_CAT_FINANCE, IDI_CAT_FINANCE); 120 m_TreeView->AddCategory(hRootItemAvailable, IDS_CAT_SCIENCE, IDI_CAT_SCIENCE); 121 m_TreeView->AddCategory(hRootItemAvailable, IDS_CAT_TOOLS, IDI_CAT_TOOLS); 122 m_TreeView->AddCategory(hRootItemAvailable, IDS_CAT_DRIVERS, IDI_CAT_DRIVERS); 123 m_TreeView->AddCategory(hRootItemAvailable, IDS_CAT_LIBS, IDI_CAT_LIBS); 124 m_TreeView->AddCategory(hRootItemAvailable, IDS_CAT_THEMES, IDI_CAT_THEMES); 125 m_TreeView->AddCategory(hRootItemAvailable, IDS_CAT_OTHER, IDI_CAT_OTHER); 126 127 Finish: 128 m_TreeView->SetImageList(); 129 m_TreeView->Expand(hRootItemInstalled, TVE_EXPAND); 130 if (!m_bAppwizMode) 131 m_TreeView->Expand(hRootItemAvailable, TVE_EXPAND); 132 m_TreeView->SelectItem(m_bAppwizMode ? hRootItemInstalled : hRootItemAvailable); 133 } 134 135 BOOL 136 CMainWindow::CreateStatusBar() 137 { 138 m_StatusBar = new CUiWindow<CStatusBar>(); 139 m_StatusBar->m_VerticalAlignment = UiAlign_RightBtm; 140 m_StatusBar->m_HorizontalAlignment = UiAlign_Stretch; 141 m_ClientPanel->Children().Append(m_StatusBar); 142 143 return m_StatusBar->Create(m_hWnd, (HMENU)IDC_STATUSBAR) != NULL; 144 } 145 146 BOOL 147 CMainWindow::CreateTreeView() 148 { 149 m_TreeView = new CSideTreeView(); 150 m_TreeView->m_VerticalAlignment = UiAlign_Stretch; 151 m_TreeView->m_HorizontalAlignment = UiAlign_Stretch; 152 m_VSplitter->First().Append(m_TreeView); 153 154 return m_TreeView->Create(m_hWnd) != NULL; 155 } 156 157 BOOL 158 CMainWindow::CreateApplicationView() 159 { 160 m_ApplicationView = new CApplicationView(this); // pass this to ApplicationView for callback purpose 161 m_ApplicationView->m_VerticalAlignment = UiAlign_Stretch; 162 m_ApplicationView->m_HorizontalAlignment = UiAlign_Stretch; 163 m_VSplitter->Second().Append(m_ApplicationView); 164 165 return m_ApplicationView->Create(m_hWnd) != NULL; 166 } 167 168 BOOL 169 CMainWindow::CreateVSplitter() 170 { 171 m_VSplitter = new CUiSplitPanel(); 172 m_VSplitter->m_VerticalAlignment = UiAlign_Stretch; 173 m_VSplitter->m_HorizontalAlignment = UiAlign_Stretch; 174 m_VSplitter->m_DynamicFirst = FALSE; 175 m_VSplitter->m_Horizontal = FALSE; 176 m_VSplitter->m_MinFirst = 0; 177 178 // TODO: m_MinSecond should be calculate dynamically instead of hard-coded 179 m_VSplitter->m_MinSecond = 480; 180 m_VSplitter->m_Pos = 240; 181 m_ClientPanel->Children().Append(m_VSplitter); 182 183 return m_VSplitter->Create(m_hWnd) != NULL; 184 } 185 186 BOOL 187 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 221 CMainWindow::LayoutCleanup() 222 { 223 delete m_TreeView; 224 delete m_ApplicationView; 225 delete m_VSplitter; 226 delete m_StatusBar; 227 return; 228 } 229 230 BOOL 231 CMainWindow::InitControls() 232 { 233 if (CreateLayout()) 234 { 235 InitCategoriesList(); 236 UpdateStatusBarText(); 237 238 return TRUE; 239 } 240 241 return FALSE; 242 } 243 244 VOID 245 CMainWindow::OnSize(HWND hwnd, WPARAM wParam, LPARAM lParam) 246 { 247 if (wParam == SIZE_MINIMIZED) 248 return; 249 250 /* Size status bar */ 251 m_StatusBar->SendMessage(WM_SIZE, 0, 0); 252 253 RECT r = {0, 0, LOWORD(lParam), HIWORD(lParam)}; 254 HDWP hdwp = NULL; 255 INT count = m_ClientPanel->CountSizableChildren(); 256 257 hdwp = BeginDeferWindowPos(count); 258 if (hdwp) 259 { 260 hdwp = m_ClientPanel->OnParentSize(r, hdwp); 261 if (hdwp) 262 { 263 EndDeferWindowPos(hdwp); 264 } 265 } 266 } 267 268 BOOL 269 CMainWindow::RemoveSelectedAppFromRegistry() 270 { 271 if (!IsInstalledEnum(SelectedEnumType)) 272 return FALSE; 273 274 CStringW szMsgText, szMsgTitle; 275 276 if (!szMsgText.LoadStringW(IDS_APP_REG_REMOVE) || !szMsgTitle.LoadStringW(IDS_INFORMATION)) 277 return FALSE; 278 279 CAppInfo *InstalledApp = (CAppInfo *)m_ApplicationView->GetFocusedItemData(); 280 if (!InstalledApp) 281 return FALSE; 282 283 if (MessageBoxW(szMsgText, szMsgTitle, MB_YESNO | MB_ICONQUESTION) == IDYES) 284 return m_Db->RemoveInstalledAppFromRegistry(InstalledApp) == ERROR_SUCCESS; 285 286 return FALSE; 287 } 288 289 BOOL 290 CMainWindow::UninstallSelectedApp(BOOL bModify) 291 { 292 if (!IsInstalledEnum(SelectedEnumType)) 293 return FALSE; 294 295 CAppInfo *InstalledApp = (CAppInfo *)m_ApplicationView->GetFocusedItemData(); 296 if (!InstalledApp) 297 return FALSE; 298 299 return InstalledApp->UninstallApplication(bModify ? UCF_MODIFY : UCF_NONE); 300 } 301 302 VOID 303 CMainWindow::CheckAvailable() 304 { 305 if (m_Db->GetAvailableCount() == 0) 306 { 307 CUpdateDatabaseMutex lock; 308 m_Db->RemoveCached(); 309 m_Db->UpdateAvailable(); 310 } 311 } 312 313 BOOL 314 CMainWindow::ProcessWindowMessage(HWND hwnd, UINT Msg, WPARAM wParam, LPARAM lParam, LRESULT &theResult, DWORD dwMapId) 315 { 316 theResult = 0; 317 switch (Msg) 318 { 319 case WM_CREATE: 320 if (!InitControls()) 321 ::PostMessageW(hwnd, WM_CLOSE, 0, 0); 322 ::PostMessageW(hwnd, DM_REPOSITION, 0, 0); 323 break; 324 325 case WM_DESTROY: 326 { 327 hMainWnd = NULL; 328 SaveSettings(hwnd, &SettingsInfo); 329 FreeLogs(); 330 331 delete m_ClientPanel; 332 333 PostQuitMessage(0); 334 return 0; 335 } 336 337 case WM_CLOSE: 338 ShowWindow(SW_HIDE); 339 return g_Busy; 340 341 case WM_NOTIFY_OPERATIONCOMPLETED: 342 if (!g_Busy && !IsWindowVisible()) 343 SendMessage(WM_CLOSE, 0, 0); 344 break; 345 346 case DM_REPOSITION: 347 EmulateDialogReposition(hwnd); // We are not a real dialog, we need help from a real one 348 break; 349 350 case WM_COMMAND: 351 OnCommand(wParam, lParam); 352 break; 353 354 case WM_NOTIFY: 355 { 356 LPNMHDR data = (LPNMHDR)lParam; 357 358 switch (data->code) 359 { 360 case TVN_ITEMEXPANDING: 361 { 362 if (data->hwndFrom == m_TreeView->m_hWnd) 363 { 364 // APPWIZ-mode: forbid item collapse. 365 // FIXME: Prevent collapse (COMCTL32 is buggy) 366 // https://bugs.winehq.org/show_bug.cgi?id=53727 367 if (m_bAppwizMode && (((LPNMTREEVIEW)lParam)->action & TVE_TOGGLE) == TVE_COLLAPSE) 368 { 369 theResult = TRUE; 370 return TRUE; // Handled 371 } 372 } 373 break; 374 } 375 376 case TVN_SELCHANGED: 377 { 378 if (data->hwndFrom == m_TreeView->m_hWnd) 379 { 380 switch (((LPNMTREEVIEW)lParam)->itemNew.lParam) 381 { 382 case IDS_INSTALLED: 383 UpdateApplicationsList(ENUM_ALL_INSTALLED); 384 break; 385 386 case IDS_APPLICATIONS: 387 UpdateApplicationsList(ENUM_INSTALLED_APPLICATIONS); 388 break; 389 390 case IDS_UPDATES: 391 UpdateApplicationsList(ENUM_UPDATES); 392 break; 393 394 case IDS_AVAILABLEFORINST: 395 UpdateApplicationsList(ENUM_ALL_AVAILABLE, FALSE, TRUE); 396 break; 397 398 case IDS_CAT_AUDIO: 399 UpdateApplicationsList(ENUM_CAT_AUDIO, FALSE, TRUE); 400 break; 401 402 case IDS_CAT_DEVEL: 403 UpdateApplicationsList(ENUM_CAT_DEVEL, FALSE, TRUE); 404 break; 405 406 case IDS_CAT_DRIVERS: 407 UpdateApplicationsList(ENUM_CAT_DRIVERS, FALSE, TRUE); 408 break; 409 410 case IDS_CAT_EDU: 411 UpdateApplicationsList(ENUM_CAT_EDU, FALSE, TRUE); 412 break; 413 414 case IDS_CAT_ENGINEER: 415 UpdateApplicationsList(ENUM_CAT_ENGINEER, FALSE, TRUE); 416 break; 417 418 case IDS_CAT_FINANCE: 419 UpdateApplicationsList(ENUM_CAT_FINANCE, FALSE, TRUE); 420 break; 421 422 case IDS_CAT_GAMES: 423 UpdateApplicationsList(ENUM_CAT_GAMES, FALSE, TRUE); 424 break; 425 426 case IDS_CAT_GRAPHICS: 427 UpdateApplicationsList(ENUM_CAT_GRAPHICS, FALSE, TRUE); 428 break; 429 430 case IDS_CAT_INTERNET: 431 UpdateApplicationsList(ENUM_CAT_INTERNET, FALSE, TRUE); 432 break; 433 434 case IDS_CAT_LIBS: 435 UpdateApplicationsList(ENUM_CAT_LIBS, FALSE, TRUE); 436 break; 437 438 case IDS_CAT_OFFICE: 439 UpdateApplicationsList(ENUM_CAT_OFFICE, FALSE, TRUE); 440 break; 441 442 case IDS_CAT_OTHER: 443 UpdateApplicationsList(ENUM_CAT_OTHER, FALSE, TRUE); 444 break; 445 446 case IDS_CAT_SCIENCE: 447 UpdateApplicationsList(ENUM_CAT_SCIENCE, FALSE, TRUE); 448 break; 449 450 case IDS_CAT_TOOLS: 451 UpdateApplicationsList(ENUM_CAT_TOOLS, FALSE, TRUE); 452 break; 453 454 case IDS_CAT_VIDEO: 455 UpdateApplicationsList(ENUM_CAT_VIDEO, FALSE, TRUE); 456 break; 457 458 case IDS_CAT_THEMES: 459 UpdateApplicationsList(ENUM_CAT_THEMES, FALSE, TRUE); 460 break; 461 462 case IDS_SELECTEDFORINST: 463 UpdateApplicationsList(ENUM_CAT_SELECTED); 464 break; 465 } 466 } 467 } 468 break; 469 } 470 } 471 break; 472 473 case WM_SIZE: 474 OnSize(hwnd, wParam, lParam); 475 break; 476 477 case WM_SIZING: 478 { 479 LPRECT pRect = (LPRECT)lParam; 480 481 if (pRect->right - pRect->left < 565) 482 pRect->right = pRect->left + 565; 483 484 if (pRect->bottom - pRect->top < 300) 485 pRect->bottom = pRect->top + 300; 486 487 return TRUE; 488 } 489 490 case WM_SYSCOLORCHANGE: 491 { 492 /* Forward WM_SYSCOLORCHANGE to common controls */ 493 m_ApplicationView->SendMessageW(WM_SYSCOLORCHANGE, wParam, lParam); 494 m_TreeView->SendMessageW(WM_SYSCOLORCHANGE, wParam, lParam); 495 } 496 break; 497 498 case WM_SETTINGCHANGE: 499 if (wParam == SPI_SETNONCLIENTMETRICS || wParam == SPI_SETICONMETRICS) 500 { 501 DestroyIcon(g_hDefaultPackageIcon); 502 g_hDefaultPackageIcon = NULL; // Trigger imagelist recreation on next load 503 } 504 break; 505 506 case WM_TIMER: 507 if (wParam == SEARCH_TIMER_ID) 508 { 509 ::KillTimer(hwnd, SEARCH_TIMER_ID); 510 511 UpdateApplicationsList(SelectedEnumType); 512 } 513 break; 514 } 515 516 return FALSE; 517 } 518 519 VOID 520 CMainWindow::ShowAboutDlg() 521 { 522 CStringW szApp; 523 CStringW szAuthors; 524 525 szApp.LoadStringW(IDS_APPTITLE); 526 szAuthors.LoadStringW(IDS_APP_AUTHORS); 527 ShellAboutW(m_hWnd, szApp, szAuthors, 528 LoadIconW(hInst, MAKEINTRESOURCEW(IDI_MAIN))); 529 } 530 531 VOID 532 CMainWindow::OnCommand(WPARAM wParam, LPARAM lParam) 533 { 534 const BOOL bReload = TRUE; 535 WORD wCommand = LOWORD(wParam); 536 537 if (!lParam) 538 { 539 switch (wCommand) 540 { 541 case ID_SETTINGS: 542 CreateSettingsDlg(m_hWnd); 543 break; 544 545 case ID_EXIT: 546 PostMessageW(WM_CLOSE, 0, 0); 547 break; 548 549 case ID_SEARCH: 550 m_ApplicationView->SetFocusOnSearchBar(); 551 break; 552 553 case ID_INSTALL: 554 if (IsAvailableEnum(SelectedEnumType)) 555 { 556 if (!m_Selected.IsEmpty()) 557 { 558 if (DownloadListOfApplications(m_Selected, FALSE)) 559 { 560 m_Selected.RemoveAll(); 561 UpdateApplicationsList(SelectedEnumType); 562 } 563 } 564 else 565 { 566 CAppInfo *App = (CAppInfo *)m_ApplicationView->GetFocusedItemData(); 567 if (App) 568 { 569 InstallApplication(App); 570 } 571 } 572 } 573 break; 574 575 case ID_UNINSTALL: 576 if (UninstallSelectedApp(FALSE)) 577 UpdateApplicationsList(SelectedEnumType, bReload); 578 break; 579 580 case ID_MODIFY: 581 if (UninstallSelectedApp(TRUE)) 582 UpdateApplicationsList(SelectedEnumType, bReload); 583 break; 584 585 case ID_REGREMOVE: 586 if (RemoveSelectedAppFromRegistry()) 587 UpdateApplicationsList(SelectedEnumType, bReload); 588 break; 589 590 case ID_REFRESH: 591 UpdateApplicationsList(SelectedEnumType, bReload); 592 break; 593 594 case ID_RESETDB: 595 { 596 CUpdateDatabaseMutex lock; 597 m_Db->RemoveCached(); 598 UpdateApplicationsList(SelectedEnumType, bReload); 599 break; 600 } 601 602 case ID_HELP: 603 MessageBoxW(L"Help not implemented yet", NULL, MB_OK); 604 break; 605 606 case ID_ABOUT: 607 ShowAboutDlg(); 608 break; 609 610 case ID_CHECK_ALL: 611 m_ApplicationView->CheckAll(); 612 break; 613 614 case ID_ACTIVATE_APPWIZ: 615 if (hRootItemInstalled) 616 m_TreeView->SelectItem(hRootItemInstalled); 617 break; 618 } 619 } 620 } 621 622 VOID 623 CMainWindow::UpdateStatusBarText() 624 { 625 if (m_StatusBar) 626 { 627 CStringW szBuffer; 628 szBuffer.Format(IDS_APPS_COUNT, m_ApplicationView->GetItemCount()); 629 630 // Append the number of selected apps if not in APPWIZ-mode. 631 if (!m_bAppwizMode) 632 { 633 CStringW szBuffer2; 634 szBuffer2.Format(IDS_APPS_SELECT_COUNT, m_Selected.GetCount()); 635 szBuffer += szBuffer2; 636 } 637 638 m_StatusBar->SetText(szBuffer); 639 } 640 } 641 642 VOID 643 CMainWindow::AddApplicationsToView(CAtlList<CAppInfo *> &List) 644 { 645 POSITION CurrentListPosition = List.GetHeadPosition(); 646 while (CurrentListPosition) 647 { 648 CAppInfo *Info = List.GetNext(CurrentListPosition); 649 if (szSearchPattern.IsEmpty() || SearchPatternMatch(Info->szDisplayName, szSearchPattern) || 650 SearchPatternMatch(Info->szComments, szSearchPattern)) 651 { 652 BOOL bSelected = m_Selected.Find(Info) != NULL; 653 m_ApplicationView->AddApplication(Info, bSelected); 654 } 655 } 656 m_ApplicationView->AddApplication(NULL, FALSE); // Tell the list we are done 657 } 658 659 VOID 660 CMainWindow::UpdateApplicationsList(AppsCategories EnumType, BOOL bReload, BOOL bCheckAvailable) 661 { 662 // Only installed applications should be enumerated in APPWIZ-mode. 663 if (m_bAppwizMode && !IsInstalledEnum(EnumType)) 664 { 665 ATLASSERT(FALSE && "Should not be called in APPWIZ-mode"); 666 return; 667 } 668 669 bUpdating = TRUE; 670 671 if (HCURSOR hCursor = LoadCursor(NULL, IDC_APPSTARTING)) 672 { 673 // The database (.ini files) is parsed on the UI thread, let the user know we are busy 674 SetCursor(hCursor); 675 PostMessage(WM_SETCURSOR, (WPARAM)m_hWnd, MAKELONG(HTCLIENT, WM_MOUSEMOVE)); 676 } 677 678 if (bCheckAvailable) 679 CheckAvailable(); 680 681 BOOL TryRestoreSelection = SelectedEnumType == EnumType; 682 if (SelectedEnumType != EnumType) 683 SelectedEnumType = EnumType; 684 685 CApplicationView::RESTORELISTSELECTION RestoreSelection; 686 if (TryRestoreSelection) 687 m_ApplicationView->GetRestoreListSelectionData(RestoreSelection); 688 689 if (bReload) 690 m_Selected.RemoveAll(); 691 692 m_ApplicationView->SetRedraw(FALSE); 693 if (IsInstalledEnum(EnumType)) 694 { 695 if (bReload) 696 m_Db->UpdateInstalled(); 697 698 // set the display type of application-view. this will remove all the item in application-view too. 699 m_ApplicationView->SetDisplayAppType(AppViewTypeInstalledApps); 700 701 CAtlList<CAppInfo *> List; 702 m_Db->GetApps(List, EnumType); 703 AddApplicationsToView(List); 704 } 705 else if (IsAvailableEnum(EnumType)) 706 { 707 // We shouldn't get there in APPWIZ-mode. 708 ATLASSERT(!m_bAppwizMode); 709 710 if (bReload) 711 m_Db->UpdateAvailable(); 712 713 // set the display type of application-view. this will remove all the item in application-view too. 714 m_ApplicationView->SetDisplayAppType(AppViewTypeAvailableApps); 715 716 // enum available softwares 717 if (EnumType == ENUM_CAT_SELECTED) 718 { 719 AddApplicationsToView(m_Selected); 720 } 721 else 722 { 723 CAtlList<CAppInfo *> List; 724 m_Db->GetApps(List, EnumType); 725 AddApplicationsToView(List); 726 } 727 } 728 else 729 { 730 ATLASSERT(0 && "This should be unreachable!"); 731 } 732 733 if (TryRestoreSelection) 734 m_ApplicationView->RestoreListSelection(RestoreSelection); 735 m_ApplicationView->SetRedraw(TRUE); 736 m_ApplicationView->RedrawWindow(0, 0, RDW_INVALIDATE | RDW_ALLCHILDREN); // force the child window to repaint 737 UpdateStatusBarText(); 738 739 CStringW text; 740 if (m_ApplicationView->GetItemCount() == 0 && !szSearchPattern.IsEmpty()) 741 { 742 text.LoadString(IDS_NO_SEARCH_RESULTS); 743 } 744 m_ApplicationView->SetWatermark(text); 745 746 bUpdating = FALSE; 747 } 748 749 ATL::CWndClassInfo & 750 CMainWindow::GetWndClassInfo() 751 { 752 DWORD csStyle = CS_VREDRAW | CS_HREDRAW; 753 static ATL::CWndClassInfo wc = { 754 {sizeof(WNDCLASSEX), csStyle, StartWindowProc, 0, 0, NULL, 755 LoadIconW(_AtlBaseModule.GetModuleInstance(), MAKEINTRESOURCEW(IDI_MAIN)), LoadCursorW(NULL, IDC_ARROW), 756 (HBRUSH)(COLOR_BTNFACE + 1), MAKEINTRESOURCEW(IDR_MAINMENU), szWindowClass, NULL}, 757 NULL, 758 NULL, 759 IDC_ARROW, 760 TRUE, 761 0, 762 _T("")}; 763 return wc; 764 } 765 766 HWND 767 CMainWindow::Create() 768 { 769 const CStringW szWindowName(MAKEINTRESOURCEW(m_bAppwizMode ? IDS_APPWIZ_TITLE : IDS_APPTITLE)); 770 771 RECT r = { 772 (SettingsInfo.bSaveWndPos ? SettingsInfo.Left : CW_USEDEFAULT), 773 (SettingsInfo.bSaveWndPos ? SettingsInfo.Top : CW_USEDEFAULT), 774 (SettingsInfo.bSaveWndPos ? SettingsInfo.Width : 680), (SettingsInfo.bSaveWndPos ? SettingsInfo.Height : 450)}; 775 r.right += r.left; 776 r.bottom += r.top; 777 778 return CWindowImpl::Create( 779 NULL, r, szWindowName.GetString(), WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, WS_EX_WINDOWEDGE); 780 } 781 782 // this function is called when a item of application-view is checked/unchecked 783 // CallbackParam is the param passed to application-view when adding the item (the one getting focus now). 784 VOID 785 CMainWindow::ItemCheckStateChanged(BOOL bChecked, LPVOID CallbackParam) 786 { 787 if (bUpdating) 788 return; 789 790 CAppInfo *Info = (CAppInfo *)CallbackParam; 791 792 if (bChecked) 793 { 794 m_Selected.AddTail(Info); 795 } 796 else 797 { 798 POSITION Pos = m_Selected.Find(Info); 799 ATLASSERT(Pos != NULL); 800 801 if (Pos != NULL) 802 { 803 m_Selected.RemoveAt(Pos); 804 } 805 } 806 807 UpdateStatusBarText(); 808 } 809 810 // this function is called when one or more application(s) should be installed 811 // if Info is not zero, this app should be installed. otherwise those checked apps should be installed 812 BOOL 813 CMainWindow::InstallApplication(CAppInfo *Info) 814 { 815 if (Info) 816 { 817 if (DownloadApplication(Info)) 818 { 819 //FIXME: Delay UpdateApplicationsList(SelectedEnumType); until install completes 820 return TRUE; 821 } 822 } 823 824 return FALSE; 825 } 826 827 BOOL 828 CMainWindow::SearchTextChanged(CStringW &SearchText) 829 { 830 if (szSearchPattern == SearchText) 831 { 832 return FALSE; 833 } 834 835 szSearchPattern = SearchText; 836 837 DWORD dwDelay = 0; 838 SystemParametersInfoW(SPI_GETMENUSHOWDELAY, 0, &dwDelay, 0); 839 SetTimer(SEARCH_TIMER_ID, dwDelay); 840 841 return TRUE; 842 } 843 844 void 845 CMainWindow::HandleTabOrder(int direction) 846 { 847 ATL::CSimpleArray<HWND> TabOrderHwndList; 848 849 m_TreeView->AppendTabOrderWindow(direction, TabOrderHwndList); 850 m_ApplicationView->AppendTabOrderWindow(direction, TabOrderHwndList); 851 852 if (TabOrderHwndList.GetSize() == 0) 853 { 854 // in case the list is empty 855 return; 856 } 857 858 int FocusIndex; 859 860 if ((FocusIndex = TabOrderHwndList.Find(GetFocus())) == -1) 861 { 862 FocusIndex = 0; // focus the first window in the list 863 } 864 else 865 { 866 FocusIndex += direction; 867 FocusIndex += 868 TabOrderHwndList.GetSize(); // FocusIndex might be negative. we don't want to mod a negative number 869 FocusIndex %= TabOrderHwndList.GetSize(); 870 } 871 872 ::SetFocus(TabOrderHwndList[FocusIndex]); 873 return; 874 } 875 // **** CMainWindow **** 876 877 VOID 878 MainWindowLoop(CMainWindow *wnd, INT nShowCmd) 879 { 880 HACCEL KeyBrd; 881 MSG Msg; 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 && Msg.wParam == VK_TAB) 900 { 901 // Move backwards if shift is held down 902 int direction = (GetKeyState(VK_SHIFT) & 0x8000) ? -1 : 1; 903 904 wnd->HandleTabOrder(direction); 905 continue; 906 } 907 908 TranslateMessage(&Msg); 909 DispatchMessageW(&Msg); 910 } 911 } 912 } 913