xref: /reactos/base/applications/rapps/appview.cpp (revision 77e6348f)
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