https://git.reactos.org/?p=reactos.git;a=commitdiff;h=10c0ff7416d6e8066c170b...
commit 10c0ff7416d6e8066c170bb3abdfb22303c472ed Author: He Yang 1160386205@qq.com AuthorDate: Tue Jul 21 22:13:39 2020 +0800 Commit: Mark Jansen mark.jansen@reactos.org CommitDate: Sun Sep 6 17:09:20 2020 +0200
[RAPPS] listview refactor (#2970)
This makes it easier to maintain the listview, and better separates the application list and listview.
* [RAPPS] fix memory leak when cleanup. some renaming are also done * [RAPPS] move the code adding apps info inside class CAppsListView * [RAPPS] add table view, create listview and AppInfoDisplay inside it * [RAPPS] rename INSTALLED_INFO as CInstalledApplicationInfo now it corresponds with CAvailableApplicationInfo * [RAPPS] add CInstalledApps * [RAPPS] optimize the speed when refreshing listview * [RAPPS] correctly handle Enum for InstalledApps * [RAPPS] make check all working properly (this also fixes some bugs) the old version has some bugs when check all items after switching tags in tree-view * [RAPPS] add handling for wow64 * [RAPPS] use an inline function to replace INSERT_TEXT macro * [RAPPS] fix the bug that StatusBar won't update when switching tags * [RAPPS] now TableView always reset bIsAscending in SetDisplayMode * [RAPPS] rename TableView to ApplicationView * [RAPPS] now bIsAscending would be reset when switching column in listview --- base/applications/rapps/available.cpp | 169 ++-- base/applications/rapps/gui.cpp | 1335 ++++++++++++++++----------- base/applications/rapps/include/available.h | 16 +- base/applications/rapps/include/installed.h | 46 +- base/applications/rapps/include/misc.h | 2 + base/applications/rapps/include/rosui.h | 5 +- base/applications/rapps/installed.cpp | 306 ++++-- base/applications/rapps/misc.cpp | 33 + 8 files changed, 1203 insertions(+), 709 deletions(-)
diff --git a/base/applications/rapps/available.cpp b/base/applications/rapps/available.cpp index 8af6c78a8b9..62d4585d3a5 100644 --- a/base/applications/rapps/available.cpp +++ b/base/applications/rapps/available.cpp @@ -19,7 +19,7 @@
// CAvailableApplicationInfo CAvailableApplicationInfo::CAvailableApplicationInfo(const ATL::CStringW& sFileNameParam, AvailableStrings& AvlbStrings) - : m_IsSelected(FALSE), m_LicenseType(LICENSE_NONE), m_SizeBytes(0), m_sFileName(sFileNameParam), + : m_LicenseType(LICENSE_NONE), m_SizeBytes(0), m_sFileName(sFileNameParam), m_IsInstalled(FALSE), m_HasLanguageInfo(FALSE), m_HasInstalledVersion(FALSE) { RetrieveGeneralInfo(AvlbStrings); @@ -400,71 +400,127 @@ BOOL CAvailableApps::ForceUpdateAppsDB()
BOOL CAvailableApps::Enum(INT EnumType, AVAILENUMPROC lpEnumProc, PVOID param) { + if (EnumType == ENUM_CAT_SELECTED) + { + CAvailableApplicationInfo *EnumAvlbInfo = NULL;
- HANDLE hFind = INVALID_HANDLE_VALUE; - WIN32_FIND_DATAW FindFileData; - - hFind = FindFirstFileW(m_Strings.szSearchPath.GetString(), &FindFileData); + // enum all object in m_SelectedList and invoke callback + for(POSITION CurrentPosition = m_SelectedList.GetHeadPosition(); + CurrentPosition && (EnumAvlbInfo = m_SelectedList.GetAt(CurrentPosition)); + m_SelectedList.GetNext(CurrentPosition)) + { + EnumAvlbInfo->RefreshAppInfo(m_Strings);
- if (hFind == INVALID_HANDLE_VALUE) - { - //no db yet - return FALSE; + if (lpEnumProc) + lpEnumProc(EnumAvlbInfo, TRUE, param); + } + return TRUE; } - - do + else { - // loop for all the cached entries - POSITION CurrentListPosition = m_InfoList.GetHeadPosition(); - CAvailableApplicationInfo* Info = NULL; + HANDLE hFind = INVALID_HANDLE_VALUE; + WIN32_FIND_DATAW FindFileData; + + hFind = FindFirstFileW(m_Strings.szSearchPath.GetString(), &FindFileData); + + if (hFind == INVALID_HANDLE_VALUE) + { + //no db yet + return FALSE; + }
- while (CurrentListPosition != NULL) + do { - POSITION LastListPosition = CurrentListPosition; - Info = m_InfoList.GetNext(CurrentListPosition); + // loop for all the cached entries + POSITION CurrentListPosition = m_InfoList.GetHeadPosition(); + CAvailableApplicationInfo *Info = NULL;
- // do we already have this entry in cache? - if (Info->m_sFileName == FindFileData.cFileName) + while (CurrentListPosition != NULL) { - // is it current enough, or the file has been modified since our last time here? - if (CompareFileTime(&FindFileData.ftLastWriteTime, &Info->m_ftCacheStamp) == 1) - { - // recreate our cache, this is the slow path - m_InfoList.RemoveAt(LastListPosition); + POSITION LastListPosition = CurrentListPosition; + Info = m_InfoList.GetNext(CurrentListPosition);
- delete Info; - Info = NULL; - break; + // do we already have this entry in cache? + if (Info->m_sFileName == FindFileData.cFileName) + { + // is it current enough, or the file has been modified since our last time here? + if (CompareFileTime(&FindFileData.ftLastWriteTime, &Info->m_ftCacheStamp) == 1) + { + // recreate our cache, this is the slow path + m_InfoList.RemoveAt(LastListPosition); + + // also remove this in selected list (if exist) + RemoveSelected(Info); + + delete Info; + Info = NULL; + break; + } + else + { + // speedy path, compare directly, we already have the data + goto skip_if_cached; + } } - else + } + + // create a new entry + Info = new CAvailableApplicationInfo(FindFileData.cFileName, m_Strings); + + // set a timestamp for the next time + Info->SetLastWriteTime(&FindFileData.ftLastWriteTime); + m_InfoList.AddTail(Info); + + skip_if_cached: + if (EnumType == Info->m_Category + || EnumType == ENUM_ALL_AVAILABLE) + { + Info->RefreshAppInfo(m_Strings); + + if (lpEnumProc) { - // speedy path, compare directly, we already have the data - goto skip_if_cached; + if (m_SelectedList.Find(Info)) + { + lpEnumProc(Info, TRUE, param); + } + else + { + lpEnumProc(Info, FALSE, param); + } } } - } + } while (FindNextFileW(hFind, &FindFileData));
- // create a new entry - Info = new CAvailableApplicationInfo(FindFileData.cFileName, m_Strings); + FindClose(hFind); + return TRUE; + } +}
- // set a timestamp for the next time - Info->SetLastWriteTime(&FindFileData.ftLastWriteTime); - m_InfoList.AddTail(Info); +BOOL CAvailableApps::AddSelected(CAvailableApplicationInfo *AvlbInfo) +{ + return m_SelectedList.AddTail(AvlbInfo) != 0; +}
-skip_if_cached: - if (EnumType == Info->m_Category - || EnumType == ENUM_ALL_AVAILABLE - || (EnumType == ENUM_CAT_SELECTED && Info->m_IsSelected)) - { - Info->RefreshAppInfo(m_Strings); +BOOL CAvailableApps::RemoveSelected(CAvailableApplicationInfo *AvlbInfo) +{ + POSITION Position = m_SelectedList.Find(AvlbInfo); + if (Position) + { + m_SelectedList.RemoveAt(Position); + return TRUE; + } + return FALSE; +}
- if (lpEnumProc) - lpEnumProc(Info, m_Strings.szAppsPath.GetString(), param); - } - } while (FindNextFileW(hFind, &FindFileData) != 0); +VOID CAvailableApps::RemoveAllSelected() +{ + m_SelectedList.RemoveAll(); + return; +}
- FindClose(hFind); - return TRUE; +int CAvailableApps::GetSelectedCount() +{ + return m_SelectedList.GetCount(); }
CAvailableApplicationInfo* CAvailableApps::FindInfo(const ATL::CStringW& szAppName) const @@ -502,23 +558,6 @@ ATL::CSimpleArray<CAvailableApplicationInfo> CAvailableApps::FindInfoList(const return result; }
-ATL::CSimpleArray<CAvailableApplicationInfo> CAvailableApps::GetSelected() const -{ - ATL::CSimpleArray<CAvailableApplicationInfo> result; - POSITION CurrentListPosition = m_InfoList.GetHeadPosition(); - CAvailableApplicationInfo* Info; - - while (CurrentListPosition != NULL) - { - Info = m_InfoList.GetNext(CurrentListPosition); - if (Info->m_IsSelected) - { - result.Add(*Info); - } - } - return result; -} - const ATL::CStringW& CAvailableApps::GetFolderPath() const { return m_Strings.szPath; diff --git a/base/applications/rapps/gui.cpp b/base/applications/rapps/gui.cpp index b0599074a47..e3aeccc579f 100644 --- a/base/applications/rapps/gui.cpp +++ b/base/applications/rapps/gui.cpp @@ -65,6 +65,21 @@ enum SCRNSHOT_STATUS
#define PI 3.1415927
+// retrieve the value using a mask +#define STATEIMAGETOINDEX(x) (((x) & LVIS_STATEIMAGEMASK) >> 12) + +// for listview with extend style LVS_EX_CHECKBOXES, State image 1 is the unchecked box, and state image 2 is the checked box. +// see this: https://docs.microsoft.com/en-us/windows/win32/controls/extended-list-view-s... +#define STATEIMAGE_UNCHECKED 1 +#define STATEIMAGE_CHECKED 2 + +enum APPLICATION_VIEW_MODE +{ + ApplicationViewEmpty, + ApplicationViewAvailableApps, + ApplicationViewInstalledApps +}; + typedef struct __ScrnshotDownloadParam { LONGLONG ID; @@ -73,6 +88,8 @@ typedef struct __ScrnshotDownloadParam ATL::CStringW DownloadFileName; } ScrnshotDownloadParam;
+class CMainWindow; + INT GetSystemColorDepth() { DEVMODEW pDevMode; @@ -250,41 +267,37 @@ public: return TRUE; }
- BOOL ShowInstalledAppInfo(PINSTALLED_INFO Info) + inline VOID InsertTextWithString(UINT StringID, DWORD StringFlags, const ATL::CStringW& Text, DWORD TextFlags) { - ATL::CStringW szText; - ATL::CStringW szInfo; + if (!Text.IsEmpty()) + { + LoadAndInsertText(StringID, Text, StringFlags, TextFlags); + } + }
- if (!Info || !Info->hSubKey) - return FALSE; + BOOL ShowInstalledAppInfo(CInstalledApplicationInfo * Info) + { + if (!Info) return FALSE;
- Info->GetApplicationString(L"DisplayName", szText); - SetText(szText, CFE_BOLD); + SetText(Info->szDisplayName, CFE_BOLD); InsertText(L"\n", 0);
-#define GET_INFO(a, b, c, d) \ - if (Info->GetApplicationString(a, szInfo)) \ - { \ - LoadAndInsertText(b, szInfo, c, d); \ - } - - GET_INFO(L"DisplayVersion", IDS_INFO_VERSION, CFE_BOLD, 0); - GET_INFO(L"Publisher", IDS_INFO_PUBLISHER, CFE_BOLD, 0); - GET_INFO(L"RegOwner", IDS_INFO_REGOWNER, CFE_BOLD, 0); - GET_INFO(L"ProductID", IDS_INFO_PRODUCTID, CFE_BOLD, 0); - GET_INFO(L"HelpLink", IDS_INFO_HELPLINK, CFE_BOLD, CFM_LINK); - GET_INFO(L"HelpTelephone", IDS_INFO_HELPPHONE, CFE_BOLD, 0); - GET_INFO(L"Readme", IDS_INFO_README, CFE_BOLD, 0); - GET_INFO(L"Contact", IDS_INFO_CONTACT, CFE_BOLD, 0); - GET_INFO(L"URLUpdateInfo", IDS_INFO_UPDATEINFO, CFE_BOLD, CFM_LINK); - GET_INFO(L"URLInfoAbout", IDS_INFO_INFOABOUT, CFE_BOLD, CFM_LINK); - GET_INFO(L"Comments", IDS_INFO_COMMENTS, CFE_BOLD, 0); - GET_INFO(L"InstallDate", IDS_INFO_INSTALLDATE, CFE_BOLD, 0); - GET_INFO(L"InstallLocation", IDS_INFO_INSTLOCATION, CFE_BOLD, 0); - GET_INFO(L"InstallSource", IDS_INFO_INSTALLSRC, CFE_BOLD, 0); - GET_INFO(L"UninstallString", IDS_INFO_UNINSTALLSTR, CFE_BOLD, 0); - GET_INFO(L"InstallSource", IDS_INFO_INSTALLSRC, CFE_BOLD, 0); - GET_INFO(L"ModifyPath", IDS_INFO_MODIFYPATH, CFE_BOLD, 0); + InsertTextWithString(IDS_INFO_VERSION, CFE_BOLD, Info->szDisplayVersion, 0); + InsertTextWithString(IDS_INFO_PUBLISHER, CFE_BOLD, Info->szPublisher, 0); + InsertTextWithString(IDS_INFO_REGOWNER, CFE_BOLD, Info->szRegOwner, 0); + InsertTextWithString(IDS_INFO_PRODUCTID, CFE_BOLD, Info->szProductID, 0); + InsertTextWithString(IDS_INFO_HELPLINK, CFE_BOLD, Info->szHelpLink, CFM_LINK); + InsertTextWithString(IDS_INFO_HELPPHONE, CFE_BOLD, Info->szHelpTelephone, 0); + InsertTextWithString(IDS_INFO_README, CFE_BOLD, Info->szReadme, 0); + InsertTextWithString(IDS_INFO_CONTACT, CFE_BOLD, Info->szContact, 0); + InsertTextWithString(IDS_INFO_UPDATEINFO, CFE_BOLD, Info->szURLUpdateInfo, CFM_LINK); + InsertTextWithString(IDS_INFO_INFOABOUT, CFE_BOLD, Info->szURLInfoAbout, CFM_LINK); + InsertTextWithString(IDS_INFO_COMMENTS, CFE_BOLD, Info->szComments, 0); + InsertTextWithString(IDS_INFO_INSTALLDATE, CFE_BOLD, Info->szInstallDate, 0); + InsertTextWithString(IDS_INFO_INSTLOCATION, CFE_BOLD, Info->szInstallLocation, 0); + InsertTextWithString(IDS_INFO_INSTALLSRC, CFE_BOLD, Info->szInstallSource, 0); + InsertTextWithString(IDS_INFO_UNINSTALLSTR, CFE_BOLD, Info->szUninstallString, 0); + InsertTextWithString(IDS_INFO_MODIFYPATH, CFE_BOLD, Info->szModifyPath, 0);
return TRUE; } @@ -908,8 +921,8 @@ private:
public:
- CAppRichEdit * RichEdit; - CAppScrnshotPreview * ScrnshotPrev; + CAppRichEdit * RichEdit = NULL; + CAppScrnshotPreview * ScrnshotPrev = NULL;
static ATL::CWndClassInfo& GetWndClassInfo() { @@ -957,7 +970,7 @@ public: return RichEdit->ShowAvailableAppInfo(Info); }
- BOOL ShowInstalledAppInfo(PINSTALLED_INFO Info) + BOOL ShowInstalledAppInfo(CInstalledApplicationInfo * Info) { ScrnshotPrev->DisplayEmpty(); ResizeChildren(); @@ -993,6 +1006,11 @@ public: } }
+ ~CAppInfoDisplay() + { + delete RichEdit; + delete ScrnshotPrev; + } };
class CMainToolbar : @@ -1177,16 +1195,19 @@ class CAppsListView : INT iSubItem; };
- BOOL bHasAllChecked; - BOOL bIsAscending; + BOOL bIsAscending = TRUE; BOOL bHasCheckboxes;
+ INT ItemCount = 0; + INT CheckedItemCount = 0; + INT ColumnCount = 0; + INT nLastHeaderID;
+ APPLICATION_VIEW_MODE ApplicationViewMode = ApplicationViewEmpty; + public: CAppsListView() : - bHasAllChecked(FALSE), - bIsAscending(TRUE), bHasCheckboxes(FALSE), nLastHeaderID(-1) { @@ -1221,6 +1242,7 @@ public: /* If the sorting column changed, remove the sorting style from the old column */ if ((nLastHeaderID != -1) && (nLastHeaderID != nHeaderID)) { + bIsAscending = TRUE; // also reset sorting method to ascending hColumn.mask = HDI_FORMAT; Header_GetItem(hHeader, nLastHeaderID, &hColumn); hColumn.fmt &= ~(HDF_SORTUP | HDF_SORTDOWN); @@ -1249,7 +1271,7 @@ public: return AddColumn(Index, const_cast<LPWSTR>(Text.GetString()), Width, Format); }
- BOOL AddColumn(INT Index, LPWSTR lpText, INT Width, INT Format) + int AddColumn(INT Index, LPWSTR lpText, INT Width, INT Format) { LVCOLUMNW Column;
@@ -1261,7 +1283,13 @@ public: Column.cx = Width; Column.fmt = Format;
- return (InsertColumn(Index, &Column) == -1) ? FALSE : TRUE; + return SendMessage(LVM_INSERTCOLUMN, Index, (LPARAM)(&Column)); + } + + void DeleteColumn(INT Index) + { + SendMessage(LVM_DELETECOLUMN, Index, 0); + return; }
INT AddItem(INT ItemIndex, INT IconIndex, LPCWSTR lpText, LPARAM lParam) @@ -1270,15 +1298,25 @@ public:
ZeroMemory(&Item, sizeof(Item));
- Item.mask = LVIF_TEXT | LVIF_PARAM | LVIF_STATE | LVIF_IMAGE; + Item.mask = LVIF_TEXT | LVIF_PARAM | LVIF_STATE; Item.pszText = const_cast<LPWSTR>(lpText); Item.lParam = lParam; Item.iItem = ItemIndex; Item.iImage = IconIndex;
+ if (IconIndex >= 0) + { + Item.iImage = IconIndex; + Item.mask |= LVIF_IMAGE; + } return InsertItem(&Item); }
+ HIMAGELIST GetImageList(int iImageList) + { + return (HIMAGELIST)SendMessage(LVM_GETIMAGELIST, iImageList, 0); + } + static INT CALLBACK s_CompareFunc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort) { SortContext * ctx = ((SortContext*) lParamSort); @@ -1319,6 +1357,12 @@ public: SetCheckboxesVisible(FALSE); }
+ HIMAGELIST hImageListView = ImageList_Create(LISTVIEW_ICON_SIZE, + LISTVIEW_ICON_SIZE, + GetSystemColorDepth() | ILC_MASK, + 0, 1); + SetImageList(hImageListView, LVSIL_SMALL); + return hwnd; }
@@ -1332,219 +1376,656 @@ public: if (bHasCheckboxes) { SetItemState(item, INDEXTOSTATEIMAGEMASK((fCheck) ? 2 : 1), LVIS_STATEIMAGEMASK); - SetSelected(item, fCheck); } }
- VOID SetSelected(INT item, BOOL value) + VOID CheckAll() { - if (item < 0) + if (bHasCheckboxes) { - for (INT i = 0; i >= 0; i = GetNextItem(i, LVNI_ALL)) + if (CheckedItemCount == ItemCount) { - CAvailableApplicationInfo* pAppInfo = (CAvailableApplicationInfo*) GetItemData(i); - if (pAppInfo) - { - pAppInfo->m_IsSelected = value; - } + // clear all + SetCheckState(-1, FALSE); } - } - else - { - CAvailableApplicationInfo* pAppInfo = (CAvailableApplicationInfo*) GetItemData(item); - if (pAppInfo) + else { - pAppInfo->m_IsSelected = value; + // check all + SetCheckState(-1, TRUE); } } } - - VOID CheckAll() + + PVOID GetFocusedItemData() { - if (bHasCheckboxes) + INT item = GetSelectionMark(); + if (item == -1) { - bHasAllChecked = !bHasAllChecked; - SetCheckState(-1, bHasAllChecked); + return (PVOID)0; } + return (PVOID)GetItemData(item); }
- ATL::CSimpleArray<CAvailableApplicationInfo> GetCheckedItems() + BOOL SetDisplayMode(APPLICATION_VIEW_MODE Mode) { - if (!bHasCheckboxes) + if (!DeleteAllItems()) return FALSE; + ApplicationViewMode = Mode; + + bIsAscending = TRUE; + + ItemCount = 0; + CheckedItemCount = 0; + + // delete old columns + while (ColumnCount) { - return ATL::CSimpleArray<CAvailableApplicationInfo>(); + DeleteColumn(--ColumnCount); }
- ATL::CSimpleArray<CAvailableApplicationInfo> list; - for (INT i = 0; i >= 0; i = GetNextItem(i, LVNI_ALL)) + ImageList_RemoveAll(GetImageList(LVSIL_SMALL)); + + // add new columns + ATL::CStringW szText; + switch (Mode) { - if (GetCheckState(i) != FALSE) - { - CAvailableApplicationInfo* pAppInfo = (CAvailableApplicationInfo*) GetItemData(i); - list.Add(*pAppInfo); - } + case ApplicationViewInstalledApps: + + /* Add columns to ListView */ + szText.LoadStringW(IDS_APP_NAME); + AddColumn(ColumnCount++, szText, 250, LVCFMT_LEFT); + + szText.LoadStringW(IDS_APP_INST_VERSION); + AddColumn(ColumnCount++, szText, 90, LVCFMT_RIGHT); + + szText.LoadStringW(IDS_APP_DESCRIPTION); + AddColumn(ColumnCount++, szText, 300, LVCFMT_LEFT); + + // disable checkboxes + SetCheckboxesVisible(FALSE); + break; + + case ApplicationViewAvailableApps: + + /* Add columns to ListView */ + szText.LoadStringW(IDS_APP_NAME); + AddColumn(ColumnCount++, szText, 250, LVCFMT_LEFT); + + szText.LoadStringW(IDS_APP_INST_VERSION); + AddColumn(ColumnCount++, szText, 90, LVCFMT_RIGHT); + + szText.LoadStringW(IDS_APP_DESCRIPTION); + AddColumn(ColumnCount++, szText, 300, LVCFMT_LEFT); + + // enable checkboxes + SetCheckboxesVisible(TRUE); + break; + + case ApplicationViewEmpty: + default: + break; } - return list; + + + return TRUE; }
- CAvailableApplicationInfo* GetSelectedData() + BOOL AddInstalledApplication(CInstalledApplicationInfo *InstAppInfo, LPVOID CallbackParam) { - INT item = GetSelectionMark(); - return (CAvailableApplicationInfo*) GetItemData(item); - } -}; + if (ApplicationViewMode != ApplicationViewInstalledApps) + { + return FALSE; + }
-class CSideTreeView : - public CUiWindow<CTreeView> -{ - HIMAGELIST hImageTreeView; + HICON hIcon = (HICON)LoadIconW(hInst, MAKEINTRESOURCEW(IDI_MAIN)); + HIMAGELIST hImageList = GetImageList(LVSIL_SMALL); + int IconIndex = ImageList_AddIcon(hImageList, hIcon); + DestroyIcon(hIcon);
-public: - CSideTreeView() : - CUiWindow(), - hImageTreeView(ImageList_Create(TREEVIEW_ICON_SIZE, TREEVIEW_ICON_SIZE, - GetSystemColorDepth() | ILC_MASK, - 0, 1)) - { - } + int Index = AddItem(ItemCount, IconIndex, InstAppInfo->szDisplayName, (LPARAM)CallbackParam); + SetItemText(Index, 1, InstAppInfo->szDisplayVersion.IsEmpty() ? L"---" : InstAppInfo->szDisplayVersion); + SetItemText(Index, 2, InstAppInfo->szComments.IsEmpty() ? L"---" : InstAppInfo->szComments);
- HTREEITEM AddItem(HTREEITEM hParent, ATL::CStringW &Text, INT Image, INT SelectedImage, LPARAM lParam) - { - return CUiWindow<CTreeView>::AddItem(hParent, const_cast<LPWSTR>(Text.GetString()), Image, SelectedImage, lParam); + ItemCount++; + return TRUE; }
- HTREEITEM AddCategory(HTREEITEM hRootItem, UINT TextIndex, UINT IconIndex) + BOOL AddAvailableApplication(CAvailableApplicationInfo *AvlbAppInfo, BOOL InitCheckState, LPVOID CallbackParam) { - ATL::CStringW szText; - INT Index; - HICON hIcon; + if (ApplicationViewMode != ApplicationViewAvailableApps) + { + return FALSE; + }
- hIcon = (HICON) LoadImageW(hInst, - MAKEINTRESOURCE(IconIndex), - IMAGE_ICON, - TREEVIEW_ICON_SIZE, - TREEVIEW_ICON_SIZE, - LR_CREATEDIBSECTION); - if (hIcon) + /* Load icon from file */ + HICON hIcon = NULL; + ATL::CStringW szIconPath; + if (AvlbAppInfo->RetrieveIcon(szIconPath)) { - Index = ImageList_AddIcon(hImageTreeView, hIcon); - DestroyIcon(hIcon); + hIcon = (HICON)LoadImageW(NULL, + szIconPath.GetString(), + IMAGE_ICON, + LISTVIEW_ICON_SIZE, + LISTVIEW_ICON_SIZE, + LR_LOADFROMFILE); }
- szText.LoadStringW(TextIndex); - return AddItem(hRootItem, szText, Index, Index, TextIndex); - } + if (!hIcon || GetLastError() != ERROR_SUCCESS) + { + /* Load default icon */ + hIcon = (HICON)LoadIconW(hInst, MAKEINTRESOURCEW(IDI_MAIN)); + }
- HIMAGELIST SetImageList() - { - return CUiWindow<CTreeView>::SetImageList(hImageTreeView, TVSIL_NORMAL); - } + HIMAGELIST hImageList = GetImageList(LVSIL_SMALL);
- VOID DestroyImageList() - { - if (hImageTreeView) - ImageList_Destroy(hImageTreeView); - } + int IconIndex = ImageList_AddIcon(hImageList, hIcon); + DestroyIcon(hIcon);
- ~CSideTreeView() - { - DestroyImageList(); - } -}; + int Index = AddItem(ItemCount, IconIndex, AvlbAppInfo->m_szName, (LPARAM)CallbackParam);
-class CSearchBar : - public CWindow -{ -public: - const INT m_Width; - const INT m_Height; + if (InitCheckState) + { + SetCheckState(Index, TRUE); + }
- CSearchBar() : m_Width(200), m_Height(22) - { - } + SetItemText(Index, 1, AvlbAppInfo->m_szVersion); + SetItemText(Index, 2, AvlbAppInfo->m_szDesc);
- VOID SetText(LPCWSTR lpszText) - { - SendMessageW(SB_SETTEXT, SBT_NOBORDERS, (LPARAM) lpszText); + ItemCount++; + return TRUE; }
- HWND Create(HWND hwndParent) + // this function is called when parent window receiving an notification about checkstate changing + VOID ItemCheckStateNotify(int iItem, BOOL bCheck) { - ATL::CStringW szBuf; - m_hWnd = CreateWindowExW(WS_EX_CLIENTEDGE, L"Edit", NULL, - WS_CHILD | WS_VISIBLE | ES_LEFT | ES_AUTOHSCROLL, - 0, 0, m_Width, m_Height, - hwndParent, (HMENU) NULL, - hInst, 0); - - SendMessageW(WM_SETFONT, (WPARAM) GetStockObject(DEFAULT_GUI_FONT), 0); - szBuf.LoadStringW(IDS_SEARCH_TEXT); - SetWindowTextW(szBuf); - return m_hWnd; + if (bCheck) + { + CheckedItemCount++; + } + else + { + CheckedItemCount--; + } } - };
-class CMainWindow : - public CWindowImpl<CMainWindow, CWindow, CFrameWinTraits> +class CApplicationView : + public CUiWindow<CWindowImpl<CApplicationView>> { - CUiPanel* m_ClientPanel; - CUiSplitPanel* m_VSplitter; - CUiSplitPanel* m_HSplitter; +private: + CUiPanel *m_Panel = NULL;
- CMainToolbar* m_Toolbar; - CAppsListView* m_ListView; + CAppsListView *m_ListView = NULL; + CAppInfoDisplay *m_AppsInfo = NULL; + CUiSplitPanel *m_HSplitter = NULL; + CMainWindow *m_MainWindow = NULL; + APPLICATION_VIEW_MODE ApplicationViewMode = ApplicationViewEmpty;
- CSideTreeView* m_TreeView; - CUiWindow<CStatusBar>* m_StatusBar; - CAppInfoDisplay* m_AppInfo; + BOOL ProcessWindowMessage(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam, LRESULT& theResult, DWORD dwMapId) + { + theResult = 0; + switch (message) + { + case WM_CREATE: + { + BOOL bSuccess = TRUE; + m_Panel = new CUiPanel(); + m_Panel->m_VerticalAlignment = UiAlign_Stretch; + m_Panel->m_HorizontalAlignment = UiAlign_Stretch;
- CUiWindow<CSearchBar>* m_SearchBar; - CAvailableApps m_AvailableApps; + bSuccess &= CreateHSplitter(); + bSuccess &= CreateListView(); + bSuccess &= CreateAppInfoDisplay();
- INT nSelectedApps; + if (!bSuccess) + { + return -1; // creation failure + } + } + break; + case WM_NOTIFY: + { + LPNMHDR pNotifyHeader = (LPNMHDR)lParam; + if (pNotifyHeader->hwndFrom == m_ListView->GetWindow()) + { + switch (pNotifyHeader->code) + { + case LVN_ITEMCHANGED: + { + LPNMLISTVIEW pnic = (LPNMLISTVIEW)lParam;
- BOOL bSearchEnabled; - BOOL bUpdating; + /* Check if this is a valid item + * (technically, it can be also an unselect) */ + INT ItemIndex = pnic->iItem; + if (ItemIndex == -1 || + ItemIndex >= ListView_GetItemCount(pnic->hdr.hwndFrom)) + { + break; + }
- ATL::CStringW szSearchPattern; - INT SelectedEnumType; + /* Check if the focus has been moved to another item */ + if ((pnic->uChanged & LVIF_STATE) && + (pnic->uNewState & LVIS_FOCUSED) && + !(pnic->uOldState & LVIS_FOCUSED)) + { + ItemGetFocus((LPVOID)pnic->lParam); + }
-public: - CMainWindow() : - m_ClientPanel(NULL), - bSearchEnabled(FALSE), - SelectedEnumType(ENUM_ALL_INSTALLED) - { - } + /* Check if the item is checked/unchecked */ + if (pnic->uChanged & LVIF_STATE) + { + int iOldState = STATEIMAGETOINDEX(pnic->uOldState); + int iNewState = STATEIMAGETOINDEX(pnic->uNewState); + + if (iOldState == STATEIMAGE_UNCHECKED && iNewState == STATEIMAGE_CHECKED) + { + // this item is just checked + m_ListView->ItemCheckStateNotify(pnic->iItem, TRUE); + ItemCheckStateChanged(TRUE, (LPVOID)pnic->lParam); + } + else if (iOldState == STATEIMAGE_CHECKED && iNewState == STATEIMAGE_UNCHECKED) + { + // this item is just unchecked + m_ListView->ItemCheckStateNotify(pnic->iItem, FALSE); + ItemCheckStateChanged(FALSE, (LPVOID)pnic->lParam); + } + } + } + break;
-private: - VOID InitApplicationsList() - { - ATL::CStringW szText; + case LVN_COLUMNCLICK: + { + LPNMLISTVIEW pnmv = (LPNMLISTVIEW)lParam; + + m_ListView->ColumnClick(pnmv); + } + break;
- /* Add columns to ListView */ - szText.LoadStringW(IDS_APP_NAME); - m_ListView->AddColumn(0, szText, 250, LVCFMT_LEFT); + case NM_DBLCLK: + { + if (((LPNMLISTVIEW)lParam)->iItem != -1) + { + /* this won't do anything if the program is already installed */ + + // TODO: the same problem I've mentioned in NM_RCLICK + // I think if user double-click this app, then this app should be installed, not those checked apps. + if (ApplicationViewMode == ApplicationViewAvailableApps) + { + SendMessageW(GetParent(), WM_COMMAND, ID_INSTALL, 0); + } + } + } + break;
- szText.LoadStringW(IDS_APP_INST_VERSION); - m_ListView->AddColumn(1, szText, 90, LVCFMT_RIGHT); + case NM_RCLICK: + { + if (((LPNMLISTVIEW)lParam)->iItem != -1) + { + // TODO: currently the menu will send WM_COMMAND directly to MainWindow. + // possibly it should be handled by this application-view first + // and then forward to mainwindow. + + // TODO: I think if user right-click on one item, and select "Install" + // that means the user want install "this" application + // but if some apps are checked, this will lead to those checked apps to be installed. + // this should be improved. + ShowPopupMenu(m_ListView->m_hWnd, 0, ID_INSTALL); + } + } + break; + } + } + } + break;
- szText.LoadStringW(IDS_APP_DESCRIPTION); - m_ListView->AddColumn(3, szText, 300, LVCFMT_LEFT); + case WM_SYSCOLORCHANGE: + { + /* Forward WM_SYSCOLORCHANGE to common controls */ + m_ListView->SendMessageW(WM_SYSCOLORCHANGE, wParam, lParam); + m_ListView->SendMessageW(EM_SETBKGNDCOLOR, 0, GetSysColor(COLOR_BTNFACE)); + } + break;
- // Unnesesary since the list updates on every TreeView selection - // UpdateApplicationsList(ENUM_ALL_COMPONENTS); + case WM_SIZE: + { + OnSize(hwnd, wParam, lParam); + break; + } + } + return FALSE; }
- VOID InitCategoriesList() + BOOL CreateHSplitter() { - HTREEITEM hRootItemInstalled, hRootItemAvailable; - - hRootItemInstalled = m_TreeView->AddCategory(TVI_ROOT, IDS_INSTALLED, IDI_CATEGORY); - m_TreeView->AddCategory(hRootItemInstalled, IDS_APPLICATIONS, IDI_APPS); - m_TreeView->AddCategory(hRootItemInstalled, IDS_UPDATES, IDI_APPUPD); - + m_HSplitter = new CUiSplitPanel(); + m_HSplitter->m_VerticalAlignment = UiAlign_Stretch; + m_HSplitter->m_HorizontalAlignment = UiAlign_Stretch; + m_HSplitter->m_DynamicFirst = TRUE; + m_HSplitter->m_Horizontal = TRUE; + m_HSplitter->m_Pos = INT_MAX; //set INT_MAX to use lowest possible position (m_MinSecond) + m_HSplitter->m_MinFirst = 10; + m_HSplitter->m_MinSecond = 140; + m_Panel->Children().Append(m_HSplitter); + + return m_HSplitter->Create(m_hWnd) != NULL; + } + + BOOL CreateListView() + { + m_ListView = new CAppsListView(); + m_ListView->m_VerticalAlignment = UiAlign_Stretch; + m_ListView->m_HorizontalAlignment = UiAlign_Stretch; + m_HSplitter->First().Append(m_ListView); + + return m_ListView->Create(m_hWnd) != NULL; + } + + BOOL CreateAppInfoDisplay() + { + m_AppsInfo = new CAppInfoDisplay(); + m_AppsInfo->m_VerticalAlignment = UiAlign_Stretch; + m_AppsInfo->m_HorizontalAlignment = UiAlign_Stretch; + m_HSplitter->Second().Append(m_AppsInfo); + + return m_AppsInfo->Create(m_hWnd) != NULL; + } + + VOID OnSize(HWND hwnd, WPARAM wParam, LPARAM lParam) + { + if (wParam == SIZE_MINIMIZED) + return; + + RECT r = { 0, 0, LOWORD(lParam), HIWORD(lParam) }; + HDWP hdwp = NULL; + INT count = m_Panel->CountSizableChildren(); + + hdwp = BeginDeferWindowPos(count); + if (hdwp) + { + hdwp = m_Panel->OnParentSize(r, hdwp); + if (hdwp) + { + EndDeferWindowPos(hdwp); + } + } + } + +public: + + CApplicationView(CMainWindow *MainWindow) + : m_MainWindow(MainWindow) + { + } + + ~CApplicationView() + { + delete m_ListView; + delete m_AppsInfo; + delete m_HSplitter; + } + + static ATL::CWndClassInfo& GetWndClassInfo() + { + DWORD csStyle = CS_VREDRAW | CS_HREDRAW; + static ATL::CWndClassInfo wc = + { + { + sizeof(WNDCLASSEX), + csStyle, + StartWindowProc, + 0, + 0, + NULL, + NULL, + NULL, + (HBRUSH)(COLOR_BTNFACE + 1), + NULL, + L"RAppsApplicationView", + NULL + }, + NULL, NULL, IDC_ARROW, TRUE, 0, _T("") + }; + return wc; + } + + HWND Create(HWND hwndParent) + { + RECT r = { 0,0,0,0 }; + + return CWindowImpl::Create(hwndParent, r, L"", WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS); + } + + BOOL SetDisplayMode(APPLICATION_VIEW_MODE Mode) + { + if (!m_ListView->SetDisplayMode(Mode)) + { + return FALSE; + } + ApplicationViewMode = Mode; + m_AppsInfo->SetWelcomeText(); + + HMENU lvwMenu = ::GetMenu(m_ListView->m_hWnd); + switch (Mode) + { + case ApplicationViewEmpty: + default: + EnableMenuItem(lvwMenu, ID_REGREMOVE, MF_GRAYED); + EnableMenuItem(lvwMenu, ID_INSTALL, MF_GRAYED); + EnableMenuItem(lvwMenu, ID_UNINSTALL, MF_GRAYED); + EnableMenuItem(lvwMenu, ID_MODIFY, MF_GRAYED); + break; + + case ApplicationViewInstalledApps: + EnableMenuItem(lvwMenu, ID_REGREMOVE, MF_ENABLED); + EnableMenuItem(lvwMenu, ID_INSTALL, MF_GRAYED); + EnableMenuItem(lvwMenu, ID_UNINSTALL, MF_ENABLED); + EnableMenuItem(lvwMenu, ID_MODIFY, MF_ENABLED); + break; + + case ApplicationViewAvailableApps: + EnableMenuItem(lvwMenu, ID_REGREMOVE, MF_GRAYED); + EnableMenuItem(lvwMenu, ID_INSTALL, MF_ENABLED); + EnableMenuItem(lvwMenu, ID_UNINSTALL, MF_GRAYED); + EnableMenuItem(lvwMenu, ID_MODIFY, MF_GRAYED); + break; + } + return TRUE; + } + + BOOL AddInstalledApplication(CInstalledApplicationInfo * InstAppInfo, LPVOID param) + { + if (ApplicationViewMode != ApplicationViewInstalledApps) + { + return FALSE; + } + return m_ListView->AddInstalledApplication(InstAppInfo, param); + } + + BOOL AddAvailableApplication(CAvailableApplicationInfo * AvlbAppInfo, BOOL InitCheckState, LPVOID param) + { + if (ApplicationViewMode != ApplicationViewAvailableApps) + { + return FALSE; + } + return m_ListView->AddAvailableApplication(AvlbAppInfo, InitCheckState, param); + } + + void CheckAll() + { + m_ListView->CheckAll(); + return; + } + + PVOID GetFocusedItemData() + { + return m_ListView->GetFocusedItemData(); + } + + int GetItemCount() + { + return m_ListView->GetItemCount(); + } + + // this function is called when a item of listview get focus. + // CallbackParam is the param passed to listview when adding the item (the one getting focus now). + BOOL ItemGetFocus(LPVOID CallbackParam) + { + switch (ApplicationViewMode) + { + case ApplicationViewInstalledApps: + return m_AppsInfo->ShowInstalledAppInfo((CInstalledApplicationInfo *)CallbackParam); + + case ApplicationViewAvailableApps: + return m_AppsInfo->ShowAvailableAppInfo((CAvailableApplicationInfo *)CallbackParam); + + case ApplicationViewEmpty: + default: + m_AppsInfo->SetWelcomeText(); + return FALSE; + } + } + + // this function is called when a item of listview is checked/unchecked + // CallbackParam is the param passed to listview when adding the item (the one getting focus now). + BOOL ItemCheckStateChanged(BOOL bChecked, LPVOID CallbackParam); + +}; + +class CSideTreeView : + public CUiWindow<CTreeView> +{ + HIMAGELIST hImageTreeView; + +public: + CSideTreeView() : + CUiWindow(), + hImageTreeView(ImageList_Create(TREEVIEW_ICON_SIZE, TREEVIEW_ICON_SIZE, + GetSystemColorDepth() | ILC_MASK, + 0, 1)) + { + } + + HTREEITEM AddItem(HTREEITEM hParent, ATL::CStringW &Text, INT Image, INT SelectedImage, LPARAM lParam) + { + return CUiWindow<CTreeView>::AddItem(hParent, const_cast<LPWSTR>(Text.GetString()), Image, SelectedImage, lParam); + } + + HTREEITEM AddCategory(HTREEITEM hRootItem, UINT TextIndex, UINT IconIndex) + { + ATL::CStringW szText; + INT Index; + HICON hIcon; + + hIcon = (HICON) LoadImageW(hInst, + MAKEINTRESOURCE(IconIndex), + IMAGE_ICON, + TREEVIEW_ICON_SIZE, + TREEVIEW_ICON_SIZE, + LR_CREATEDIBSECTION); + if (hIcon) + { + Index = ImageList_AddIcon(hImageTreeView, hIcon); + DestroyIcon(hIcon); + } + + szText.LoadStringW(TextIndex); + return AddItem(hRootItem, szText, Index, Index, TextIndex); + } + + HIMAGELIST SetImageList() + { + return CUiWindow<CTreeView>::SetImageList(hImageTreeView, TVSIL_NORMAL); + } + + VOID DestroyImageList() + { + if (hImageTreeView) + ImageList_Destroy(hImageTreeView); + } + + ~CSideTreeView() + { + DestroyImageList(); + } +}; + +class CSearchBar : + public CWindow +{ +public: + const INT m_Width; + const INT m_Height; + + CSearchBar() : m_Width(200), m_Height(22) + { + } + + VOID SetText(LPCWSTR lpszText) + { + SendMessageW(SB_SETTEXT, SBT_NOBORDERS, (LPARAM) lpszText); + } + + HWND Create(HWND hwndParent) + { + ATL::CStringW szBuf; + m_hWnd = CreateWindowExW(WS_EX_CLIENTEDGE, L"Edit", NULL, + WS_CHILD | WS_VISIBLE | ES_LEFT | ES_AUTOHSCROLL, + 0, 0, m_Width, m_Height, + hwndParent, (HMENU) NULL, + hInst, 0); + + SendMessageW(WM_SETFONT, (WPARAM) GetStockObject(DEFAULT_GUI_FONT), 0); + szBuf.LoadStringW(IDS_SEARCH_TEXT); + SetWindowTextW(szBuf); + return m_hWnd; + } + +}; + +class CMainWindow : + public CWindowImpl<CMainWindow, CWindow, CFrameWinTraits> +{ + CUiPanel* m_ClientPanel = NULL; + CUiSplitPanel* m_VSplitter = NULL; + + CMainToolbar* m_Toolbar = NULL; + + CSideTreeView* m_TreeView = NULL; + CUiWindow<CStatusBar>* m_StatusBar = NULL; + + CApplicationView* m_ApplicationView = NULL; + + CUiWindow<CSearchBar>* m_SearchBar = NULL; + CAvailableApps m_AvailableApps; + CInstalledApps m_InstalledApps; + + BOOL bSearchEnabled; + BOOL bUpdating = FALSE; + + ATL::CStringW szSearchPattern; + INT SelectedEnumType; + +public: + CMainWindow() : + m_ClientPanel(NULL), + bSearchEnabled(FALSE), + SelectedEnumType(ENUM_ALL_INSTALLED) + { + } + + ~CMainWindow() + { + LayoutCleanup(); + } +private: + + VOID InitCategoriesList() + { + HTREEITEM hRootItemInstalled, hRootItemAvailable; + + hRootItemInstalled = m_TreeView->AddCategory(TVI_ROOT, IDS_INSTALLED, IDI_CATEGORY); + m_TreeView->AddCategory(hRootItemInstalled, IDS_APPLICATIONS, IDI_APPS); + m_TreeView->AddCategory(hRootItemInstalled, IDS_UPDATES, IDI_APPUPD); + m_TreeView->AddCategory(TVI_ROOT, IDS_SELECTEDFORINST, IDI_SELECTEDFORINST);
hRootItemAvailable = m_TreeView->AddCategory(TVI_ROOT, IDS_AVAILABLEFORINST, IDI_CATEGORY); @@ -1601,24 +2082,14 @@ private: return m_TreeView->Create(m_hWnd) != NULL; }
- BOOL CreateListView() + BOOL CreateApplicationView() { - m_ListView = new CAppsListView(); - m_ListView->m_VerticalAlignment = UiAlign_Stretch; - m_ListView->m_HorizontalAlignment = UiAlign_Stretch; - m_HSplitter->First().Append(m_ListView); + m_ApplicationView = new CApplicationView(this); // pass this to ApplicationView for callback purpose + m_ApplicationView->m_VerticalAlignment = UiAlign_Stretch; + m_ApplicationView->m_HorizontalAlignment = UiAlign_Stretch; + m_VSplitter->Second().Append(m_ApplicationView);
- return m_ListView->Create(m_hWnd) != NULL; - } - - BOOL CreateRichEdit() - { - m_AppInfo = new CAppInfoDisplay(); - m_AppInfo->m_VerticalAlignment = UiAlign_Stretch; - m_AppInfo->m_HorizontalAlignment = UiAlign_Stretch; - m_HSplitter->Second().Append(m_AppInfo); - - return m_AppInfo->Create(m_hWnd) != NULL; + return m_ApplicationView->Create(m_hWnd) != NULL; }
BOOL CreateVSplitter() @@ -1636,21 +2107,6 @@ private: return m_VSplitter->Create(m_hWnd) != NULL; }
- BOOL CreateHSplitter() - { - m_HSplitter = new CUiSplitPanel(); - m_HSplitter->m_VerticalAlignment = UiAlign_Stretch; - m_HSplitter->m_HorizontalAlignment = UiAlign_Stretch; - m_HSplitter->m_DynamicFirst = TRUE; - m_HSplitter->m_Horizontal = TRUE; - m_HSplitter->m_Pos = INT_MAX; //set INT_MAX to use lowest possible position (m_MinSecond) - m_HSplitter->m_MinFirst = 10; - m_HSplitter->m_MinSecond = 140; - m_VSplitter->Second().Append(m_HSplitter); - - return m_HSplitter->Create(m_hWnd) != NULL; - } - BOOL CreateSearchBar() { m_SearchBar = new CUiWindow<CSearchBar>(); @@ -1678,12 +2134,8 @@ private: b = b && CreateVSplitter();
// Inside V Splitter - b = b && CreateHSplitter(); b = b && CreateTreeView(); - - // Inside H Splitter - b = b && CreateListView(); - b = b && CreateRichEdit(); + b = b && CreateApplicationView();
if (b) { @@ -1707,15 +2159,23 @@ private: return b; }
+ VOID LayoutCleanup() + { + delete m_TreeView; + delete m_ApplicationView; + delete m_VSplitter; + delete m_SearchBar; + delete m_Toolbar; + delete m_StatusBar; + return; + } + BOOL InitControls() { if (CreateLayout()) { - - InitApplicationsList(); InitCategoriesList();
- nSelectedApps = 0; UpdateStatusBarText();
return TRUE; @@ -1724,28 +2184,6 @@ private: return FALSE; }
- VOID ShowAppInfo(INT Index) - { - if (IsInstalledEnum(SelectedEnumType)) - { - if (Index == -1) - Index = m_ListView->GetSelectionMark(); - - PINSTALLED_INFO Info = (PINSTALLED_INFO) m_ListView->GetItemData(Index); - - m_AppInfo->ShowInstalledAppInfo(Info); - } - else if (IsAvailableEnum(SelectedEnumType)) - { - if (Index == -1) - return; - - CAvailableApplicationInfo* Info = (CAvailableApplicationInfo*) m_ListView->GetItemData(Index); - - m_AppInfo->ShowAvailableAppInfo(Info); - } - } - VOID OnSize(HWND hwnd, WPARAM wParam, LPARAM lParam) { if (wParam == SIZE_MINIMIZED) @@ -1782,7 +2220,6 @@ private: { EndDeferWindowPos(hdwp); } - }
// TODO: Sub-layouts for children of children @@ -1796,64 +2233,47 @@ private: EndDeferWindowPos(hdwp); } } - }
- VOID RemoveSelectedAppFromRegistry() + BOOL RemoveSelectedAppFromRegistry() { - PINSTALLED_INFO Info; - WCHAR szFullName[MAX_PATH] = L"Software\Microsoft\Windows\CurrentVersion\Uninstall\"; - ATL::CStringW szMsgText, szMsgTitle; - INT ItemIndex = m_ListView->GetNextItem(-1, LVNI_FOCUSED); - if (!IsInstalledEnum(SelectedEnumType)) - return; + return FALSE;
- Info = reinterpret_cast<PINSTALLED_INFO>(m_ListView->GetItemData(ItemIndex)); - if (!Info || !Info->hSubKey || (ItemIndex == -1)) - return; + ATL::CStringW szMsgText, szMsgTitle;
if (!szMsgText.LoadStringW(IDS_APP_REG_REMOVE) || !szMsgTitle.LoadStringW(IDS_INFORMATION)) - return; + return FALSE;
if (MessageBoxW(szMsgText, szMsgTitle, MB_YESNO | MB_ICONQUESTION) == IDYES) { - ATL::CStringW::CopyChars(szFullName, - MAX_PATH, - Info->szKeyName.GetString(), - MAX_PATH - wcslen(szFullName)); - - if (RegDeleteKeyW(Info->hRootKey, szFullName) == ERROR_SUCCESS) + CInstalledApplicationInfo *InstalledApp = (CInstalledApplicationInfo *)m_ApplicationView->GetFocusedItemData(); + LSTATUS Result = InstalledApp->RemoveFromRegistry(); + if (Result != ERROR_SUCCESS) { - m_ListView->DeleteItem(ItemIndex); - return; + // TODO: popup a messagebox telling user it fails somehow + return FALSE; }
- if (!szMsgText.LoadStringW(IDS_UNABLE_TO_REMOVE)) - return; - - MessageBoxW(szMsgText.GetString(), NULL, MB_OK | MB_ICONERROR); + // as it's already removed form registry, this will also remove it from the list + UpdateApplicationsList(-1); + return TRUE; } + + return FALSE; }
BOOL UninstallSelectedApp(BOOL bModify) { - WCHAR szAppName[MAX_STR_LEN]; - if (!IsInstalledEnum(SelectedEnumType)) return FALSE;
- INT ItemIndex = m_ListView->GetNextItem(-1, LVNI_FOCUSED); - if (ItemIndex == -1) - return FALSE; - - m_ListView->GetItemText(ItemIndex, 0, szAppName, _countof(szAppName)); - WriteLogMessage(EVENTLOG_SUCCESS, MSG_SUCCESS_REMOVE, szAppName); + CInstalledApplicationInfo *InstalledApp = (CInstalledApplicationInfo *)m_ApplicationView->GetFocusedItemData();
- PINSTALLED_INFO ItemInfo = (PINSTALLED_INFO)m_ListView->GetItemData(ItemIndex); - return UninstallApplication(ItemInfo, bModify); + return InstalledApp->UninstallApplication(bModify); } + BOOL ProcessWindowMessage(HWND hwnd, UINT Msg, WPARAM wParam, LPARAM lParam, LRESULT& theResult, DWORD dwMapId) { theResult = 0; @@ -1871,9 +2291,7 @@ private:
FreeLogs(); m_AvailableApps.FreeCachedEntries(); - - if (IsInstalledEnum(SelectedEnumType)) - FreeInstalledAppList(); + m_InstalledApps.FreeCachedEntries();
delete m_ClientPanel;
@@ -1984,7 +2402,6 @@ private: }
HMENU mainMenu = ::GetMenu(hwnd); - HMENU lvwMenu = ::GetMenu(m_ListView->m_hWnd);
/* Disable/enable items based on treeview selection */ if (IsSelectedNodeInstalled()) @@ -1994,11 +2411,6 @@ private: EnableMenuItem(mainMenu, ID_UNINSTALL, MF_ENABLED); EnableMenuItem(mainMenu, ID_MODIFY, MF_ENABLED);
- EnableMenuItem(lvwMenu, ID_REGREMOVE, MF_ENABLED); - EnableMenuItem(lvwMenu, ID_INSTALL, MF_GRAYED); - EnableMenuItem(lvwMenu, ID_UNINSTALL, MF_ENABLED); - EnableMenuItem(lvwMenu, ID_MODIFY, MF_ENABLED); - m_Toolbar->SendMessageW(TB_ENABLEBUTTON, ID_REGREMOVE, TRUE); m_Toolbar->SendMessageW(TB_ENABLEBUTTON, ID_INSTALL, FALSE); m_Toolbar->SendMessageW(TB_ENABLEBUTTON, ID_UNINSTALL, TRUE); @@ -2011,11 +2423,6 @@ private: EnableMenuItem(mainMenu, ID_UNINSTALL, MF_GRAYED); EnableMenuItem(mainMenu, ID_MODIFY, MF_GRAYED);
- EnableMenuItem(lvwMenu, ID_REGREMOVE, MF_GRAYED); - EnableMenuItem(lvwMenu, ID_INSTALL, MF_ENABLED); - EnableMenuItem(lvwMenu, ID_UNINSTALL, MF_GRAYED); - EnableMenuItem(lvwMenu, ID_MODIFY, MF_GRAYED); - m_Toolbar->SendMessageW(TB_ENABLEBUTTON, ID_REGREMOVE, FALSE); m_Toolbar->SendMessageW(TB_ENABLEBUTTON, ID_INSTALL, TRUE); m_Toolbar->SendMessageW(TB_ENABLEBUTTON, ID_UNINSTALL, FALSE); @@ -2024,91 +2431,6 @@ private: } break;
- case LVN_ITEMCHANGED: - { - LPNMLISTVIEW pnic = (LPNMLISTVIEW) lParam; - - if (pnic->hdr.hwndFrom == m_ListView->m_hWnd) - { - /* Check if this is a valid item - * (technically, it can be also an unselect) */ - INT ItemIndex = pnic->iItem; - if (ItemIndex == -1 || - ItemIndex >= ListView_GetItemCount(pnic->hdr.hwndFrom)) - { - break; - } - - /* Check if the focus has been moved to another item */ - if ((pnic->uChanged & LVIF_STATE) && - (pnic->uNewState & LVIS_FOCUSED) && - !(pnic->uOldState & LVIS_FOCUSED)) - { - ShowAppInfo(ItemIndex); - } - /* Check if the item is checked */ - if ((pnic->uNewState & LVIS_STATEIMAGEMASK) && !bUpdating) - { - BOOL checked = m_ListView->GetCheckState(pnic->iItem); - /* FIXME: HAX! - - preventing decremention below zero as a safeguard for ReactOS - In ReactOS this action is triggered whenever user changes *selection*, but should be only when *checkbox* state toggled - Maybe LVIS_STATEIMAGEMASK is set incorrectly - */ - nSelectedApps += - (checked) - ? 1 - : ((nSelectedApps > 0) - ? -1 - : 0); - - /* Update item's selection status */ - m_ListView->SetSelected(pnic->iItem, checked); - - UpdateStatusBarText(); - } - } - } - break; - - case LVN_COLUMNCLICK: - { - LPNMLISTVIEW pnmv = (LPNMLISTVIEW) lParam; - - m_ListView->ColumnClick(pnmv); - } - break; - - case NM_CLICK: - { - if (data->hwndFrom == m_ListView->m_hWnd && ((LPNMLISTVIEW) lParam)->iItem != -1) - { - ShowAppInfo(-1); - } - } - break; - - case NM_DBLCLK: - { - if (data->hwndFrom == m_ListView->m_hWnd && ((LPNMLISTVIEW) lParam)->iItem != -1) - { - /* this won't do anything if the program is already installed */ - SendMessageW(hwnd, WM_COMMAND, ID_INSTALL, 0); - } - } - break; - - case NM_RCLICK: - { - if (data->hwndFrom == m_ListView->m_hWnd && ((LPNMLISTVIEW) lParam)->iItem != -1) - { - ShowPopupMenu(m_ListView->m_hWnd, 0, ID_INSTALL); - } - } - break; - - - case TTN_GETDISPINFO: m_Toolbar->OnGetDispInfo((LPTOOLTIPTEXT) lParam); break; @@ -2136,10 +2458,9 @@ private: case WM_SYSCOLORCHANGE: { /* Forward WM_SYSCOLORCHANGE to common controls */ - m_ListView->SendMessageW(WM_SYSCOLORCHANGE, 0, 0); - m_TreeView->SendMessageW(WM_SYSCOLORCHANGE, 0, 0); + m_ApplicationView->SendMessageW(WM_SYSCOLORCHANGE, wParam, lParam); + m_TreeView->SendMessageW(WM_SYSCOLORCHANGE, wParam, lParam); m_Toolbar->SendMessageW(WM_SYSCOLORCHANGE, 0, 0); - m_ListView->SendMessageW(EM_SETBKGNDCOLOR, 0, GetSysColor(COLOR_BTNFACE)); } break;
@@ -2272,17 +2593,36 @@ private: case ID_INSTALL: if (IsAvailableEnum(SelectedEnumType)) { - if (nSelectedApps > 0) + ATL::CSimpleArray<CAvailableApplicationInfo> AppsList; + + // enum all selected apps + m_AvailableApps.Enum(ENUM_CAT_SELECTED, s_EnumSelectedAppForDownloadProc, (PVOID)&AppsList); + + if (AppsList.GetSize()) { - DownloadListOfApplications(m_AvailableApps.GetSelected(), FALSE); - UpdateApplicationsList(-1); - m_ListView->SetSelected(-1, FALSE); + if (DownloadListOfApplications(AppsList, FALSE)) + { + m_AvailableApps.RemoveAllSelected(); + UpdateApplicationsList(-1); + } } - else if (DownloadApplication(m_ListView->GetSelectedData(), FALSE)) + else { - UpdateApplicationsList(-1); + // use the currently focused item in application-view + CAvailableApplicationInfo *FocusedApps = (CAvailableApplicationInfo *)m_ApplicationView->GetFocusedItemData(); + if (FocusedApps) + { + if (DownloadApplication(FocusedApps, FALSE)) + { + UpdateApplicationsList(-1); + } + } + else + { + // TODO: in this case, Install button in toolbar (and all other places) should be disabled + // or at least popup a messagebox telling user to select/check some app first + } } - } break;
@@ -2318,28 +2658,11 @@ private: break;
case ID_CHECK_ALL: - m_ListView->CheckAll(); + m_ApplicationView->CheckAll(); break; } }
- VOID FreeInstalledAppList() - { - INT Count = m_ListView->GetItemCount() - 1; - PINSTALLED_INFO Info; - - while (Count >= 0) - { - Info = (PINSTALLED_INFO) m_ListView->GetItemData(Count); - if (Info) - { - RegCloseKey(Info->hSubKey); - delete Info; - } - Count--; - } - } - static BOOL SearchPatternMatch(LPCWSTR szHaystack, LPCWSTR szNeedle) { if (!*szNeedle) @@ -2348,91 +2671,42 @@ private: return StrStrIW(szHaystack, szNeedle) != NULL; }
- BOOL CALLBACK EnumInstalledAppProc(INT ItemIndex, ATL::CStringW &m_szName, PINSTALLED_INFO Info) + BOOL CALLBACK EnumInstalledAppProc(CInstalledApplicationInfo * Info) { - PINSTALLED_INFO ItemInfo; - ATL::CStringW szText; - INT Index; - - if (!SearchPatternMatch(m_szName.GetString(), szSearchPattern)) + if (!SearchPatternMatch(Info->szDisplayName.GetString(), szSearchPattern)) { - RegCloseKey(Info->hSubKey); return TRUE; } - - ItemInfo = new INSTALLED_INFO(*Info); - if (!ItemInfo) - { - RegCloseKey(Info->hSubKey); - return FALSE; - } - - Index = m_ListView->AddItem(ItemIndex, 0, m_szName.GetString(), (LPARAM) ItemInfo); - - /* Get version info */ - ItemInfo->GetApplicationString(L"DisplayVersion", szText); - m_ListView->SetItemText(Index, 1, szText.GetString()); - - /* Get comments */ - ItemInfo->GetApplicationString(L"Comments", szText); - m_ListView->SetItemText(Index, 2, szText.GetString()); - - return TRUE; + return m_ApplicationView->AddInstalledApplication(Info, Info); // currently, the callback param is Info itself }
- BOOL EnumAvailableAppProc(CAvailableApplicationInfo* Info, LPCWSTR szFolderPath) + BOOL CALLBACK EnumAvailableAppProc(CAvailableApplicationInfo * Info, BOOL bInitialCheckState) { - INT Index; - HICON hIcon = NULL; - - HIMAGELIST hImageListView = (HIMAGELIST)m_ListView->SendMessage(LVM_GETIMAGELIST, LVSIL_SMALL, 0); - if (!SearchPatternMatch(Info->m_szName.GetString(), szSearchPattern) && !SearchPatternMatch(Info->m_szDesc.GetString(), szSearchPattern)) { return TRUE; } - - /* Load icon from file */ - ATL::CStringW szIconPath; - if (Info->RetrieveIcon(szIconPath)) - { - hIcon = (HICON)LoadImageW(NULL, - szIconPath.GetString(), - IMAGE_ICON, - LISTVIEW_ICON_SIZE, - LISTVIEW_ICON_SIZE, - LR_LOADFROMFILE); - } - - if (!hIcon || GetLastError() != ERROR_SUCCESS) - { - /* Load default icon */ - hIcon = (HICON) LoadIconW(hInst, MAKEINTRESOURCEW(IDI_MAIN)); - } - - Index = ImageList_AddIcon(hImageListView, hIcon); - DestroyIcon(hIcon); - - Index = m_ListView->AddItem(Info->m_Category, Index, Info->m_szName.GetString(), (LPARAM) Info); - m_ListView->SetImageList(hImageListView, LVSIL_SMALL); - m_ListView->SetItemText(Index, 1, Info->m_szVersion.GetString()); - m_ListView->SetItemText(Index, 2, Info->m_szDesc.GetString()); - m_ListView->SetCheckState(Index, Info->m_IsSelected); - - return TRUE; + return m_ApplicationView->AddAvailableApplication(Info, bInitialCheckState, Info); // currently, the callback param is Info itself }
- static BOOL CALLBACK s_EnumInstalledAppProc(INT ItemIndex, ATL::CStringW &m_szName, PINSTALLED_INFO Info, PVOID param) + static BOOL CALLBACK s_EnumInstalledAppProc(CInstalledApplicationInfo * Info, PVOID param) { CMainWindow* pThis = (CMainWindow*)param; - return pThis->EnumInstalledAppProc(ItemIndex, m_szName, Info); + return pThis->EnumInstalledAppProc(Info); }
- static BOOL CALLBACK s_EnumAvailableAppProc(CAvailableApplicationInfo* Info, LPCWSTR szFolderPath, PVOID param) + static BOOL CALLBACK s_EnumAvailableAppProc(CAvailableApplicationInfo* Info, BOOL bInitialCheckState, PVOID param) { CMainWindow* pThis = (CMainWindow*)param; - return pThis->EnumAvailableAppProc(Info, szFolderPath); + return pThis->EnumAvailableAppProc(Info, bInitialCheckState); + } + + static BOOL CALLBACK s_EnumSelectedAppForDownloadProc(CAvailableApplicationInfo *Info, BOOL bInitialCheckState, PVOID param) + { + ATL::CSimpleArray<CAvailableApplicationInfo> *pAppList = (ATL::CSimpleArray<CAvailableApplicationInfo> *)param; + pAppList->Add(*Info); + return TRUE; }
VOID UpdateStatusBarText() @@ -2441,83 +2715,46 @@ private: { ATL::CStringW szBuffer;
- szBuffer.Format(IDS_APPS_COUNT, m_ListView->GetItemCount(), nSelectedApps); + szBuffer.Format(IDS_APPS_COUNT, m_ApplicationView->GetItemCount(), m_AvailableApps.GetSelectedCount()); m_StatusBar->SetText(szBuffer); } }
VOID UpdateApplicationsList(INT EnumType) { - ATL::CStringW szBuffer1, szBuffer2; - HIMAGELIST hImageListView; - BOOL bWasInInstalled = IsInstalledEnum(SelectedEnumType); - bUpdating = TRUE; - m_ListView->SetRedraw(FALSE);
- if (EnumType < 0) + if (EnumType == -1) { + // keep the old enum type EnumType = SelectedEnumType; } - - //if previous one was INSTALLED purge the list - //TODO: make the Installed category a separate class to avoid doing this - if (bWasInInstalled) - { - FreeInstalledAppList(); - } - - m_ListView->DeleteAllItems(); - - // Create new ImageList - hImageListView = ImageList_Create(LISTVIEW_ICON_SIZE, - LISTVIEW_ICON_SIZE, - GetSystemColorDepth() | ILC_MASK, - 0, 1); - HIMAGELIST hImageListBuf = m_ListView->SetImageList(hImageListView, LVSIL_SMALL); - if (hImageListBuf) + else { - ImageList_Destroy(hImageListBuf); + SelectedEnumType = EnumType; }
+ m_ApplicationView->SetRedraw(FALSE); if (IsInstalledEnum(EnumType)) { - if (!bWasInInstalled) - { - m_ListView->SetCheckboxesVisible(FALSE); - } + // set the display mode of application-view. this will remove all the item in application-view too. + m_ApplicationView->SetDisplayMode(ApplicationViewInstalledApps);
- HICON hIcon = (HICON) LoadIconW(hInst, MAKEINTRESOURCEW(IDI_MAIN)); - ImageList_AddIcon(hImageListView, hIcon); - DestroyIcon(hIcon); - - // Enum installed applications and updates - EnumInstalledApplications(EnumType, TRUE, s_EnumInstalledAppProc, this); - EnumInstalledApplications(EnumType, FALSE, s_EnumInstalledAppProc, this); + // enum installed softwares + m_InstalledApps.Enum(EnumType, s_EnumInstalledAppProc, this); } else if (IsAvailableEnum(EnumType)) { - if (bWasInInstalled) - { - m_ListView->SetCheckboxesVisible(TRUE); - } + // set the display mode of application-view. this will remove all the item in application-view too. + m_ApplicationView->SetDisplayMode(ApplicationViewAvailableApps);
- // Enum available applications + // enum available softwares m_AvailableApps.Enum(EnumType, s_EnumAvailableAppProc, this); } - - SelectedEnumType = EnumType; + m_ApplicationView->SetRedraw(TRUE); + m_ApplicationView->RedrawWindow(0, 0, RDW_INVALIDATE | RDW_ALLCHILDREN); // force the child window to repaint UpdateStatusBarText(); - m_AppInfo->SetWelcomeText(); - - // Set automatic column width for program names if the list is not empty - if (m_ListView->GetItemCount() > 0) - { - ListView_SetColumnWidth(m_ListView->GetWindow(), 0, LVSCW_AUTOSIZE); - } - bUpdating = FALSE; - m_ListView->SetRedraw(TRUE); }
public: @@ -2562,9 +2799,39 @@ public: return CWindowImpl::Create(NULL, r, szWindowName.GetString(), WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, WS_EX_WINDOWEDGE); }
+ // this function is called when a item of application-view is checked/unchecked + // CallbackParam is the param passed to application-view when adding the item (the one getting focus now). + BOOL ItemCheckStateChanged(BOOL bChecked, LPVOID CallbackParam) + { + if (!bUpdating) + { + if (bChecked) + { + if (!m_AvailableApps.AddSelected((CAvailableApplicationInfo *)CallbackParam)) + { + return FALSE; + } + } + else + { + if (!m_AvailableApps.RemoveSelected((CAvailableApplicationInfo *)CallbackParam)) + { + return FALSE; + } + } + + UpdateStatusBarText(); + return TRUE; + } + else + { + return TRUE; + } + } + void HandleTabOrder(int direction) { - HWND Controls[] = { m_Toolbar->m_hWnd, m_SearchBar->m_hWnd, m_TreeView->m_hWnd, m_ListView->m_hWnd, m_AppInfo->m_hWnd }; + HWND Controls[] = { m_Toolbar->m_hWnd, m_SearchBar->m_hWnd, m_TreeView->m_hWnd, m_ApplicationView->m_hWnd}; // When there is no control found, go to the first or last (depending on tab vs shift-tab) int current = direction > 0 ? 0 : (_countof(Controls) - 1); HWND hActive = ::GetFocus(); @@ -2586,6 +2853,12 @@ public: } };
+BOOL CApplicationView::ItemCheckStateChanged(BOOL bChecked, LPVOID CallbackParam) +{ + m_MainWindow->ItemCheckStateChanged(bChecked, CallbackParam); + return TRUE; +} + VOID ShowMainWindow(INT nShowCmd) { HACCEL KeyBrd; diff --git a/base/applications/rapps/include/available.h b/base/applications/rapps/include/available.h index 259b644dce1..2ee8136cc91 100644 --- a/base/applications/rapps/include/available.h +++ b/base/applications/rapps/include/available.h @@ -37,10 +37,11 @@ struct AvailableStrings AvailableStrings(); };
-struct CAvailableApplicationInfo +class CAvailableApplicationInfo { +public: INT m_Category; - BOOL m_IsSelected; + //BOOL m_IsSelected; LicenseType m_LicenseType; ATL::CStringW m_szName; ATL::CStringW m_szRegName; @@ -98,11 +99,12 @@ private: inline BOOL FindInLanguages(LCID what) const; };
-typedef BOOL(CALLBACK *AVAILENUMPROC)(CAvailableApplicationInfo *Info, LPCWSTR szFolderPath, PVOID param); +typedef BOOL(CALLBACK *AVAILENUMPROC)(CAvailableApplicationInfo *Info, BOOL bInitialCheckState, PVOID param);
class CAvailableApps { ATL::CAtlList<CAvailableApplicationInfo*> m_InfoList; + ATL::CAtlList< CAvailableApplicationInfo*> m_SelectedList;
public: static AvailableStrings m_Strings; @@ -116,9 +118,15 @@ public: VOID FreeCachedEntries(); BOOL Enum(INT EnumType, AVAILENUMPROC lpEnumProc, PVOID param);
+ BOOL AddSelected(CAvailableApplicationInfo *AvlbInfo); + BOOL RemoveSelected(CAvailableApplicationInfo *AvlbInfo); + + VOID RemoveAllSelected(); + int GetSelectedCount(); + CAvailableApplicationInfo* FindInfo(const ATL::CStringW& szAppName) const; ATL::CSimpleArray<CAvailableApplicationInfo> FindInfoList(const ATL::CSimpleArrayATL::CStringW &arrAppsNames) const; - ATL::CSimpleArray<CAvailableApplicationInfo> GetSelected() const; + //ATL::CSimpleArray<CAvailableApplicationInfo> GetSelected() const;
const ATL::CStringW& GetFolderPath() const; const ATL::CStringW& GetAppPath() const; diff --git a/base/applications/rapps/include/installed.h b/base/applications/rapps/include/installed.h index 76a12418c1c..f13e37e1b48 100644 --- a/base/applications/rapps/include/installed.h +++ b/base/applications/rapps/include/installed.h @@ -3,19 +3,51 @@ #include <windef.h> #include <atlstr.h>
-struct INSTALLED_INFO +class CInstalledApplicationInfo { - HKEY hRootKey; +public: + BOOL IsUserKey; + REGSAM WowKey; HKEY hSubKey; + BOOL bIsUpdate = FALSE; + ATL::CStringW szKeyName;
+ CInstalledApplicationInfo(BOOL bIsUserKey, REGSAM RegWowKey, HKEY hKey); BOOL GetApplicationString(LPCWSTR lpKeyName, ATL::CStringW& String); + BOOL UninstallApplication(BOOL bModify); + LSTATUS RemoveFromRegistry(); + + ATL::CStringW szDisplayName; + ATL::CStringW szDisplayVersion; + ATL::CStringW szPublisher; + ATL::CStringW szRegOwner; + ATL::CStringW szProductID; + ATL::CStringW szHelpLink; + ATL::CStringW szHelpTelephone; + ATL::CStringW szReadme; + ATL::CStringW szContact; + ATL::CStringW szURLUpdateInfo; + ATL::CStringW szURLInfoAbout; + ATL::CStringW szComments; + ATL::CStringW szInstallDate; + ATL::CStringW szInstallLocation; + ATL::CStringW szInstallSource; + ATL::CStringW szUninstallString; + ATL::CStringW szModifyPath; + + ~CInstalledApplicationInfo(); };
-typedef INSTALLED_INFO *PINSTALLED_INFO; -typedef BOOL(CALLBACK *APPENUMPROC)(INT ItemIndex, ATL::CStringW &Name, PINSTALLED_INFO Info, PVOID param); +typedef BOOL(CALLBACK *APPENUMPROC)(CInstalledApplicationInfo * Info, PVOID param);
-BOOL EnumInstalledApplications(INT EnumType, BOOL IsUserKey, APPENUMPROC lpEnumProc, PVOID param); -BOOL GetApplicationString(HKEY hKey, LPCWSTR lpKeyName, LPWSTR szString); +class CInstalledApps +{ + ATL::CAtlList<CInstalledApplicationInfo *> m_InfoList; + +public: + BOOL Enum(INT EnumType, APPENUMPROC lpEnumProc, PVOID param); + + VOID FreeCachedEntries(); +};
-BOOL UninstallApplication(PINSTALLED_INFO ItemInfo, BOOL bModify); diff --git a/base/applications/rapps/include/misc.h b/base/applications/rapps/include/misc.h index 4c1fc6a4b1d..7e3b1567b65 100644 --- a/base/applications/rapps/include/misc.h +++ b/base/applications/rapps/include/misc.h @@ -46,3 +46,5 @@ public: };
BOOL PathAppendNoDirEscapeW(LPWSTR pszPath, LPCWSTR pszMore); + +BOOL IsSystem64Bit(); diff --git a/base/applications/rapps/include/rosui.h b/base/applications/rapps/include/rosui.h index 378e677c053..19ba8d26f0d 100644 --- a/base/applications/rapps/include/rosui.h +++ b/base/applications/rapps/include/rosui.h @@ -495,7 +495,10 @@ public:
virtual ~CUiWindow() { - T::DestroyWindow(); + if (T::IsWindow()) + { + T::DestroyWindow(); + } }
VOID GetWindowTextW(ATL::CStringW& szText) diff --git a/base/applications/rapps/installed.cpp b/base/applications/rapps/installed.cpp index c35f141f526..065422762ad 100644 --- a/base/applications/rapps/installed.cpp +++ b/base/applications/rapps/installed.cpp @@ -12,149 +12,253 @@
#include "misc.h"
-BOOL INSTALLED_INFO::GetApplicationString(LPCWSTR lpKeyName, ATL::CStringW& String) +CInstalledApplicationInfo::CInstalledApplicationInfo(BOOL bIsUserKey, REGSAM RegWowKey, HKEY hKey) + : IsUserKey(bIsUserKey), WowKey(RegWowKey), hSubKey(hKey) { - BOOL result = ::GetApplicationString(hSubKey, lpKeyName, String.GetBuffer(MAX_PATH)); - String.ReleaseBuffer(); - return result; + // if Initialize failed, hSubKey will be closed automatically and set to zero + + DWORD dwSize = MAX_PATH, dwType, dwValue; + BOOL bIsSystemComponent; + ATL::CStringW szParentKeyName; + + dwType = REG_DWORD; + dwSize = sizeof(DWORD); + + if (RegQueryValueExW(hSubKey, + L"SystemComponent", + NULL, + &dwType, + (LPBYTE)&dwValue, + &dwSize) == ERROR_SUCCESS) + { + bIsSystemComponent = (dwValue == 0x1); + } + else + { + bIsSystemComponent = FALSE; + } + + dwType = REG_SZ; + dwSize = MAX_PATH * sizeof(WCHAR); + bIsUpdate = (RegQueryValueExW(hSubKey, + L"ParentKeyName", + NULL, + &dwType, + (LPBYTE)szParentKeyName.GetBuffer(MAX_PATH), + &dwSize) == ERROR_SUCCESS); + szParentKeyName.ReleaseBuffer(); + + if (bIsSystemComponent) + { + CloseHandle(hSubKey); + hSubKey = NULL; + } + }
-BOOL GetApplicationString(HKEY hKey, LPCWSTR lpKeyName, LPWSTR szString) +CInstalledApplicationInfo::~CInstalledApplicationInfo() { - DWORD dwSize = MAX_PATH * sizeof(WCHAR); - - if (RegQueryValueExW(hKey, - lpKeyName, - NULL, - NULL, - (LPBYTE) szString, - &dwSize) == ERROR_SUCCESS) + if (hSubKey) { - return TRUE; + CloseHandle(hSubKey); + hSubKey = NULL; } - - StringCchCopyW(szString, MAX_PATH, L"---"); - return FALSE; }
-BOOL UninstallApplication(PINSTALLED_INFO ItemInfo, BOOL bModify) +BOOL CInstalledApplicationInfo::GetApplicationString(LPCWSTR lpKeyName, ATL::CStringW& String) { - LPCWSTR szModify = L"ModifyPath"; - LPCWSTR szUninstall = L"UninstallString"; - DWORD dwType, dwSize; - WCHAR szPath[MAX_PATH]; + DWORD dwSize = 0; + String.Empty(); + DWORD dwType;
- dwType = REG_SZ; - dwSize = MAX_PATH * sizeof(WCHAR); - if (RegQueryValueExW(ItemInfo->hSubKey, - bModify ? szModify : szUninstall, - NULL, - &dwType, - (LPBYTE) szPath, - &dwSize) != ERROR_SUCCESS) + // retrieve the size of value first. + if (RegQueryValueExW(hSubKey, + lpKeyName, + NULL, + &dwType, + NULL, + &dwSize) != ERROR_SUCCESS) { return FALSE; }
- return StartProcess(szPath, TRUE); + // TODO: I assume the type as REG_SZ. but I think REG_EXPAND_SZ should be handled correctly too. + if (dwType != REG_SZ) + { + return FALSE; + } + + // allocate buffer. + // attention: dwSize is size in bytes, and RegQueryValueExW does not guarantee the terminating null character. + String.GetBuffer(dwSize + sizeof(WCHAR)); + + // query the value + if (RegQueryValueExW(hSubKey, + lpKeyName, + NULL, + NULL, + (LPBYTE)String.GetBuffer(), + &dwSize) != ERROR_SUCCESS) + { + String.ReleaseBuffer(); + String.Empty(); + return FALSE; + } + String.GetBuffer()[dwSize / sizeof(WCHAR)] = L'\0'; // ensure zero terminated + String.ReleaseBuffer(); + return TRUE; }
-BOOL EnumInstalledApplications(INT EnumType, BOOL IsUserKey, APPENUMPROC lpEnumProc, PVOID param) +BOOL CInstalledApplicationInfo::UninstallApplication(BOOL bModify) { - DWORD dwSize = MAX_PATH, dwType, dwValue; - BOOL bIsSystemComponent, bIsUpdate; - ATL::CStringW szParentKeyName; - ATL::CStringW szDisplayName; - INSTALLED_INFO Info; - HKEY hKey; - LONG ItemIndex = 0; + return StartProcess(bModify ? szModifyPath : szUninstallString, TRUE); +} + +LSTATUS CInstalledApplicationInfo::RemoveFromRegistry() +{ + ATL::CStringW szFullName = L"Software\Microsoft\Windows\CurrentVersion\Uninstall\" + szKeyName;
- Info.hRootKey = IsUserKey ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE; + // TODO: if there are subkeys inside, simply RegDeleteKeyExW will fail + // we don't have RegDeleteTree for ReactOS now. (It's a WinVista API) + // write a function to delete all subkeys recursively to solve this + // or consider letting ReactOS having this API
- if (RegOpenKeyW(Info.hRootKey, - L"Software\Microsoft\Windows\CurrentVersion\Uninstall", - &hKey) != ERROR_SUCCESS) + return RegDeleteKeyExW(IsUserKey ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE, szFullName, WowKey, 0); +} + +BOOL CInstalledApps::Enum(INT EnumType, APPENUMPROC lpEnumProc, PVOID param) +{ + FreeCachedEntries(); + + HKEY RootKeyEnum[3] = { HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE, HKEY_LOCAL_MACHINE }; + REGSAM RegSamEnum[3] = { KEY_WOW64_32KEY, KEY_WOW64_32KEY, KEY_WOW64_64KEY }; + + int LoopTime; + + // test if the OS is 64 bit. + if (IsSystem64Bit()) { - return FALSE; + // loop for all 3 combination. + // note that HKEY_CURRENT_USER\Software don't have a redirect + // https://docs.microsoft.com/en-us/windows/win32/winprog64/shared-registry-key... + LoopTime = 3; + } + else + { + // loop for 2 combination for KEY_WOW64_32KEY only + LoopTime = 2; }
- while (RegEnumKeyExW(hKey, ItemIndex, Info.szKeyName.GetBuffer(MAX_PATH), &dwSize, NULL, NULL, NULL, NULL) == ERROR_SUCCESS) + // loop for all combination + for (int i = 0; i < LoopTime; i++) { - Info.szKeyName.ReleaseBuffer(); - if (RegOpenKeyW(hKey, Info.szKeyName.GetString(), &Info.hSubKey) == ERROR_SUCCESS) + DWORD dwSize = MAX_PATH; + HKEY hKey, hSubKey; + LONG ItemIndex = 0; + ATL::CStringW szKeyName; + + if (RegOpenKeyExW(RootKeyEnum[i], + L"Software\Microsoft\Windows\CurrentVersion\Uninstall", + NULL, + KEY_READ | RegSamEnum[i], + &hKey) != ERROR_SUCCESS) { - dwType = REG_DWORD; - dwSize = sizeof(DWORD); - - if (RegQueryValueExW(Info.hSubKey, - L"SystemComponent", - NULL, - &dwType, - (LPBYTE) &dwValue, - &dwSize) == ERROR_SUCCESS) - { - bIsSystemComponent = (dwValue == 0x1); - } - else + return FALSE; + } + + while (1) + { + dwSize = MAX_PATH; + if (RegEnumKeyExW(hKey, ItemIndex, szKeyName.GetBuffer(MAX_PATH), &dwSize, NULL, NULL, NULL, NULL) != ERROR_SUCCESS) { - bIsSystemComponent = FALSE; + break; }
- dwType = REG_SZ; - dwSize = MAX_PATH * sizeof(WCHAR); - bIsUpdate = (RegQueryValueExW(Info.hSubKey, - L"ParentKeyName", - NULL, - &dwType, - (LPBYTE) szParentKeyName.GetBuffer(MAX_PATH), - &dwSize) == ERROR_SUCCESS); - szParentKeyName.ReleaseBuffer(); - - dwType = REG_SZ; - dwSize = MAX_PATH * sizeof(WCHAR); - if (RegQueryValueExW(Info.hSubKey, - L"DisplayName", - NULL, - &dwType, - (LPBYTE) szDisplayName.GetBuffer(MAX_PATH), - &dwSize) == ERROR_SUCCESS) + ItemIndex++; + + szKeyName.ReleaseBuffer(); + if (RegOpenKeyW(hKey, szKeyName.GetString(), &hSubKey) == ERROR_SUCCESS) { - szDisplayName.ReleaseBuffer(); - if (EnumType < ENUM_ALL_INSTALLED || EnumType > ENUM_UPDATES) - EnumType = ENUM_ALL_INSTALLED; + BOOL bSuccess = FALSE; + CInstalledApplicationInfo *Info = new CInstalledApplicationInfo(RootKeyEnum[i] == HKEY_CURRENT_USER, RegSamEnum[i], hSubKey); + Info->szKeyName = szKeyName;
- if (!bIsSystemComponent) + // check for failure. if failed to init, Info->hSubKey will be set to NULL + if (Info->hSubKey) { - if ((EnumType == ENUM_ALL_INSTALLED) || /* All components */ - ((EnumType == ENUM_INSTALLED_APPLICATIONS) && (!bIsUpdate)) || /* Applications only */ - ((EnumType == ENUM_UPDATES) && (bIsUpdate))) /* Updates only */ + // those items without display name are ignored + if (Info->GetApplicationString(L"DisplayName", Info->szDisplayName)) { - if (!lpEnumProc(ItemIndex, szDisplayName, &Info, param)) - break; + Info->GetApplicationString(L"DisplayVersion", Info->szDisplayVersion); + Info->GetApplicationString(L"Publisher", Info->szPublisher); + Info->GetApplicationString(L"RegOwner", Info->szRegOwner); + Info->GetApplicationString(L"ProductID", Info->szProductID); + Info->GetApplicationString(L"HelpLink", Info->szHelpLink); + Info->GetApplicationString(L"HelpTelephone", Info->szHelpTelephone); + Info->GetApplicationString(L"Readme", Info->szReadme); + Info->GetApplicationString(L"Contact", Info->szContact); + Info->GetApplicationString(L"URLUpdateInfo", Info->szURLUpdateInfo); + Info->GetApplicationString(L"URLInfoAbout", Info->szURLInfoAbout); + Info->GetApplicationString(L"Comments", Info->szComments); + Info->GetApplicationString(L"InstallDate", Info->szInstallDate); + Info->GetApplicationString(L"InstallLocation", Info->szInstallLocation); + Info->GetApplicationString(L"InstallSource", Info->szInstallSource); + Info->GetApplicationString(L"UninstallString", Info->szUninstallString); + Info->GetApplicationString(L"ModifyPath", Info->szModifyPath); + + bSuccess = TRUE; } - else + } + + // close handle + if (Info->hSubKey) + { + CloseHandle(Info->hSubKey); + Info->hSubKey = NULL; + } + + if (bSuccess) + { + // add to InfoList. + m_InfoList.AddTail(Info); + + // invoke callback + if (lpEnumProc) { - RegCloseKey(Info.hSubKey); + if ((EnumType == ENUM_ALL_INSTALLED) || /* All components */ + ((EnumType == ENUM_INSTALLED_APPLICATIONS) && (!Info->bIsUpdate)) || /* Applications only */ + ((EnumType == ENUM_UPDATES) && (Info->bIsUpdate))) /* Updates only */ + { + lpEnumProc(Info, param); + } } } else { - RegCloseKey(Info.hSubKey); + // destory object + delete Info; } } - else - { - szDisplayName.ReleaseBuffer(); - RegCloseKey(Info.hSubKey); - } }
- dwSize = MAX_PATH; - ItemIndex++; + szKeyName.ReleaseBuffer(); + RegCloseKey(hKey); } - - Info.szKeyName.ReleaseBuffer(); - RegCloseKey(hKey); +
return TRUE; } + +VOID CInstalledApps::FreeCachedEntries() +{ + POSITION InfoListPosition = m_InfoList.GetHeadPosition(); + + /* loop and deallocate all the cached app infos in the list */ + while (InfoListPosition) + { + CInstalledApplicationInfo *Info = m_InfoList.GetNext(InfoListPosition); + delete Info; + } + + m_InfoList.RemoveAll(); +} diff --git a/base/applications/rapps/misc.cpp b/base/applications/rapps/misc.cpp index 8e164db8648..90f4e88cd3c 100644 --- a/base/applications/rapps/misc.cpp +++ b/base/applications/rapps/misc.cpp @@ -13,6 +13,9 @@
static HANDLE hLog = NULL;
+static BOOL bIsSys64ResultCached = FALSE; +static BOOL bIsSys64Result = FALSE; + INT GetWindowWidth(HWND hwnd) { RECT Rect; @@ -453,3 +456,33 @@ BOOL PathAppendNoDirEscapeW(LPWSTR pszPath, LPCWSTR pszMore)
return TRUE; } + + + +BOOL IsSystem64Bit() +{ + if (bIsSys64ResultCached) + { + // just return cached result + return bIsSys64Result; + } + + SYSTEM_INFO si; + typedef void (WINAPI *LPFN_PGNSI)(LPSYSTEM_INFO); + LPFN_PGNSI pGetNativeSystemInfo = (LPFN_PGNSI)GetProcAddress(GetModuleHandle(L"kernel32.dll"), "GetNativeSystemInfo"); + if (pGetNativeSystemInfo) + { + pGetNativeSystemInfo(&si); + if (si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64 || si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_IA64) + { + bIsSys64Result = TRUE; + } + } + else + { + bIsSys64Result = FALSE; + } + + bIsSys64ResultCached = TRUE; // next time calling this function, it will directly return bIsSys64Result + return bIsSys64Result; +}