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