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