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