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