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