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