[RAPPS] CMainWindow: use more atl
[reactos.git] / base / applications / rapps / gui.cpp
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 OnCommand(WPARAM wParam, LPARAM lParam)
1473 {
1474 WORD wCommand = LOWORD(wParam);
1475
1476 if (lParam == (LPARAM) m_SearchBar->m_hWnd)
1477 {
1478 ATL::CStringW szBuf;
1479
1480 switch (HIWORD(wParam))
1481 {
1482 case EN_SETFOCUS:
1483 {
1484 ATL::CStringW szWndText;
1485
1486 szBuf.LoadStringW(IDS_SEARCH_TEXT);
1487 m_SearchBar->GetWindowTextW(szWndText);
1488 if (szBuf == szWndText)
1489 {
1490 bSearchEnabled = FALSE;
1491 m_SearchBar->SetWindowTextW(L"");
1492 }
1493 }
1494 break;
1495
1496 case EN_KILLFOCUS:
1497 {
1498 m_SearchBar->GetWindowTextW(szBuf);
1499 if (szBuf.IsEmpty())
1500 {
1501 szBuf.LoadStringW(IDS_SEARCH_TEXT);
1502 bSearchEnabled = FALSE;
1503 m_SearchBar->SetWindowTextW(szBuf.GetString());
1504 }
1505 }
1506 break;
1507
1508 case EN_CHANGE:
1509 {
1510 ATL::CStringW szWndText;
1511
1512 if (!bSearchEnabled)
1513 {
1514 bSearchEnabled = TRUE;
1515 break;
1516 }
1517
1518 szBuf.LoadStringW(IDS_SEARCH_TEXT);
1519 m_SearchBar->GetWindowTextW(szWndText);
1520 if (szBuf == szWndText)
1521 {
1522 szSearchPattern.Empty();
1523 }
1524 else
1525 {
1526 szSearchPattern = szWndText;
1527 }
1528
1529 DWORD dwDelay;
1530 SystemParametersInfoW(SPI_GETMENUSHOWDELAY, 0, &dwDelay, 0);
1531 SetTimer(SEARCH_TIMER_ID, dwDelay);
1532 }
1533 break;
1534 }
1535
1536 return;
1537 }
1538
1539 switch (wCommand)
1540 {
1541 case ID_OPEN_LINK:
1542 ShellExecuteW(m_hWnd, L"open", pLink, NULL, NULL, SW_SHOWNOACTIVATE);
1543 HeapFree(GetProcessHeap(), 0, pLink);
1544 break;
1545
1546 case ID_COPY_LINK:
1547 CopyTextToClipboard(pLink);
1548 HeapFree(GetProcessHeap(), 0, pLink);
1549 break;
1550
1551 case ID_SETTINGS:
1552 CreateSettingsDlg(m_hWnd);
1553 break;
1554
1555 case ID_EXIT:
1556 PostMessageW(WM_CLOSE, 0, 0);
1557 break;
1558
1559 case ID_SEARCH:
1560 m_SearchBar->SetFocus();
1561 break;
1562
1563 case ID_INSTALL:
1564 if (IsAvailableEnum(SelectedEnumType))
1565 {
1566 if (nSelectedApps > 0)
1567 {
1568 DownloadListOfApplications(m_AvailableApps.GetSelected(), FALSE);
1569 UpdateApplicationsList(-1);
1570 m_ListView->SetSelected(-1, FALSE);
1571 }
1572 else if (DownloadApplication(m_ListView->GetSelectedData(), FALSE))
1573 {
1574 UpdateApplicationsList(-1);
1575 }
1576
1577 }
1578 break;
1579
1580 case ID_UNINSTALL:
1581 if (UninstallSelectedApp(FALSE))
1582 UpdateApplicationsList(-1);
1583 break;
1584
1585 case ID_MODIFY:
1586 if (UninstallSelectedApp(TRUE))
1587 UpdateApplicationsList(-1);
1588 break;
1589
1590 case ID_REGREMOVE:
1591 RemoveSelectedAppFromRegistry();
1592 break;
1593
1594 case ID_REFRESH:
1595 UpdateApplicationsList(-1);
1596 break;
1597
1598 case ID_RESETDB:
1599 CAvailableApps::ForceUpdateAppsDB();
1600 UpdateApplicationsList(-1);
1601 break;
1602
1603 case ID_HELP:
1604 MessageBoxW(L"Help not implemented yet", NULL, MB_OK);
1605 break;
1606
1607 case ID_ABOUT:
1608 ShowAboutDialog();
1609 break;
1610
1611 case ID_CHECK_ALL:
1612 m_ListView->CheckAll();
1613 break;
1614 }
1615 }
1616
1617 VOID FreeInstalledAppList()
1618 {
1619 INT Count = m_ListView->GetItemCount() - 1;
1620 PINSTALLED_INFO Info;
1621
1622 while (Count >= 0)
1623 {
1624 Info = (PINSTALLED_INFO) m_ListView->GetItemData(Count);
1625 if (Info)
1626 {
1627 RegCloseKey(Info->hSubKey);
1628 delete Info;
1629 }
1630 Count--;
1631 }
1632 }
1633
1634 static BOOL SearchPatternMatch(LPCWSTR szHaystack, LPCWSTR szNeedle)
1635 {
1636 if (!*szNeedle)
1637 return TRUE;
1638 /* TODO: Improve pattern search beyond a simple case-insensitive substring search. */
1639 return StrStrIW(szHaystack, szNeedle) != NULL;
1640 }
1641
1642 BOOL CALLBACK EnumInstalledAppProc(INT ItemIndex, ATL::CStringW &m_szName, PINSTALLED_INFO Info)
1643 {
1644 PINSTALLED_INFO ItemInfo;
1645 ATL::CStringW szText;
1646 INT Index;
1647
1648 if (!SearchPatternMatch(m_szName.GetString(), szSearchPattern))
1649 {
1650 RegCloseKey(Info->hSubKey);
1651 return TRUE;
1652 }
1653
1654 ItemInfo = new INSTALLED_INFO(*Info);
1655 if (!ItemInfo)
1656 {
1657 RegCloseKey(Info->hSubKey);
1658 return FALSE;
1659 }
1660
1661 Index = m_ListView->AddItem(ItemIndex, 0, m_szName.GetString(), (LPARAM) ItemInfo);
1662
1663 /* Get version info */
1664 ItemInfo->GetApplicationString(L"DisplayVersion", szText);
1665 m_ListView->SetItemText(Index, 1, szText.GetString());
1666
1667 /* Get comments */
1668 ItemInfo->GetApplicationString(L"Comments", szText);
1669 m_ListView->SetItemText(Index, 2, szText.GetString());
1670
1671 return TRUE;
1672 }
1673
1674 BOOL EnumAvailableAppProc(CAvailableApplicationInfo* Info, LPCWSTR szFolderPath)
1675 {
1676 INT Index;
1677 HICON hIcon = NULL;
1678
1679 HIMAGELIST hImageListView = (HIMAGELIST)m_ListView->SendMessage(LVM_GETIMAGELIST, LVSIL_SMALL, 0);
1680
1681 if (!SearchPatternMatch(Info->m_szName.GetString(), szSearchPattern) &&
1682 !SearchPatternMatch(Info->m_szDesc.GetString(), szSearchPattern))
1683 {
1684 return TRUE;
1685 }
1686
1687 /* Load icon from file */
1688 ATL::CStringW szIconPath;
1689 szIconPath.Format(L"%lsicons\\%ls.ico", szFolderPath, Info->m_szName.GetString());
1690 hIcon = (HICON) LoadImageW(NULL,
1691 szIconPath.GetString(),
1692 IMAGE_ICON,
1693 LISTVIEW_ICON_SIZE,
1694 LISTVIEW_ICON_SIZE,
1695 LR_LOADFROMFILE);
1696
1697 if (!hIcon || GetLastError() != ERROR_SUCCESS)
1698 {
1699 /* Load default icon */
1700 hIcon = (HICON) LoadIconW(hInst, MAKEINTRESOURCEW(IDI_MAIN));
1701 }
1702
1703 Index = ImageList_AddIcon(hImageListView, hIcon);
1704 DestroyIcon(hIcon);
1705
1706 Index = m_ListView->AddItem(Info->m_Category, Index, Info->m_szName.GetString(), (LPARAM) Info);
1707 m_ListView->SetImageList(hImageListView, LVSIL_SMALL);
1708 m_ListView->SetItemText(Index, 1, Info->m_szVersion.GetString());
1709 m_ListView->SetItemText(Index, 2, Info->m_szDesc.GetString());
1710 m_ListView->SetCheckState(Index, Info->m_IsSelected);
1711
1712 return TRUE;
1713 }
1714
1715 static BOOL CALLBACK s_EnumInstalledAppProc(INT ItemIndex, ATL::CStringW &m_szName, PINSTALLED_INFO Info, PVOID param)
1716 {
1717 CMainWindow* pThis = (CMainWindow*)param;
1718 return pThis->EnumInstalledAppProc(ItemIndex, m_szName, Info);
1719 }
1720
1721 static BOOL CALLBACK s_EnumAvailableAppProc(CAvailableApplicationInfo* Info, LPCWSTR szFolderPath, PVOID param)
1722 {
1723 CMainWindow* pThis = (CMainWindow*)param;
1724 return pThis->EnumAvailableAppProc(Info, szFolderPath);
1725 }
1726
1727 VOID UpdateStatusBarText()
1728 {
1729 if (m_StatusBar)
1730 {
1731 ATL::CStringW szBuffer;
1732
1733 szBuffer.Format(IDS_APPS_COUNT, m_ListView->GetItemCount(), nSelectedApps);
1734 m_StatusBar->SetText(szBuffer);
1735 }
1736 }
1737
1738 VOID UpdateApplicationsList(INT EnumType)
1739 {
1740 ATL::CStringW szBuffer1, szBuffer2;
1741 HIMAGELIST hImageListView;
1742 BOOL bWasInInstalled = IsInstalledEnum(SelectedEnumType);
1743
1744 bUpdating = TRUE;
1745 m_ListView->SetRedraw(FALSE);
1746
1747 if (EnumType < 0)
1748 {
1749 EnumType = SelectedEnumType;
1750 }
1751
1752 //if previous one was INSTALLED purge the list
1753 //TODO: make the Installed category a separate class to avoid doing this
1754 if (bWasInInstalled)
1755 {
1756 FreeInstalledAppList();
1757 }
1758
1759 m_ListView->DeleteAllItems();
1760
1761 // Create new ImageList
1762 hImageListView = ImageList_Create(LISTVIEW_ICON_SIZE,
1763 LISTVIEW_ICON_SIZE,
1764 GetSystemColorDepth() | ILC_MASK,
1765 0, 1);
1766 HIMAGELIST hImageListBuf = m_ListView->SetImageList(hImageListView, LVSIL_SMALL);
1767 if (hImageListBuf)
1768 {
1769 ImageList_Destroy(hImageListBuf);
1770 }
1771
1772 if (IsInstalledEnum(EnumType))
1773 {
1774 if (!bWasInInstalled)
1775 {
1776 m_ListView->SetCheckboxesVisible(FALSE);
1777 }
1778
1779 HICON hIcon = (HICON) LoadIconW(hInst, MAKEINTRESOURCEW(IDI_MAIN));
1780 ImageList_AddIcon(hImageListView, hIcon);
1781 DestroyIcon(hIcon);
1782
1783 // Enum installed applications and updates
1784 EnumInstalledApplications(EnumType, TRUE, s_EnumInstalledAppProc, this);
1785 EnumInstalledApplications(EnumType, FALSE, s_EnumInstalledAppProc, this);
1786 }
1787 else if (IsAvailableEnum(EnumType))
1788 {
1789 if (bWasInInstalled)
1790 {
1791 m_ListView->SetCheckboxesVisible(TRUE);
1792 }
1793
1794 // Enum available applications
1795 m_AvailableApps.Enum(EnumType, s_EnumAvailableAppProc, this);
1796 }
1797
1798 SelectedEnumType = EnumType;
1799 UpdateStatusBarText();
1800 m_RichEdit->SetWelcomeText();
1801
1802 // Set automatic column width for program names if the list is not empty
1803 if (m_ListView->GetItemCount() > 0)
1804 {
1805 ListView_SetColumnWidth(m_ListView->GetWindow(), 0, LVSCW_AUTOSIZE);
1806 }
1807
1808 bUpdating = FALSE;
1809 m_ListView->SetRedraw(TRUE);
1810 }
1811
1812 public:
1813 static ATL::CWndClassInfo& GetWndClassInfo()
1814 {
1815 DWORD csStyle = CS_VREDRAW | CS_HREDRAW;
1816 static ATL::CWndClassInfo wc =
1817 {
1818 {
1819 sizeof(WNDCLASSEX),
1820 csStyle,
1821 StartWindowProc,
1822 0,
1823 0,
1824 NULL,
1825 LoadIconW(_AtlBaseModule.GetModuleInstance(), MAKEINTRESOURCEW(IDI_MAIN)),
1826 LoadCursorW(NULL, IDC_ARROW),
1827 (HBRUSH) (COLOR_BTNFACE + 1),
1828 MAKEINTRESOURCEW(IDR_MAINMENU),
1829 L"RAppsWnd",
1830 NULL
1831 },
1832 NULL, NULL, IDC_ARROW, TRUE, 0, _T("")
1833 };
1834 return wc;
1835 }
1836
1837 HWND Create()
1838 {
1839 ATL::CStringW szWindowName;
1840 szWindowName.LoadStringW(IDS_APPTITLE);
1841
1842 RECT r = {
1843 (SettingsInfo.bSaveWndPos ? SettingsInfo.Left : CW_USEDEFAULT),
1844 (SettingsInfo.bSaveWndPos ? SettingsInfo.Top : CW_USEDEFAULT),
1845 (SettingsInfo.bSaveWndPos ? SettingsInfo.Width : 680),
1846 (SettingsInfo.bSaveWndPos ? SettingsInfo.Height : 450)
1847 };
1848 r.right += r.left;
1849 r.bottom += r.top;
1850
1851 return CWindowImpl::Create(NULL, r, szWindowName.GetString(), WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, WS_EX_WINDOWEDGE);
1852 }
1853 };
1854
1855 VOID ShowMainWindow(INT nShowCmd)
1856 {
1857 HACCEL KeyBrd;
1858 MSG Msg;
1859
1860 CMainWindow* wnd = new CMainWindow();
1861 if (!wnd)
1862 return;
1863
1864 hMainWnd = wnd->Create();
1865 if (!hMainWnd)
1866 return;
1867
1868 /* Maximize it if we must */
1869 wnd->ShowWindow((SettingsInfo.bSaveWndPos && SettingsInfo.Maximized) ? SW_MAXIMIZE : nShowCmd);
1870 wnd->UpdateWindow();
1871
1872 /* Load the menu hotkeys */
1873 KeyBrd = LoadAcceleratorsW(NULL, MAKEINTRESOURCEW(HOTKEYS));
1874
1875 /* Message Loop */
1876 while (GetMessageW(&Msg, NULL, 0, 0))
1877 {
1878 if (!TranslateAcceleratorW(hMainWnd, KeyBrd, &Msg))
1879 {
1880 TranslateMessage(&Msg);
1881 DispatchMessageW(&Msg);
1882 }
1883 }
1884
1885 delete wnd;
1886 }