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