e8ea73139236529821cc81f7e789dba302adacb1
[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 (sanchaez@reactos.org)
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 LICENSE_OPENSOURCE:
117 szLicense.LoadStringW(IDS_LICENSE_OPENSOURCE);
118 break;
119 case LICENSE_FREEWARE:
120 szLicense.LoadStringW(IDS_LICENSE_FREEWARE);
121 break;
122 case LICENSE_TRIAL:
123 szLicense.LoadStringW(IDS_LICENSE_TRIAL);
124 break;
125 default:
126 InsertTextAfterLoaded_RichEdit(IDS_AINFO_LICENSE, Info->m_szLicense, CFE_BOLD, 0);
127 return;
128 }
129
130 szLicense += L" (" + Info->m_szLicense + L")";
131 InsertTextAfterLoaded_RichEdit(IDS_AINFO_LICENSE, szLicense, CFE_BOLD, 0);
132 }
133
134 static VOID InsertLanguageInfo_RichEdit(CAvailableApplicationInfo* Info)
135 {
136 if (!Info->HasLanguageInfo())
137 {
138 return;
139 }
140
141 const INT nTranslations = Info->m_LanguageLCIDs.GetSize();
142 ATL::CStringW szLangInfo;
143 ATL::CStringW szLoadedTextAvailability;
144 ATL::CStringW szLoadedAInfoText;
145
146 szLoadedAInfoText.LoadStringW(IDS_AINFO_LANGUAGES);
147
148 //TODO: replace those hardcoded strings
149 if (Info->HasNativeLanguage())
150 {
151 szLoadedTextAvailability.LoadStringW(IDS_LANGUAGE_AVAILABLE_TRANSLATION);
152 if (nTranslations > 1)
153 {
154 ATL::CStringW buf;
155 buf.LoadStringW(IDS_LANGUAGE_MORE_PLACEHOLDER);
156 szLangInfo.Format(buf, nTranslations - 1);
157 }
158 else
159 {
160 szLangInfo.LoadStringW(IDS_LANGUAGE_SINGLE);
161 szLangInfo = L" (" + szLangInfo + L")";
162 }
163 }
164 else if (Info->HasEnglishLanguage())
165 {
166 szLoadedTextAvailability.LoadStringW(IDS_LANGUAGE_ENGLISH_TRANSLATION);
167 if (nTranslations > 1)
168 {
169 ATL::CStringW buf;
170 buf.LoadStringW(IDS_LANGUAGE_AVAILABLE_PLACEHOLDER);
171 szLangInfo.Format(buf, nTranslations - 1);
172 }
173 else
174 {
175 szLangInfo.LoadStringW(IDS_LANGUAGE_SINGLE);
176 szLangInfo = L" (" + szLangInfo + L")";
177 }
178 }
179 else
180 {
181 szLoadedTextAvailability.LoadStringW(IDS_LANGUAGE_NO_TRANSLATION);
182 }
183
184 InsertRichEditText(szLoadedAInfoText, CFE_BOLD);
185 InsertRichEditText(szLoadedTextAvailability, NULL);
186 InsertRichEditText(szLangInfo, CFE_ITALIC);
187 }
188
189 public:
190 static BOOL ShowAvailableAppInfo(INT Index)
191 {
192 CAvailableApplicationInfo* Info = (CAvailableApplicationInfo*) ListViewGetlParam(Index);
193 if (!Info) return FALSE;
194
195 NewRichEditText(Info->m_szName, CFE_BOLD);
196 InsertVersionInfo_RichEdit(Info);
197 InsertLicenseInfo_RichEdit(Info);
198 InsertLanguageInfo_RichEdit(Info);
199
200 InsertTextAfterLoaded_RichEdit(IDS_AINFO_SIZE, Info->m_szSize, CFE_BOLD, 0);
201 InsertTextAfterLoaded_RichEdit(IDS_AINFO_URLSITE, Info->m_szUrlSite, CFE_BOLD, CFE_LINK);
202 InsertTextAfterLoaded_RichEdit(IDS_AINFO_DESCRIPTION, Info->m_szDesc, CFE_BOLD, 0);
203 InsertTextAfterLoaded_RichEdit(IDS_AINFO_URLDOWNLOAD, Info->m_szUrlDownload, CFE_BOLD, CFE_LINK);
204
205 return TRUE;
206 }
207 };
208
209 class CMainToolbar :
210 public CUiWindow< CToolbar<> >
211 {
212 #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 bHasAllChecked(FALSE),
372 bAscending(TRUE),
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;
561
562 public:
563 CSideTreeView() :
564 CUiWindow(),
565 hImageTreeView(ImageList_Create(TREEVIEW_ICON_SIZE, TREEVIEW_ICON_SIZE,
566 GetSystemColorDepth() | ILC_MASK,
567 0, 1))
568 {
569 }
570
571 HTREEITEM AddItem(HTREEITEM hParent, ATL::CStringW &Text, INT Image, INT SelectedImage, LPARAM lParam)
572 {
573 return CUiWindow<CTreeView>::AddItem(hParent, const_cast<LPWSTR>(Text.GetString()), Image, SelectedImage, lParam);
574 }
575
576 HTREEITEM AddCategory(HTREEITEM hRootItem, UINT TextIndex, UINT IconIndex)
577 {
578 ATL::CStringW szText;
579 INT Index;
580 HICON hIcon;
581
582 hIcon = (HICON) LoadImageW(hInst,
583 MAKEINTRESOURCE(IconIndex),
584 IMAGE_ICON,
585 TREEVIEW_ICON_SIZE,
586 TREEVIEW_ICON_SIZE,
587 LR_CREATEDIBSECTION);
588 if (hIcon)
589 {
590 Index = ImageList_AddIcon(hImageTreeView, hIcon);
591 DestroyIcon(hIcon);
592 }
593
594 szText.LoadStringW(TextIndex);
595 return AddItem(hRootItem, szText, Index, Index, TextIndex);
596 }
597
598 HIMAGELIST SetImageList()
599 {
600 return CUiWindow<CTreeView>::SetImageList(hImageTreeView, TVSIL_NORMAL);
601 }
602
603 VOID DestroyImageList()
604 {
605 if (hImageTreeView)
606 ImageList_Destroy(hImageTreeView);
607 }
608
609 ~CSideTreeView()
610 {
611 DestroyImageList();
612 }
613 };
614
615 class CSearchBar :
616 public CWindow
617 {
618 public:
619 VOID SetText(LPCWSTR lpszText)
620 {
621 SendMessageW(SB_SETTEXT, SBT_NOBORDERS, (LPARAM) lpszText);
622 }
623
624 HWND Create(HWND hwndParent)
625 {
626 ATL::CStringW szBuf;
627 m_hWnd = CreateWindowExW(WS_EX_CLIENTEDGE, L"Edit", NULL,
628 WS_CHILD | WS_VISIBLE | ES_LEFT | ES_AUTOHSCROLL,
629 0, 0, 200, 22,
630 hwndParent, (HMENU) NULL,
631 hInst, 0);
632
633 SendMessageW(WM_SETFONT, (WPARAM) GetStockObject(DEFAULT_GUI_FONT), 0);
634 szBuf.LoadStringW(IDS_SEARCH_TEXT);
635 SetWindowTextW(szBuf);
636 return m_hWnd;
637 }
638
639 };
640
641 class CMainWindow :
642 public CWindowImpl<CMainWindow, CWindow, CFrameWinTraits>
643 {
644 CUiPanel* m_ClientPanel;
645 CUiSplitPanel* m_VSplitter;
646 CUiSplitPanel* m_HSplitter;
647
648 CMainToolbar* m_Toolbar;
649 CAppsListView* m_ListView;
650
651 CSideTreeView* m_TreeView;
652 CUiWindow<CStatusBar>* m_StatusBar;
653 CUiWindow<CRichEdit>* m_RichEdit;
654
655 CUiWindow<CSearchBar>* m_SearchBar;
656 CAvailableApps m_AvailableApps;
657
658 LPWSTR pLink;
659
660 INT nSelectedApps;
661
662 BOOL bSearchEnabled;
663 BOOL bUpdating;
664 public:
665 CMainWindow() :
666 m_ClientPanel(NULL),
667 pLink(NULL),
668 bSearchEnabled(FALSE)
669 {
670 }
671
672 private:
673 VOID InitApplicationsList()
674 {
675 ATL::CStringW szText;
676
677 /* Add columns to ListView */
678 szText.LoadStringW(IDS_APP_NAME);
679 m_ListView->AddColumn(0, szText, 200, LVCFMT_LEFT);
680
681 szText.LoadStringW(IDS_APP_INST_VERSION);
682 m_ListView->AddColumn(1, szText, 90, LVCFMT_RIGHT);
683
684 szText.LoadStringW(IDS_APP_DESCRIPTION);
685 m_ListView->AddColumn(3, szText, 250, LVCFMT_LEFT);
686
687 // Unnesesary since the list updates on every TreeView selection
688 // UpdateApplicationsList(ENUM_ALL_COMPONENTS);
689 }
690
691 HTREEITEM AddCategory(HTREEITEM hRootItem, UINT TextIndex, UINT IconIndex)
692 {
693 return m_TreeView->AddCategory(hRootItem, TextIndex, IconIndex);
694 }
695
696 VOID InitCategoriesList()
697 {
698 HTREEITEM hRootItem1, hRootItem2;
699
700 hRootItem1 = AddCategory(TVI_ROOT, IDS_INSTALLED, IDI_CATEGORY);
701 AddCategory(hRootItem1, IDS_APPLICATIONS, IDI_APPS);
702 AddCategory(hRootItem1, IDS_UPDATES, IDI_APPUPD);
703
704 hRootItem2 = AddCategory(TVI_ROOT, IDS_AVAILABLEFORINST, IDI_CATEGORY);
705 AddCategory(hRootItem2, IDS_CAT_AUDIO, IDI_CAT_AUDIO);
706 AddCategory(hRootItem2, IDS_CAT_VIDEO, IDI_CAT_VIDEO);
707 AddCategory(hRootItem2, IDS_CAT_GRAPHICS, IDI_CAT_GRAPHICS);
708 AddCategory(hRootItem2, IDS_CAT_GAMES, IDI_CAT_GAMES);
709 AddCategory(hRootItem2, IDS_CAT_INTERNET, IDI_CAT_INTERNET);
710 AddCategory(hRootItem2, IDS_CAT_OFFICE, IDI_CAT_OFFICE);
711 AddCategory(hRootItem2, IDS_CAT_DEVEL, IDI_CAT_DEVEL);
712 AddCategory(hRootItem2, IDS_CAT_EDU, IDI_CAT_EDU);
713 AddCategory(hRootItem2, IDS_CAT_ENGINEER, IDI_CAT_ENGINEER);
714 AddCategory(hRootItem2, IDS_CAT_FINANCE, IDI_CAT_FINANCE);
715 AddCategory(hRootItem2, IDS_CAT_SCIENCE, IDI_CAT_SCIENCE);
716 AddCategory(hRootItem2, IDS_CAT_TOOLS, IDI_CAT_TOOLS);
717 AddCategory(hRootItem2, IDS_CAT_DRIVERS, IDI_CAT_DRIVERS);
718 AddCategory(hRootItem2, IDS_CAT_LIBS, IDI_CAT_LIBS);
719 AddCategory(hRootItem2, IDS_CAT_OTHER, IDI_CAT_OTHER);
720
721 m_TreeView->SetImageList();
722 m_TreeView->Expand(hRootItem1, TVE_EXPAND);
723 m_TreeView->Expand(hRootItem2, TVE_EXPAND);
724 m_TreeView->SelectItem(hRootItem1);
725 }
726
727 BOOL CreateStatusBar()
728 {
729 m_StatusBar = new CUiWindow<CStatusBar>();
730 m_StatusBar->m_VerticalAlignment = UiAlign_RightBtm;
731 m_StatusBar->m_HorizontalAlignment = UiAlign_Stretch;
732 m_ClientPanel->Children().Append(m_StatusBar);
733
734 return m_StatusBar->Create(m_hWnd, (HMENU) IDC_STATUSBAR) != NULL;
735 }
736
737 BOOL CreateToolbar()
738 {
739 m_Toolbar = new CMainToolbar();
740 m_Toolbar->m_VerticalAlignment = UiAlign_LeftTop;
741 m_Toolbar->m_HorizontalAlignment = UiAlign_Stretch;
742 m_ClientPanel->Children().Append(m_Toolbar);
743
744 return m_Toolbar->Create(m_hWnd) != NULL;
745 }
746
747 BOOL CreateTreeView()
748 {
749 m_TreeView = new CSideTreeView();
750 m_TreeView->m_VerticalAlignment = UiAlign_Stretch;
751 m_TreeView->m_HorizontalAlignment = UiAlign_Stretch;
752 m_VSplitter->First().Append(m_TreeView);
753
754 return m_TreeView->Create(m_hWnd) != NULL;
755 }
756
757 BOOL CreateListView()
758 {
759 m_ListView = new CAppsListView();
760 m_ListView->m_VerticalAlignment = UiAlign_Stretch;
761 m_ListView->m_HorizontalAlignment = UiAlign_Stretch;
762 m_HSplitter->First().Append(m_ListView);
763
764 hListView = m_ListView->Create(m_hWnd);
765 return hListView != NULL;
766 }
767
768 BOOL CreateRichEdit()
769 {
770 m_RichEdit = new CUiWindow<CRichEdit>();
771 m_RichEdit->m_VerticalAlignment = UiAlign_Stretch;
772 m_RichEdit->m_HorizontalAlignment = UiAlign_Stretch;
773 m_HSplitter->Second().Append(m_RichEdit);
774
775 return m_RichEdit->Create(m_hWnd) != NULL;
776 }
777
778 BOOL CreateVSplitter()
779 {
780 m_VSplitter = new CUiSplitPanel();
781 m_VSplitter->m_VerticalAlignment = UiAlign_Stretch;
782 m_VSplitter->m_HorizontalAlignment = UiAlign_Stretch;
783 m_VSplitter->m_DynamicFirst = FALSE;
784 m_VSplitter->m_Horizontal = FALSE;
785 m_VSplitter->m_MinFirst = 0;
786 m_VSplitter->m_MinSecond = 320;
787 m_VSplitter->m_Pos = 240;
788 m_ClientPanel->Children().Append(m_VSplitter);
789
790 return m_VSplitter->Create(m_hWnd) != NULL;
791 }
792
793 BOOL CreateHSplitter()
794 {
795 m_HSplitter = new CUiSplitPanel();
796 m_HSplitter->m_VerticalAlignment = UiAlign_Stretch;
797 m_HSplitter->m_HorizontalAlignment = UiAlign_Stretch;
798 m_HSplitter->m_DynamicFirst = TRUE;
799 m_HSplitter->m_Horizontal = TRUE;
800 m_HSplitter->m_Pos = INT_MAX; //set INT_MAX to use lowest possible position (m_MinSecond)
801 m_HSplitter->m_MinFirst = 10;
802 m_HSplitter->m_MinSecond = 140;
803 m_VSplitter->Second().Append(m_HSplitter);
804
805 return m_HSplitter->Create(m_hWnd) != NULL;
806 }
807
808 BOOL CreateSearchBar()
809 {
810 m_SearchBar = new CUiWindow<CSearchBar>();
811 m_SearchBar->m_VerticalAlignment = UiAlign_LeftTop;
812 m_SearchBar->m_HorizontalAlignment = UiAlign_RightBtm;
813 m_SearchBar->m_Margin.top = 6;
814 m_SearchBar->m_Margin.right = 6;
815
816 return m_SearchBar->Create(m_Toolbar->m_hWnd) != NULL;
817 }
818
819 BOOL CreateLayout()
820 {
821 BOOL b = TRUE;
822 bUpdating = TRUE;
823
824 m_ClientPanel = new CUiPanel();
825 m_ClientPanel->m_VerticalAlignment = UiAlign_Stretch;
826 m_ClientPanel->m_HorizontalAlignment = UiAlign_Stretch;
827
828 // Top level
829 b = b && CreateStatusBar();
830 b = b && CreateToolbar();
831 b = b && CreateSearchBar();
832 b = b && CreateVSplitter();
833
834 // Inside V Splitter
835 b = b && CreateHSplitter();
836 b = b && CreateTreeView();
837
838 // Inside H Splitter
839 b = b && CreateListView();
840 b = b && CreateRichEdit();
841
842 if (b)
843 {
844 RECT rTop;
845 RECT rBottom;
846
847 /* Size status bar */
848 m_StatusBar->SendMessageW(WM_SIZE, 0, 0);
849
850 /* Size tool bar */
851 m_Toolbar->AutoSize();
852
853 ::GetWindowRect(m_Toolbar->m_hWnd, &rTop);
854 ::GetWindowRect(m_StatusBar->m_hWnd, &rBottom);
855
856 m_VSplitter->m_Margin.top = rTop.bottom - rTop.top;
857 m_VSplitter->m_Margin.bottom = rBottom.bottom - rBottom.top;
858 }
859
860 bUpdating = FALSE;
861 return b;
862 }
863
864 BOOL InitControls()
865 {
866 if (CreateLayout())
867 {
868
869 InitApplicationsList();
870 InitCategoriesList();
871
872 nSelectedApps = 0;
873 UpdateStatusBarText();
874
875 return TRUE;
876 }
877
878 return FALSE;
879 }
880
881 VOID OnSize(HWND hwnd, WPARAM wParam, LPARAM lParam)
882 {
883 /* Size status bar */
884 m_StatusBar->SendMessage(WM_SIZE, 0, 0);
885
886 /* Size tool bar */
887 m_Toolbar->AutoSize();
888
889 RECT r = {0, 0, LOWORD(lParam), HIWORD(lParam)};
890 HDWP hdwp = NULL;
891 INT count = m_ClientPanel->CountSizableChildren();
892
893 hdwp = BeginDeferWindowPos(count);
894 if (hdwp)
895 {
896 hdwp = m_ClientPanel->OnParentSize(r, hdwp);
897 }
898 if (hdwp)
899 {
900 EndDeferWindowPos(hdwp);
901 }
902
903 // TODO: Sub-layouts for children of children
904 count = m_SearchBar->CountSizableChildren();
905 hdwp = BeginDeferWindowPos(count);
906 if (hdwp)
907 {
908 hdwp = m_SearchBar->OnParentSize(r, hdwp);
909 }
910 if (hdwp)
911 {
912 EndDeferWindowPos(hdwp);
913 }
914 }
915
916 BOOL ProcessWindowMessage(HWND hwnd, UINT Msg, WPARAM wParam, LPARAM lParam, LRESULT& theResult, DWORD dwMapId)
917 {
918 theResult = 0;
919 switch (Msg)
920 {
921 case WM_CREATE:
922 if (!InitControls())
923 ::PostMessage(hwnd, WM_CLOSE, 0, 0);
924 break;
925
926 case WM_DESTROY:
927 {
928 ShowWindow(SW_HIDE);
929 SaveSettings(hwnd);
930
931 FreeLogs();
932 m_AvailableApps.FreeCachedEntries();
933
934 if (IsInstalledEnum(SelectedEnumType))
935 FreeInstalledAppList();
936
937 delete m_ClientPanel;
938
939 PostQuitMessage(0);
940 return 0;
941 }
942
943 case WM_COMMAND:
944 OnCommand(wParam, lParam);
945 break;
946
947 case WM_NOTIFY:
948 {
949 LPNMHDR data = (LPNMHDR) lParam;
950
951 switch (data->code)
952 {
953 case TVN_SELCHANGED:
954 {
955 if (data->hwndFrom == m_TreeView->m_hWnd)
956 {
957 switch (((LPNMTREEVIEW) lParam)->itemNew.lParam)
958 {
959 case IDS_INSTALLED:
960 UpdateApplicationsList(ENUM_ALL_INSTALLED);
961 break;
962
963 case IDS_APPLICATIONS:
964 UpdateApplicationsList(ENUM_INSTALLED_APPLICATIONS);
965 break;
966
967 case IDS_UPDATES:
968 UpdateApplicationsList(ENUM_UPDATES);
969 break;
970
971 case IDS_AVAILABLEFORINST:
972 UpdateApplicationsList(ENUM_ALL_AVAILABLE);
973 break;
974
975 case IDS_CAT_AUDIO:
976 UpdateApplicationsList(ENUM_CAT_AUDIO);
977 break;
978
979 case IDS_CAT_DEVEL:
980 UpdateApplicationsList(ENUM_CAT_DEVEL);
981 break;
982
983 case IDS_CAT_DRIVERS:
984 UpdateApplicationsList(ENUM_CAT_DRIVERS);
985 break;
986
987 case IDS_CAT_EDU:
988 UpdateApplicationsList(ENUM_CAT_EDU);
989 break;
990
991 case IDS_CAT_ENGINEER:
992 UpdateApplicationsList(ENUM_CAT_ENGINEER);
993 break;
994
995 case IDS_CAT_FINANCE:
996 UpdateApplicationsList(ENUM_CAT_FINANCE);
997 break;
998
999 case IDS_CAT_GAMES:
1000 UpdateApplicationsList(ENUM_CAT_GAMES);
1001 break;
1002
1003 case IDS_CAT_GRAPHICS:
1004 UpdateApplicationsList(ENUM_CAT_GRAPHICS);
1005 break;
1006
1007 case IDS_CAT_INTERNET:
1008 UpdateApplicationsList(ENUM_CAT_INTERNET);
1009 break;
1010
1011 case IDS_CAT_LIBS:
1012 UpdateApplicationsList(ENUM_CAT_LIBS);
1013 break;
1014
1015 case IDS_CAT_OFFICE:
1016 UpdateApplicationsList(ENUM_CAT_OFFICE);
1017 break;
1018
1019 case IDS_CAT_OTHER:
1020 UpdateApplicationsList(ENUM_CAT_OTHER);
1021 break;
1022
1023 case IDS_CAT_SCIENCE:
1024 UpdateApplicationsList(ENUM_CAT_SCIENCE);
1025 break;
1026
1027 case IDS_CAT_TOOLS:
1028 UpdateApplicationsList(ENUM_CAT_TOOLS);
1029 break;
1030
1031 case IDS_CAT_VIDEO:
1032 UpdateApplicationsList(ENUM_CAT_VIDEO);
1033 break;
1034 }
1035 }
1036
1037 HMENU mainMenu = ::GetMenu(hwnd);
1038 HMENU lvwMenu = ::GetMenu(m_ListView->m_hWnd);
1039
1040 /* Disable/enable items based on treeview selection */
1041 if (IsSelectedNodeInstalled())
1042 {
1043 EnableMenuItem(mainMenu, ID_REGREMOVE, MF_ENABLED);
1044 EnableMenuItem(mainMenu, ID_INSTALL, MF_GRAYED);
1045 EnableMenuItem(mainMenu, ID_UNINSTALL, MF_ENABLED);
1046 EnableMenuItem(mainMenu, ID_MODIFY, MF_ENABLED);
1047
1048 EnableMenuItem(lvwMenu, ID_REGREMOVE, MF_ENABLED);
1049 EnableMenuItem(lvwMenu, ID_INSTALL, MF_GRAYED);
1050 EnableMenuItem(lvwMenu, ID_UNINSTALL, MF_ENABLED);
1051 EnableMenuItem(lvwMenu, ID_MODIFY, MF_ENABLED);
1052
1053 m_Toolbar->SendMessageW(TB_ENABLEBUTTON, ID_REGREMOVE, TRUE);
1054 m_Toolbar->SendMessageW(TB_ENABLEBUTTON, ID_INSTALL, FALSE);
1055 m_Toolbar->SendMessageW(TB_ENABLEBUTTON, ID_UNINSTALL, TRUE);
1056 m_Toolbar->SendMessageW(TB_ENABLEBUTTON, ID_MODIFY, TRUE);
1057 }
1058 else
1059 {
1060 EnableMenuItem(mainMenu, ID_REGREMOVE, MF_GRAYED);
1061 EnableMenuItem(mainMenu, ID_INSTALL, MF_ENABLED);
1062 EnableMenuItem(mainMenu, ID_UNINSTALL, MF_GRAYED);
1063 EnableMenuItem(mainMenu, ID_MODIFY, MF_GRAYED);
1064
1065 EnableMenuItem(lvwMenu, ID_REGREMOVE, MF_GRAYED);
1066 EnableMenuItem(lvwMenu, ID_INSTALL, MF_ENABLED);
1067 EnableMenuItem(lvwMenu, ID_UNINSTALL, MF_GRAYED);
1068 EnableMenuItem(lvwMenu, ID_MODIFY, MF_GRAYED);
1069
1070 m_Toolbar->SendMessageW(TB_ENABLEBUTTON, ID_REGREMOVE, FALSE);
1071 m_Toolbar->SendMessageW(TB_ENABLEBUTTON, ID_INSTALL, TRUE);
1072 m_Toolbar->SendMessageW(TB_ENABLEBUTTON, ID_UNINSTALL, FALSE);
1073 m_Toolbar->SendMessageW(TB_ENABLEBUTTON, ID_MODIFY, FALSE);
1074 }
1075 }
1076 break;
1077
1078 case LVN_ITEMCHANGED:
1079 {
1080 LPNMLISTVIEW pnic = (LPNMLISTVIEW) lParam;
1081
1082 if (pnic->hdr.hwndFrom == m_ListView->m_hWnd)
1083 {
1084 /* Check if this is a valid item
1085 * (technically, it can be also an unselect) */
1086 INT ItemIndex = pnic->iItem;
1087 if (ItemIndex == -1 ||
1088 ItemIndex >= ListView_GetItemCount(pnic->hdr.hwndFrom))
1089 {
1090 break;
1091 }
1092
1093 /* Check if the focus has been moved to another item */
1094 if ((pnic->uChanged & LVIF_STATE) &&
1095 (pnic->uNewState & LVIS_FOCUSED) &&
1096 !(pnic->uOldState & LVIS_FOCUSED))
1097 {
1098 if (IsInstalledEnum(SelectedEnumType))
1099 ShowInstalledAppInfo(ItemIndex);
1100 if (IsAvailableEnum(SelectedEnumType))
1101 CAvailableAppView::ShowAvailableAppInfo(ItemIndex);
1102 }
1103 /* Check if the item is checked */
1104 if ((pnic->uNewState & LVIS_STATEIMAGEMASK) && !bUpdating)
1105 {
1106 BOOL checked = ListView_GetCheckState(pnic->hdr.hwndFrom, pnic->iItem);
1107 /* FIXME: HAX!
1108 - preventing decremention below zero as a safeguard for ReactOS
1109 In ReactOS this action is triggered whenever user changes *selection*, but should be only when *checkbox* state toggled
1110 Maybe LVIS_STATEIMAGEMASK is set incorrectly
1111 */
1112 nSelectedApps +=
1113 (checked)
1114 ? 1
1115 : ((nSelectedApps > 0)
1116 ? -1
1117 : 0);
1118 UpdateStatusBarText();
1119 }
1120 }
1121 }
1122 break;
1123
1124 case LVN_COLUMNCLICK:
1125 {
1126 LPNMLISTVIEW pnmv = (LPNMLISTVIEW) lParam;
1127
1128 m_ListView->ColumnClick(pnmv);
1129 }
1130 break;
1131
1132 case NM_CLICK:
1133 {
1134 if (data->hwndFrom == m_ListView->m_hWnd && ((LPNMLISTVIEW) lParam)->iItem != -1)
1135 {
1136 if (IsInstalledEnum(SelectedEnumType))
1137 ShowInstalledAppInfo(-1);
1138 if (IsAvailableEnum(SelectedEnumType))
1139 CAvailableAppView::ShowAvailableAppInfo(-1);
1140 }
1141 }
1142 break;
1143
1144 case NM_DBLCLK:
1145 {
1146 if (data->hwndFrom == m_ListView->m_hWnd && ((LPNMLISTVIEW) lParam)->iItem != -1)
1147 {
1148 /* this won't do anything if the program is already installed */
1149 SendMessageW(hwnd, WM_COMMAND, ID_INSTALL, 0);
1150 }
1151 }
1152 break;
1153
1154 case NM_RCLICK:
1155 {
1156 if (data->hwndFrom == m_ListView->m_hWnd && ((LPNMLISTVIEW) lParam)->iItem != -1)
1157 {
1158 ShowPopupMenu(m_ListView->m_hWnd, 0, ID_INSTALL);
1159 }
1160 }
1161 break;
1162
1163 case EN_LINK:
1164 OnLink((ENLINK*) lParam);
1165 break;
1166
1167 case TTN_GETDISPINFO:
1168 m_Toolbar->OnGetDispInfo((LPTOOLTIPTEXT) lParam);
1169 break;
1170 }
1171 }
1172 break;
1173
1174 case WM_SIZE:
1175 OnSize(hwnd, wParam, lParam);
1176 break;
1177
1178 case WM_SIZING:
1179 {
1180 LPRECT pRect = (LPRECT) lParam;
1181
1182 if (pRect->right - pRect->left < 565)
1183 pRect->right = pRect->left + 565;
1184
1185 if (pRect->bottom - pRect->top < 300)
1186 pRect->bottom = pRect->top + 300;
1187
1188 return TRUE;
1189 }
1190
1191 case WM_SYSCOLORCHANGE:
1192 {
1193 /* Forward WM_SYSCOLORCHANGE to common controls */
1194 m_ListView->SendMessageW(WM_SYSCOLORCHANGE, 0, 0);
1195 m_TreeView->SendMessageW(WM_SYSCOLORCHANGE, 0, 0);
1196 m_Toolbar->SendMessageW(WM_SYSCOLORCHANGE, 0, 0);
1197 m_ListView->SendMessageW(EM_SETBKGNDCOLOR, 0, GetSysColor(COLOR_BTNFACE));
1198 }
1199 break;
1200
1201 case WM_TIMER:
1202 if (wParam == SEARCH_TIMER_ID)
1203 {
1204 ::KillTimer(hwnd, SEARCH_TIMER_ID);
1205 if (bSearchEnabled)
1206 UpdateApplicationsList(-1);
1207 }
1208 break;
1209 }
1210
1211 return FALSE;
1212 }
1213
1214 virtual VOID OnLink(ENLINK *Link)
1215 {
1216 switch (Link->msg)
1217 {
1218 case WM_LBUTTONUP:
1219 case WM_RBUTTONUP:
1220 {
1221 if (pLink) HeapFree(GetProcessHeap(), 0, pLink);
1222
1223 pLink = (LPWSTR) HeapAlloc(GetProcessHeap(), 0,
1224 (max(Link->chrg.cpMin, Link->chrg.cpMax) -
1225 min(Link->chrg.cpMin, Link->chrg.cpMax) + 1) * sizeof(WCHAR));
1226 if (!pLink)
1227 {
1228 /* TODO: Error message */
1229 return;
1230 }
1231
1232 m_RichEdit->SendMessageW(EM_SETSEL, Link->chrg.cpMin, Link->chrg.cpMax);
1233 m_RichEdit->SendMessageW(EM_GETSELTEXT, 0, (LPARAM) pLink);
1234
1235 ShowPopupMenu(m_RichEdit->m_hWnd, IDR_LINKMENU, -1);
1236 }
1237 break;
1238 }
1239 }
1240
1241 BOOL IsSelectedNodeInstalled()
1242 {
1243 HTREEITEM hSelectedItem = m_TreeView->GetSelection();
1244 TV_ITEM tItem;
1245
1246 tItem.mask = TVIF_PARAM | TVIF_HANDLE;
1247 tItem.hItem = hSelectedItem;
1248 m_TreeView->GetItem(&tItem);
1249 switch (tItem.lParam)
1250 {
1251 case IDS_INSTALLED:
1252 case IDS_APPLICATIONS:
1253 case IDS_UPDATES:
1254 return TRUE;
1255 default:
1256 return FALSE;
1257 }
1258 }
1259
1260 VOID OnCommand(WPARAM wParam, LPARAM lParam)
1261 {
1262 WORD wCommand = LOWORD(wParam);
1263
1264 if (lParam == (LPARAM) m_SearchBar->m_hWnd)
1265 {
1266 ATL::CStringW szBuf;
1267
1268 switch (HIWORD(wParam))
1269 {
1270 case EN_SETFOCUS:
1271 {
1272 ATL::CStringW szWndText;
1273
1274 szBuf.LoadStringW(IDS_SEARCH_TEXT);
1275 m_SearchBar->GetWindowTextW(szWndText);
1276 if (szBuf == szWndText)
1277 {
1278 bSearchEnabled = FALSE;
1279 m_SearchBar->SetWindowTextW(L"");
1280 }
1281 }
1282 break;
1283
1284 case EN_KILLFOCUS:
1285 {
1286 m_SearchBar->GetWindowTextW(szBuf);
1287 if (szBuf.IsEmpty())
1288 {
1289 szBuf.LoadStringW(IDS_SEARCH_TEXT);
1290 bSearchEnabled = FALSE;
1291 m_SearchBar->SetWindowTextW(szBuf.GetString());
1292 }
1293 }
1294 break;
1295
1296 case EN_CHANGE:
1297 {
1298 ATL::CStringW szWndText;
1299
1300 if (!bSearchEnabled)
1301 {
1302 bSearchEnabled = TRUE;
1303 break;
1304 }
1305
1306 szBuf.LoadStringW(IDS_SEARCH_TEXT);
1307 m_SearchBar->GetWindowTextW(szWndText);
1308 if (szBuf == szWndText)
1309 {
1310 szSearchPattern.Empty();
1311 }
1312 else
1313 {
1314 szSearchPattern = szWndText;
1315 }
1316
1317 DWORD dwDelay;
1318 SystemParametersInfoW(SPI_GETMENUSHOWDELAY, 0, &dwDelay, 0);
1319 SetTimer(SEARCH_TIMER_ID, dwDelay);
1320 }
1321 break;
1322 }
1323
1324 return;
1325 }
1326
1327 switch (wCommand)
1328 {
1329 case ID_OPEN_LINK:
1330 ShellExecuteW(m_hWnd, L"open", pLink, NULL, NULL, SW_SHOWNOACTIVATE);
1331 HeapFree(GetProcessHeap(), 0, pLink);
1332 break;
1333
1334 case ID_COPY_LINK:
1335 CopyTextToClipboard(pLink);
1336 HeapFree(GetProcessHeap(), 0, pLink);
1337 break;
1338
1339 case ID_SETTINGS:
1340 CreateSettingsDlg(m_hWnd);
1341 break;
1342
1343 case ID_EXIT:
1344 PostMessageW(WM_CLOSE, 0, 0);
1345 break;
1346
1347 case ID_INSTALL:
1348 if (IsAvailableEnum(SelectedEnumType))
1349 {
1350 if (nSelectedApps > 0)
1351 {
1352 CDownloadManager::DownloadListOfApplications(m_ListView->GetCheckedItems());
1353 UpdateApplicationsList(-1);
1354 }
1355 else if (CDownloadManager::DownloadApplication(m_ListView->GetSelectedData()))
1356 {
1357 UpdateApplicationsList(-1);
1358 }
1359
1360 }
1361 break;
1362
1363 case ID_UNINSTALL:
1364 if (UninstallApplication(-1, FALSE))
1365 UpdateApplicationsList(-1);
1366 break;
1367
1368 case ID_MODIFY:
1369 if (UninstallApplication(-1, TRUE))
1370 UpdateApplicationsList(-1);
1371 break;
1372
1373 case ID_REGREMOVE:
1374 RemoveAppFromRegistry(-1);
1375 break;
1376
1377 case ID_REFRESH:
1378 UpdateApplicationsList(-1);
1379 break;
1380
1381 case ID_RESETDB:
1382 CAvailableApps::ForceUpdateAppsDB();
1383 UpdateApplicationsList(-1);
1384 break;
1385
1386 case ID_HELP:
1387 MessageBoxW(L"Help not implemented yet", NULL, MB_OK);
1388 break;
1389
1390 case ID_ABOUT:
1391 ShowAboutDialog();
1392 break;
1393
1394 case ID_CHECK_ALL:
1395 m_ListView->CheckAll();
1396 break;
1397 }
1398 }
1399
1400 VOID FreeInstalledAppList()
1401 {
1402 INT Count = m_ListView->GetItemCount() - 1;
1403 PINSTALLED_INFO Info;
1404
1405 while (Count >= 0)
1406 {
1407 Info = (PINSTALLED_INFO) ListViewGetlParam(Count);
1408 if (Info)
1409 {
1410 RegCloseKey(Info->hSubKey);
1411 delete Info;
1412 }
1413 Count--;
1414 }
1415 }
1416
1417 static BOOL SearchPatternMatch(LPCWSTR szHaystack, LPCWSTR szNeedle)
1418 {
1419 if (!*szNeedle)
1420 return TRUE;
1421 /* TODO: Improve pattern search beyond a simple case-insensitive substring search. */
1422 return StrStrIW(szHaystack, szNeedle) != NULL;
1423 }
1424
1425 static BOOL CALLBACK s_EnumInstalledAppProc(INT ItemIndex, ATL::CStringW &m_szName, PINSTALLED_INFO Info)
1426 {
1427 PINSTALLED_INFO ItemInfo;
1428 ATL::CStringW szText;
1429 INT Index;
1430
1431 if (!SearchPatternMatch(m_szName.GetString(), szSearchPattern))
1432 {
1433 RegCloseKey(Info->hSubKey);
1434 return TRUE;
1435 }
1436
1437 ItemInfo = new INSTALLED_INFO(*Info);
1438 if (!ItemInfo)
1439 {
1440 RegCloseKey(Info->hSubKey);
1441 return FALSE;
1442 }
1443
1444 Index = ListViewAddItem(ItemIndex, 0, m_szName, (LPARAM) ItemInfo);
1445
1446 /* Get version info */
1447 GetApplicationString(ItemInfo->hSubKey, L"DisplayVersion", szText);
1448 ListView_SetItemText(hListView, Index, 1, const_cast<LPWSTR>(szText.GetString()));
1449
1450 /* Get comments */
1451 GetApplicationString(ItemInfo->hSubKey, L"Comments", szText);
1452 ListView_SetItemText(hListView, Index, 2, const_cast<LPWSTR>(szText.GetString()));
1453
1454 return TRUE;
1455 }
1456
1457 static BOOL CALLBACK s_EnumAvailableAppProc(CAvailableApplicationInfo* Info, LPCWSTR szFolderPath)
1458 {
1459 INT Index;
1460 HICON hIcon = NULL;
1461
1462 HIMAGELIST hImageListView = ListView_GetImageList(hListView, LVSIL_SMALL);
1463
1464 if (!SearchPatternMatch(Info->m_szName.GetString(), szSearchPattern) &&
1465 !SearchPatternMatch(Info->m_szDesc.GetString(), szSearchPattern))
1466 {
1467 return TRUE;
1468 }
1469
1470 /* Load icon from file */
1471 ATL::CStringW szIconPath;
1472 szIconPath.Format(L"%lsicons\\%ls.ico", szFolderPath, Info->m_szName.GetString());
1473 hIcon = (HICON) LoadImageW(NULL,
1474 szIconPath.GetString(),
1475 IMAGE_ICON,
1476 LISTVIEW_ICON_SIZE,
1477 LISTVIEW_ICON_SIZE,
1478 LR_LOADFROMFILE);
1479
1480 if (!hIcon || GetLastError() != ERROR_SUCCESS)
1481 {
1482 /* Load default icon */
1483 hIcon = (HICON) LoadIconW(hInst, MAKEINTRESOURCEW(IDI_MAIN));
1484 }
1485
1486 Index = ImageList_AddIcon(hImageListView, hIcon);
1487 DestroyIcon(hIcon);
1488
1489 Index = ListViewAddItem(Info->m_Category, Index, Info->m_szName.GetString(), (LPARAM) Info);
1490 ListView_SetImageList(hListView, hImageListView, LVSIL_SMALL);
1491
1492 ListView_SetItemText(hListView, Index, 1, const_cast<LPWSTR>(Info->m_szVersion.GetString()));
1493 ListView_SetItemText(hListView, Index, 2, const_cast<LPWSTR>(Info->m_szDesc.GetString()));
1494
1495 return TRUE;
1496 }
1497
1498 VOID UpdateStatusBarText()
1499 {
1500 if (m_StatusBar)
1501 {
1502 ATL::CStringW szBuffer;
1503
1504 szBuffer.Format(IDS_APPS_COUNT, m_ListView->GetItemCount(), nSelectedApps);
1505 m_StatusBar->SetText(szBuffer);
1506 }
1507 }
1508
1509 VOID UpdateApplicationsList(INT EnumType)
1510 {
1511 ATL::CStringW szBuffer1, szBuffer2;
1512 HIMAGELIST hImageListView;
1513 BOOL bWasInInstalled = IsInstalledEnum(SelectedEnumType);
1514
1515 bUpdating = TRUE;
1516 m_ListView->SetRedraw(FALSE);
1517
1518 nSelectedApps = 0;
1519 if (EnumType < 0)
1520 {
1521 EnumType = SelectedEnumType;
1522 }
1523
1524 //if previous one was INSTALLED purge the list
1525 //TODO: make the Installed category a separate class to avoid doing this
1526 if (bWasInInstalled)
1527 {
1528 FreeInstalledAppList();
1529 }
1530
1531 m_ListView->DeleteAllItems();
1532
1533 // Create new ImageList
1534 hImageListView = ImageList_Create(LISTVIEW_ICON_SIZE,
1535 LISTVIEW_ICON_SIZE,
1536 GetSystemColorDepth() | ILC_MASK,
1537 0, 1);
1538 HIMAGELIST hImageListBuf = m_ListView->SetImageList(hImageListView, LVSIL_SMALL);
1539 if (hImageListBuf)
1540 {
1541 ImageList_Destroy(hImageListBuf);
1542 }
1543
1544 if (IsInstalledEnum(EnumType))
1545 {
1546 if (!bWasInInstalled)
1547 {
1548 m_ListView->SetCheckboxesVisible(FALSE);
1549 }
1550
1551 HICON hIcon = (HICON) LoadIconW(hInst, MAKEINTRESOURCEW(IDI_MAIN));
1552 ImageList_AddIcon(hImageListView, hIcon);
1553 DestroyIcon(hIcon);
1554
1555 // Enum installed applications and updates
1556 EnumInstalledApplications(EnumType, TRUE, s_EnumInstalledAppProc);
1557 EnumInstalledApplications(EnumType, FALSE, s_EnumInstalledAppProc);
1558 }
1559 else if (IsAvailableEnum(EnumType))
1560 {
1561 if (bWasInInstalled)
1562 {
1563 m_ListView->SetCheckboxesVisible(TRUE);
1564 }
1565
1566 // Enum available applications
1567 m_AvailableApps.Enum(EnumType, s_EnumAvailableAppProc);
1568 }
1569
1570 SelectedEnumType = EnumType;
1571 UpdateStatusBarText();
1572 SetWelcomeText();
1573
1574 // Set automatic column width for program names if the list is not empty
1575 if (m_ListView->GetItemCount() > 0)
1576 {
1577 ListView_SetColumnWidth(m_ListView->GetWindow(), 0, LVSCW_AUTOSIZE);
1578 }
1579
1580 bUpdating = FALSE;
1581 m_ListView->SetRedraw(TRUE);
1582 }
1583
1584 public:
1585 static ATL::CWndClassInfo& GetWndClassInfo()
1586 {
1587 DWORD csStyle = CS_VREDRAW | CS_HREDRAW;
1588 static ATL::CWndClassInfo wc =
1589 {
1590 {
1591 sizeof(WNDCLASSEX),
1592 csStyle,
1593 StartWindowProc,
1594 0,
1595 0,
1596 NULL,
1597 LoadIconW(_AtlBaseModule.GetModuleInstance(), MAKEINTRESOURCEW(IDI_MAIN)),
1598 LoadCursorW(NULL, IDC_ARROW),
1599 (HBRUSH) (COLOR_BTNFACE + 1),
1600 MAKEINTRESOURCEW(IDR_MAINMENU),
1601 L"RAppsWnd",
1602 NULL
1603 },
1604 NULL, NULL, IDC_ARROW, TRUE, 0, _T("")
1605 };
1606 return wc;
1607 }
1608
1609 HWND Create()
1610 {
1611 ATL::CStringW szWindowName;
1612 szWindowName.LoadStringW(IDS_APPTITLE);
1613
1614 RECT r = {
1615 (SettingsInfo.bSaveWndPos ? SettingsInfo.Left : CW_USEDEFAULT),
1616 (SettingsInfo.bSaveWndPos ? SettingsInfo.Top : CW_USEDEFAULT),
1617 (SettingsInfo.bSaveWndPos ? SettingsInfo.Width : 680),
1618 (SettingsInfo.bSaveWndPos ? SettingsInfo.Height : 450)
1619 };
1620 r.right += r.left;
1621 r.bottom += r.top;
1622
1623 return CWindowImpl::Create(NULL, r, szWindowName.GetString(), WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, WS_EX_WINDOWEDGE);
1624 }
1625
1626 CStatusBar * GetStatusBar()
1627 {
1628 return m_StatusBar;
1629 }
1630
1631 CAppsListView * GetListView()
1632 {
1633 return m_ListView;
1634 }
1635
1636 CRichEdit * GetRichEdit()
1637 {
1638 return m_RichEdit;
1639 }
1640
1641 CAvailableApps * GetAvailableApps()
1642 {
1643 return &m_AvailableApps;
1644 }
1645 };
1646
1647 // global interface
1648 CMainWindow * g_MainWindow;
1649
1650 HWND CreateMainWindow()
1651 {
1652 g_MainWindow = new CMainWindow();
1653 return g_MainWindow->Create();
1654 }
1655
1656 DWORD_PTR ListViewGetlParam(INT item)
1657 {
1658 if (item < 0)
1659 {
1660 item = g_MainWindow->GetListView()->GetSelectionMark();
1661 }
1662 return g_MainWindow->GetListView()->GetItemData(item);
1663 }
1664
1665 VOID SetStatusBarText(LPCWSTR szText)
1666 {
1667 g_MainWindow->GetStatusBar()->SetText(szText);
1668 }
1669
1670 INT ListViewAddItem(INT ItemIndex, INT IconIndex, LPWSTR lpName, LPARAM lParam)
1671 {
1672 return g_MainWindow->GetListView()->AddItem(ItemIndex, IconIndex, lpName, lParam);
1673 }
1674
1675 VOID NewRichEditText(LPCWSTR szText, DWORD flags)
1676 {
1677 g_MainWindow->GetRichEdit()->SetText(szText, flags);
1678 }
1679
1680 VOID InsertRichEditText(LPCWSTR szText, DWORD flags)
1681 {
1682 g_MainWindow->GetRichEdit()->InsertText(szText, flags);
1683 }
1684
1685 CAvailableApps* GetAvailableApps()
1686 {
1687 return g_MainWindow->GetAvailableApps();
1688 }
1689
1690 // ATL version of functions above
1691 VOID SetStatusBarText(const ATL::CStringW& szText)
1692 {
1693 SetStatusBarText(szText.GetString());
1694 }
1695
1696 INT ListViewAddItem(INT ItemIndex, INT IconIndex, const ATL::CStringW& Name, LPARAM lParam)
1697 {
1698 return ListViewAddItem(ItemIndex, IconIndex, const_cast<LPWSTR>(Name.GetString()), lParam);
1699 }
1700
1701 VOID NewRichEditText(const ATL::CStringW& szText, DWORD flags)
1702 {
1703 NewRichEditText(szText.GetString(), flags);
1704 }
1705
1706 VOID InsertRichEditText(const ATL::CStringW& szText, DWORD flags)
1707 {
1708 InsertRichEditText(szText.GetString(), flags);
1709 }