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