xref: /reactos/base/applications/rapps/gui.cpp (revision 8c2e9189)
1 /*
2  * PROJECT:     ReactOS Applications Manager
3  * LICENSE:     GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
4  * FILE:        base/applications/rapps/gui.cpp
5  * PURPOSE:     GUI classes for RAPPS
6  * COPYRIGHT:   Copyright 2015 David Quintana           (gigaherz@gmail.com)
7  *              Copyright 2017 Alexander Shaposhnikov   (sanchaez@reactos.org)
8  */
9 #include "rapps.h"
10 
11 #include "rapps.h"
12 #include "rosui.h"
13 #include "crichedit.h"
14 
15 #include <shlobj_undoc.h>
16 #include <shlguid_undoc.h>
17 
18 #include <atlbase.h>
19 #include <atlcom.h>
20 #include <atlwin.h>
21 #include <wininet.h>
22 #include <shellutils.h>
23 #include <rosctrls.h>
24 
25 #define SEARCH_TIMER_ID 'SR'
26 #define LISTVIEW_ICON_SIZE 24
27 #define TREEVIEW_ICON_SIZE 24
28 
29 HWND hListView = NULL;
30 
31 INT GetSystemColorDepth()
32 {
33     DEVMODEW pDevMode;
34     INT ColorDepth;
35 
36     pDevMode.dmSize = sizeof(pDevMode);
37     pDevMode.dmDriverExtra = 0;
38 
39     if (!EnumDisplaySettingsW(NULL, ENUM_CURRENT_SETTINGS, &pDevMode))
40     {
41         /* TODO: Error message */
42         return ILC_COLOR;
43     }
44 
45     switch (pDevMode.dmBitsPerPel)
46     {
47     case 32: ColorDepth = ILC_COLOR32; break;
48     case 24: ColorDepth = ILC_COLOR24; break;
49     case 16: ColorDepth = ILC_COLOR16; break;
50     case  8: ColorDepth = ILC_COLOR8;  break;
51     case  4: ColorDepth = ILC_COLOR4;  break;
52     default: ColorDepth = ILC_COLOR;   break;
53     }
54 
55     return ColorDepth;
56 }
57 
58 class CAvailableAppView
59 {
60     static inline VOID InsertTextAfterLoaded_RichEdit(UINT uStringID,
61                                                       const ATL::CStringW& szText,
62                                                       DWORD StringFlags,
63                                                       DWORD TextFlags)
64     {
65         ATL::CStringW szLoadedText;
66         if (!szText.IsEmpty() && szLoadedText.LoadStringW(uStringID))
67         {
68             InsertRichEditText(szLoadedText, StringFlags);
69             InsertRichEditText(szText, TextFlags);
70         }
71     }
72 
73     static inline VOID InsertLoadedTextNewl_RichEdit(UINT uStringID,
74                                                      DWORD StringFlags)
75     {
76         ATL::CStringW szLoadedText;
77         if (szLoadedText.LoadStringW(uStringID))
78         {
79             InsertRichEditText(L"\n", 0);
80             InsertRichEditText(szLoadedText, StringFlags);
81             InsertRichEditText(L"\n", 0);
82         }
83     }
84 
85     static VOID InsertVersionInfo_RichEdit(CAvailableApplicationInfo* Info)
86     {
87         if (Info->IsInstalled())
88         {
89             if (Info->HasInstalledVersion())
90             {
91                 if (Info->HasUpdate())
92                     InsertLoadedTextNewl_RichEdit(IDS_STATUS_UPDATE_AVAILABLE, CFE_ITALIC);
93                 else
94                     InsertLoadedTextNewl_RichEdit(IDS_STATUS_INSTALLED, CFE_ITALIC);
95 
96                 InsertTextAfterLoaded_RichEdit(IDS_AINFO_VERSION, Info->m_szInstalledVersion, CFE_BOLD, 0);
97             }
98             else
99             {
100                 InsertLoadedTextNewl_RichEdit(IDS_STATUS_INSTALLED, CFE_ITALIC);
101             }
102         }
103         else
104         {
105             InsertLoadedTextNewl_RichEdit(IDS_STATUS_NOTINSTALLED, CFE_ITALIC);
106         }
107 
108         InsertTextAfterLoaded_RichEdit(IDS_AINFO_AVAILABLEVERSION, Info->m_szVersion, CFE_BOLD, 0);
109     }
110 
111     static VOID InsertLicenseInfo_RichEdit(CAvailableApplicationInfo* Info)
112     {
113         ATL::CStringW szLicense;
114         switch (Info->m_LicenseType)
115         {
116         case LICENSE_OPENSOURCE:
117             szLicense.LoadStringW(IDS_LICENSE_OPENSOURCE);
118             break;
119         case LICENSE_FREEWARE:
120             szLicense.LoadStringW(IDS_LICENSE_FREEWARE);
121             break;
122         case LICENSE_TRIAL:
123             szLicense.LoadStringW(IDS_LICENSE_TRIAL);
124             break;
125         default:
126             InsertTextAfterLoaded_RichEdit(IDS_AINFO_LICENSE, Info->m_szLicense, CFE_BOLD, 0);
127             return;
128         }
129 
130         szLicense += L" (" + Info->m_szLicense + L")";
131         InsertTextAfterLoaded_RichEdit(IDS_AINFO_LICENSE, szLicense, CFE_BOLD, 0);
132     }
133 
134     static VOID InsertLanguageInfo_RichEdit(CAvailableApplicationInfo* Info)
135     {
136         if (!Info->HasLanguageInfo())
137         {
138             return;
139         }
140 
141         const INT nTranslations = Info->m_LanguageLCIDs.GetSize();
142         ATL::CStringW szLangInfo;
143         ATL::CStringW szLoadedTextAvailability;
144         ATL::CStringW szLoadedAInfoText;
145 
146         szLoadedAInfoText.LoadStringW(IDS_AINFO_LANGUAGES);
147 
148         if (Info->HasNativeLanguage())
149         {
150             szLoadedTextAvailability.LoadStringW(IDS_LANGUAGE_AVAILABLE_TRANSLATION);
151             if (nTranslations > 1)
152             {
153                 ATL::CStringW buf;
154                 buf.LoadStringW(IDS_LANGUAGE_MORE_PLACEHOLDER);
155                 szLangInfo.Format(buf, nTranslations - 1);
156             }
157             else
158             {
159                 szLangInfo.LoadStringW(IDS_LANGUAGE_SINGLE);
160                 szLangInfo = L" (" + szLangInfo + L")";
161             }
162         }
163         else if (Info->HasEnglishLanguage())
164         {
165             szLoadedTextAvailability.LoadStringW(IDS_LANGUAGE_ENGLISH_TRANSLATION);
166             if (nTranslations > 1)
167             {
168                 ATL::CStringW buf;
169                 buf.LoadStringW(IDS_LANGUAGE_AVAILABLE_PLACEHOLDER);
170                 szLangInfo.Format(buf, nTranslations - 1);
171             }
172             else
173             {
174                 szLangInfo.LoadStringW(IDS_LANGUAGE_SINGLE);
175                 szLangInfo = L" (" + szLangInfo + L")";
176             }
177         }
178         else
179         {
180             szLoadedTextAvailability.LoadStringW(IDS_LANGUAGE_NO_TRANSLATION);
181         }
182 
183         InsertRichEditText(szLoadedAInfoText, CFE_BOLD);
184         InsertRichEditText(szLoadedTextAvailability, NULL);
185         InsertRichEditText(szLangInfo, CFE_ITALIC);
186     }
187 
188 public:
189     static BOOL ShowAvailableAppInfo(INT Index)
190     {
191         CAvailableApplicationInfo* Info = (CAvailableApplicationInfo*) ListViewGetlParam(Index);
192         if (!Info) return FALSE;
193 
194         NewRichEditText(Info->m_szName, CFE_BOLD);
195         InsertVersionInfo_RichEdit(Info);
196         InsertLicenseInfo_RichEdit(Info);
197         InsertLanguageInfo_RichEdit(Info);
198 
199         InsertTextAfterLoaded_RichEdit(IDS_AINFO_SIZE, Info->m_szSize, CFE_BOLD, 0);
200         InsertTextAfterLoaded_RichEdit(IDS_AINFO_URLSITE, Info->m_szUrlSite, CFE_BOLD, CFE_LINK);
201         InsertTextAfterLoaded_RichEdit(IDS_AINFO_DESCRIPTION, Info->m_szDesc, CFE_BOLD, 0);
202         InsertTextAfterLoaded_RichEdit(IDS_AINFO_URLDOWNLOAD, Info->m_szUrlDownload, CFE_BOLD, CFE_LINK);
203 
204         return TRUE;
205     }
206 };
207 
208 class CMainToolbar :
209     public CUiWindow< CToolbar<> >
210 {
211     const INT m_iToolbarHeight;
212     DWORD m_dButtonsWidthMax;
213 
214     WCHAR szInstallBtn[MAX_STR_LEN];
215     WCHAR szUninstallBtn[MAX_STR_LEN];
216     WCHAR szModifyBtn[MAX_STR_LEN];
217     WCHAR szSelectAll[MAX_STR_LEN];
218 
219     VOID AddImageToImageList(HIMAGELIST hImageList, UINT ImageIndex)
220     {
221         HICON hImage;
222 
223         if (!(hImage = (HICON) LoadImageW(hInst,
224                                           MAKEINTRESOURCE(ImageIndex),
225                                           IMAGE_ICON,
226                                           m_iToolbarHeight,
227                                           m_iToolbarHeight,
228                                           0)))
229         {
230             /* TODO: Error message */
231         }
232 
233         ImageList_AddIcon(hImageList, hImage);
234         DeleteObject(hImage);
235     }
236 
237     HIMAGELIST InitImageList()
238     {
239         HIMAGELIST hImageList;
240 
241         /* Create the toolbar icon image list */
242         hImageList = ImageList_Create(m_iToolbarHeight,//GetSystemMetrics(SM_CXSMICON),
243                                       m_iToolbarHeight,//GetSystemMetrics(SM_CYSMICON),
244                                       ILC_MASK | GetSystemColorDepth(),
245                                       1, 1);
246         if (!hImageList)
247         {
248             /* TODO: Error message */
249             return NULL;
250         }
251 
252         AddImageToImageList(hImageList, IDI_INSTALL);
253         AddImageToImageList(hImageList, IDI_UNINSTALL);
254         AddImageToImageList(hImageList, IDI_MODIFY);
255         AddImageToImageList(hImageList, IDI_CHECK_ALL);
256         AddImageToImageList(hImageList, IDI_REFRESH);
257         AddImageToImageList(hImageList, IDI_UPDATE_DB);
258         AddImageToImageList(hImageList, IDI_SETTINGS);
259         AddImageToImageList(hImageList, IDI_EXIT);
260 
261         return hImageList;
262     }
263 
264 public:
265     CMainToolbar() : m_iToolbarHeight(24)
266     {
267     }
268 
269     VOID OnGetDispInfo(LPTOOLTIPTEXT lpttt)
270     {
271         UINT idButton = (UINT) lpttt->hdr.idFrom;
272 
273         switch (idButton)
274         {
275         case ID_EXIT:
276             lpttt->lpszText = MAKEINTRESOURCEW(IDS_TOOLTIP_EXIT);
277             break;
278 
279         case ID_INSTALL:
280             lpttt->lpszText = MAKEINTRESOURCEW(IDS_TOOLTIP_INSTALL);
281             break;
282 
283         case ID_UNINSTALL:
284             lpttt->lpszText = MAKEINTRESOURCEW(IDS_TOOLTIP_UNINSTALL);
285             break;
286 
287         case ID_MODIFY:
288             lpttt->lpszText = MAKEINTRESOURCEW(IDS_TOOLTIP_MODIFY);
289             break;
290 
291         case ID_SETTINGS:
292             lpttt->lpszText = MAKEINTRESOURCEW(IDS_TOOLTIP_SETTINGS);
293             break;
294 
295         case ID_REFRESH:
296             lpttt->lpszText = MAKEINTRESOURCEW(IDS_TOOLTIP_REFRESH);
297             break;
298 
299         case ID_RESETDB:
300             lpttt->lpszText = MAKEINTRESOURCEW(IDS_TOOLTIP_UPDATE_DB);
301             break;
302         }
303     }
304 
305     HWND Create(HWND hwndParent)
306     {
307         /* Create buttons */
308         TBBUTTON Buttons[] =
309         {   /* iBitmap, idCommand, fsState, fsStyle, bReserved[2], dwData, iString */
310             {  0, ID_INSTALL,   TBSTATE_ENABLED, BTNS_BUTTON | BTNS_AUTOSIZE, { 0 }, 0, (INT_PTR) szInstallBtn      },
311             {  1, ID_UNINSTALL, TBSTATE_ENABLED, BTNS_BUTTON | BTNS_AUTOSIZE, { 0 }, 0, (INT_PTR) szUninstallBtn    },
312             {  2, ID_MODIFY,    TBSTATE_ENABLED, BTNS_BUTTON | BTNS_AUTOSIZE, { 0 }, 0, (INT_PTR) szModifyBtn       },
313             {  3, ID_CHECK_ALL, TBSTATE_ENABLED, BTNS_BUTTON | BTNS_AUTOSIZE, { 0 }, 0, (INT_PTR) szSelectAll       },
314             { -1, 0,            TBSTATE_ENABLED, BTNS_SEP,                    { 0 }, 0, 0                           },
315             {  4, ID_REFRESH,   TBSTATE_ENABLED, BTNS_BUTTON | BTNS_AUTOSIZE, { 0 }, 0, 0                           },
316             {  5, ID_RESETDB,   TBSTATE_ENABLED, BTNS_BUTTON | BTNS_AUTOSIZE, { 0 }, 0, 0                           },
317             { -1, 0,            TBSTATE_ENABLED, BTNS_SEP,                    { 0 }, 0, 0                           },
318             {  6, ID_SETTINGS,  TBSTATE_ENABLED, BTNS_BUTTON | BTNS_AUTOSIZE, { 0 }, 0, 0                           },
319             {  7, ID_EXIT,      TBSTATE_ENABLED, BTNS_BUTTON | BTNS_AUTOSIZE, { 0 }, 0, 0                           },
320         };
321 
322         LoadStringW(hInst, IDS_INSTALL, szInstallBtn, _countof(szInstallBtn));
323         LoadStringW(hInst, IDS_UNINSTALL, szUninstallBtn, _countof(szUninstallBtn));
324         LoadStringW(hInst, IDS_MODIFY, szModifyBtn, _countof(szModifyBtn));
325         LoadStringW(hInst, IDS_SELECT_ALL, szSelectAll, _countof(szSelectAll));
326 
327         m_hWnd = CreateWindowExW(0, TOOLBARCLASSNAMEW, NULL,
328                                  WS_CHILD | WS_VISIBLE | TBSTYLE_FLAT | TBSTYLE_TOOLTIPS | TBSTYLE_LIST,
329                                  0, 0, 0, 0,
330                                  hwndParent,
331                                  0, hInst, NULL);
332 
333         if (!m_hWnd)
334         {
335             /* TODO: Show error message */
336             return FALSE;
337         }
338 
339         SendMessageW(TB_SETEXTENDEDSTYLE, 0, TBSTYLE_EX_HIDECLIPPEDBUTTONS);
340         SetButtonStructSize();
341 
342         /* Set image list */
343         HIMAGELIST hImageList = InitImageList();
344 
345         if (!hImageList)
346         {
347             /* TODO: Show error message */
348             return FALSE;
349         }
350 
351         ImageList_Destroy((HIMAGELIST) SetImageList(hImageList));
352 
353         AddButtons(_countof(Buttons), Buttons);
354 
355         /* Remember ideal width to use as a max width of buttons */
356         SIZE size;
357         GetIdealSize(FALSE, &size);
358         m_dButtonsWidthMax = size.cx;
359 
360         return m_hWnd;
361     }
362 
363     VOID HideButtonCaption()
364     {
365         DWORD dCurrentExStyle = (DWORD) SendMessageW(TB_GETEXTENDEDSTYLE, 0, 0);
366         SendMessageW(TB_SETEXTENDEDSTYLE, 0, dCurrentExStyle | TBSTYLE_EX_MIXEDBUTTONS);
367     }
368 
369     VOID ShowButtonCaption()
370     {
371         DWORD dCurrentExStyle = (DWORD) SendMessageW(TB_GETEXTENDEDSTYLE, 0, 0);
372         SendMessageW(TB_SETEXTENDEDSTYLE, 0, dCurrentExStyle & ~TBSTYLE_EX_MIXEDBUTTONS);
373     }
374 
375     DWORD GetMaxButtonsWidth() const
376     {
377         return m_dButtonsWidthMax;
378     }
379 };
380 
381 class CAppsListView :
382     public CUiWindow<CListView>
383 {
384     struct SortContext
385     {
386         CAppsListView * lvw;
387         INT iSubItem;
388     };
389 
390     BOOL bHasAllChecked;
391     BOOL bIsAscending;
392     BOOL bHasCheckboxes;
393 
394     INT nLastHeaderID;
395 
396 public:
397     CAppsListView() :
398         bHasAllChecked(FALSE),
399         bIsAscending(TRUE),
400         bHasCheckboxes(FALSE),
401         nLastHeaderID(-1)
402     {
403     }
404 
405     VOID SetCheckboxesVisible(BOOL bIsVisible)
406     {
407         if (bIsVisible)
408         {
409             SetExtendedListViewStyle(LVS_EX_CHECKBOXES | LVS_EX_FULLROWSELECT);
410         }
411         else
412         {
413             SetExtendedListViewStyle(LVS_EX_FULLROWSELECT);
414         }
415 
416         bHasCheckboxes = bIsVisible;
417     }
418 
419     VOID ColumnClick(LPNMLISTVIEW pnmv)
420     {
421         HWND hHeader;
422         HDITEMW hColumn;
423         INT nHeaderID = pnmv->iSubItem;
424 
425         if ((GetWindowLongPtr(GWL_STYLE) & ~LVS_NOSORTHEADER) == 0)
426             return;
427 
428         hHeader = (HWND) SendMessage(LVM_GETHEADER, 0, 0);
429         ZeroMemory(&hColumn, sizeof(hColumn));
430 
431         /* If the sorting column changed, remove the sorting style from the old column */
432         if ((nLastHeaderID != -1) && (nLastHeaderID != nHeaderID))
433         {
434             hColumn.mask = HDI_FORMAT;
435             Header_GetItem(hHeader, nLastHeaderID, &hColumn);
436             hColumn.fmt &= ~(HDF_SORTUP | HDF_SORTDOWN);
437             Header_SetItem(hHeader, nLastHeaderID, &hColumn);
438         }
439 
440         /* Set the sorting style to the new column */
441         hColumn.mask = HDI_FORMAT;
442         Header_GetItem(hHeader, nHeaderID, &hColumn);
443 
444         hColumn.fmt &= (bIsAscending ? ~HDF_SORTDOWN : ~HDF_SORTUP);
445         hColumn.fmt |= (bIsAscending ? HDF_SORTUP : HDF_SORTDOWN);
446         Header_SetItem(hHeader, nHeaderID, &hColumn);
447 
448         /* Sort the list, using the current values of nHeaderID and bIsAscending */
449         SortContext ctx = {this, nHeaderID};
450         SortItems(s_CompareFunc, &ctx);
451 
452         /* Save new values */
453         nLastHeaderID = nHeaderID;
454         bIsAscending = !bIsAscending;
455     }
456 
457     PVOID GetLParam(INT Index)
458     {
459         INT ItemIndex;
460         LVITEMW Item;
461 
462         if (Index == -1)
463         {
464             ItemIndex = (INT) SendMessage(LVM_GETNEXTITEM, -1, LVNI_FOCUSED);
465             if (ItemIndex == -1)
466                 return NULL;
467         }
468         else
469         {
470             ItemIndex = Index;
471         }
472 
473         ZeroMemory(&Item, sizeof(Item));
474 
475         Item.mask = LVIF_PARAM;
476         Item.iItem = ItemIndex;
477         if (!GetItem(&Item))
478             return NULL;
479 
480         return (PVOID) Item.lParam;
481     }
482 
483     BOOL AddColumn(INT Index, ATL::CStringW& Text, INT Width, INT Format)
484     {
485         return AddColumn(Index, const_cast<LPWSTR>(Text.GetString()), Width, Format);
486     }
487 
488     BOOL AddColumn(INT Index, LPWSTR lpText, INT Width, INT Format)
489     {
490         LVCOLUMNW Column;
491 
492         ZeroMemory(&Column, sizeof(Column));
493 
494         Column.mask = LVCF_FMT | LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM;
495         Column.iSubItem = Index;
496         Column.pszText = lpText;
497         Column.cx = Width;
498         Column.fmt = Format;
499 
500         return (InsertColumn(Index, &Column) == -1) ? FALSE : TRUE;
501     }
502 
503     INT AddItem(INT ItemIndex, INT IconIndex, LPWSTR lpText, LPARAM lParam)
504     {
505         LVITEMW Item;
506 
507         ZeroMemory(&Item, sizeof(Item));
508 
509         Item.mask = LVIF_TEXT | LVIF_PARAM | LVIF_STATE | LVIF_IMAGE;
510         Item.pszText = lpText;
511         Item.lParam = lParam;
512         Item.iItem = ItemIndex;
513         Item.iImage = IconIndex;
514 
515         return InsertItem(&Item);
516     }
517 
518     static INT CALLBACK s_CompareFunc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
519     {
520         SortContext * ctx = ((SortContext*) lParamSort);
521         return ctx->lvw->CompareFunc(lParam1, lParam2, ctx->iSubItem);
522     }
523 
524     INT CompareFunc(LPARAM lParam1, LPARAM lParam2, INT iSubItem)
525     {
526         ATL::CStringW Item1, Item2;
527         LVFINDINFOW IndexInfo;
528         INT Index;
529 
530         IndexInfo.flags = LVFI_PARAM;
531 
532         IndexInfo.lParam = lParam1;
533         Index = FindItem(-1, &IndexInfo);
534         GetItemText(Index, iSubItem, Item1.GetBuffer(MAX_STR_LEN), MAX_STR_LEN);
535         Item1.ReleaseBuffer();
536 
537         IndexInfo.lParam = lParam2;
538         Index = FindItem(-1, &IndexInfo);
539         GetItemText(Index, iSubItem, Item2.GetBuffer(MAX_STR_LEN), MAX_STR_LEN);
540         Item2.ReleaseBuffer();
541 
542         return bIsAscending ? Item1.Compare(Item2) : Item2.Compare(Item1);
543     }
544 
545     HWND Create(HWND hwndParent)
546     {
547         RECT r = {205, 28, 465, 250};
548         DWORD style = WS_CHILD | WS_VISIBLE | LVS_SORTASCENDING | LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS;
549         HMENU menu = GetSubMenu(LoadMenuW(hInst, MAKEINTRESOURCEW(IDR_APPLICATIONMENU)), 0);
550 
551         HWND hwnd = CListView::Create(hwndParent, r, NULL, style, WS_EX_CLIENTEDGE, menu);
552 
553         if (hwnd)
554         {
555             SetCheckboxesVisible(FALSE);
556         }
557 
558         return hwnd;
559     }
560 
561     BOOL GetCheckState(INT item)
562     {
563         return (BOOL) (GetItemState(item, LVIS_STATEIMAGEMASK) >> 12) - 1;
564     }
565 
566     VOID SetCheckState(INT item, BOOL fCheck)
567     {
568         if (bHasCheckboxes)
569         {
570             SetItemState(item, INDEXTOSTATEIMAGEMASK((fCheck) ? 2 : 1), LVIS_STATEIMAGEMASK);
571             SetSelected(item, fCheck);
572         }
573     }
574 
575     VOID SetSelected(INT item, BOOL value)
576     {
577         if (item < 0)
578         {
579             for (INT i = 0; i >= 0; i = GetNextItem(i, LVNI_ALL))
580             {
581                 CAvailableApplicationInfo* pAppInfo = (CAvailableApplicationInfo*) GetItemData(i);
582                 if (pAppInfo)
583                 {
584                     pAppInfo->m_IsSelected = value;
585                 }
586             }
587         }
588         else
589         {
590             CAvailableApplicationInfo* pAppInfo = (CAvailableApplicationInfo*) GetItemData(item);
591             if (pAppInfo)
592             {
593                 pAppInfo->m_IsSelected = value;
594             }
595         }
596     }
597 
598     VOID CheckAll()
599     {
600         if (bHasCheckboxes)
601         {
602             bHasAllChecked = !bHasAllChecked;
603             SetCheckState(-1, bHasAllChecked);
604         }
605     }
606 
607     ATL::CSimpleArray<CAvailableApplicationInfo> GetCheckedItems()
608     {
609         if (!bHasCheckboxes)
610         {
611             return ATL::CSimpleArray<CAvailableApplicationInfo>();
612         }
613 
614         ATL::CSimpleArray<CAvailableApplicationInfo> list;
615         for (INT i = 0; i >= 0; i = GetNextItem(i, LVNI_ALL))
616         {
617             if (GetCheckState(i) != FALSE)
618             {
619                 CAvailableApplicationInfo* pAppInfo = (CAvailableApplicationInfo*) GetItemData(i);
620                 list.Add(*pAppInfo);
621             }
622         }
623         return list;
624     }
625 
626     CAvailableApplicationInfo* GetSelectedData()
627     {
628         INT item = GetSelectionMark();
629         return (CAvailableApplicationInfo*) GetItemData(item);
630     }
631 };
632 
633 class CSideTreeView :
634     public CUiWindow<CTreeView>
635 {
636     HIMAGELIST hImageTreeView;
637 
638 public:
639     CSideTreeView() :
640         CUiWindow(),
641         hImageTreeView(ImageList_Create(TREEVIEW_ICON_SIZE, TREEVIEW_ICON_SIZE,
642                                         GetSystemColorDepth() | ILC_MASK,
643                                         0, 1))
644     {
645     }
646 
647     HTREEITEM AddItem(HTREEITEM hParent, ATL::CStringW &Text, INT Image, INT SelectedImage, LPARAM lParam)
648     {
649         return CUiWindow<CTreeView>::AddItem(hParent, const_cast<LPWSTR>(Text.GetString()), Image, SelectedImage, lParam);
650     }
651 
652     HTREEITEM AddCategory(HTREEITEM hRootItem, UINT TextIndex, UINT IconIndex)
653     {
654         ATL::CStringW szText;
655         INT Index;
656         HICON hIcon;
657 
658         hIcon = (HICON) LoadImageW(hInst,
659                                    MAKEINTRESOURCE(IconIndex),
660                                    IMAGE_ICON,
661                                    TREEVIEW_ICON_SIZE,
662                                    TREEVIEW_ICON_SIZE,
663                                    LR_CREATEDIBSECTION);
664         if (hIcon)
665         {
666             Index = ImageList_AddIcon(hImageTreeView, hIcon);
667             DestroyIcon(hIcon);
668         }
669 
670         szText.LoadStringW(TextIndex);
671         return AddItem(hRootItem, szText, Index, Index, TextIndex);
672     }
673 
674     HIMAGELIST SetImageList()
675     {
676         return CUiWindow<CTreeView>::SetImageList(hImageTreeView, TVSIL_NORMAL);
677     }
678 
679     VOID DestroyImageList()
680     {
681         if (hImageTreeView)
682             ImageList_Destroy(hImageTreeView);
683     }
684 
685     ~CSideTreeView()
686     {
687         DestroyImageList();
688     }
689 };
690 
691 class CSearchBar :
692     public CWindow
693 {
694 public:
695     const INT m_Width;
696     const INT m_Height;
697 
698     CSearchBar() : m_Width(200), m_Height(22)
699     {
700     }
701 
702     VOID SetText(LPCWSTR lpszText)
703     {
704         SendMessageW(SB_SETTEXT, SBT_NOBORDERS, (LPARAM) lpszText);
705     }
706 
707     HWND Create(HWND hwndParent)
708     {
709         ATL::CStringW szBuf;
710         m_hWnd = CreateWindowExW(WS_EX_CLIENTEDGE, L"Edit", NULL,
711                                  WS_CHILD | WS_VISIBLE | ES_LEFT | ES_AUTOHSCROLL,
712                                  0, 0, m_Width, m_Height,
713                                  hwndParent, (HMENU) NULL,
714                                  hInst, 0);
715 
716         SendMessageW(WM_SETFONT, (WPARAM) GetStockObject(DEFAULT_GUI_FONT), 0);
717         szBuf.LoadStringW(IDS_SEARCH_TEXT);
718         SetWindowTextW(szBuf);
719         return m_hWnd;
720     }
721 
722 };
723 
724 class CMainWindow :
725     public CWindowImpl<CMainWindow, CWindow, CFrameWinTraits>
726 {
727     CUiPanel* m_ClientPanel;
728     CUiSplitPanel* m_VSplitter;
729     CUiSplitPanel* m_HSplitter;
730 
731     CMainToolbar* m_Toolbar;
732     CAppsListView* m_ListView;
733 
734     CSideTreeView* m_TreeView;
735     CUiWindow<CStatusBar>* m_StatusBar;
736     CUiWindow<CRichEdit>* m_RichEdit;
737 
738     CUiWindow<CSearchBar>* m_SearchBar;
739     CAvailableApps m_AvailableApps;
740 
741     LPWSTR pLink;
742 
743     INT nSelectedApps;
744 
745     BOOL bSearchEnabled;
746     BOOL bUpdating;
747 
748 public:
749     CMainWindow() :
750         m_ClientPanel(NULL),
751         pLink(NULL),
752         bSearchEnabled(FALSE)
753     {
754     }
755 
756 private:
757     VOID InitApplicationsList()
758     {
759         ATL::CStringW szText;
760 
761         /* Add columns to ListView */
762         szText.LoadStringW(IDS_APP_NAME);
763         m_ListView->AddColumn(0, szText, 250, LVCFMT_LEFT);
764 
765         szText.LoadStringW(IDS_APP_INST_VERSION);
766         m_ListView->AddColumn(1, szText, 90, LVCFMT_RIGHT);
767 
768         szText.LoadStringW(IDS_APP_DESCRIPTION);
769         m_ListView->AddColumn(3, szText, 300, LVCFMT_LEFT);
770 
771         // Unnesesary since the list updates on every TreeView selection
772         // UpdateApplicationsList(ENUM_ALL_COMPONENTS);
773     }
774 
775     HTREEITEM AddCategory(HTREEITEM hRootItem, UINT TextIndex, UINT IconIndex)
776     {
777         return m_TreeView->AddCategory(hRootItem, TextIndex, IconIndex);
778     }
779 
780     VOID InitCategoriesList()
781     {
782         HTREEITEM hRootItemInstalled, hRootItemAvailable;
783 
784         hRootItemInstalled = AddCategory(TVI_ROOT, IDS_INSTALLED, IDI_CATEGORY);
785         AddCategory(hRootItemInstalled, IDS_APPLICATIONS, IDI_APPS);
786         AddCategory(hRootItemInstalled, IDS_UPDATES, IDI_APPUPD);
787 
788         AddCategory(TVI_ROOT, IDS_SELECTEDFORINST, IDI_SELECTEDFORINST);
789 
790         hRootItemAvailable = AddCategory(TVI_ROOT, IDS_AVAILABLEFORINST, IDI_CATEGORY);
791         AddCategory(hRootItemAvailable, IDS_CAT_AUDIO, IDI_CAT_AUDIO);
792         AddCategory(hRootItemAvailable, IDS_CAT_VIDEO, IDI_CAT_VIDEO);
793         AddCategory(hRootItemAvailable, IDS_CAT_GRAPHICS, IDI_CAT_GRAPHICS);
794         AddCategory(hRootItemAvailable, IDS_CAT_GAMES, IDI_CAT_GAMES);
795         AddCategory(hRootItemAvailable, IDS_CAT_INTERNET, IDI_CAT_INTERNET);
796         AddCategory(hRootItemAvailable, IDS_CAT_OFFICE, IDI_CAT_OFFICE);
797         AddCategory(hRootItemAvailable, IDS_CAT_DEVEL, IDI_CAT_DEVEL);
798         AddCategory(hRootItemAvailable, IDS_CAT_EDU, IDI_CAT_EDU);
799         AddCategory(hRootItemAvailable, IDS_CAT_ENGINEER, IDI_CAT_ENGINEER);
800         AddCategory(hRootItemAvailable, IDS_CAT_FINANCE, IDI_CAT_FINANCE);
801         AddCategory(hRootItemAvailable, IDS_CAT_SCIENCE, IDI_CAT_SCIENCE);
802         AddCategory(hRootItemAvailable, IDS_CAT_TOOLS, IDI_CAT_TOOLS);
803         AddCategory(hRootItemAvailable, IDS_CAT_DRIVERS, IDI_CAT_DRIVERS);
804         AddCategory(hRootItemAvailable, IDS_CAT_LIBS, IDI_CAT_LIBS);
805         AddCategory(hRootItemAvailable, IDS_CAT_OTHER, IDI_CAT_OTHER);
806 
807         m_TreeView->SetImageList();
808         m_TreeView->Expand(hRootItemInstalled, TVE_EXPAND);
809         m_TreeView->Expand(hRootItemAvailable, TVE_EXPAND);
810         m_TreeView->SelectItem(hRootItemAvailable);
811     }
812 
813     BOOL CreateStatusBar()
814     {
815         m_StatusBar = new CUiWindow<CStatusBar>();
816         m_StatusBar->m_VerticalAlignment = UiAlign_RightBtm;
817         m_StatusBar->m_HorizontalAlignment = UiAlign_Stretch;
818         m_ClientPanel->Children().Append(m_StatusBar);
819 
820         return m_StatusBar->Create(m_hWnd, (HMENU) IDC_STATUSBAR) != NULL;
821     }
822 
823     BOOL CreateToolbar()
824     {
825         m_Toolbar = new CMainToolbar();
826         m_Toolbar->m_VerticalAlignment = UiAlign_LeftTop;
827         m_Toolbar->m_HorizontalAlignment = UiAlign_Stretch;
828         m_ClientPanel->Children().Append(m_Toolbar);
829 
830         return m_Toolbar->Create(m_hWnd) != NULL;
831     }
832 
833     BOOL CreateTreeView()
834     {
835         m_TreeView = new CSideTreeView();
836         m_TreeView->m_VerticalAlignment = UiAlign_Stretch;
837         m_TreeView->m_HorizontalAlignment = UiAlign_Stretch;
838         m_VSplitter->First().Append(m_TreeView);
839 
840         return m_TreeView->Create(m_hWnd) != NULL;
841     }
842 
843     BOOL CreateListView()
844     {
845         m_ListView = new CAppsListView();
846         m_ListView->m_VerticalAlignment = UiAlign_Stretch;
847         m_ListView->m_HorizontalAlignment = UiAlign_Stretch;
848         m_HSplitter->First().Append(m_ListView);
849 
850         hListView = m_ListView->Create(m_hWnd);
851         return hListView != NULL;
852     }
853 
854     BOOL CreateRichEdit()
855     {
856         m_RichEdit = new CUiWindow<CRichEdit>();
857         m_RichEdit->m_VerticalAlignment = UiAlign_Stretch;
858         m_RichEdit->m_HorizontalAlignment = UiAlign_Stretch;
859         m_HSplitter->Second().Append(m_RichEdit);
860 
861         return m_RichEdit->Create(m_hWnd) != NULL;
862     }
863 
864     BOOL CreateVSplitter()
865     {
866         m_VSplitter = new CUiSplitPanel();
867         m_VSplitter->m_VerticalAlignment = UiAlign_Stretch;
868         m_VSplitter->m_HorizontalAlignment = UiAlign_Stretch;
869         m_VSplitter->m_DynamicFirst = FALSE;
870         m_VSplitter->m_Horizontal = FALSE;
871         m_VSplitter->m_MinFirst = 0;
872         m_VSplitter->m_MinSecond = 320;
873         m_VSplitter->m_Pos = 240;
874         m_ClientPanel->Children().Append(m_VSplitter);
875 
876         return m_VSplitter->Create(m_hWnd) != NULL;
877     }
878 
879     BOOL CreateHSplitter()
880     {
881         m_HSplitter = new CUiSplitPanel();
882         m_HSplitter->m_VerticalAlignment = UiAlign_Stretch;
883         m_HSplitter->m_HorizontalAlignment = UiAlign_Stretch;
884         m_HSplitter->m_DynamicFirst = TRUE;
885         m_HSplitter->m_Horizontal = TRUE;
886         m_HSplitter->m_Pos = INT_MAX; //set INT_MAX to use lowest possible position (m_MinSecond)
887         m_HSplitter->m_MinFirst = 10;
888         m_HSplitter->m_MinSecond = 140;
889         m_VSplitter->Second().Append(m_HSplitter);
890 
891         return m_HSplitter->Create(m_hWnd) != NULL;
892     }
893 
894     BOOL CreateSearchBar()
895     {
896         m_SearchBar = new CUiWindow<CSearchBar>();
897         m_SearchBar->m_VerticalAlignment = UiAlign_LeftTop;
898         m_SearchBar->m_HorizontalAlignment = UiAlign_RightBtm;
899         m_SearchBar->m_Margin.top = 4;
900         m_SearchBar->m_Margin.right = 6;
901 
902         return m_SearchBar->Create(m_Toolbar->m_hWnd) != NULL;
903     }
904 
905     BOOL CreateLayout()
906     {
907         BOOL b = TRUE;
908         bUpdating = TRUE;
909 
910         m_ClientPanel = new CUiPanel();
911         m_ClientPanel->m_VerticalAlignment = UiAlign_Stretch;
912         m_ClientPanel->m_HorizontalAlignment = UiAlign_Stretch;
913 
914         // Top level
915         b = b && CreateStatusBar();
916         b = b && CreateToolbar();
917         b = b && CreateSearchBar();
918         b = b && CreateVSplitter();
919 
920         // Inside V Splitter
921         b = b && CreateHSplitter();
922         b = b && CreateTreeView();
923 
924         // Inside H Splitter
925         b = b && CreateListView();
926         b = b && CreateRichEdit();
927 
928         if (b)
929         {
930             RECT rTop;
931             RECT rBottom;
932 
933             /* Size status bar */
934             m_StatusBar->SendMessageW(WM_SIZE, 0, 0);
935 
936             /* Size tool bar */
937             m_Toolbar->AutoSize();
938 
939             ::GetWindowRect(m_Toolbar->m_hWnd, &rTop);
940             ::GetWindowRect(m_StatusBar->m_hWnd, &rBottom);
941 
942             m_VSplitter->m_Margin.top = rTop.bottom - rTop.top;
943             m_VSplitter->m_Margin.bottom = rBottom.bottom - rBottom.top;
944         }
945 
946         bUpdating = FALSE;
947         return b;
948     }
949 
950     BOOL InitControls()
951     {
952         if (CreateLayout())
953         {
954 
955             InitApplicationsList();
956             InitCategoriesList();
957 
958             nSelectedApps = 0;
959             UpdateStatusBarText();
960 
961             return TRUE;
962         }
963 
964         return FALSE;
965     }
966 
967     VOID OnSize(HWND hwnd, WPARAM wParam, LPARAM lParam)
968     {
969         if (wParam == SIZE_MINIMIZED)
970             return;
971 
972         /* Size status bar */
973         m_StatusBar->SendMessage(WM_SIZE, 0, 0);
974 
975         /* Size tool bar */
976         m_Toolbar->AutoSize();
977 
978         /* Automatically hide captions */
979         DWORD dToolbarTreshold = m_Toolbar->GetMaxButtonsWidth();
980         DWORD dSearchbarMargin = (LOWORD(lParam) - m_SearchBar->m_Width);
981 
982         if (dSearchbarMargin > dToolbarTreshold)
983         {
984             m_Toolbar->ShowButtonCaption();
985         }
986         else if (dSearchbarMargin < dToolbarTreshold)
987         {
988             m_Toolbar->HideButtonCaption();
989         }
990 
991         RECT r = {0, 0, LOWORD(lParam), HIWORD(lParam)};
992         HDWP hdwp = NULL;
993         INT count = m_ClientPanel->CountSizableChildren();
994 
995         hdwp = BeginDeferWindowPos(count);
996         if (hdwp)
997         {
998             hdwp = m_ClientPanel->OnParentSize(r, hdwp);
999             if (hdwp)
1000             {
1001                 EndDeferWindowPos(hdwp);
1002             }
1003 
1004         }
1005 
1006         // TODO: Sub-layouts for children of children
1007         count = m_SearchBar->CountSizableChildren();
1008         hdwp = BeginDeferWindowPos(count);
1009         if (hdwp)
1010         {
1011             hdwp = m_SearchBar->OnParentSize(r, hdwp);
1012             if (hdwp)
1013             {
1014                 EndDeferWindowPos(hdwp);
1015             }
1016         }
1017 
1018     }
1019 
1020     BOOL ProcessWindowMessage(HWND hwnd, UINT Msg, WPARAM wParam, LPARAM lParam, LRESULT& theResult, DWORD dwMapId)
1021     {
1022         theResult = 0;
1023         switch (Msg)
1024         {
1025         case WM_CREATE:
1026             if (!InitControls())
1027                 ::PostMessageW(hwnd, WM_CLOSE, 0, 0);
1028             break;
1029 
1030         case WM_DESTROY:
1031         {
1032             ShowWindow(SW_HIDE);
1033             SaveSettings(hwnd);
1034 
1035             FreeLogs();
1036             m_AvailableApps.FreeCachedEntries();
1037 
1038             if (IsInstalledEnum(SelectedEnumType))
1039                 FreeInstalledAppList();
1040 
1041             delete m_ClientPanel;
1042 
1043             PostQuitMessage(0);
1044             return 0;
1045         }
1046 
1047         case WM_COMMAND:
1048             OnCommand(wParam, lParam);
1049             break;
1050 
1051         case WM_NOTIFY:
1052         {
1053             LPNMHDR data = (LPNMHDR) lParam;
1054 
1055             switch (data->code)
1056             {
1057             case TVN_SELCHANGED:
1058             {
1059                 if (data->hwndFrom == m_TreeView->m_hWnd)
1060                 {
1061                     switch (((LPNMTREEVIEW) lParam)->itemNew.lParam)
1062                     {
1063                     case IDS_INSTALLED:
1064                         UpdateApplicationsList(ENUM_ALL_INSTALLED);
1065                         break;
1066 
1067                     case IDS_APPLICATIONS:
1068                         UpdateApplicationsList(ENUM_INSTALLED_APPLICATIONS);
1069                         break;
1070 
1071                     case IDS_UPDATES:
1072                         UpdateApplicationsList(ENUM_UPDATES);
1073                         break;
1074 
1075                     case IDS_AVAILABLEFORINST:
1076                         UpdateApplicationsList(ENUM_ALL_AVAILABLE);
1077                         break;
1078 
1079                     case IDS_CAT_AUDIO:
1080                         UpdateApplicationsList(ENUM_CAT_AUDIO);
1081                         break;
1082 
1083                     case IDS_CAT_DEVEL:
1084                         UpdateApplicationsList(ENUM_CAT_DEVEL);
1085                         break;
1086 
1087                     case IDS_CAT_DRIVERS:
1088                         UpdateApplicationsList(ENUM_CAT_DRIVERS);
1089                         break;
1090 
1091                     case IDS_CAT_EDU:
1092                         UpdateApplicationsList(ENUM_CAT_EDU);
1093                         break;
1094 
1095                     case IDS_CAT_ENGINEER:
1096                         UpdateApplicationsList(ENUM_CAT_ENGINEER);
1097                         break;
1098 
1099                     case IDS_CAT_FINANCE:
1100                         UpdateApplicationsList(ENUM_CAT_FINANCE);
1101                         break;
1102 
1103                     case IDS_CAT_GAMES:
1104                         UpdateApplicationsList(ENUM_CAT_GAMES);
1105                         break;
1106 
1107                     case IDS_CAT_GRAPHICS:
1108                         UpdateApplicationsList(ENUM_CAT_GRAPHICS);
1109                         break;
1110 
1111                     case IDS_CAT_INTERNET:
1112                         UpdateApplicationsList(ENUM_CAT_INTERNET);
1113                         break;
1114 
1115                     case IDS_CAT_LIBS:
1116                         UpdateApplicationsList(ENUM_CAT_LIBS);
1117                         break;
1118 
1119                     case IDS_CAT_OFFICE:
1120                         UpdateApplicationsList(ENUM_CAT_OFFICE);
1121                         break;
1122 
1123                     case IDS_CAT_OTHER:
1124                         UpdateApplicationsList(ENUM_CAT_OTHER);
1125                         break;
1126 
1127                     case IDS_CAT_SCIENCE:
1128                         UpdateApplicationsList(ENUM_CAT_SCIENCE);
1129                         break;
1130 
1131                     case IDS_CAT_TOOLS:
1132                         UpdateApplicationsList(ENUM_CAT_TOOLS);
1133                         break;
1134 
1135                     case IDS_CAT_VIDEO:
1136                         UpdateApplicationsList(ENUM_CAT_VIDEO);
1137                         break;
1138 
1139                     case IDS_SELECTEDFORINST:
1140                         UpdateApplicationsList(ENUM_CAT_SELECTED);
1141                         break;
1142                     }
1143                 }
1144 
1145                 HMENU mainMenu = ::GetMenu(hwnd);
1146                 HMENU lvwMenu = ::GetMenu(m_ListView->m_hWnd);
1147 
1148                 /* Disable/enable items based on treeview selection */
1149                 if (IsSelectedNodeInstalled())
1150                 {
1151                     EnableMenuItem(mainMenu, ID_REGREMOVE, MF_ENABLED);
1152                     EnableMenuItem(mainMenu, ID_INSTALL, MF_GRAYED);
1153                     EnableMenuItem(mainMenu, ID_UNINSTALL, MF_ENABLED);
1154                     EnableMenuItem(mainMenu, ID_MODIFY, MF_ENABLED);
1155 
1156                     EnableMenuItem(lvwMenu, ID_REGREMOVE, MF_ENABLED);
1157                     EnableMenuItem(lvwMenu, ID_INSTALL, MF_GRAYED);
1158                     EnableMenuItem(lvwMenu, ID_UNINSTALL, MF_ENABLED);
1159                     EnableMenuItem(lvwMenu, ID_MODIFY, MF_ENABLED);
1160 
1161                     m_Toolbar->SendMessageW(TB_ENABLEBUTTON, ID_REGREMOVE, TRUE);
1162                     m_Toolbar->SendMessageW(TB_ENABLEBUTTON, ID_INSTALL, FALSE);
1163                     m_Toolbar->SendMessageW(TB_ENABLEBUTTON, ID_UNINSTALL, TRUE);
1164                     m_Toolbar->SendMessageW(TB_ENABLEBUTTON, ID_MODIFY, TRUE);
1165                 }
1166                 else
1167                 {
1168                     EnableMenuItem(mainMenu, ID_REGREMOVE, MF_GRAYED);
1169                     EnableMenuItem(mainMenu, ID_INSTALL, MF_ENABLED);
1170                     EnableMenuItem(mainMenu, ID_UNINSTALL, MF_GRAYED);
1171                     EnableMenuItem(mainMenu, ID_MODIFY, MF_GRAYED);
1172 
1173                     EnableMenuItem(lvwMenu, ID_REGREMOVE, MF_GRAYED);
1174                     EnableMenuItem(lvwMenu, ID_INSTALL, MF_ENABLED);
1175                     EnableMenuItem(lvwMenu, ID_UNINSTALL, MF_GRAYED);
1176                     EnableMenuItem(lvwMenu, ID_MODIFY, MF_GRAYED);
1177 
1178                     m_Toolbar->SendMessageW(TB_ENABLEBUTTON, ID_REGREMOVE, FALSE);
1179                     m_Toolbar->SendMessageW(TB_ENABLEBUTTON, ID_INSTALL, TRUE);
1180                     m_Toolbar->SendMessageW(TB_ENABLEBUTTON, ID_UNINSTALL, FALSE);
1181                     m_Toolbar->SendMessageW(TB_ENABLEBUTTON, ID_MODIFY, FALSE);
1182                 }
1183             }
1184             break;
1185 
1186             case LVN_ITEMCHANGED:
1187             {
1188                 LPNMLISTVIEW pnic = (LPNMLISTVIEW) lParam;
1189 
1190                 if (pnic->hdr.hwndFrom == m_ListView->m_hWnd)
1191                 {
1192                     /* Check if this is a valid item
1193                     * (technically, it can be also an unselect) */
1194                     INT ItemIndex = pnic->iItem;
1195                     if (ItemIndex == -1 ||
1196                         ItemIndex >= ListView_GetItemCount(pnic->hdr.hwndFrom))
1197                     {
1198                         break;
1199                     }
1200 
1201                     /* Check if the focus has been moved to another item */
1202                     if ((pnic->uChanged & LVIF_STATE) &&
1203                         (pnic->uNewState & LVIS_FOCUSED) &&
1204                         !(pnic->uOldState & LVIS_FOCUSED))
1205                     {
1206                         if (IsInstalledEnum(SelectedEnumType))
1207                             ShowInstalledAppInfo(ItemIndex);
1208                         if (IsAvailableEnum(SelectedEnumType))
1209                             CAvailableAppView::ShowAvailableAppInfo(ItemIndex);
1210                     }
1211                     /* Check if the item is checked */
1212                     if ((pnic->uNewState & LVIS_STATEIMAGEMASK) && !bUpdating)
1213                     {
1214                         BOOL checked = m_ListView->GetCheckState(pnic->iItem);
1215                         /* FIXME: HAX!
1216                         - preventing decremention below zero as a safeguard for ReactOS
1217                           In ReactOS this action is triggered whenever user changes *selection*, but should be only when *checkbox* state toggled
1218                           Maybe LVIS_STATEIMAGEMASK is set incorrectly
1219                         */
1220                         nSelectedApps +=
1221                             (checked)
1222                             ? 1
1223                             : ((nSelectedApps > 0)
1224                                ? -1
1225                                : 0);
1226 
1227                         /* Update item's selection status */
1228                         m_ListView->SetSelected(pnic->iItem, checked);
1229 
1230                         UpdateStatusBarText();
1231                     }
1232                 }
1233             }
1234             break;
1235 
1236             case LVN_COLUMNCLICK:
1237             {
1238                 LPNMLISTVIEW pnmv = (LPNMLISTVIEW) lParam;
1239 
1240                 m_ListView->ColumnClick(pnmv);
1241             }
1242             break;
1243 
1244             case NM_CLICK:
1245             {
1246                 if (data->hwndFrom == m_ListView->m_hWnd && ((LPNMLISTVIEW) lParam)->iItem != -1)
1247                 {
1248                     if (IsInstalledEnum(SelectedEnumType))
1249                         ShowInstalledAppInfo(-1);
1250                     if (IsAvailableEnum(SelectedEnumType))
1251                         CAvailableAppView::ShowAvailableAppInfo(-1);
1252                 }
1253             }
1254             break;
1255 
1256             case NM_DBLCLK:
1257             {
1258                 if (data->hwndFrom == m_ListView->m_hWnd && ((LPNMLISTVIEW) lParam)->iItem != -1)
1259                 {
1260                     /* this won't do anything if the program is already installed */
1261                     SendMessageW(hwnd, WM_COMMAND, ID_INSTALL, 0);
1262                 }
1263             }
1264             break;
1265 
1266             case NM_RCLICK:
1267             {
1268                 if (data->hwndFrom == m_ListView->m_hWnd && ((LPNMLISTVIEW) lParam)->iItem != -1)
1269                 {
1270                     ShowPopupMenu(m_ListView->m_hWnd, 0, ID_INSTALL);
1271                 }
1272             }
1273             break;
1274 
1275             case EN_LINK:
1276                 OnLink((ENLINK*) lParam);
1277                 break;
1278 
1279             case TTN_GETDISPINFO:
1280                 m_Toolbar->OnGetDispInfo((LPTOOLTIPTEXT) lParam);
1281                 break;
1282             }
1283         }
1284         break;
1285 
1286         case WM_SIZE:
1287             OnSize(hwnd, wParam, lParam);
1288             break;
1289 
1290         case WM_SIZING:
1291         {
1292             LPRECT pRect = (LPRECT) lParam;
1293 
1294             if (pRect->right - pRect->left < 565)
1295                 pRect->right = pRect->left + 565;
1296 
1297             if (pRect->bottom - pRect->top < 300)
1298                 pRect->bottom = pRect->top + 300;
1299 
1300             return TRUE;
1301         }
1302 
1303         case WM_SYSCOLORCHANGE:
1304         {
1305             /* Forward WM_SYSCOLORCHANGE to common controls */
1306             m_ListView->SendMessageW(WM_SYSCOLORCHANGE, 0, 0);
1307             m_TreeView->SendMessageW(WM_SYSCOLORCHANGE, 0, 0);
1308             m_Toolbar->SendMessageW(WM_SYSCOLORCHANGE, 0, 0);
1309             m_ListView->SendMessageW(EM_SETBKGNDCOLOR, 0, GetSysColor(COLOR_BTNFACE));
1310         }
1311         break;
1312 
1313         case WM_TIMER:
1314             if (wParam == SEARCH_TIMER_ID)
1315             {
1316                 ::KillTimer(hwnd, SEARCH_TIMER_ID);
1317                 if (bSearchEnabled)
1318                     UpdateApplicationsList(-1);
1319             }
1320             break;
1321         }
1322 
1323         return FALSE;
1324     }
1325 
1326     virtual VOID OnLink(ENLINK *Link)
1327     {
1328         switch (Link->msg)
1329         {
1330         case WM_LBUTTONUP:
1331         case WM_RBUTTONUP:
1332         {
1333             if (pLink) HeapFree(GetProcessHeap(), 0, pLink);
1334 
1335             pLink = (LPWSTR) HeapAlloc(GetProcessHeap(), 0,
1336                 (max(Link->chrg.cpMin, Link->chrg.cpMax) -
1337                  min(Link->chrg.cpMin, Link->chrg.cpMax) + 1) * sizeof(WCHAR));
1338             if (!pLink)
1339             {
1340                 /* TODO: Error message */
1341                 return;
1342             }
1343 
1344             m_RichEdit->SendMessageW(EM_SETSEL, Link->chrg.cpMin, Link->chrg.cpMax);
1345             m_RichEdit->SendMessageW(EM_GETSELTEXT, 0, (LPARAM) pLink);
1346 
1347             ShowPopupMenu(m_RichEdit->m_hWnd, IDR_LINKMENU, -1);
1348         }
1349         break;
1350         }
1351     }
1352 
1353     BOOL IsSelectedNodeInstalled()
1354     {
1355         HTREEITEM hSelectedItem = m_TreeView->GetSelection();
1356         TV_ITEM tItem;
1357 
1358         tItem.mask = TVIF_PARAM | TVIF_HANDLE;
1359         tItem.hItem = hSelectedItem;
1360         m_TreeView->GetItem(&tItem);
1361         switch (tItem.lParam)
1362         {
1363         case IDS_INSTALLED:
1364         case IDS_APPLICATIONS:
1365         case IDS_UPDATES:
1366             return TRUE;
1367         default:
1368             return FALSE;
1369         }
1370     }
1371 
1372     VOID OnCommand(WPARAM wParam, LPARAM lParam)
1373     {
1374         WORD wCommand = LOWORD(wParam);
1375 
1376         if (lParam == (LPARAM) m_SearchBar->m_hWnd)
1377         {
1378             ATL::CStringW szBuf;
1379 
1380             switch (HIWORD(wParam))
1381             {
1382             case EN_SETFOCUS:
1383             {
1384                 ATL::CStringW szWndText;
1385 
1386                 szBuf.LoadStringW(IDS_SEARCH_TEXT);
1387                 m_SearchBar->GetWindowTextW(szWndText);
1388                 if (szBuf == szWndText)
1389                 {
1390                     bSearchEnabled = FALSE;
1391                     m_SearchBar->SetWindowTextW(L"");
1392                 }
1393             }
1394             break;
1395 
1396             case EN_KILLFOCUS:
1397             {
1398                 m_SearchBar->GetWindowTextW(szBuf);
1399                 if (szBuf.IsEmpty())
1400                 {
1401                     szBuf.LoadStringW(IDS_SEARCH_TEXT);
1402                     bSearchEnabled = FALSE;
1403                     m_SearchBar->SetWindowTextW(szBuf.GetString());
1404                 }
1405             }
1406             break;
1407 
1408             case EN_CHANGE:
1409             {
1410                 ATL::CStringW szWndText;
1411 
1412                 if (!bSearchEnabled)
1413                 {
1414                     bSearchEnabled = TRUE;
1415                     break;
1416                 }
1417 
1418                 szBuf.LoadStringW(IDS_SEARCH_TEXT);
1419                 m_SearchBar->GetWindowTextW(szWndText);
1420                 if (szBuf == szWndText)
1421                 {
1422                     szSearchPattern.Empty();
1423                 }
1424                 else
1425                 {
1426                     szSearchPattern = szWndText;
1427                 }
1428 
1429                 DWORD dwDelay;
1430                 SystemParametersInfoW(SPI_GETMENUSHOWDELAY, 0, &dwDelay, 0);
1431                 SetTimer(SEARCH_TIMER_ID, dwDelay);
1432             }
1433             break;
1434             }
1435 
1436             return;
1437         }
1438 
1439         switch (wCommand)
1440         {
1441         case ID_OPEN_LINK:
1442             ShellExecuteW(m_hWnd, L"open", pLink, NULL, NULL, SW_SHOWNOACTIVATE);
1443             HeapFree(GetProcessHeap(), 0, pLink);
1444             break;
1445 
1446         case ID_COPY_LINK:
1447             CopyTextToClipboard(pLink);
1448             HeapFree(GetProcessHeap(), 0, pLink);
1449             break;
1450 
1451         case ID_SETTINGS:
1452             CreateSettingsDlg(m_hWnd);
1453             break;
1454 
1455         case ID_EXIT:
1456             PostMessageW(WM_CLOSE, 0, 0);
1457             break;
1458 
1459         case ID_INSTALL:
1460             if (IsAvailableEnum(SelectedEnumType))
1461             {
1462                 if (nSelectedApps > 0)
1463                 {
1464                     CDownloadManager::DownloadListOfApplications(m_AvailableApps.GetSelected());
1465                     UpdateApplicationsList(-1);
1466                 }
1467                 else if (CDownloadManager::DownloadApplication(m_ListView->GetSelectedData()))
1468                 {
1469                     UpdateApplicationsList(-1);
1470                 }
1471 
1472             }
1473             break;
1474 
1475         case ID_UNINSTALL:
1476             if (UninstallApplication(-1, FALSE))
1477                 UpdateApplicationsList(-1);
1478             break;
1479 
1480         case ID_MODIFY:
1481             if (UninstallApplication(-1, TRUE))
1482                 UpdateApplicationsList(-1);
1483             break;
1484 
1485         case ID_REGREMOVE:
1486             RemoveAppFromRegistry(-1);
1487             break;
1488 
1489         case ID_REFRESH:
1490             UpdateApplicationsList(-1);
1491             break;
1492 
1493         case ID_RESETDB:
1494             CAvailableApps::ForceUpdateAppsDB();
1495             UpdateApplicationsList(-1);
1496             break;
1497 
1498         case ID_HELP:
1499             MessageBoxW(L"Help not implemented yet", NULL, MB_OK);
1500             break;
1501 
1502         case ID_ABOUT:
1503             ShowAboutDialog();
1504             break;
1505 
1506         case ID_CHECK_ALL:
1507             m_ListView->CheckAll();
1508             break;
1509         }
1510     }
1511 
1512     VOID FreeInstalledAppList()
1513     {
1514         INT Count = m_ListView->GetItemCount() - 1;
1515         PINSTALLED_INFO Info;
1516 
1517         while (Count >= 0)
1518         {
1519             Info = (PINSTALLED_INFO) ListViewGetlParam(Count);
1520             if (Info)
1521             {
1522                 RegCloseKey(Info->hSubKey);
1523                 delete Info;
1524             }
1525             Count--;
1526         }
1527     }
1528 
1529     static BOOL SearchPatternMatch(LPCWSTR szHaystack, LPCWSTR szNeedle)
1530     {
1531         if (!*szNeedle)
1532             return TRUE;
1533         /* TODO: Improve pattern search beyond a simple case-insensitive substring search. */
1534         return StrStrIW(szHaystack, szNeedle) != NULL;
1535     }
1536 
1537     static BOOL CALLBACK s_EnumInstalledAppProc(INT ItemIndex, ATL::CStringW &m_szName, PINSTALLED_INFO Info)
1538     {
1539         PINSTALLED_INFO ItemInfo;
1540         ATL::CStringW szText;
1541         INT Index;
1542 
1543         if (!SearchPatternMatch(m_szName.GetString(), szSearchPattern))
1544         {
1545             RegCloseKey(Info->hSubKey);
1546             return TRUE;
1547         }
1548 
1549         ItemInfo = new INSTALLED_INFO(*Info);
1550         if (!ItemInfo)
1551         {
1552             RegCloseKey(Info->hSubKey);
1553             return FALSE;
1554         }
1555 
1556         Index = ListViewAddItem(ItemIndex, 0, m_szName, (LPARAM) ItemInfo);
1557 
1558         /* Get version info */
1559         GetApplicationString(ItemInfo->hSubKey, L"DisplayVersion", szText);
1560         ListView_SetItemText(hListView, Index, 1, const_cast<LPWSTR>(szText.GetString()));
1561 
1562         /* Get comments */
1563         GetApplicationString(ItemInfo->hSubKey, L"Comments", szText);
1564         ListView_SetItemText(hListView, Index, 2, const_cast<LPWSTR>(szText.GetString()));
1565 
1566         return TRUE;
1567     }
1568 
1569     static BOOL CALLBACK s_EnumAvailableAppProc(CAvailableApplicationInfo* Info, LPCWSTR szFolderPath)
1570     {
1571         INT Index;
1572         HICON hIcon = NULL;
1573 
1574         HIMAGELIST hImageListView = ListView_GetImageList(hListView, LVSIL_SMALL);
1575 
1576         if (!SearchPatternMatch(Info->m_szName.GetString(), szSearchPattern) &&
1577             !SearchPatternMatch(Info->m_szDesc.GetString(), szSearchPattern))
1578         {
1579             return TRUE;
1580         }
1581 
1582         /* Load icon from file */
1583         ATL::CStringW szIconPath;
1584         szIconPath.Format(L"%lsicons\\%ls.ico", szFolderPath, Info->m_szName.GetString());
1585         hIcon = (HICON) LoadImageW(NULL,
1586                                    szIconPath.GetString(),
1587                                    IMAGE_ICON,
1588                                    LISTVIEW_ICON_SIZE,
1589                                    LISTVIEW_ICON_SIZE,
1590                                    LR_LOADFROMFILE);
1591 
1592         if (!hIcon || GetLastError() != ERROR_SUCCESS)
1593         {
1594             /* Load default icon */
1595             hIcon = (HICON) LoadIconW(hInst, MAKEINTRESOURCEW(IDI_MAIN));
1596         }
1597 
1598         Index = ImageList_AddIcon(hImageListView, hIcon);
1599         DestroyIcon(hIcon);
1600 
1601         Index = ListViewAddItem(Info->m_Category, Index, Info->m_szName.GetString(), (LPARAM) Info);
1602         ListView_SetImageList(hListView, hImageListView, LVSIL_SMALL);
1603 
1604         ListView_SetItemText(hListView, Index, 1, const_cast<LPWSTR>(Info->m_szVersion.GetString()));
1605         ListView_SetItemText(hListView, Index, 2, const_cast<LPWSTR>(Info->m_szDesc.GetString()));
1606         ListView_SetCheckState(hListView, Index, Info->m_IsSelected);
1607 
1608         return TRUE;
1609     }
1610 
1611     VOID UpdateStatusBarText()
1612     {
1613         if (m_StatusBar)
1614         {
1615             ATL::CStringW szBuffer;
1616 
1617             szBuffer.Format(IDS_APPS_COUNT, m_ListView->GetItemCount(), nSelectedApps);
1618             m_StatusBar->SetText(szBuffer);
1619         }
1620     }
1621 
1622     VOID UpdateApplicationsList(INT EnumType)
1623     {
1624         ATL::CStringW szBuffer1, szBuffer2;
1625         HIMAGELIST hImageListView;
1626         BOOL bWasInInstalled = IsInstalledEnum(SelectedEnumType);
1627 
1628         bUpdating = TRUE;
1629         m_ListView->SetRedraw(FALSE);
1630 
1631         if (EnumType < 0)
1632         {
1633             EnumType = SelectedEnumType;
1634         }
1635 
1636         //if previous one was INSTALLED purge the list
1637         //TODO: make the Installed category a separate class to avoid doing this
1638         if (bWasInInstalled)
1639         {
1640             FreeInstalledAppList();
1641         }
1642 
1643         m_ListView->DeleteAllItems();
1644 
1645         // Create new ImageList
1646         hImageListView = ImageList_Create(LISTVIEW_ICON_SIZE,
1647                                           LISTVIEW_ICON_SIZE,
1648                                           GetSystemColorDepth() | ILC_MASK,
1649                                           0, 1);
1650         HIMAGELIST hImageListBuf = m_ListView->SetImageList(hImageListView, LVSIL_SMALL);
1651         if (hImageListBuf)
1652         {
1653             ImageList_Destroy(hImageListBuf);
1654         }
1655 
1656         if (IsInstalledEnum(EnumType))
1657         {
1658             if (!bWasInInstalled)
1659             {
1660                 m_ListView->SetCheckboxesVisible(FALSE);
1661             }
1662 
1663             HICON hIcon = (HICON) LoadIconW(hInst, MAKEINTRESOURCEW(IDI_MAIN));
1664             ImageList_AddIcon(hImageListView, hIcon);
1665             DestroyIcon(hIcon);
1666 
1667             // Enum installed applications and updates
1668             EnumInstalledApplications(EnumType, TRUE, s_EnumInstalledAppProc);
1669             EnumInstalledApplications(EnumType, FALSE, s_EnumInstalledAppProc);
1670         }
1671         else if (IsAvailableEnum(EnumType))
1672         {
1673             if (bWasInInstalled)
1674             {
1675                 m_ListView->SetCheckboxesVisible(TRUE);
1676             }
1677 
1678             // Enum available applications
1679             m_AvailableApps.Enum(EnumType, s_EnumAvailableAppProc);
1680         }
1681 
1682         SelectedEnumType = EnumType;
1683         UpdateStatusBarText();
1684         SetWelcomeText();
1685 
1686         // Set automatic column width for program names if the list is not empty
1687         if (m_ListView->GetItemCount() > 0)
1688         {
1689             ListView_SetColumnWidth(m_ListView->GetWindow(), 0, LVSCW_AUTOSIZE);
1690         }
1691 
1692         bUpdating = FALSE;
1693         m_ListView->SetRedraw(TRUE);
1694     }
1695 
1696 public:
1697     static ATL::CWndClassInfo& GetWndClassInfo()
1698     {
1699         DWORD csStyle = CS_VREDRAW | CS_HREDRAW;
1700         static ATL::CWndClassInfo wc =
1701         {
1702             {
1703                 sizeof(WNDCLASSEX),
1704                 csStyle,
1705                 StartWindowProc,
1706                 0,
1707                 0,
1708                 NULL,
1709                 LoadIconW(_AtlBaseModule.GetModuleInstance(), MAKEINTRESOURCEW(IDI_MAIN)),
1710                 LoadCursorW(NULL, IDC_ARROW),
1711                 (HBRUSH) (COLOR_BTNFACE + 1),
1712                 MAKEINTRESOURCEW(IDR_MAINMENU),
1713                 L"RAppsWnd",
1714                 NULL
1715             },
1716             NULL, NULL, IDC_ARROW, TRUE, 0, _T("")
1717         };
1718         return wc;
1719     }
1720 
1721     HWND Create()
1722     {
1723         ATL::CStringW szWindowName;
1724         szWindowName.LoadStringW(IDS_APPTITLE);
1725 
1726         RECT r = {
1727             (SettingsInfo.bSaveWndPos ? SettingsInfo.Left : CW_USEDEFAULT),
1728             (SettingsInfo.bSaveWndPos ? SettingsInfo.Top : CW_USEDEFAULT),
1729             (SettingsInfo.bSaveWndPos ? SettingsInfo.Width : 680),
1730             (SettingsInfo.bSaveWndPos ? SettingsInfo.Height : 450)
1731         };
1732         r.right += r.left;
1733         r.bottom += r.top;
1734 
1735         return CWindowImpl::Create(NULL, r, szWindowName.GetString(), WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, WS_EX_WINDOWEDGE);
1736     }
1737 
1738     CStatusBar * GetStatusBar()
1739     {
1740         return m_StatusBar;
1741     }
1742 
1743     CAppsListView * GetListView()
1744     {
1745         return m_ListView;
1746     }
1747 
1748     CRichEdit * GetRichEdit()
1749     {
1750         return m_RichEdit;
1751     }
1752 
1753     CAvailableApps * GetAvailableApps()
1754     {
1755         return &m_AvailableApps;
1756     }
1757 };
1758 
1759 // global interface
1760 CMainWindow * g_MainWindow;
1761 
1762 HWND CreateMainWindow()
1763 {
1764     g_MainWindow = new CMainWindow();
1765     return g_MainWindow->Create();
1766 }
1767 
1768 DWORD_PTR ListViewGetlParam(INT item)
1769 {
1770     if (item < 0)
1771     {
1772         item = g_MainWindow->GetListView()->GetSelectionMark();
1773     }
1774     return g_MainWindow->GetListView()->GetItemData(item);
1775 }
1776 
1777 VOID SetStatusBarText(LPCWSTR szText)
1778 {
1779     g_MainWindow->GetStatusBar()->SetText(szText);
1780 }
1781 
1782 INT ListViewAddItem(INT ItemIndex, INT IconIndex, LPWSTR lpName, LPARAM lParam)
1783 {
1784     return g_MainWindow->GetListView()->AddItem(ItemIndex, IconIndex, lpName, lParam);
1785 }
1786 
1787 VOID NewRichEditText(LPCWSTR szText, DWORD flags)
1788 {
1789     g_MainWindow->GetRichEdit()->SetText(szText, flags);
1790 }
1791 
1792 VOID InsertRichEditText(LPCWSTR szText, DWORD flags)
1793 {
1794     g_MainWindow->GetRichEdit()->InsertText(szText, flags);
1795 }
1796 
1797 CAvailableApps* GetAvailableApps()
1798 {
1799     return g_MainWindow->GetAvailableApps();
1800 }
1801 
1802 // ATL version of functions above
1803 VOID SetStatusBarText(const ATL::CStringW& szText)
1804 {
1805     SetStatusBarText(szText.GetString());
1806 }
1807 
1808 INT ListViewAddItem(INT ItemIndex, INT IconIndex, const ATL::CStringW& Name, LPARAM lParam)
1809 {
1810     return ListViewAddItem(ItemIndex, IconIndex, const_cast<LPWSTR>(Name.GetString()), lParam);
1811 }
1812 
1813 VOID NewRichEditText(const ATL::CStringW& szText, DWORD flags)
1814 {
1815     NewRichEditText(szText.GetString(), flags);
1816 }
1817 
1818 VOID InsertRichEditText(const ATL::CStringW& szText, DWORD flags)
1819 {
1820     InsertRichEditText(szText.GetString(), flags);
1821 }
1822