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