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