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