[RAPPS] Adjusted splitter values a bit. CORE-13163
[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 = 0;
778 m_VSplitter->m_MinSecond = 320;
779 m_VSplitter->m_Pos = 240;
780 m_ClientPanel->Children().Append(m_VSplitter);
781
782 return m_VSplitter->Create(m_hWnd) != NULL;
783 }
784
785 BOOL CreateHSplitter()
786 {
787 m_HSplitter = new CUiSplitPanel();
788 m_HSplitter->m_VerticalAlignment = UiAlign_Stretch;
789 m_HSplitter->m_HorizontalAlignment = UiAlign_Stretch;
790 m_HSplitter->m_DynamicFirst = TRUE;
791 m_HSplitter->m_Horizontal = TRUE;
792 m_HSplitter->m_Pos = INT_MAX; //set INT_MAX to use lowest possible position (m_MinSecond)
793 m_HSplitter->m_MinFirst = 10;
794 m_HSplitter->m_MinSecond = 140;
795 m_VSplitter->Second().Append(m_HSplitter);
796
797 return m_HSplitter->Create(m_hWnd) != NULL;
798 }
799
800 BOOL CreateSearchBar()
801 {
802 m_SearchBar = new CUiWindow<CSearchBar>();
803 m_SearchBar->m_VerticalAlignment = UiAlign_LeftTop;
804 m_SearchBar->m_HorizontalAlignment = UiAlign_RightBtm;
805 m_SearchBar->m_Margin.top = 6;
806 m_SearchBar->m_Margin.right = 6;
807
808 return m_SearchBar->Create(m_Toolbar->m_hWnd) != NULL;
809 }
810
811 BOOL CreateLayout()
812 {
813 BOOL b = TRUE;
814 bUpdating = TRUE;
815
816 m_ClientPanel = new CUiPanel();
817 m_ClientPanel->m_VerticalAlignment = UiAlign_Stretch;
818 m_ClientPanel->m_HorizontalAlignment = UiAlign_Stretch;
819
820 // Top level
821 b = b && CreateStatusBar();
822 b = b && CreateToolbar();
823 b = b && CreateSearchBar();
824 b = b && CreateVSplitter();
825
826 // Inside V Splitter
827 b = b && CreateHSplitter();
828 b = b && CreateTreeView();
829
830 // Inside H Splitter
831 b = b && CreateListView();
832 b = b && CreateRichEdit();
833
834 if (b)
835 {
836 RECT rTop;
837 RECT rBottom;
838
839 /* Size status bar */
840 m_StatusBar->SendMessageW(WM_SIZE, 0, 0);
841
842 /* Size tool bar */
843 m_Toolbar->AutoSize();
844
845 ::GetWindowRect(m_Toolbar->m_hWnd, &rTop);
846 ::GetWindowRect(m_StatusBar->m_hWnd, &rBottom);
847
848 m_VSplitter->m_Margin.top = rTop.bottom - rTop.top;
849 m_VSplitter->m_Margin.bottom = rBottom.bottom - rBottom.top;
850 }
851
852 bUpdating = FALSE;
853 return b;
854 }
855
856 BOOL InitControls()
857 {
858 if (CreateLayout())
859 {
860
861 InitApplicationsList();
862 InitCategoriesList();
863
864 nSelectedApps = 0;
865 UpdateStatusBarText();
866
867 return TRUE;
868 }
869
870 return FALSE;
871 }
872
873 VOID OnSize(HWND hwnd, WPARAM wParam, LPARAM lParam)
874 {
875 /* Size status bar */
876 m_StatusBar->SendMessage(WM_SIZE, 0, 0);
877
878 /* Size tool bar */
879 m_Toolbar->AutoSize();
880
881 RECT r = {0, 0, LOWORD(lParam), HIWORD(lParam)};
882 HDWP hdwp = NULL;
883 INT count = m_ClientPanel->CountSizableChildren();
884
885 hdwp = BeginDeferWindowPos(count);
886 if (hdwp)
887 {
888 hdwp = m_ClientPanel->OnParentSize(r, hdwp);
889 }
890 if (hdwp)
891 {
892 EndDeferWindowPos(hdwp);
893 }
894
895 // TODO: Sub-layouts for children of children
896 count = m_SearchBar->CountSizableChildren();
897 hdwp = BeginDeferWindowPos(count);
898 if (hdwp)
899 {
900 hdwp = m_SearchBar->OnParentSize(r, hdwp);
901 }
902 if (hdwp)
903 {
904 EndDeferWindowPos(hdwp);
905 }
906 }
907
908 BOOL ProcessWindowMessage(HWND hwnd, UINT Msg, WPARAM wParam, LPARAM lParam, LRESULT& theResult, DWORD dwMapId)
909 {
910 theResult = 0;
911 switch (Msg)
912 {
913 case WM_CREATE:
914 if (!InitControls())
915 ::PostMessage(hwnd, WM_CLOSE, 0, 0);
916 break;
917
918 case WM_DESTROY:
919 {
920 ShowWindow(SW_HIDE);
921 SaveSettings(hwnd);
922
923 FreeLogs();
924 m_AvailableApps.FreeCachedEntries();
925
926 if (IS_INSTALLED_ENUM(SelectedEnumType))
927 FreeInstalledAppList();
928
929 delete m_ClientPanel;
930
931 PostQuitMessage(0);
932 return 0;
933 }
934
935 case WM_COMMAND:
936 OnCommand(wParam, lParam);
937 break;
938
939 case WM_NOTIFY:
940 {
941 LPNMHDR data = (LPNMHDR) lParam;
942
943 switch (data->code)
944 {
945 case TVN_SELCHANGED:
946 {
947 if (data->hwndFrom == m_TreeView->m_hWnd)
948 {
949 switch (((LPNMTREEVIEW) lParam)->itemNew.lParam)
950 {
951 case IDS_INSTALLED:
952 UpdateApplicationsList(ENUM_ALL_COMPONENTS);
953 break;
954
955 case IDS_APPLICATIONS:
956 UpdateApplicationsList(ENUM_APPLICATIONS);
957 break;
958
959 case IDS_UPDATES:
960 UpdateApplicationsList(ENUM_UPDATES);
961 break;
962
963 case IDS_AVAILABLEFORINST:
964 UpdateApplicationsList(ENUM_ALL_AVAILABLE);
965 break;
966
967 case IDS_CAT_AUDIO:
968 UpdateApplicationsList(ENUM_CAT_AUDIO);
969 break;
970
971 case IDS_CAT_DEVEL:
972 UpdateApplicationsList(ENUM_CAT_DEVEL);
973 break;
974
975 case IDS_CAT_DRIVERS:
976 UpdateApplicationsList(ENUM_CAT_DRIVERS);
977 break;
978
979 case IDS_CAT_EDU:
980 UpdateApplicationsList(ENUM_CAT_EDU);
981 break;
982
983 case IDS_CAT_ENGINEER:
984 UpdateApplicationsList(ENUM_CAT_ENGINEER);
985 break;
986
987 case IDS_CAT_FINANCE:
988 UpdateApplicationsList(ENUM_CAT_FINANCE);
989 break;
990
991 case IDS_CAT_GAMES:
992 UpdateApplicationsList(ENUM_CAT_GAMES);
993 break;
994
995 case IDS_CAT_GRAPHICS:
996 UpdateApplicationsList(ENUM_CAT_GRAPHICS);
997 break;
998
999 case IDS_CAT_INTERNET:
1000 UpdateApplicationsList(ENUM_CAT_INTERNET);
1001 break;
1002
1003 case IDS_CAT_LIBS:
1004 UpdateApplicationsList(ENUM_CAT_LIBS);
1005 break;
1006
1007 case IDS_CAT_OFFICE:
1008 UpdateApplicationsList(ENUM_CAT_OFFICE);
1009 break;
1010
1011 case IDS_CAT_OTHER:
1012 UpdateApplicationsList(ENUM_CAT_OTHER);
1013 break;
1014
1015 case IDS_CAT_SCIENCE:
1016 UpdateApplicationsList(ENUM_CAT_SCIENCE);
1017 break;
1018
1019 case IDS_CAT_TOOLS:
1020 UpdateApplicationsList(ENUM_CAT_TOOLS);
1021 break;
1022
1023 case IDS_CAT_VIDEO:
1024 UpdateApplicationsList(ENUM_CAT_VIDEO);
1025 break;
1026 }
1027 }
1028
1029 HMENU mainMenu = ::GetMenu(hwnd);
1030 HMENU lvwMenu = ::GetMenu(m_ListView->m_hWnd);
1031
1032 /* Disable/enable items based on treeview selection */
1033 if (IsSelectedNodeInstalled())
1034 {
1035 EnableMenuItem(mainMenu, ID_REGREMOVE, MF_ENABLED);
1036 EnableMenuItem(mainMenu, ID_INSTALL, MF_GRAYED);
1037 EnableMenuItem(mainMenu, ID_UNINSTALL, MF_ENABLED);
1038 EnableMenuItem(mainMenu, ID_MODIFY, MF_ENABLED);
1039
1040 EnableMenuItem(lvwMenu, ID_REGREMOVE, MF_ENABLED);
1041 EnableMenuItem(lvwMenu, ID_INSTALL, MF_GRAYED);
1042 EnableMenuItem(lvwMenu, ID_UNINSTALL, MF_ENABLED);
1043 EnableMenuItem(lvwMenu, ID_MODIFY, MF_ENABLED);
1044
1045 m_Toolbar->SendMessageW(TB_ENABLEBUTTON, ID_REGREMOVE, TRUE);
1046 m_Toolbar->SendMessageW(TB_ENABLEBUTTON, ID_INSTALL, FALSE);
1047 m_Toolbar->SendMessageW(TB_ENABLEBUTTON, ID_UNINSTALL, TRUE);
1048 m_Toolbar->SendMessageW(TB_ENABLEBUTTON, ID_MODIFY, TRUE);
1049 }
1050 else
1051 {
1052 EnableMenuItem(mainMenu, ID_REGREMOVE, MF_GRAYED);
1053 EnableMenuItem(mainMenu, ID_INSTALL, MF_ENABLED);
1054 EnableMenuItem(mainMenu, ID_UNINSTALL, MF_GRAYED);
1055 EnableMenuItem(mainMenu, ID_MODIFY, MF_GRAYED);
1056
1057 EnableMenuItem(lvwMenu, ID_REGREMOVE, MF_GRAYED);
1058 EnableMenuItem(lvwMenu, ID_INSTALL, MF_ENABLED);
1059 EnableMenuItem(lvwMenu, ID_UNINSTALL, MF_GRAYED);
1060 EnableMenuItem(lvwMenu, ID_MODIFY, MF_GRAYED);
1061
1062 m_Toolbar->SendMessageW(TB_ENABLEBUTTON, ID_REGREMOVE, FALSE);
1063 m_Toolbar->SendMessageW(TB_ENABLEBUTTON, ID_INSTALL, TRUE);
1064 m_Toolbar->SendMessageW(TB_ENABLEBUTTON, ID_UNINSTALL, FALSE);
1065 m_Toolbar->SendMessageW(TB_ENABLEBUTTON, ID_MODIFY, FALSE);
1066 }
1067 }
1068 break;
1069
1070 case LVN_ITEMCHANGED:
1071 {
1072 LPNMLISTVIEW pnic = (LPNMLISTVIEW) lParam;
1073
1074 if (pnic->hdr.hwndFrom == m_ListView->m_hWnd)
1075 {
1076 /* Check if this is a valid item
1077 * (technically, it can be also an unselect) */
1078 INT ItemIndex = pnic->iItem;
1079 if (ItemIndex == -1 ||
1080 ItemIndex >= ListView_GetItemCount(pnic->hdr.hwndFrom))
1081 {
1082 break;
1083 }
1084
1085 /* Check if the focus has been moved to another item */
1086 if ((pnic->uChanged & LVIF_STATE) &&
1087 (pnic->uNewState & LVIS_FOCUSED) &&
1088 !(pnic->uOldState & LVIS_FOCUSED))
1089 {
1090 if (IS_INSTALLED_ENUM(SelectedEnumType))
1091 ShowInstalledAppInfo(ItemIndex);
1092 if (IsAvailableEnum(SelectedEnumType))
1093 CAvailableAppView::ShowAvailableAppInfo(ItemIndex);
1094 }
1095 /* Check if the item is checked */
1096 if ((pnic->uNewState & LVIS_STATEIMAGEMASK) && !bUpdating)
1097 {
1098 BOOL checked = ListView_GetCheckState(pnic->hdr.hwndFrom, pnic->iItem);
1099 /* FIXME: HAX!
1100 - preventing decremention below zero as a safeguard for ReactOS
1101 In ReactOS this action is triggered whenever user changes *selection*, but should be only when *checkbox* state toggled
1102 Maybe LVIS_STATEIMAGEMASK is set incorrectly
1103 */
1104 nSelectedApps +=
1105 (checked)
1106 ? 1
1107 : ((nSelectedApps > 0)
1108 ? -1
1109 : 0);
1110 UpdateStatusBarText();
1111 }
1112 }
1113 }
1114 break;
1115
1116 case LVN_COLUMNCLICK:
1117 {
1118 LPNMLISTVIEW pnmv = (LPNMLISTVIEW) lParam;
1119
1120 m_ListView->ColumnClick(pnmv);
1121 }
1122 break;
1123
1124 case NM_CLICK:
1125 {
1126 if (data->hwndFrom == m_ListView->m_hWnd && ((LPNMLISTVIEW) lParam)->iItem != -1)
1127 {
1128 if (IS_INSTALLED_ENUM(SelectedEnumType))
1129 ShowInstalledAppInfo(-1);
1130 if (IsAvailableEnum(SelectedEnumType))
1131 CAvailableAppView::ShowAvailableAppInfo(-1);
1132 }
1133 }
1134 break;
1135
1136 case NM_DBLCLK:
1137 {
1138 if (data->hwndFrom == m_ListView->m_hWnd && ((LPNMLISTVIEW) lParam)->iItem != -1)
1139 {
1140 /* this won't do anything if the program is already installed */
1141 SendMessageW(hwnd, WM_COMMAND, ID_INSTALL, 0);
1142 }
1143 }
1144 break;
1145
1146 case NM_RCLICK:
1147 {
1148 if (data->hwndFrom == m_ListView->m_hWnd && ((LPNMLISTVIEW) lParam)->iItem != -1)
1149 {
1150 ShowPopupMenu(m_ListView->m_hWnd, 0, ID_INSTALL);
1151 }
1152 }
1153 break;
1154
1155 case EN_LINK:
1156 OnLink((ENLINK*) lParam);
1157 break;
1158
1159 case TTN_GETDISPINFO:
1160 m_Toolbar->OnGetDispInfo((LPTOOLTIPTEXT) lParam);
1161 break;
1162 }
1163 }
1164 break;
1165
1166 case WM_SIZE:
1167 OnSize(hwnd, wParam, lParam);
1168 break;
1169
1170 case WM_SIZING:
1171 {
1172 LPRECT pRect = (LPRECT) lParam;
1173
1174 if (pRect->right - pRect->left < 565)
1175 pRect->right = pRect->left + 565;
1176
1177 if (pRect->bottom - pRect->top < 300)
1178 pRect->bottom = pRect->top + 300;
1179
1180 return TRUE;
1181 }
1182
1183 case WM_SYSCOLORCHANGE:
1184 {
1185 /* Forward WM_SYSCOLORCHANGE to common controls */
1186 m_ListView->SendMessageW(WM_SYSCOLORCHANGE, 0, 0);
1187 m_TreeView->SendMessageW(WM_SYSCOLORCHANGE, 0, 0);
1188 m_Toolbar->SendMessageW(WM_SYSCOLORCHANGE, 0, 0);
1189 m_ListView->SendMessageW(EM_SETBKGNDCOLOR, 0, GetSysColor(COLOR_BTNFACE));
1190 }
1191 break;
1192
1193 case WM_TIMER:
1194 if (wParam == SEARCH_TIMER_ID)
1195 {
1196 ::KillTimer(hwnd, SEARCH_TIMER_ID);
1197 if (bSearchEnabled)
1198 UpdateApplicationsList(-1);
1199 }
1200 break;
1201 }
1202
1203 return FALSE;
1204 }
1205
1206 virtual VOID OnLink(ENLINK *Link)
1207 {
1208 switch (Link->msg)
1209 {
1210 case WM_LBUTTONUP:
1211 case WM_RBUTTONUP:
1212 {
1213 if (pLink) HeapFree(GetProcessHeap(), 0, pLink);
1214
1215 pLink = (LPWSTR) HeapAlloc(GetProcessHeap(), 0,
1216 (max(Link->chrg.cpMin, Link->chrg.cpMax) -
1217 min(Link->chrg.cpMin, Link->chrg.cpMax) + 1) * sizeof(WCHAR));
1218 if (!pLink)
1219 {
1220 /* TODO: Error message */
1221 return;
1222 }
1223
1224 m_RichEdit->SendMessageW(EM_SETSEL, Link->chrg.cpMin, Link->chrg.cpMax);
1225 m_RichEdit->SendMessageW(EM_GETSELTEXT, 0, (LPARAM) pLink);
1226
1227 ShowPopupMenu(m_RichEdit->m_hWnd, IDR_LINKMENU, -1);
1228 }
1229 break;
1230 }
1231 }
1232
1233 BOOL IsSelectedNodeInstalled()
1234 {
1235 HTREEITEM hSelectedItem = m_TreeView->GetSelection();
1236 TV_ITEM tItem;
1237
1238 tItem.mask = TVIF_PARAM | TVIF_HANDLE;
1239 tItem.hItem = hSelectedItem;
1240 m_TreeView->GetItem(&tItem);
1241 switch (tItem.lParam)
1242 {
1243 case IDS_INSTALLED:
1244 case IDS_APPLICATIONS:
1245 case IDS_UPDATES:
1246 return TRUE;
1247 default:
1248 return FALSE;
1249 }
1250 }
1251
1252 VOID OnCommand(WPARAM wParam, LPARAM lParam)
1253 {
1254 WORD wCommand = LOWORD(wParam);
1255
1256 if (lParam == (LPARAM) m_SearchBar->m_hWnd)
1257 {
1258 ATL::CStringW szBuf;
1259
1260 switch (HIWORD(wParam))
1261 {
1262 case EN_SETFOCUS:
1263 {
1264 ATL::CStringW szWndText;
1265
1266 szBuf.LoadStringW(IDS_SEARCH_TEXT);
1267 m_SearchBar->GetWindowTextW(szWndText);
1268 if (szBuf == szWndText)
1269 {
1270 bSearchEnabled = FALSE;
1271 m_SearchBar->SetWindowTextW(L"");
1272 }
1273 }
1274 break;
1275
1276 case EN_KILLFOCUS:
1277 {
1278 m_SearchBar->GetWindowTextW(szBuf);
1279 if (szBuf.IsEmpty())
1280 {
1281 szBuf.LoadStringW(IDS_SEARCH_TEXT);
1282 bSearchEnabled = FALSE;
1283 m_SearchBar->SetWindowTextW(szBuf.GetString());
1284 }
1285 }
1286 break;
1287
1288 case EN_CHANGE:
1289 {
1290 ATL::CStringW szWndText;
1291
1292 if (!bSearchEnabled)
1293 {
1294 bSearchEnabled = TRUE;
1295 break;
1296 }
1297
1298 szBuf.LoadStringW(IDS_SEARCH_TEXT);
1299 m_SearchBar->GetWindowTextW(szWndText);
1300 if (szBuf == szWndText)
1301 {
1302 szSearchPattern.Empty();
1303 }
1304 else
1305 {
1306 szSearchPattern = szWndText;
1307 }
1308
1309 DWORD dwDelay;
1310 SystemParametersInfoW(SPI_GETMENUSHOWDELAY, 0, &dwDelay, 0);
1311 SetTimer(SEARCH_TIMER_ID, dwDelay);
1312 }
1313 break;
1314 }
1315
1316 return;
1317 }
1318
1319 switch (wCommand)
1320 {
1321 case ID_OPEN_LINK:
1322 ShellExecuteW(m_hWnd, L"open", pLink, NULL, NULL, SW_SHOWNOACTIVATE);
1323 HeapFree(GetProcessHeap(), 0, pLink);
1324 break;
1325
1326 case ID_COPY_LINK:
1327 CopyTextToClipboard(pLink);
1328 HeapFree(GetProcessHeap(), 0, pLink);
1329 break;
1330
1331 case ID_SETTINGS:
1332 CreateSettingsDlg(m_hWnd);
1333 break;
1334
1335 case ID_EXIT:
1336 PostMessageW(WM_CLOSE, 0, 0);
1337 break;
1338
1339 case ID_INSTALL:
1340 if (IsAvailableEnum(SelectedEnumType))
1341 {
1342 if (nSelectedApps > 0)
1343 {
1344 CDownloadManager::DownloadListOfApplications(m_ListView->GetCheckedItems());
1345 UpdateApplicationsList(-1);
1346 }
1347 else if (CDownloadManager::DownloadApplication(m_ListView->GetSelectedData()))
1348 {
1349 UpdateApplicationsList(-1);
1350 }
1351
1352 }
1353 break;
1354
1355 case ID_UNINSTALL:
1356 if (UninstallApplication(-1, FALSE))
1357 UpdateApplicationsList(-1);
1358 break;
1359
1360 case ID_MODIFY:
1361 if (UninstallApplication(-1, TRUE))
1362 UpdateApplicationsList(-1);
1363 break;
1364
1365 case ID_REGREMOVE:
1366 RemoveAppFromRegistry(-1);
1367 break;
1368
1369 case ID_REFRESH:
1370 UpdateApplicationsList(-1);
1371 break;
1372
1373 case ID_RESETDB:
1374 CAvailableApps::ForceUpdateAppsDB();
1375 UpdateApplicationsList(-1);
1376 break;
1377
1378 case ID_HELP:
1379 MessageBoxW(L"Help not implemented yet", NULL, MB_OK);
1380 break;
1381
1382 case ID_ABOUT:
1383 ShowAboutDialog();
1384 break;
1385
1386 case ID_CHECK_ALL:
1387 m_ListView->CheckAll();
1388 break;
1389 }
1390 }
1391
1392 VOID FreeInstalledAppList()
1393 {
1394 INT Count = m_ListView->GetItemCount() - 1;
1395 PINSTALLED_INFO Info;
1396
1397 while (Count >= 0)
1398 {
1399 Info = (PINSTALLED_INFO) ListViewGetlParam(Count);
1400 if (Info)
1401 {
1402 RegCloseKey(Info->hSubKey);
1403 delete Info;
1404 }
1405 Count--;
1406 }
1407 }
1408
1409 static BOOL SearchPatternMatch(PCWSTR szHaystack, PCWSTR szNeedle)
1410 {
1411 if (!*szNeedle)
1412 return TRUE;
1413 /* TODO: Improve pattern search beyond a simple case-insensitive substring search. */
1414 return StrStrIW(szHaystack, szNeedle) != NULL;
1415 }
1416
1417 static BOOL CALLBACK s_EnumInstalledAppProc(INT ItemIndex, ATL::CStringW &szName, PINSTALLED_INFO Info)
1418 {
1419 PINSTALLED_INFO ItemInfo;
1420 ATL::CStringW szText;
1421 INT Index;
1422
1423 if (!SearchPatternMatch(szName.GetString(), szSearchPattern))
1424 {
1425 RegCloseKey(Info->hSubKey);
1426 return TRUE;
1427 }
1428
1429 ItemInfo = new INSTALLED_INFO(*Info);
1430 if (!ItemInfo)
1431 {
1432 RegCloseKey(Info->hSubKey);
1433 return FALSE;
1434 }
1435
1436 Index = ListViewAddItem(ItemIndex, 0, szName, (LPARAM) ItemInfo);
1437
1438 /* Get version info */
1439 GetApplicationString(ItemInfo->hSubKey, L"DisplayVersion", szText);
1440 ListView_SetItemText(hListView, Index, 1, const_cast<LPWSTR>(szText.GetString()));
1441
1442 /* Get comments */
1443 GetApplicationString(ItemInfo->hSubKey, L"Comments", szText);
1444 ListView_SetItemText(hListView, Index, 2, const_cast<LPWSTR>(szText.GetString()));
1445
1446 return TRUE;
1447 }
1448
1449 static BOOL CALLBACK s_EnumAvailableAppProc(CAvailableApplicationInfo* Info, LPCWSTR szFolderPath)
1450 {
1451 INT Index;
1452 HICON hIcon = NULL;
1453
1454 HIMAGELIST hImageListView = ListView_GetImageList(hListView, LVSIL_SMALL);
1455
1456 if (!SearchPatternMatch(Info->szName, szSearchPattern) &&
1457 !SearchPatternMatch(Info->szDesc, szSearchPattern))
1458 {
1459 return TRUE;
1460 }
1461
1462 /* Load icon from file */
1463 ATL::CStringW szIconPath;
1464 szIconPath.Format(L"%lsicons\\%ls.ico", szFolderPath, Info->szName);
1465 hIcon = (HICON) LoadImageW(NULL,
1466 szIconPath.GetString(),
1467 IMAGE_ICON,
1468 LISTVIEW_ICON_SIZE,
1469 LISTVIEW_ICON_SIZE,
1470 LR_LOADFROMFILE);
1471
1472 if (!hIcon || GetLastError() != ERROR_SUCCESS)
1473 {
1474 /* Load default icon */
1475 hIcon = (HICON) LoadIconW(hInst, MAKEINTRESOURCEW(IDI_MAIN));
1476 }
1477
1478 Index = ImageList_AddIcon(hImageListView, hIcon);
1479 DestroyIcon(hIcon);
1480
1481 Index = ListViewAddItem(Info->Category, Index, Info->szName, (LPARAM) Info);
1482 ListView_SetImageList(hListView, hImageListView, LVSIL_SMALL);
1483
1484 ListView_SetItemText(hListView, Index, 1, const_cast<LPWSTR>(Info->szVersion.GetString()));
1485 ListView_SetItemText(hListView, Index, 2, const_cast<LPWSTR>(Info->szDesc.GetString()));
1486
1487 return TRUE;
1488 }
1489
1490 VOID UpdateStatusBarText()
1491 {
1492 if (m_StatusBar)
1493 {
1494 ATL::CStringW szBuffer;
1495
1496 szBuffer.Format(IDS_APPS_COUNT, m_ListView->GetItemCount(), nSelectedApps);
1497 m_StatusBar->SetText(szBuffer);
1498 }
1499 }
1500
1501 VOID UpdateApplicationsList(INT EnumType)
1502 {
1503 ATL::CStringW szBuffer1, szBuffer2;
1504 HIMAGELIST hImageListView;
1505 BOOL bWasInInstalled = IS_INSTALLED_ENUM(SelectedEnumType);
1506
1507 bUpdating = TRUE;
1508 m_ListView->SetRedraw(FALSE);
1509
1510 nSelectedApps = 0;
1511 if (EnumType < 0)
1512 {
1513 EnumType = SelectedEnumType;
1514 }
1515
1516 //if previous one was INSTALLED purge the list
1517 //TODO: make the Installed category a separate class to avoid doing this
1518 if (bWasInInstalled)
1519 {
1520 FreeInstalledAppList();
1521 }
1522
1523 m_ListView->DeleteAllItems();
1524
1525 // Create new ImageList
1526 hImageListView = ImageList_Create(LISTVIEW_ICON_SIZE,
1527 LISTVIEW_ICON_SIZE,
1528 GetSystemColorDepth() | ILC_MASK,
1529 0, 1);
1530 HIMAGELIST hImageListBuf = m_ListView->SetImageList(hImageListView, LVSIL_SMALL);
1531 if (hImageListBuf)
1532 {
1533 ImageList_Destroy(hImageListBuf);
1534 }
1535
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 }