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