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