1 /* 2 * PROJECT: ReactOS Applications Manager 3 * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) 4 * PURPOSE: Application view class and other classes used by it 5 * COPYRIGHT: Copyright 2020 He Yang (1160386205@qq.com) 6 * Copyright 2022,2023 Mark Jansen <mark.jansen@reactos.org> 7 */ 8 9 #include "rapps.h" 10 #include "appview.h" 11 #include "gui.h" 12 #include <windowsx.h> 13 14 using namespace Gdiplus; 15 16 // **** CMainToolbar **** 17 18 VOID 19 CMainToolbar::AddImageToImageList(HIMAGELIST hImageList, UINT ImageIndex) 20 { 21 HICON hImage; 22 23 if (!(hImage = 24 (HICON)LoadImageW(hInst, MAKEINTRESOURCE(ImageIndex), IMAGE_ICON, m_iToolbarHeight, m_iToolbarHeight, 0))) 25 { 26 return; 27 } 28 29 ImageList_AddIcon(hImageList, hImage); 30 DeleteObject(hImage); 31 } 32 33 HIMAGELIST 34 CMainToolbar::InitImageList() 35 { 36 HIMAGELIST hImageList; 37 38 /* Create the toolbar icon image list */ 39 hImageList = ImageList_Create(m_iToolbarHeight, m_iToolbarHeight, ILC_MASK | GetSystemColorDepth(), 1, 1); 40 if (!hImageList) 41 { 42 return NULL; 43 } 44 45 AddImageToImageList(hImageList, IDI_INSTALL); 46 AddImageToImageList(hImageList, IDI_UNINSTALL); 47 AddImageToImageList(hImageList, IDI_MODIFY); 48 AddImageToImageList(hImageList, IDI_CHECK_ALL); 49 AddImageToImageList(hImageList, IDI_REFRESH); 50 AddImageToImageList(hImageList, IDI_UPDATE_DB); 51 AddImageToImageList(hImageList, IDI_SETTINGS); 52 AddImageToImageList(hImageList, IDI_EXIT); 53 54 return hImageList; 55 } 56 57 CMainToolbar::CMainToolbar() : m_iToolbarHeight(24), m_dButtonsWidthMax(0) 58 { 59 } 60 61 VOID 62 CMainToolbar::OnGetDispInfo(LPTOOLTIPTEXT lpttt) 63 { 64 UINT idButton = (UINT)lpttt->hdr.idFrom; 65 66 switch (idButton) 67 { 68 case ID_EXIT: 69 lpttt->lpszText = MAKEINTRESOURCEW(IDS_TOOLTIP_EXIT); 70 break; 71 72 case ID_INSTALL: 73 lpttt->lpszText = MAKEINTRESOURCEW(IDS_TOOLTIP_INSTALL); 74 break; 75 76 case ID_UNINSTALL: 77 lpttt->lpszText = MAKEINTRESOURCEW(IDS_TOOLTIP_UNINSTALL); 78 break; 79 80 case ID_MODIFY: 81 lpttt->lpszText = MAKEINTRESOURCEW(IDS_TOOLTIP_MODIFY); 82 break; 83 84 case ID_SETTINGS: 85 lpttt->lpszText = MAKEINTRESOURCEW(IDS_TOOLTIP_SETTINGS); 86 break; 87 88 case ID_REFRESH: 89 lpttt->lpszText = MAKEINTRESOURCEW(IDS_TOOLTIP_REFRESH); 90 break; 91 92 case ID_RESETDB: 93 lpttt->lpszText = MAKEINTRESOURCEW(IDS_TOOLTIP_UPDATE_DB); 94 break; 95 } 96 } 97 98 HWND 99 CMainToolbar::Create(HWND hwndParent) 100 { 101 CStringW szInstallBtn; 102 CStringW szUninstallBtn; 103 CStringW szModifyBtn; 104 CStringW szSelectAllBtn; 105 CStringW szRefreshBtn; 106 CStringW szUpdateDbBtn; 107 108 /* Load tooltip strings */ 109 szInstallBtn.LoadStringW(IDS_TOOLTIP_INSTALL); 110 szUninstallBtn.LoadStringW(IDS_TOOLTIP_UNINSTALL); 111 szModifyBtn.LoadStringW(IDS_TOOLTIP_MODIFY); 112 szSelectAllBtn.LoadStringW(IDS_TOOLTIP_SELECT_ALL); 113 szRefreshBtn.LoadStringW(IDS_TOOLTIP_REFRESH); 114 szUpdateDbBtn.LoadStringW(IDS_TOOLTIP_UPDATE_DB); 115 116 /* Create buttons */ 117 TBBUTTON Buttons[] = { 118 /* iBitmap, idCommand, fsState, fsStyle, bReserved[2], dwData, iString */ 119 {0, ID_INSTALL, TBSTATE_ENABLED, BTNS_BUTTON | BTNS_AUTOSIZE, {0}, 0, (INT_PTR)szInstallBtn.GetString()}, 120 {1, ID_UNINSTALL, TBSTATE_ENABLED, BTNS_BUTTON | BTNS_AUTOSIZE, {0}, 0, (INT_PTR)szUninstallBtn.GetString()}, 121 {2, ID_MODIFY, TBSTATE_ENABLED, BTNS_BUTTON | BTNS_AUTOSIZE, {0}, 0, (INT_PTR)szModifyBtn.GetString()}, 122 {3, ID_CHECK_ALL, TBSTATE_ENABLED, BTNS_BUTTON | BTNS_AUTOSIZE, {0}, 0, (INT_PTR)szSelectAllBtn.GetString()}, 123 {-1, 0, TBSTATE_ENABLED, BTNS_SEP, {0}, 0, 0}, 124 {4, ID_REFRESH, TBSTATE_ENABLED, BTNS_BUTTON | BTNS_AUTOSIZE, {0}, 0, (INT_PTR)szRefreshBtn.GetString()}, 125 {5, ID_RESETDB, TBSTATE_ENABLED, BTNS_BUTTON | BTNS_AUTOSIZE, {0}, 0, (INT_PTR)szUpdateDbBtn.GetString()}}; 126 127 m_hWnd = CreateWindowExW( 128 0, TOOLBARCLASSNAMEW, NULL, WS_CHILD | WS_VISIBLE | TBSTYLE_FLAT | TBSTYLE_TOOLTIPS | TBSTYLE_LIST, 0, 0, 0, 0, 129 hwndParent, 0, hInst, NULL); 130 131 if (!m_hWnd) 132 { 133 return FALSE; 134 } 135 136 SendMessageW(TB_SETEXTENDEDSTYLE, 0, TBSTYLE_EX_HIDECLIPPEDBUTTONS); 137 SetButtonStructSize(); 138 139 /* Set image list */ 140 HIMAGELIST hImageList = InitImageList(); 141 142 if (hImageList) 143 { 144 ImageList_Destroy(SetImageList(hImageList)); 145 } 146 147 AddButtons(_countof(Buttons), Buttons); 148 149 /* Remember ideal width to use as a max width of buttons */ 150 SIZE size; 151 GetIdealSize(FALSE, &size); 152 m_dButtonsWidthMax = size.cx; 153 154 return m_hWnd; 155 } 156 157 VOID 158 CMainToolbar::HideButtonCaption() 159 { 160 DWORD dCurrentExStyle = (DWORD)SendMessageW(TB_GETEXTENDEDSTYLE, 0, 0); 161 SendMessageW(TB_SETEXTENDEDSTYLE, 0, dCurrentExStyle | TBSTYLE_EX_MIXEDBUTTONS); 162 } 163 164 VOID 165 CMainToolbar::ShowButtonCaption() 166 { 167 DWORD dCurrentExStyle = (DWORD)SendMessageW(TB_GETEXTENDEDSTYLE, 0, 0); 168 SendMessageW(TB_SETEXTENDEDSTYLE, 0, dCurrentExStyle & ~TBSTYLE_EX_MIXEDBUTTONS); 169 } 170 171 DWORD 172 CMainToolbar::GetMaxButtonsWidth() const 173 { 174 return m_dButtonsWidthMax; 175 } 176 // **** CMainToolbar **** 177 178 // **** CSearchBar **** 179 180 CSearchBar::CSearchBar() : m_Width(180), m_Height(22) 181 { 182 } 183 184 VOID 185 CSearchBar::SetText(LPCWSTR lpszText) 186 { 187 SendMessageW(SB_SETTEXT, SBT_NOBORDERS, (LPARAM)lpszText); 188 } 189 190 HWND 191 CSearchBar::Create(HWND hwndParent) 192 { 193 CStringW szBuf; 194 m_hWnd = CreateWindowExW( 195 WS_EX_CLIENTEDGE, L"Edit", NULL, WS_CHILD | WS_VISIBLE | ES_LEFT | ES_AUTOHSCROLL, 0, 0, m_Width, m_Height, 196 hwndParent, (HMENU)NULL, hInst, 0); 197 198 SendMessageW(WM_SETFONT, (WPARAM)GetStockObject(DEFAULT_GUI_FONT), 0); 199 szBuf.LoadStringW(IDS_SEARCH_TEXT); 200 SetWindowTextW(szBuf); 201 return m_hWnd; 202 } 203 // **** CSearchBar **** 204 205 // **** CComboBox **** 206 207 CComboBox::CComboBox() : m_Width(80), m_Height(22) 208 { 209 } 210 211 HWND 212 CComboBox::Create(HWND hwndParent) 213 { 214 m_hWnd = CreateWindowW( 215 WC_COMBOBOX, L"", CBS_DROPDOWNLIST | CBS_HASSTRINGS | WS_CHILD | WS_OVERLAPPED | WS_VISIBLE, 0, 0, m_Width, 216 m_Height, hwndParent, NULL, 0, NULL); 217 218 SendMessageW(WM_SETFONT, (WPARAM)GetStockObject(DEFAULT_GUI_FONT), 0); 219 220 for (int i = 0; i < (int)_countof(m_TypeStringID); i++) 221 { 222 CStringW szBuf; 223 szBuf.LoadStringW(m_TypeStringID[i]); 224 SendMessageW(CB_ADDSTRING, 0, (LPARAM)(LPCWSTR)szBuf); 225 } 226 227 SendMessageW(CB_SETCURSEL, m_DefaultSelectType, 0); // select the first item 228 229 return m_hWnd; 230 } 231 // **** CComboBox **** 232 233 // **** CAppRichEdit **** 234 235 VOID 236 CAppRichEdit::LoadAndInsertText(UINT uStringID, const CStringW &szText, DWORD TextFlags) 237 { 238 CStringW szLoadedText; 239 if (!szText.IsEmpty() && szLoadedText.LoadStringW(uStringID)) 240 { 241 const DWORD StringFlags = CFE_BOLD; 242 InsertText(szLoadedText, StringFlags); 243 InsertText(szText, TextFlags); 244 } 245 } 246 247 VOID 248 CAppRichEdit::LoadAndInsertText(UINT uStringID, DWORD StringFlags) 249 { 250 CStringW szLoadedText; 251 if (szLoadedText.LoadStringW(uStringID)) 252 { 253 InsertText(L"\n", 0); 254 InsertText(szLoadedText, StringFlags); 255 InsertText(L"\n", 0); 256 } 257 } 258 259 VOID 260 CAppRichEdit::InsertTextWithString(UINT StringID, const CStringW &Text, DWORD TextFlags) 261 { 262 if (!Text.IsEmpty()) 263 { 264 LoadAndInsertText(StringID, Text, TextFlags); 265 } 266 } 267 268 VOID 269 CAppRichEdit::SetWelcomeText() 270 { 271 CStringW szText; 272 273 szText.LoadStringW(IDS_WELCOME_TITLE); 274 SetText(szText, CFE_BOLD); 275 276 szText.LoadStringW(IDS_WELCOME_TEXT); 277 InsertText(szText, 0); 278 279 szText.LoadStringW(IDS_WELCOME_URL); 280 InsertText(szText, CFM_LINK); 281 } 282 // **** CAppRichEdit **** 283 284 int 285 ScrnshotDownloadCallback(pASYNCINET AsyncInet, ASYNC_EVENT Event, WPARAM wParam, LPARAM lParam, VOID *Extension) 286 { 287 ScrnshotDownloadParam *DownloadParam = (ScrnshotDownloadParam *)Extension; 288 switch (Event) 289 { 290 case ASYNCINET_DATA: 291 DWORD BytesWritten; 292 WriteFile(DownloadParam->hFile, (LPCVOID)wParam, (DWORD)lParam, &BytesWritten, NULL); 293 break; 294 case ASYNCINET_COMPLETE: 295 CloseHandle(DownloadParam->hFile); 296 SendMessage( 297 DownloadParam->hwndNotify, WM_RAPPS_DOWNLOAD_COMPLETE, (WPARAM)ERROR_SUCCESS, (LPARAM)DownloadParam); 298 break; 299 case ASYNCINET_CANCELLED: 300 CloseHandle(DownloadParam->hFile); 301 SendMessage( 302 DownloadParam->hwndNotify, WM_RAPPS_DOWNLOAD_COMPLETE, (WPARAM)ERROR_CANCELLED, (LPARAM)DownloadParam); 303 break; 304 case ASYNCINET_ERROR: 305 CloseHandle(DownloadParam->hFile); 306 SendMessage(DownloadParam->hwndNotify, WM_RAPPS_DOWNLOAD_COMPLETE, wParam, (LPARAM)DownloadParam); 307 break; 308 default: 309 ATLASSERT(FALSE); 310 break; 311 } 312 return 0; 313 } 314 315 // **** CAppScrnshotPreview **** 316 317 CAppScrnshotPreview::CAppScrnshotPreview(const CStringW &BasePath) : m_BasePath(BasePath) 318 { 319 } 320 321 BOOL 322 CAppScrnshotPreview::ProcessWindowMessage( 323 HWND hwnd, 324 UINT Msg, 325 WPARAM wParam, 326 LPARAM lParam, 327 LRESULT &theResult, 328 DWORD dwMapId) 329 { 330 theResult = 0; 331 switch (Msg) 332 { 333 case WM_CREATE: 334 hBrokenImgIcon = 335 (HICON)LoadImage(hInst, MAKEINTRESOURCE(IDI_BROKEN_IMAGE), IMAGE_ICON, BrokenImgSize, BrokenImgSize, 0); 336 break; 337 case WM_SIZE: 338 { 339 if (BrokenImgSize != min(min(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)), BROKENIMG_ICON_SIZE)) 340 { 341 BrokenImgSize = min(min(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)), BROKENIMG_ICON_SIZE); 342 343 if (hBrokenImgIcon) 344 { 345 DeleteObject(hBrokenImgIcon); 346 hBrokenImgIcon = (HICON)LoadImage( 347 hInst, MAKEINTRESOURCE(IDI_BROKEN_IMAGE), IMAGE_ICON, BrokenImgSize, BrokenImgSize, 0); 348 } 349 } 350 break; 351 } 352 case WM_RAPPS_DOWNLOAD_COMPLETE: 353 { 354 ScrnshotDownloadParam *DownloadParam = (ScrnshotDownloadParam *)lParam; 355 AsyncInetRelease(AsyncInet); 356 AsyncInet = NULL; 357 switch (wParam) 358 { 359 case ERROR_SUCCESS: 360 if (ContentID == DownloadParam->ID) 361 { 362 DisplayFile(DownloadParam->DownloadFileName); 363 // send a message to trigger resizing 364 ::SendMessageW(::GetParent(m_hWnd), WM_RAPPS_RESIZE_CHILDREN, 0, 0); 365 InvalidateRect(0, 0); 366 TempImagePath = 367 DownloadParam->DownloadFileName; // record tmp file path in order to delete it when cleanup 368 } 369 else 370 { 371 // the picture downloaded is already outdated. delete it. 372 DeleteFileW(DownloadParam->DownloadFileName); 373 } 374 break; 375 case ERROR_CANCELLED: 376 DeleteFileW(DownloadParam->DownloadFileName); 377 break; 378 default: 379 DisplayFailed(); 380 // send a message to trigger resizing 381 ::SendMessageW(::GetParent(m_hWnd), WM_RAPPS_RESIZE_CHILDREN, 0, 0); 382 InvalidateRect(0, 0); 383 DeleteFileW(DownloadParam->DownloadFileName); 384 break; 385 } 386 delete DownloadParam; 387 break; 388 } 389 case WM_PAINT: 390 { 391 PAINTSTRUCT ps; 392 HDC hdc = BeginPaint(&ps); 393 CRect rect; 394 GetClientRect(&rect); 395 396 PaintOnDC(hdc, rect.Width(), rect.Height(), ps.fErase); 397 398 EndPaint(&ps); 399 break; 400 } 401 case WM_PRINTCLIENT: 402 { 403 if (lParam & PRF_CHECKVISIBLE) 404 { 405 if (!IsWindowVisible()) 406 break; 407 } 408 CRect rect; 409 GetClientRect(&rect); 410 411 PaintOnDC((HDC)wParam, rect.Width(), rect.Height(), lParam & PRF_ERASEBKGND); 412 break; 413 } 414 case WM_ERASEBKGND: 415 { 416 return TRUE; // do not erase to avoid blinking 417 } 418 case WM_TIMER: 419 { 420 switch (wParam) 421 { 422 case TIMER_LOADING_ANIMATION: 423 LoadingAnimationFrame++; 424 LoadingAnimationFrame %= (LOADING_ANIMATION_PERIOD * LOADING_ANIMATION_FPS); 425 HDC hdc = GetDC(); 426 CRect rect; 427 GetClientRect(&rect); 428 429 PaintOnDC(hdc, rect.Width(), rect.Height(), TRUE); 430 ReleaseDC(hdc); 431 } 432 break; 433 } 434 case WM_DESTROY: 435 { 436 PreviousDisplayCleanup(); 437 DeleteObject(hBrokenImgIcon); 438 hBrokenImgIcon = NULL; 439 break; 440 } 441 } 442 return FALSE; 443 } 444 445 VOID 446 CAppScrnshotPreview::DisplayLoading() 447 { 448 SetStatus(SCRNSHOT_PREV_LOADING); 449 if (bLoadingTimerOn) 450 { 451 KillTimer(TIMER_LOADING_ANIMATION); 452 } 453 LoadingAnimationFrame = 0; 454 bLoadingTimerOn = TRUE; 455 SetTimer(TIMER_LOADING_ANIMATION, 1000 / LOADING_ANIMATION_FPS, 0); 456 } 457 458 VOID 459 CAppScrnshotPreview::DisplayFailed() 460 { 461 InterlockedIncrement64(&ContentID); 462 SetStatus(SCRNSHOT_PREV_FAILED); 463 PreviousDisplayCleanup(); 464 } 465 466 BOOL 467 CAppScrnshotPreview::DisplayFile(LPCWSTR lpszFileName) 468 { 469 PreviousDisplayCleanup(); 470 SetStatus(SCRNSHOT_PREV_IMAGE); 471 pImage = Bitmap::FromFile(lpszFileName, 0); 472 if (pImage->GetLastStatus() != Ok) 473 { 474 DisplayFailed(); 475 return FALSE; 476 } 477 return TRUE; 478 } 479 480 VOID 481 CAppScrnshotPreview::SetStatus(SCRNSHOT_STATUS Status) 482 { 483 ScrnshotPrevStauts = Status; 484 } 485 486 VOID 487 CAppScrnshotPreview::PaintOnDC(HDC hdc, int width, int height, BOOL bDrawBkgnd) 488 { 489 // use an off screen dc to avoid blinking 490 HDC hdcMem = CreateCompatibleDC(hdc); 491 HBITMAP hBitmap = CreateCompatibleBitmap(hdc, width, height); 492 SelectObject(hdcMem, hBitmap); 493 494 if (bDrawBkgnd) 495 { 496 HBRUSH hOldBrush = (HBRUSH)SelectObject(hdcMem, (HGDIOBJ)GetSysColorBrush(COLOR_BTNFACE)); 497 PatBlt(hdcMem, 0, 0, width, height, PATCOPY); 498 SelectObject(hdcMem, hOldBrush); 499 } 500 501 switch (ScrnshotPrevStauts) 502 { 503 case SCRNSHOT_PREV_EMPTY: 504 { 505 } 506 break; 507 508 case SCRNSHOT_PREV_LOADING: 509 { 510 Graphics graphics(hdcMem); 511 Color color(255, 0, 0); 512 SolidBrush dotBrush(Color(255, 100, 100, 100)); 513 514 graphics.SetSmoothingMode(SmoothingMode::SmoothingModeAntiAlias); 515 516 // Paint three dot 517 float DotWidth = GetLoadingDotWidth(width, height); 518 graphics.FillEllipse( 519 (Brush *)(&dotBrush), (REAL)width / 2.0 - min(width, height) * 2.0 / 16.0 - DotWidth / 2.0, 520 (REAL)height / 2.0 - 521 GetFrameDotShift(LoadingAnimationFrame + LOADING_ANIMATION_FPS / 4, width, height) - DotWidth / 2.0, 522 DotWidth, DotWidth); 523 524 graphics.FillEllipse( 525 (Brush *)(&dotBrush), (REAL)width / 2.0 - DotWidth / 2.0, 526 (REAL)height / 2.0 - GetFrameDotShift(LoadingAnimationFrame, width, height) - DotWidth / 2.0, DotWidth, 527 DotWidth); 528 529 graphics.FillEllipse( 530 (Brush *)(&dotBrush), (REAL)width / 2.0 + min(width, height) * 2.0 / 16.0 - DotWidth / 2.0, 531 (REAL)height / 2.0 - 532 GetFrameDotShift(LoadingAnimationFrame - LOADING_ANIMATION_FPS / 4, width, height) - DotWidth / 2.0, 533 DotWidth, DotWidth); 534 } 535 break; 536 537 case SCRNSHOT_PREV_IMAGE: 538 { 539 if (pImage) 540 { 541 // always draw entire image inside the window. 542 Graphics graphics(hdcMem); 543 float ZoomRatio = 544 min(((float)width / (float)pImage->GetWidth()), ((float)height / (float)pImage->GetHeight())); 545 float ZoomedImgWidth = ZoomRatio * (float)pImage->GetWidth(); 546 float ZoomedImgHeight = ZoomRatio * (float)pImage->GetHeight(); 547 548 graphics.DrawImage( 549 pImage, ((float)width - ZoomedImgWidth) / 2.0, ((float)height - ZoomedImgHeight) / 2.0, 550 ZoomedImgWidth, ZoomedImgHeight); 551 } 552 } 553 break; 554 555 case SCRNSHOT_PREV_FAILED: 556 { 557 DrawIconEx( 558 hdcMem, (width - BrokenImgSize) / 2, (height - BrokenImgSize) / 2, hBrokenImgIcon, BrokenImgSize, 559 BrokenImgSize, NULL, NULL, DI_NORMAL | DI_COMPAT); 560 } 561 break; 562 } 563 564 // copy the content form off-screen dc to hdc 565 BitBlt(hdc, 0, 0, width, height, hdcMem, 0, 0, SRCCOPY); 566 DeleteDC(hdcMem); 567 DeleteObject(hBitmap); 568 } 569 570 float 571 CAppScrnshotPreview::GetLoadingDotWidth(int width, int height) 572 { 573 return min(width, height) / 20.0; 574 } 575 576 float 577 CAppScrnshotPreview::GetFrameDotShift(int Frame, int width, int height) 578 { 579 return min(width, height) * (1.0 / 16.0) * (2.0 / (2.0 - sqrt(3.0))) * 580 (max(sin((float)Frame * 2 * PI / (LOADING_ANIMATION_PERIOD * LOADING_ANIMATION_FPS)), sqrt(3.0) / 2.0) - 581 sqrt(3.0) / 2.0); 582 } 583 584 ATL::CWndClassInfo & 585 CAppScrnshotPreview::GetWndClassInfo() 586 { 587 DWORD csStyle = CS_VREDRAW | CS_HREDRAW; 588 static ATL::CWndClassInfo wc = { 589 {sizeof(WNDCLASSEX), csStyle, StartWindowProc, 0, 0, NULL, 0, LoadCursorW(NULL, IDC_ARROW), 590 (HBRUSH)(COLOR_BTNFACE + 1), 0, L"RAppsScrnshotPreview", NULL}, 591 NULL, 592 NULL, 593 IDC_ARROW, 594 TRUE, 595 0, 596 _T("")}; 597 return wc; 598 } 599 600 HWND 601 CAppScrnshotPreview::Create(HWND hParent) 602 { 603 RECT r = {0, 0, 0, 0}; 604 605 return CWindowImpl::Create(hParent, r, L"", WS_CHILD | WS_VISIBLE); 606 } 607 608 VOID 609 CAppScrnshotPreview::PreviousDisplayCleanup() 610 { 611 if (bLoadingTimerOn) 612 { 613 KillTimer(TIMER_LOADING_ANIMATION); 614 bLoadingTimerOn = FALSE; 615 } 616 LoadingAnimationFrame = 0; 617 if (pImage) 618 { 619 delete pImage; 620 pImage = NULL; 621 } 622 if (AsyncInet) 623 { 624 AsyncInetCancel(AsyncInet); 625 } 626 if (!TempImagePath.IsEmpty()) 627 { 628 DeleteFileW(TempImagePath.GetString()); 629 TempImagePath.Empty(); 630 } 631 } 632 633 VOID 634 CAppScrnshotPreview::DisplayEmpty() 635 { 636 InterlockedIncrement64(&ContentID); 637 SetStatus(SCRNSHOT_PREV_EMPTY); 638 PreviousDisplayCleanup(); 639 } 640 641 BOOL 642 CAppScrnshotPreview::DisplayImage(LPCWSTR lpszLocation) 643 { 644 LONGLONG ID = InterlockedIncrement64(&ContentID); 645 PreviousDisplayCleanup(); 646 647 if (PathIsURLW(lpszLocation)) 648 { 649 DisplayLoading(); 650 651 ScrnshotDownloadParam *DownloadParam = new ScrnshotDownloadParam; 652 if (!DownloadParam) 653 return FALSE; 654 655 DownloadParam->hwndNotify = m_hWnd; 656 DownloadParam->ID = ID; 657 // generate a filename 658 CStringW ScrnshotFolder = m_BasePath; 659 PathAppendW(ScrnshotFolder.GetBuffer(MAX_PATH), L"screenshots"); 660 ScrnshotFolder.ReleaseBuffer(); 661 662 if (!PathIsDirectoryW(ScrnshotFolder.GetString())) 663 { 664 CreateDirectoryW(ScrnshotFolder.GetString(), NULL); 665 } 666 667 if (!GetTempFileNameW( 668 ScrnshotFolder.GetString(), L"img", 0, DownloadParam->DownloadFileName.GetBuffer(MAX_PATH))) 669 { 670 DownloadParam->DownloadFileName.ReleaseBuffer(); 671 delete DownloadParam; 672 DisplayFailed(); 673 return FALSE; 674 } 675 DownloadParam->DownloadFileName.ReleaseBuffer(); 676 677 DownloadParam->hFile = CreateFileW( 678 DownloadParam->DownloadFileName.GetString(), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 679 NULL); 680 if (DownloadParam->hFile == INVALID_HANDLE_VALUE) 681 { 682 delete DownloadParam; 683 DisplayFailed(); 684 return FALSE; 685 } 686 687 AsyncInet = AsyncInetDownload( 688 0, INTERNET_OPEN_TYPE_PRECONFIG, 0, 0, lpszLocation, TRUE, ScrnshotDownloadCallback, DownloadParam); 689 if (!AsyncInet) 690 { 691 CloseHandle(DownloadParam->hFile); 692 DeleteFileW(DownloadParam->DownloadFileName.GetBuffer()); 693 delete DownloadParam; 694 DisplayFailed(); 695 return FALSE; 696 } 697 return TRUE; 698 } 699 else 700 { 701 return DisplayFile(lpszLocation); 702 } 703 } 704 705 int 706 CAppScrnshotPreview::GetRequestedWidth(int Height) // calculate requested window width by given height 707 { 708 switch (ScrnshotPrevStauts) 709 { 710 case SCRNSHOT_PREV_EMPTY: 711 return 0; 712 case SCRNSHOT_PREV_LOADING: 713 return 200; 714 case SCRNSHOT_PREV_IMAGE: 715 if (pImage) 716 { 717 // return the width needed to display image inside the window. 718 // and always keep window w/h ratio inside [ 1/SCRNSHOT_MAX_ASPECT_RAT, SCRNSHOT_MAX_ASPECT_RAT ] 719 return (int)floor( 720 (float)Height * 721 max(min((float)pImage->GetWidth() / (float)pImage->GetHeight(), (float)SCRNSHOT_MAX_ASPECT_RAT), 722 1.0 / (float)SCRNSHOT_MAX_ASPECT_RAT)); 723 } 724 return 0; 725 case SCRNSHOT_PREV_FAILED: 726 return 200; 727 default: 728 return 0; 729 } 730 } 731 732 CAppScrnshotPreview::~CAppScrnshotPreview() 733 { 734 PreviousDisplayCleanup(); 735 } 736 // **** CAppScrnshotPreview **** 737 738 // **** CAppInfoDisplay **** 739 740 BOOL 741 CAppInfoDisplay::ProcessWindowMessage( 742 HWND hwnd, 743 UINT message, 744 WPARAM wParam, 745 LPARAM lParam, 746 LRESULT &theResult, 747 DWORD dwMapId) 748 { 749 theResult = 0; 750 switch (message) 751 { 752 case WM_CREATE: 753 { 754 RichEdit = new CAppRichEdit(); 755 RichEdit->Create(hwnd); 756 757 CStringW Directory; 758 ::GetStorageDirectory(Directory); 759 ScrnshotPrev = new CAppScrnshotPreview(Directory); 760 ScrnshotPrev->Create(hwnd); 761 break; 762 } 763 case WM_SIZE: 764 { 765 ResizeChildren(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); 766 break; 767 } 768 case WM_RAPPS_RESIZE_CHILDREN: 769 { 770 ResizeChildren(); 771 break; 772 } 773 case WM_COMMAND: 774 { 775 OnCommand(wParam, lParam); 776 break; 777 } 778 case WM_NOTIFY: 779 { 780 NMHDR *NotifyHeader = (NMHDR *)lParam; 781 if (NotifyHeader->hwndFrom == RichEdit->m_hWnd) 782 { 783 switch (NotifyHeader->code) 784 { 785 case EN_LINK: 786 OnLink((ENLINK *)lParam); 787 break; 788 } 789 } 790 break; 791 } 792 } 793 794 return FALSE; 795 } 796 797 VOID 798 CAppInfoDisplay::ResizeChildren() 799 { 800 CRect rect; 801 GetWindowRect(&rect); 802 ResizeChildren(rect.Width(), rect.Height()); 803 } 804 805 VOID 806 CAppInfoDisplay::ResizeChildren(int Width, int Height) 807 { 808 int ScrnshotWidth = ScrnshotPrev->GetRequestedWidth(Height); 809 810 // make sure richedit always have room to display 811 ScrnshotWidth = min(ScrnshotWidth, Width - INFO_DISPLAY_PADDING - RICHEDIT_MIN_WIDTH); 812 813 DWORD dwError = ERROR_SUCCESS; 814 HDWP hDwp = BeginDeferWindowPos(2); 815 816 if (hDwp) 817 { 818 hDwp = ::DeferWindowPos(hDwp, ScrnshotPrev->m_hWnd, NULL, 0, 0, ScrnshotWidth, Height, 0); 819 820 if (hDwp) 821 { 822 // hide the padding if scrnshot window width == 0 823 int RicheditPosX = ScrnshotWidth ? (ScrnshotWidth + INFO_DISPLAY_PADDING) : 0; 824 825 hDwp = ::DeferWindowPos(hDwp, RichEdit->m_hWnd, NULL, RicheditPosX, 0, Width - RicheditPosX, Height, 0); 826 827 if (hDwp) 828 { 829 EndDeferWindowPos(hDwp); 830 } 831 else 832 { 833 dwError = GetLastError(); 834 } 835 } 836 else 837 { 838 dwError = GetLastError(); 839 } 840 } 841 else 842 { 843 dwError = GetLastError(); 844 } 845 846 #if DBG 847 ATLASSERT(dwError == ERROR_SUCCESS); 848 #endif 849 UNREFERENCED_PARAMETER(dwError); 850 851 UpdateWindow(); 852 } 853 854 VOID 855 CAppInfoDisplay::OnLink(ENLINK *Link) 856 { 857 switch (Link->msg) 858 { 859 case WM_LBUTTONUP: 860 case WM_RBUTTONUP: 861 { 862 if (pLink) 863 HeapFree(GetProcessHeap(), 0, pLink); 864 865 pLink = (LPWSTR)HeapAlloc( 866 GetProcessHeap(), 0, 867 (max(Link->chrg.cpMin, Link->chrg.cpMax) - min(Link->chrg.cpMin, Link->chrg.cpMax) + 1) * 868 sizeof(WCHAR)); 869 if (!pLink) 870 { 871 /* TODO: Error message */ 872 return; 873 } 874 875 RichEdit->SendMessageW(EM_SETSEL, Link->chrg.cpMin, Link->chrg.cpMax); 876 RichEdit->SendMessageW(EM_GETSELTEXT, 0, (LPARAM)pLink); 877 878 ShowPopupMenuEx(m_hWnd, m_hWnd, IDR_LINKMENU, -1); 879 } 880 break; 881 } 882 } 883 884 ATL::CWndClassInfo & 885 CAppInfoDisplay::GetWndClassInfo() 886 { 887 DWORD csStyle = CS_VREDRAW | CS_HREDRAW; 888 static ATL::CWndClassInfo wc = {/*.m_wc=*/ 889 {/*cbSize=*/sizeof(WNDCLASSEX), 890 /*style=*/csStyle, 891 /*lpfnWndProc=*/StartWindowProc, 892 /*cbClsExtra=*/0, 893 /*cbWndExtra=*/0, 894 /*hInstance=*/NULL, 895 /*hIcon=*/NULL, 896 /*hCursor*/ NULL, 897 /*hbrBackground=*/(HBRUSH)(COLOR_BTNFACE + 1), 898 /*lpszMenuName=*/NULL, 899 /*lpszClassName=*/L"RAppsAppInfo", 900 /*hIconSm=*/NULL}, 901 /*m_lpszOrigName=*/NULL, 902 /*pWndProc=*/NULL, 903 /*m_lpszCursorID=*/IDC_ARROW, 904 /*m_bSystemCursor*/ TRUE, 905 /*m_atom=*/0, 906 /*m_szAutoName=*/_T("")}; 907 return wc; 908 } 909 910 HWND 911 CAppInfoDisplay::Create(HWND hwndParent) 912 { 913 RECT r = {0, 0, 0, 0}; 914 915 return CWindowImpl::Create(hwndParent, r, L"", WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS); 916 } 917 918 VOID 919 CAppInfoDisplay::ShowAppInfo(CAppInfo *Info) 920 { 921 CStringW ScrnshotLocation; 922 if (Info->RetrieveScreenshot(ScrnshotLocation)) 923 { 924 ScrnshotPrev->DisplayImage(ScrnshotLocation); 925 } 926 else 927 { 928 ScrnshotPrev->DisplayEmpty(); 929 } 930 ResizeChildren(); 931 Info->ShowAppInfo(RichEdit); 932 } 933 934 VOID 935 CAppInfoDisplay::SetWelcomeText() 936 { 937 ScrnshotPrev->DisplayEmpty(); 938 ResizeChildren(); 939 RichEdit->SetWelcomeText(); 940 } 941 942 VOID 943 CAppInfoDisplay::OnCommand(WPARAM wParam, LPARAM lParam) 944 { 945 WORD wCommand = LOWORD(wParam); 946 947 switch (wCommand) 948 { 949 case ID_OPEN_LINK: 950 951 ShellExecuteW(m_hWnd, L"open", pLink, NULL, NULL, SW_SHOWNOACTIVATE); 952 HeapFree(GetProcessHeap(), 0, pLink); 953 pLink = NULL; 954 break; 955 956 case ID_COPY_LINK: 957 CopyTextToClipboard(pLink); 958 HeapFree(GetProcessHeap(), 0, pLink); 959 pLink = NULL; 960 break; 961 } 962 } 963 964 CAppInfoDisplay::~CAppInfoDisplay() 965 { 966 delete RichEdit; 967 delete ScrnshotPrev; 968 } 969 // **** CAppInfoDisplay **** 970 971 // **** CAppsListView **** 972 973 CAppsListView::CAppsListView() 974 { 975 } 976 977 CAppsListView::~CAppsListView() 978 { 979 if (m_hImageListView) 980 { 981 ImageList_Destroy(m_hImageListView); 982 } 983 } 984 985 LRESULT 986 CAppsListView::OnEraseBackground(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled) 987 { 988 LRESULT lRes = this->DefWindowProc(uMsg, wParam, lParam); 989 if (!m_Watermark.IsEmpty()) 990 { 991 RECT rc; 992 GetClientRect(&rc); 993 HGDIOBJ oldFont = SelectFont(HDC(wParam), GetStockFont(DEFAULT_GUI_FONT)); 994 DrawShadowText( 995 HDC(wParam), m_Watermark.GetString(), m_Watermark.GetLength(), &rc, 996 DT_CENTER | DT_VCENTER | DT_NOPREFIX | DT_SINGLELINE, GetSysColor(COLOR_GRAYTEXT), 997 GetSysColor(COLOR_GRAYTEXT), 1, 1); 998 SelectFont(HDC(wParam), oldFont); 999 } 1000 return lRes; 1001 } 1002 1003 VOID 1004 CAppsListView::SetWatermark(const CStringW &Text) 1005 { 1006 m_Watermark = Text; 1007 } 1008 1009 VOID 1010 CAppsListView::SetCheckboxesVisible(BOOL bIsVisible) 1011 { 1012 if (bIsVisible) 1013 { 1014 SetExtendedListViewStyle(LVS_EX_CHECKBOXES | LVS_EX_FULLROWSELECT); 1015 } 1016 else 1017 { 1018 SetExtendedListViewStyle(LVS_EX_FULLROWSELECT); 1019 } 1020 1021 bHasCheckboxes = bIsVisible; 1022 } 1023 1024 VOID 1025 CAppsListView::ColumnClick(LPNMLISTVIEW pnmv) 1026 { 1027 HWND hHeader; 1028 HDITEMW hColumn; 1029 INT nHeaderID = pnmv->iSubItem; 1030 1031 if ((GetWindowLongPtr(GWL_STYLE) & ~LVS_NOSORTHEADER) == 0) 1032 return; 1033 1034 hHeader = (HWND)SendMessage(LVM_GETHEADER, 0, 0); 1035 ZeroMemory(&hColumn, sizeof(hColumn)); 1036 1037 /* If the sorting column changed, remove the sorting style from the old column */ 1038 if ((nLastHeaderID != -1) && (nLastHeaderID != nHeaderID)) 1039 { 1040 bIsAscending = TRUE; // also reset sorting method to ascending 1041 hColumn.mask = HDI_FORMAT; 1042 Header_GetItem(hHeader, nLastHeaderID, &hColumn); 1043 hColumn.fmt &= ~(HDF_SORTUP | HDF_SORTDOWN); 1044 Header_SetItem(hHeader, nLastHeaderID, &hColumn); 1045 } 1046 1047 /* Set the sorting style to the new column */ 1048 hColumn.mask = HDI_FORMAT; 1049 Header_GetItem(hHeader, nHeaderID, &hColumn); 1050 1051 hColumn.fmt &= ~(HDF_SORTDOWN | HDF_SORTUP); 1052 hColumn.fmt |= (bIsAscending ? HDF_SORTUP : HDF_SORTDOWN); 1053 Header_SetItem(hHeader, nHeaderID, &hColumn); 1054 1055 /* Sort the list, using the current values of nHeaderID and bIsAscending */ 1056 SortContext ctx = {this, nHeaderID}; 1057 SortItems(s_CompareFunc, &ctx); 1058 1059 /* Save new values */ 1060 nLastHeaderID = nHeaderID; 1061 bIsAscending = !bIsAscending; 1062 } 1063 1064 BOOL 1065 CAppsListView::AddColumn(INT Index, CStringW &Text, INT Width, INT Format) 1066 { 1067 LVCOLUMNW Column; 1068 1069 ZeroMemory(&Column, sizeof(Column)); 1070 1071 Column.mask = LVCF_FMT | LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM; 1072 Column.iSubItem = Index; 1073 Column.pszText = const_cast<LPWSTR>(Text.GetString()); 1074 Column.cx = Width; 1075 Column.fmt = Format; 1076 1077 return SendMessage(LVM_INSERTCOLUMN, Index, (LPARAM)(&Column)); 1078 } 1079 1080 void 1081 CAppsListView::DeleteColumn(INT Index) 1082 { 1083 SendMessage(LVM_DELETECOLUMN, Index, 0); 1084 return; 1085 } 1086 1087 INT 1088 CAppsListView::AddItem(INT ItemIndex, INT IconIndex, LPCWSTR lpText, LPARAM lParam) 1089 { 1090 LVITEMW Item; 1091 1092 ZeroMemory(&Item, sizeof(Item)); 1093 1094 Item.mask = LVIF_TEXT | LVIF_PARAM | LVIF_STATE; 1095 Item.pszText = const_cast<LPWSTR>(lpText); 1096 Item.lParam = lParam; 1097 Item.iItem = ItemIndex; 1098 Item.iImage = IconIndex; 1099 1100 if (IconIndex >= 0) 1101 { 1102 Item.iImage = IconIndex; 1103 Item.mask |= LVIF_IMAGE; 1104 } 1105 return InsertItem(&Item); 1106 } 1107 1108 HIMAGELIST 1109 CAppsListView::GetImageList(int iImageList) 1110 { 1111 return (HIMAGELIST)SendMessage(LVM_GETIMAGELIST, iImageList, 0); 1112 } 1113 1114 INT CALLBACK 1115 CAppsListView::s_CompareFunc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort) 1116 { 1117 SortContext *ctx = ((SortContext *)lParamSort); 1118 return ctx->lvw->CompareFunc(lParam1, lParam2, ctx->iSubItem); 1119 } 1120 1121 INT 1122 CAppsListView::CompareFunc(LPARAM lParam1, LPARAM lParam2, INT iSubItem) 1123 { 1124 CStringW Item1, Item2; 1125 LVFINDINFOW IndexInfo; 1126 INT Index; 1127 1128 IndexInfo.flags = LVFI_PARAM; 1129 1130 IndexInfo.lParam = lParam1; 1131 Index = FindItem(-1, &IndexInfo); 1132 GetItemText(Index, iSubItem, Item1.GetBuffer(MAX_STR_LEN), MAX_STR_LEN); 1133 Item1.ReleaseBuffer(); 1134 1135 IndexInfo.lParam = lParam2; 1136 Index = FindItem(-1, &IndexInfo); 1137 GetItemText(Index, iSubItem, Item2.GetBuffer(MAX_STR_LEN), MAX_STR_LEN); 1138 Item2.ReleaseBuffer(); 1139 1140 return bIsAscending ? Item1.Compare(Item2) : Item2.Compare(Item1); 1141 } 1142 1143 HWND 1144 CAppsListView::Create(HWND hwndParent) 1145 { 1146 RECT r = {205, 28, 465, 250}; 1147 DWORD style = WS_CHILD | WS_VISIBLE | LVS_SORTASCENDING | LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS | 1148 LVS_AUTOARRANGE | LVS_SHAREIMAGELISTS; 1149 1150 HWND hwnd = CListView::Create(hwndParent, r, NULL, style, WS_EX_CLIENTEDGE); 1151 1152 if (hwnd) 1153 { 1154 SetCheckboxesVisible(FALSE); 1155 } 1156 1157 m_hImageListView = ImageList_Create(LISTVIEW_ICON_SIZE, LISTVIEW_ICON_SIZE, GetSystemColorDepth() | ILC_MASK, 0, 1); 1158 1159 SetImageList(m_hImageListView, LVSIL_SMALL); 1160 SetImageList(m_hImageListView, LVSIL_NORMAL); 1161 1162 #pragma push_macro("SubclassWindow") 1163 #undef SubclassWindow 1164 m_hWnd = NULL; 1165 SubclassWindow(hwnd); 1166 #pragma pop_macro("SubclassWindow") 1167 1168 return hwnd; 1169 } 1170 1171 BOOL 1172 CAppsListView::GetCheckState(INT item) 1173 { 1174 return (BOOL)(GetItemState(item, LVIS_STATEIMAGEMASK) >> 12) - 1; 1175 } 1176 1177 VOID 1178 CAppsListView::SetCheckState(INT item, BOOL fCheck) 1179 { 1180 if (bHasCheckboxes) 1181 { 1182 SetItemState(item, INDEXTOSTATEIMAGEMASK((fCheck) ? 2 : 1), LVIS_STATEIMAGEMASK); 1183 } 1184 } 1185 1186 VOID 1187 CAppsListView::CheckAll() 1188 { 1189 if (bHasCheckboxes) 1190 { 1191 if (CheckedItemCount == ItemCount) 1192 { 1193 // clear all 1194 SetCheckState(-1, FALSE); 1195 } 1196 else 1197 { 1198 // check all 1199 SetCheckState(-1, TRUE); 1200 } 1201 } 1202 } 1203 1204 PVOID 1205 CAppsListView::GetFocusedItemData() 1206 { 1207 INT item = GetSelectionMark(); 1208 if (item == -1) 1209 { 1210 return (PVOID)0; 1211 } 1212 return (PVOID)GetItemData(item); 1213 } 1214 1215 BOOL 1216 CAppsListView::SetDisplayAppType(APPLICATION_VIEW_TYPE AppType) 1217 { 1218 if (!DeleteAllItems()) 1219 return FALSE; 1220 ApplicationViewType = AppType; 1221 1222 bIsAscending = TRUE; 1223 1224 ItemCount = 0; 1225 CheckedItemCount = 0; 1226 1227 // delete old columns 1228 while (ColumnCount) 1229 { 1230 DeleteColumn(--ColumnCount); 1231 } 1232 1233 ImageList_RemoveAll(m_hImageListView); 1234 1235 // add new columns 1236 CStringW szText; 1237 switch (AppType) 1238 { 1239 case AppViewTypeInstalledApps: 1240 1241 /* Add columns to ListView */ 1242 szText.LoadStringW(IDS_APP_NAME); 1243 AddColumn(ColumnCount++, szText, 250, LVCFMT_LEFT); 1244 1245 szText.LoadStringW(IDS_APP_INST_VERSION); 1246 AddColumn(ColumnCount++, szText, 90, LVCFMT_RIGHT); 1247 1248 szText.LoadStringW(IDS_APP_DESCRIPTION); 1249 AddColumn(ColumnCount++, szText, 300, LVCFMT_LEFT); 1250 1251 // disable checkboxes 1252 SetCheckboxesVisible(FALSE); 1253 break; 1254 1255 case AppViewTypeAvailableApps: 1256 1257 /* Add columns to ListView */ 1258 szText.LoadStringW(IDS_APP_NAME); 1259 AddColumn(ColumnCount++, szText, 250, LVCFMT_LEFT); 1260 1261 szText.LoadStringW(IDS_APP_INST_VERSION); 1262 AddColumn(ColumnCount++, szText, 90, LVCFMT_RIGHT); 1263 1264 szText.LoadStringW(IDS_APP_DESCRIPTION); 1265 AddColumn(ColumnCount++, szText, 300, LVCFMT_LEFT); 1266 1267 // enable checkboxes 1268 SetCheckboxesVisible(TRUE); 1269 break; 1270 1271 default: 1272 break; 1273 } 1274 1275 return TRUE; 1276 } 1277 1278 BOOL 1279 CAppsListView::SetViewMode(DWORD ViewMode) 1280 { 1281 return SendMessage(LVM_SETVIEW, (WPARAM)ViewMode, 0) == 1; 1282 } 1283 1284 BOOL 1285 CAppsListView::AddApplication(CAppInfo *AppInfo, BOOL InitialCheckState) 1286 { 1287 if (ApplicationViewType == AppViewTypeInstalledApps) 1288 { 1289 /* Load icon from registry */ 1290 HICON hIcon = NULL; 1291 CStringW szIconPath; 1292 if (AppInfo->RetrieveIcon(szIconPath)) 1293 { 1294 PathParseIconLocationW((LPWSTR)szIconPath.GetString()); 1295 1296 /* Load only the 1st icon from the application executable, 1297 * because all apps provide the executables which have the main icon 1298 * as 1st in the index , so we don't need other icons here */ 1299 hIcon = ExtractIconW(hInst, szIconPath.GetString(), 0); 1300 } 1301 1302 if (!hIcon) 1303 { 1304 /* Load default icon */ 1305 hIcon = LoadIconW(hInst, MAKEINTRESOURCEW(IDI_MAIN)); 1306 } 1307 1308 int IconIndex = ImageList_AddIcon(m_hImageListView, hIcon); 1309 DestroyIcon(hIcon); 1310 1311 int Index = AddItem(ItemCount, IconIndex, AppInfo->szDisplayName, (LPARAM)AppInfo); 1312 SetItemText(Index, 1, AppInfo->szDisplayVersion.IsEmpty() ? L"---" : AppInfo->szDisplayVersion); 1313 SetItemText(Index, 2, AppInfo->szComments.IsEmpty() ? L"---" : AppInfo->szComments); 1314 1315 ItemCount++; 1316 return TRUE; 1317 } 1318 else if (ApplicationViewType == AppViewTypeAvailableApps) 1319 { 1320 /* Load icon from file */ 1321 HICON hIcon = NULL; 1322 CStringW szIconPath; 1323 if (AppInfo->RetrieveIcon(szIconPath)) 1324 { 1325 hIcon = (HICON)LoadImageW( 1326 NULL, szIconPath, IMAGE_ICON, LISTVIEW_ICON_SIZE, LISTVIEW_ICON_SIZE, LR_LOADFROMFILE); 1327 } 1328 1329 if (!hIcon) 1330 { 1331 /* Load default icon */ 1332 hIcon = LoadIconW(hInst, MAKEINTRESOURCEW(IDI_MAIN)); 1333 } 1334 1335 int IconIndex = ImageList_AddIcon(m_hImageListView, hIcon); 1336 DestroyIcon(hIcon); 1337 1338 int Index = AddItem(ItemCount, IconIndex, AppInfo->szDisplayName, (LPARAM)AppInfo); 1339 1340 if (InitialCheckState) 1341 { 1342 SetCheckState(Index, TRUE); 1343 } 1344 1345 SetItemText(Index, 1, AppInfo->szDisplayVersion); 1346 SetItemText(Index, 2, AppInfo->szComments); 1347 1348 ItemCount++; 1349 return TRUE; 1350 } 1351 else 1352 { 1353 return FALSE; 1354 } 1355 } 1356 1357 // this function is called when parent window receiving an notification about checkstate changing 1358 VOID 1359 CAppsListView::ItemCheckStateNotify(int iItem, BOOL bCheck) 1360 { 1361 if (bCheck) 1362 { 1363 CheckedItemCount++; 1364 } 1365 else 1366 { 1367 CheckedItemCount--; 1368 } 1369 } 1370 // **** CAppsListView **** 1371 1372 // **** CApplicationView **** 1373 1374 BOOL 1375 CApplicationView::ProcessWindowMessage( 1376 HWND hwnd, 1377 UINT message, 1378 WPARAM wParam, 1379 LPARAM lParam, 1380 LRESULT &theResult, 1381 DWORD dwMapId) 1382 { 1383 theResult = 0; 1384 switch (message) 1385 { 1386 case WM_CREATE: 1387 { 1388 BOOL bSuccess = TRUE; 1389 m_Panel = new CUiPanel(); 1390 m_Panel->m_VerticalAlignment = UiAlign_Stretch; 1391 m_Panel->m_HorizontalAlignment = UiAlign_Stretch; 1392 1393 bSuccess &= CreateToolbar(); 1394 bSuccess &= CreateSearchBar(); 1395 bSuccess &= CreateComboBox(); 1396 bSuccess &= CreateHSplitter(); 1397 bSuccess &= CreateListView(); 1398 bSuccess &= CreateAppInfoDisplay(); 1399 1400 m_Toolbar->AutoSize(); 1401 1402 RECT rTop; 1403 1404 ::GetWindowRect(m_Toolbar->m_hWnd, &rTop); 1405 m_HSplitter->m_Margin.top = rTop.bottom - rTop.top; 1406 if (!bSuccess) 1407 { 1408 return -1; // creation failure 1409 } 1410 } 1411 break; 1412 1413 case WM_NOTIFY: 1414 { 1415 LPNMHDR pNotifyHeader = (LPNMHDR)lParam; 1416 if (pNotifyHeader->hwndFrom == m_ListView->GetWindow()) 1417 { 1418 switch (pNotifyHeader->code) 1419 { 1420 case LVN_ITEMCHANGED: 1421 { 1422 LPNMLISTVIEW pnic = (LPNMLISTVIEW)lParam; 1423 1424 /* Check if this is a valid item 1425 * (technically, it can be also an unselect) */ 1426 INT ItemIndex = pnic->iItem; 1427 if (ItemIndex == -1 || ItemIndex >= ListView_GetItemCount(pnic->hdr.hwndFrom)) 1428 { 1429 break; 1430 } 1431 1432 /* Check if the focus has been moved to another item */ 1433 if ((pnic->uChanged & LVIF_STATE) && (pnic->uNewState & LVIS_FOCUSED) && 1434 !(pnic->uOldState & LVIS_FOCUSED)) 1435 { 1436 ItemGetFocus((LPVOID)pnic->lParam); 1437 } 1438 1439 /* Check if the item is checked/unchecked */ 1440 if (pnic->uChanged & LVIF_STATE) 1441 { 1442 int iOldState = STATEIMAGETOINDEX(pnic->uOldState); 1443 int iNewState = STATEIMAGETOINDEX(pnic->uNewState); 1444 1445 if (iOldState == STATEIMAGE_UNCHECKED && iNewState == STATEIMAGE_CHECKED) 1446 { 1447 // this item is just checked 1448 m_ListView->ItemCheckStateNotify(pnic->iItem, TRUE); 1449 ItemCheckStateChanged(TRUE, (LPVOID)pnic->lParam); 1450 } 1451 else if (iOldState == STATEIMAGE_CHECKED && iNewState == STATEIMAGE_UNCHECKED) 1452 { 1453 // this item is just unchecked 1454 m_ListView->ItemCheckStateNotify(pnic->iItem, FALSE); 1455 ItemCheckStateChanged(FALSE, (LPVOID)pnic->lParam); 1456 } 1457 } 1458 } 1459 break; 1460 1461 case LVN_COLUMNCLICK: 1462 { 1463 LPNMLISTVIEW pnmv = (LPNMLISTVIEW)lParam; 1464 1465 m_ListView->ColumnClick(pnmv); 1466 } 1467 break; 1468 1469 case NM_DBLCLK: 1470 { 1471 LPNMITEMACTIVATE Item = (LPNMITEMACTIVATE)lParam; 1472 if (Item->iItem != -1) 1473 { 1474 /* this won't do anything if the program is already installed */ 1475 1476 if (ApplicationViewType == AppViewTypeAvailableApps) 1477 { 1478 m_MainWindow->InstallApplication( 1479 (CAppInfo *)m_ListView->GetItemData(Item->iItem)); 1480 } 1481 } 1482 } 1483 break; 1484 1485 case NM_RCLICK: 1486 { 1487 if (((LPNMLISTVIEW)lParam)->iItem != -1) 1488 { 1489 ShowPopupMenuEx(m_hWnd, m_hWnd, 0, ID_INSTALL); 1490 } 1491 } 1492 break; 1493 } 1494 } 1495 else if (pNotifyHeader->hwndFrom == m_Toolbar->GetWindow()) 1496 { 1497 switch (pNotifyHeader->code) 1498 { 1499 case TTN_GETDISPINFO: 1500 m_Toolbar->OnGetDispInfo((LPTOOLTIPTEXT)lParam); 1501 break; 1502 } 1503 } 1504 } 1505 break; 1506 1507 case WM_SYSCOLORCHANGE: 1508 { 1509 /* Forward WM_SYSCOLORCHANGE to common controls */ 1510 m_ListView->SendMessageW(WM_SYSCOLORCHANGE, wParam, lParam); 1511 m_ListView->SendMessageW(EM_SETBKGNDCOLOR, 0, GetSysColor(COLOR_BTNFACE)); 1512 m_Toolbar->SendMessageW(WM_SYSCOLORCHANGE, wParam, lParam); 1513 m_ComboBox->SendMessageW(WM_SYSCOLORCHANGE, wParam, lParam); 1514 } 1515 break; 1516 1517 case WM_SIZE: 1518 { 1519 OnSize(hwnd, wParam, lParam); 1520 break; 1521 } 1522 1523 case WM_COMMAND: 1524 { 1525 OnCommand(wParam, lParam); 1526 } 1527 break; 1528 } 1529 return FALSE; 1530 } 1531 1532 BOOL 1533 CApplicationView::CreateToolbar() 1534 { 1535 m_Toolbar = new CMainToolbar(); 1536 m_Toolbar->m_VerticalAlignment = UiAlign_LeftTop; 1537 m_Toolbar->m_HorizontalAlignment = UiAlign_Stretch; 1538 m_Panel->Children().Append(m_Toolbar); 1539 1540 return m_Toolbar->Create(m_hWnd) != NULL; 1541 } 1542 1543 BOOL 1544 CApplicationView::CreateSearchBar() 1545 { 1546 m_SearchBar = new CUiWindow<CSearchBar>(); 1547 m_SearchBar->m_VerticalAlignment = UiAlign_LeftTop; 1548 m_SearchBar->m_HorizontalAlignment = UiAlign_RightBtm; 1549 m_SearchBar->m_Margin.top = 4; 1550 m_SearchBar->m_Margin.right = TOOLBAR_PADDING; 1551 1552 return m_SearchBar->Create(m_Toolbar->m_hWnd) != NULL; 1553 } 1554 1555 BOOL 1556 CApplicationView::CreateComboBox() 1557 { 1558 m_ComboBox = new CUiWindow<CComboBox>(); 1559 m_ComboBox->m_VerticalAlignment = UiAlign_LeftTop; 1560 m_ComboBox->m_HorizontalAlignment = UiAlign_RightBtm; 1561 m_ComboBox->m_Margin.top = 4; 1562 1563 return m_ComboBox->Create(m_Toolbar->m_hWnd) != NULL; 1564 } 1565 1566 BOOL 1567 CApplicationView::CreateHSplitter() 1568 { 1569 m_HSplitter = new CUiSplitPanel(); 1570 m_HSplitter->m_VerticalAlignment = UiAlign_Stretch; 1571 m_HSplitter->m_HorizontalAlignment = UiAlign_Stretch; 1572 m_HSplitter->m_DynamicFirst = TRUE; 1573 m_HSplitter->m_Horizontal = TRUE; 1574 m_HSplitter->m_Pos = INT_MAX; // set INT_MAX to use lowest possible position (m_MinSecond) 1575 m_HSplitter->m_MinFirst = 10; 1576 m_HSplitter->m_MinSecond = 140; 1577 m_Panel->Children().Append(m_HSplitter); 1578 1579 return m_HSplitter->Create(m_hWnd) != NULL; 1580 } 1581 1582 BOOL 1583 CApplicationView::CreateListView() 1584 { 1585 m_ListView = new CAppsListView(); 1586 m_ListView->m_VerticalAlignment = UiAlign_Stretch; 1587 m_ListView->m_HorizontalAlignment = UiAlign_Stretch; 1588 m_HSplitter->First().Append(m_ListView); 1589 1590 return m_ListView->Create(m_hWnd) != NULL; 1591 } 1592 1593 BOOL 1594 CApplicationView::CreateAppInfoDisplay() 1595 { 1596 m_AppsInfo = new CAppInfoDisplay(); 1597 m_AppsInfo->m_VerticalAlignment = UiAlign_Stretch; 1598 m_AppsInfo->m_HorizontalAlignment = UiAlign_Stretch; 1599 m_HSplitter->Second().Append(m_AppsInfo); 1600 1601 return m_AppsInfo->Create(m_hWnd) != NULL; 1602 } 1603 1604 void 1605 CApplicationView::SetRedraw(BOOL bRedraw) 1606 { 1607 CWindow::SetRedraw(bRedraw); 1608 m_ListView->SetRedraw(bRedraw); 1609 } 1610 1611 void 1612 CApplicationView::SetFocusOnSearchBar() 1613 { 1614 m_SearchBar->SetFocus(); 1615 } 1616 1617 VOID 1618 CApplicationView::OnSize(HWND hwnd, WPARAM wParam, LPARAM lParam) 1619 { 1620 if (wParam == SIZE_MINIMIZED) 1621 return; 1622 1623 /* Size tool bar */ 1624 m_Toolbar->AutoSize(); 1625 1626 /* Automatically hide captions */ 1627 DWORD dToolbarTreshold = m_Toolbar->GetMaxButtonsWidth(); 1628 DWORD dSearchbarMargin = (LOWORD(lParam) - m_SearchBar->m_Width - m_ComboBox->m_Width - TOOLBAR_PADDING * 2); 1629 1630 if (dSearchbarMargin > dToolbarTreshold) 1631 { 1632 m_Toolbar->ShowButtonCaption(); 1633 } 1634 else if (dSearchbarMargin < dToolbarTreshold) 1635 { 1636 m_Toolbar->HideButtonCaption(); 1637 } 1638 1639 RECT r = {0, 0, LOWORD(lParam), HIWORD(lParam)}; 1640 HDWP hdwp = NULL; 1641 INT count = m_Panel->CountSizableChildren(); 1642 1643 hdwp = BeginDeferWindowPos(count); 1644 if (hdwp) 1645 { 1646 hdwp = m_Panel->OnParentSize(r, hdwp); 1647 if (hdwp) 1648 { 1649 EndDeferWindowPos(hdwp); 1650 } 1651 } 1652 1653 count = m_SearchBar->CountSizableChildren(); 1654 hdwp = BeginDeferWindowPos(count); 1655 if (hdwp) 1656 { 1657 hdwp = m_SearchBar->OnParentSize(r, hdwp); 1658 if (hdwp) 1659 { 1660 EndDeferWindowPos(hdwp); 1661 } 1662 } 1663 1664 m_ComboBox->m_Margin.right = m_SearchBar->m_Width + m_SearchBar->m_Margin.right + TOOLBAR_PADDING; 1665 count = m_ComboBox->CountSizableChildren(); 1666 hdwp = BeginDeferWindowPos(count); 1667 if (hdwp) 1668 { 1669 hdwp = m_ComboBox->OnParentSize(r, hdwp); 1670 if (hdwp) 1671 { 1672 EndDeferWindowPos(hdwp); 1673 } 1674 } 1675 } 1676 1677 VOID 1678 CApplicationView::OnCommand(WPARAM wParam, LPARAM lParam) 1679 { 1680 if (lParam) 1681 { 1682 if ((HWND)lParam == m_SearchBar->GetWindow()) 1683 { 1684 CStringW szBuf; 1685 switch (HIWORD(wParam)) 1686 { 1687 case EN_SETFOCUS: 1688 { 1689 CStringW szWndText; 1690 1691 szBuf.LoadStringW(IDS_SEARCH_TEXT); 1692 m_SearchBar->GetWindowTextW(szWndText); 1693 if (szBuf == szWndText) 1694 { 1695 m_SearchBar->SetWindowTextW(L""); 1696 } 1697 } 1698 break; 1699 1700 case EN_KILLFOCUS: 1701 { 1702 m_SearchBar->GetWindowTextW(szBuf); 1703 if (szBuf.IsEmpty()) 1704 { 1705 szBuf.LoadStringW(IDS_SEARCH_TEXT); 1706 m_SearchBar->SetWindowTextW(szBuf.GetString()); 1707 } 1708 } 1709 break; 1710 1711 case EN_CHANGE: 1712 { 1713 CStringW szWndText; 1714 1715 szBuf.LoadStringW(IDS_SEARCH_TEXT); 1716 m_SearchBar->GetWindowTextW(szWndText); 1717 if (szBuf == szWndText) 1718 { 1719 szWndText = L""; 1720 m_MainWindow->SearchTextChanged(szWndText); 1721 } 1722 else 1723 { 1724 m_MainWindow->SearchTextChanged(szWndText); 1725 } 1726 } 1727 break; 1728 } 1729 1730 return; 1731 } 1732 else if ((HWND)lParam == m_ComboBox->GetWindow()) 1733 { 1734 int NotifyCode = HIWORD(wParam); 1735 switch (NotifyCode) 1736 { 1737 case CBN_SELCHANGE: 1738 int CurrSelection = m_ComboBox->SendMessageW(CB_GETCURSEL); 1739 1740 int ViewModeList[] = {LV_VIEW_DETAILS, LV_VIEW_LIST, LV_VIEW_TILE}; 1741 ATLASSERT(CurrSelection < (int)_countof(ViewModeList)); 1742 if (!m_ListView->SetViewMode(ViewModeList[CurrSelection])) 1743 { 1744 MessageBoxW(L"View mode invalid or unimplemented"); 1745 } 1746 break; 1747 } 1748 1749 return; 1750 } 1751 else if ((HWND)lParam == m_Toolbar->GetWindow()) 1752 { 1753 // the message is sent from Toolbar. fall down to continue process 1754 } 1755 else 1756 { 1757 return; 1758 } 1759 } 1760 1761 // the LOWORD of wParam contains a Menu or Control ID 1762 WORD wCommand = LOWORD(wParam); 1763 1764 switch (wCommand) 1765 { 1766 case ID_INSTALL: 1767 case ID_UNINSTALL: 1768 case ID_MODIFY: 1769 case ID_REGREMOVE: 1770 case ID_REFRESH: 1771 case ID_RESETDB: 1772 case ID_CHECK_ALL: 1773 m_MainWindow->SendMessageW(WM_COMMAND, wCommand, 0); 1774 break; 1775 } 1776 } 1777 1778 CApplicationView::CApplicationView(CMainWindow *MainWindow) : m_MainWindow(MainWindow) 1779 { 1780 } 1781 1782 CApplicationView::~CApplicationView() 1783 { 1784 delete m_Toolbar; 1785 delete m_SearchBar; 1786 delete m_ListView; 1787 delete m_AppsInfo; 1788 delete m_HSplitter; 1789 } 1790 1791 ATL::CWndClassInfo & 1792 CApplicationView::GetWndClassInfo() 1793 { 1794 DWORD csStyle = CS_VREDRAW | CS_HREDRAW; 1795 static ATL::CWndClassInfo wc = { 1796 {sizeof(WNDCLASSEX), csStyle, StartWindowProc, 0, 0, NULL, NULL, NULL, (HBRUSH)(COLOR_BTNFACE + 1), NULL, 1797 L"RAppsApplicationView", NULL}, 1798 NULL, 1799 NULL, 1800 IDC_ARROW, 1801 TRUE, 1802 0, 1803 _T("")}; 1804 return wc; 1805 } 1806 1807 HWND 1808 CApplicationView::Create(HWND hwndParent) 1809 { 1810 RECT r = {0, 0, 0, 0}; 1811 1812 HMENU menu = GetSubMenu(LoadMenuW(hInst, MAKEINTRESOURCEW(IDR_APPLICATIONMENU)), 0); 1813 1814 return CWindowImpl::Create(hwndParent, r, L"", WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, 0, menu); 1815 } 1816 1817 BOOL 1818 CApplicationView::SetDisplayAppType(APPLICATION_VIEW_TYPE AppType) 1819 { 1820 if (!m_ListView->SetDisplayAppType(AppType)) 1821 { 1822 return FALSE; 1823 } 1824 ApplicationViewType = AppType; 1825 m_AppsInfo->SetWelcomeText(); 1826 1827 HMENU hMenu = ::GetMenu(m_hWnd); 1828 switch (AppType) 1829 { 1830 case AppViewTypeInstalledApps: 1831 EnableMenuItem(hMenu, ID_REGREMOVE, MF_ENABLED); 1832 EnableMenuItem(hMenu, ID_INSTALL, MF_GRAYED); 1833 EnableMenuItem(hMenu, ID_UNINSTALL, MF_ENABLED); 1834 EnableMenuItem(hMenu, ID_MODIFY, MF_ENABLED); 1835 1836 m_Toolbar->SendMessageW(TB_ENABLEBUTTON, ID_INSTALL, FALSE); 1837 m_Toolbar->SendMessageW(TB_ENABLEBUTTON, ID_UNINSTALL, TRUE); 1838 m_Toolbar->SendMessageW(TB_ENABLEBUTTON, ID_MODIFY, TRUE); 1839 break; 1840 1841 case AppViewTypeAvailableApps: 1842 EnableMenuItem(hMenu, ID_REGREMOVE, MF_GRAYED); 1843 EnableMenuItem(hMenu, ID_INSTALL, MF_ENABLED); 1844 EnableMenuItem(hMenu, ID_UNINSTALL, MF_GRAYED); 1845 EnableMenuItem(hMenu, ID_MODIFY, MF_GRAYED); 1846 1847 m_Toolbar->SendMessageW(TB_ENABLEBUTTON, ID_INSTALL, TRUE); 1848 m_Toolbar->SendMessageW(TB_ENABLEBUTTON, ID_UNINSTALL, FALSE); 1849 m_Toolbar->SendMessageW(TB_ENABLEBUTTON, ID_MODIFY, FALSE); 1850 break; 1851 } 1852 return TRUE; 1853 } 1854 1855 BOOL 1856 CApplicationView::AddApplication(CAppInfo *AppInfo, BOOL InitialCheckState) 1857 { 1858 return m_ListView->AddApplication(AppInfo, InitialCheckState); 1859 } 1860 1861 VOID 1862 CApplicationView::SetWatermark(const CStringW &Text) 1863 { 1864 m_ListView->SetWatermark(Text); 1865 } 1866 1867 void 1868 CApplicationView::CheckAll() 1869 { 1870 m_ListView->CheckAll(); 1871 } 1872 1873 PVOID 1874 CApplicationView::GetFocusedItemData() 1875 { 1876 return m_ListView->GetFocusedItemData(); 1877 } 1878 1879 int 1880 CApplicationView::GetItemCount() 1881 { 1882 return m_ListView->GetItemCount(); 1883 } 1884 1885 VOID 1886 CApplicationView::AppendTabOrderWindow(int Direction, ATL::CSimpleArray<HWND> &TabOrderList) 1887 { 1888 m_Toolbar->AppendTabOrderWindow(Direction, TabOrderList); 1889 m_ComboBox->AppendTabOrderWindow(Direction, TabOrderList); 1890 m_SearchBar->AppendTabOrderWindow(Direction, TabOrderList); 1891 m_ListView->AppendTabOrderWindow(Direction, TabOrderList); 1892 m_AppsInfo->AppendTabOrderWindow(Direction, TabOrderList); 1893 } 1894 1895 // this function is called when a item of listview get focus. 1896 // CallbackParam is the param passed to listview when adding the item (the one getting focus now). 1897 VOID 1898 CApplicationView::ItemGetFocus(LPVOID CallbackParam) 1899 { 1900 if (CallbackParam) 1901 { 1902 CAppInfo *Info = static_cast<CAppInfo *>(CallbackParam); 1903 m_AppsInfo->ShowAppInfo(Info); 1904 1905 if (ApplicationViewType == AppViewTypeInstalledApps) 1906 { 1907 HMENU hMenu = ::GetMenu(m_hWnd); 1908 1909 BOOL CanModify = Info->CanModify(); 1910 1911 EnableMenuItem(hMenu, ID_MODIFY, CanModify ? MF_ENABLED : MF_GRAYED); 1912 m_Toolbar->SendMessageW(TB_ENABLEBUTTON, ID_MODIFY, CanModify); 1913 } 1914 } 1915 } 1916 1917 // this function is called when a item of listview is checked/unchecked 1918 // CallbackParam is the param passed to listview when adding the item (the one getting changed now). 1919 VOID 1920 CApplicationView::ItemCheckStateChanged(BOOL bChecked, LPVOID CallbackParam) 1921 { 1922 m_MainWindow->ItemCheckStateChanged(bChecked, CallbackParam); 1923 } 1924 // **** CApplicationView **** 1925