/* PROJECT: ReactOS CE Applications Manager * LICENSE: GPL - See COPYING in the top level directory * AUTHORS: David Quintana */ #include "rapps.h" #include #include #include #include #include #include #include #include #include "rosui.h" #include "crichedit.h" #define SEARCH_TIMER_ID 'SR' HWND hListView = NULL; class CMainToolbar : public CUiWindow< CToolbar<> > { #define TOOLBAR_HEIGHT 24 WCHAR szInstallBtn[MAX_STR_LEN]; WCHAR szUninstallBtn[MAX_STR_LEN]; WCHAR szModifyBtn[MAX_STR_LEN]; VOID AddImageToImageList(HIMAGELIST hImageList, UINT ImageIndex) { HICON hImage; if (!(hImage = (HICON) LoadImage(hInst, MAKEINTRESOURCE(ImageIndex), IMAGE_ICON, TOOLBAR_HEIGHT, TOOLBAR_HEIGHT, 0))) { /* TODO: Error message */ } ImageList_AddIcon(hImageList, hImage); DeleteObject(hImage); } HIMAGELIST InitImageList(VOID) { HIMAGELIST hImageList; /* Create the toolbar icon image list */ hImageList = ImageList_Create(TOOLBAR_HEIGHT,//GetSystemMetrics(SM_CXSMICON), TOOLBAR_HEIGHT,//GetSystemMetrics(SM_CYSMICON), ILC_MASK | GetSystemColorDepth(), 1, 1); if (!hImageList) { /* TODO: Error message */ return NULL; } AddImageToImageList(hImageList, IDI_INSTALL); AddImageToImageList(hImageList, IDI_UNINSTALL); AddImageToImageList(hImageList, IDI_MODIFY); AddImageToImageList(hImageList, IDI_REFRESH); AddImageToImageList(hImageList, IDI_UPDATE_DB); AddImageToImageList(hImageList, IDI_SETTINGS); AddImageToImageList(hImageList, IDI_EXIT); return hImageList; } public: VOID OnGetDispInfo(LPTOOLTIPTEXT lpttt) { UINT idButton = (UINT) lpttt->hdr.idFrom; switch (idButton) { case ID_EXIT: lpttt->lpszText = MAKEINTRESOURCE(IDS_TOOLTIP_EXIT); break; case ID_INSTALL: lpttt->lpszText = MAKEINTRESOURCE(IDS_TOOLTIP_INSTALL); break; case ID_UNINSTALL: lpttt->lpszText = MAKEINTRESOURCE(IDS_TOOLTIP_UNINSTALL); break; case ID_MODIFY: lpttt->lpszText = MAKEINTRESOURCE(IDS_TOOLTIP_MODIFY); break; case ID_SETTINGS: lpttt->lpszText = MAKEINTRESOURCE(IDS_TOOLTIP_SETTINGS); break; case ID_REFRESH: lpttt->lpszText = MAKEINTRESOURCE(IDS_TOOLTIP_REFRESH); break; case ID_RESETDB: lpttt->lpszText = MAKEINTRESOURCE(IDS_TOOLTIP_UPDATE_DB); break; } } HWND Create(HWND hwndParent) { static TBBUTTON Buttons [] = { /* iBitmap, idCommand, fsState, fsStyle, bReserved[2], dwData, iString */ { 0, ID_INSTALL, TBSTATE_ENABLED, BTNS_BUTTON | BTNS_AUTOSIZE, { 0 }, 0, (INT_PTR) szInstallBtn }, { 1, ID_UNINSTALL, TBSTATE_ENABLED, BTNS_BUTTON | BTNS_AUTOSIZE, { 0 }, 0, (INT_PTR) szUninstallBtn }, { 2, ID_MODIFY, TBSTATE_ENABLED, BTNS_BUTTON | BTNS_AUTOSIZE, { 0 }, 0, (INT_PTR) szModifyBtn }, { 5, 0, TBSTATE_ENABLED, BTNS_SEP, { 0 }, 0, 0 }, { 3, ID_REFRESH, TBSTATE_ENABLED, BTNS_BUTTON | BTNS_AUTOSIZE, { 0 }, 0, 0 }, { 4, ID_RESETDB, TBSTATE_ENABLED, BTNS_BUTTON | BTNS_AUTOSIZE, {0}, 0, 0}, { 5, 0, TBSTATE_ENABLED, BTNS_SEP, { 0 }, 0, 0 }, { 5, ID_SETTINGS, TBSTATE_ENABLED, BTNS_BUTTON | BTNS_AUTOSIZE, { 0 }, 0, 0 }, { 6, ID_EXIT, TBSTATE_ENABLED, BTNS_BUTTON | BTNS_AUTOSIZE, { 0 }, 0, 0 } }; INT NumButtons = sizeof(Buttons) / sizeof(Buttons[0]); HIMAGELIST hImageList; LoadStringW(hInst, IDS_INSTALL, szInstallBtn, sizeof(szInstallBtn) / sizeof(WCHAR)); LoadStringW(hInst, IDS_UNINSTALL, szUninstallBtn, sizeof(szUninstallBtn) / sizeof(WCHAR)); LoadStringW(hInst, IDS_MODIFY, szModifyBtn, sizeof(szModifyBtn) / sizeof(WCHAR)); m_hWnd = CreateWindowExW(0, TOOLBARCLASSNAMEW, NULL, WS_CHILD | WS_VISIBLE | TBSTYLE_FLAT | TBSTYLE_TOOLTIPS | TBSTYLE_LIST, 0, 0, 0, 0, hwndParent, 0, hInst, NULL); if (!m_hWnd) { /* TODO: Show error message */ return FALSE; } SendMessageW(TB_SETEXTENDEDSTYLE, 0, TBSTYLE_EX_HIDECLIPPEDBUTTONS); SetButtonStructSize(); hImageList = InitImageList(); if (!hImageList) { /* TODO: Show error message */ return FALSE; } ImageList_Destroy((HIMAGELIST) SetImageList(hImageList)); AddButtons(NumButtons, Buttons); return m_hWnd; } }; class CAppsListView : public CUiWindow { struct SortContext { CAppsListView * lvw; int iSubItem; }; public: BOOL bAscending; CAppsListView() { bAscending = TRUE; } VOID ColumnClick(LPNMLISTVIEW pnmv) { SortContext ctx = { this, pnmv->iSubItem }; SortItems(s_CompareFunc, &ctx); bAscending = !bAscending; } PVOID GetLParam(INT Index) { INT ItemIndex; LVITEM Item; if (Index == -1) { ItemIndex = (INT) SendMessage(LVM_GETNEXTITEM, -1, LVNI_FOCUSED); if (ItemIndex == -1) return NULL; } else { ItemIndex = Index; } ZeroMemory(&Item, sizeof(LVITEM)); Item.mask = LVIF_PARAM; Item.iItem = ItemIndex; if (!GetItem(&Item)) return NULL; return (PVOID) Item.lParam; } BOOL AddColumn(INT Index, LPWSTR lpText, INT Width, INT Format) { LV_COLUMN Column; ZeroMemory(&Column, sizeof(LV_COLUMN)); Column.mask = LVCF_FMT | LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM; Column.iSubItem = Index; Column.pszText = (LPTSTR) lpText; Column.cx = Width; Column.fmt = Format; return (InsertColumn(Index, &Column) == -1) ? FALSE : TRUE; } INT AddItem(INT ItemIndex, INT IconIndex, LPWSTR lpText, LPARAM lParam) { LV_ITEMW Item; ZeroMemory(&Item, sizeof(LV_ITEM)); Item.mask = LVIF_TEXT | LVIF_PARAM | LVIF_STATE | LVIF_IMAGE; Item.pszText = lpText; Item.lParam = lParam; Item.iItem = ItemIndex; Item.iImage = IconIndex; return InsertItem(&Item); } static INT CALLBACK s_CompareFunc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort) { SortContext * ctx = ((SortContext*) lParamSort); return ctx->lvw->CompareFunc(lParam1, lParam2, ctx->iSubItem); } INT CompareFunc(LPARAM lParam1, LPARAM lParam2, INT iSubItem) { WCHAR Item1[MAX_STR_LEN], Item2[MAX_STR_LEN]; LVFINDINFO IndexInfo; INT Index; IndexInfo.flags = LVFI_PARAM; IndexInfo.lParam = lParam1; Index = FindItem(-1, &IndexInfo); GetItemText(Index, iSubItem, Item1, sizeof(Item1) / sizeof(WCHAR)); IndexInfo.lParam = lParam2; Index = FindItem(-1, &IndexInfo); GetItemText(Index, iSubItem, Item2, sizeof(Item2) / sizeof(WCHAR)); if (bAscending) return wcscmp(Item2, Item1); else return wcscmp(Item1, Item2); return 0; } HWND Create(HWND hwndParent) { RECT r = { 205, 28, 465, 250 }; DWORD style = WS_CHILD | WS_VISIBLE | LVS_SORTASCENDING | LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS; HMENU menu = GetSubMenu(LoadMenuW(hInst, MAKEINTRESOURCEW(IDR_APPLICATIONMENU)), 0); HWND hwnd = CListView::Create(hwndParent, r, NULL, style, WS_EX_CLIENTEDGE, menu); if (hwnd) SetExtendedListViewStyle(LVS_EX_FULLROWSELECT); return hwnd; } }; class CMainWindow : public CWindowImpl { CUiPanel * m_ClientPanel; CUiSplitPanel * m_VSplitter; CUiSplitPanel * m_HSplitter; CMainToolbar * m_Toolbar; CAppsListView * m_ListView; CUiWindow * m_TreeView; CUiWindow * m_StatusBar; CUiWindow * m_RichEdit; CUiWindow<> * m_SearchBar; HIMAGELIST hImageTreeView; PWSTR pLink; BOOL SearchEnabled; public: CMainWindow() : m_ClientPanel(NULL), hImageTreeView(NULL), pLink(NULL), SearchEnabled(TRUE) { } private: VOID InitApplicationsList(VOID) { WCHAR szText[MAX_STR_LEN]; /* Add columns to ListView */ LoadStringW(hInst, IDS_APP_NAME, szText, _countof(szText)); m_ListView->AddColumn(0, szText, 200, LVCFMT_LEFT); LoadStringW(hInst, IDS_APP_INST_VERSION, szText, _countof(szText)); m_ListView->AddColumn(1, szText, 90, LVCFMT_RIGHT); LoadStringW(hInst, IDS_APP_DESCRIPTION, szText, _countof(szText)); m_ListView->AddColumn(3, szText, 250, LVCFMT_LEFT); UpdateApplicationsList(ENUM_ALL_COMPONENTS); } HTREEITEM AddCategory(HTREEITEM hRootItem, UINT TextIndex, UINT IconIndex) { WCHAR szText[MAX_STR_LEN]; INT Index; HICON hIcon; hIcon = (HICON) LoadImage(hInst, MAKEINTRESOURCE(IconIndex), IMAGE_ICON, TREEVIEW_ICON_SIZE, TREEVIEW_ICON_SIZE, LR_CREATEDIBSECTION); Index = ImageList_AddIcon(hImageTreeView, hIcon); DestroyIcon(hIcon); LoadStringW(hInst, TextIndex, szText, _countof(szText)); return m_TreeView->AddItem(hRootItem, szText, Index, Index, TextIndex); } VOID InitCategoriesList(VOID) { HTREEITEM hRootItem1, hRootItem2; hRootItem1 = AddCategory(TVI_ROOT, IDS_INSTALLED, IDI_CATEGORY); AddCategory(hRootItem1, IDS_APPLICATIONS, IDI_APPS); AddCategory(hRootItem1, IDS_UPDATES, IDI_APPUPD); hRootItem2 = AddCategory(TVI_ROOT, IDS_AVAILABLEFORINST, IDI_CATEGORY); AddCategory(hRootItem2, IDS_CAT_AUDIO, IDI_CAT_AUDIO); AddCategory(hRootItem2, IDS_CAT_VIDEO, IDI_CAT_VIDEO); AddCategory(hRootItem2, IDS_CAT_GRAPHICS, IDI_CAT_GRAPHICS); AddCategory(hRootItem2, IDS_CAT_GAMES, IDI_CAT_GAMES); AddCategory(hRootItem2, IDS_CAT_INTERNET, IDI_CAT_INTERNET); AddCategory(hRootItem2, IDS_CAT_OFFICE, IDI_CAT_OFFICE); AddCategory(hRootItem2, IDS_CAT_DEVEL, IDI_CAT_DEVEL); AddCategory(hRootItem2, IDS_CAT_EDU, IDI_CAT_EDU); AddCategory(hRootItem2, IDS_CAT_ENGINEER, IDI_CAT_ENGINEER); AddCategory(hRootItem2, IDS_CAT_FINANCE, IDI_CAT_FINANCE); AddCategory(hRootItem2, IDS_CAT_SCIENCE, IDI_CAT_SCIENCE); AddCategory(hRootItem2, IDS_CAT_TOOLS, IDI_CAT_TOOLS); AddCategory(hRootItem2, IDS_CAT_DRIVERS, IDI_CAT_DRIVERS); AddCategory(hRootItem2, IDS_CAT_LIBS, IDI_CAT_LIBS); AddCategory(hRootItem2, IDS_CAT_OTHER, IDI_CAT_OTHER); m_TreeView->SetImageList(hImageTreeView, TVSIL_NORMAL); m_TreeView->Expand(hRootItem2, TVE_EXPAND); m_TreeView->Expand(hRootItem1, TVE_EXPAND); m_TreeView->SelectItem(hRootItem1); } BOOL CreateStatusBar() { m_StatusBar = new CUiWindow(); m_StatusBar->m_VerticalAlignment = UiAlign_RightBtm; m_StatusBar->m_HorizontalAlignment = UiAlign_Stretch; m_ClientPanel->Children().Append(m_StatusBar); return m_StatusBar->Create(m_hWnd, (HMENU)IDC_STATUSBAR) != NULL; } BOOL CreateToolbar() { m_Toolbar = new CMainToolbar(); m_Toolbar->m_VerticalAlignment = UiAlign_LeftTop; m_Toolbar->m_HorizontalAlignment = UiAlign_Stretch; m_ClientPanel->Children().Append(m_Toolbar); return m_Toolbar->Create(m_hWnd) != NULL; } BOOL CreateTreeView() { m_TreeView = new CUiWindow(); m_TreeView->m_VerticalAlignment = UiAlign_Stretch; m_TreeView->m_HorizontalAlignment = UiAlign_Stretch; m_VSplitter->First().Append(m_TreeView); return m_TreeView->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); hListView = m_ListView->Create(m_hWnd); return hListView != NULL; } BOOL CreateRichEdit() { m_RichEdit = new CUiWindow(); m_RichEdit->m_VerticalAlignment = UiAlign_Stretch; m_RichEdit->m_HorizontalAlignment = UiAlign_Stretch; m_HSplitter->Second().Append(m_RichEdit); return m_RichEdit->Create(m_hWnd) != NULL; } BOOL CreateVSplitter() { m_VSplitter = new CUiSplitPanel(); m_VSplitter->m_VerticalAlignment = UiAlign_Stretch; m_VSplitter->m_HorizontalAlignment = UiAlign_Stretch; m_VSplitter->m_DynamicFirst = FALSE; m_VSplitter->m_Horizontal = FALSE; m_VSplitter->m_MinFirst = 240; m_VSplitter->m_MinSecond = 300; m_ClientPanel->Children().Append(m_VSplitter); 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 = 32768; m_HSplitter->m_MinFirst = 300; m_HSplitter->m_MinSecond = 80; m_VSplitter->Second().Append(m_HSplitter); return m_HSplitter->Create(m_hWnd) != NULL; } BOOL CreateSearchBar(VOID) { WCHAR szBuf[MAX_STR_LEN]; // TODO: WRAPPER m_SearchBar = new CUiWindow<>(); m_SearchBar->m_VerticalAlignment = UiAlign_LeftTop; m_SearchBar->m_HorizontalAlignment = UiAlign_RightBtm; m_SearchBar->m_Margin.top = 6; m_SearchBar->m_Margin.right = 6; //m_ClientPanel->Children().Append(m_SearchBar); HWND hwnd = CreateWindowExW(WS_EX_CLIENTEDGE, L"Edit", NULL, WS_CHILD | WS_VISIBLE | ES_LEFT | ES_AUTOHSCROLL, 0, 0, 200, 22, m_Toolbar->m_hWnd, (HMENU) 0, hInst, 0); m_SearchBar->m_hWnd = hwnd; m_SearchBar->SendMessageW(WM_SETFONT, (WPARAM) GetStockObject(DEFAULT_GUI_FONT), 0); LoadStringW(hInst, IDS_SEARCH_TEXT, szBuf, sizeof(szBuf) / sizeof(WCHAR)); m_SearchBar->SetWindowTextW(szBuf); return hwnd != NULL; } BOOL CreateLayout() { bool b = TRUE; m_ClientPanel = new CUiPanel(); m_ClientPanel->m_VerticalAlignment = UiAlign_Stretch; m_ClientPanel->m_HorizontalAlignment = UiAlign_Stretch; // Top level b = b && CreateStatusBar(); b = b && CreateToolbar(); b = b && CreateSearchBar(); b = b && CreateVSplitter(); // Inside V Splitter b = b && CreateHSplitter(); b = b && CreateTreeView(); // Inside H Splitter b = b && CreateListView(); b = b && CreateRichEdit(); if (b) { RECT rTop; RECT rBottom; /* Size status bar */ m_StatusBar->SendMessage(WM_SIZE, 0, 0); /* Size tool bar */ m_Toolbar->AutoSize(); ::GetWindowRect(m_Toolbar->m_hWnd, &rTop); ::GetWindowRect(m_StatusBar->m_hWnd, &rBottom); m_VSplitter->m_Margin.top = rTop.bottom - rTop.top; m_VSplitter->m_Margin.bottom = rBottom.bottom-rBottom.top; } return b; } BOOL InitControls() { /* Create image list */ hImageTreeView = ImageList_Create(TREEVIEW_ICON_SIZE, TREEVIEW_ICON_SIZE, GetSystemColorDepth() | ILC_MASK, 0, 1); if (CreateLayout()) { WCHAR szBuffer1[MAX_STR_LEN], szBuffer2[MAX_STR_LEN]; InitApplicationsList(); InitCategoriesList(); LoadStringW(hInst, IDS_APPS_COUNT, szBuffer2, _countof(szBuffer2)); StringCbPrintfW(szBuffer1, sizeof(szBuffer1), szBuffer2, m_ListView->GetItemCount()); m_StatusBar->SetText(szBuffer1); return TRUE; } return FALSE; } VOID OnSize(HWND hwnd, WPARAM wParam, LPARAM lParam) { /* Size status bar */ m_StatusBar->SendMessage(WM_SIZE, 0, 0); /* Size tool bar */ m_Toolbar->AutoSize(); RECT r = { 0, 0, LOWORD(lParam), HIWORD(lParam) }; HDWP hdwp = NULL; int count = m_ClientPanel->CountSizableChildren(); hdwp = BeginDeferWindowPos(count); if (hdwp) hdwp = m_ClientPanel->OnParentSize(r, hdwp); if (hdwp) EndDeferWindowPos(hdwp); // TODO: Sub-layouts for children of children count = m_SearchBar->CountSizableChildren(); hdwp = BeginDeferWindowPos(count); if (hdwp) hdwp = m_SearchBar->OnParentSize(r, hdwp); if (hdwp) EndDeferWindowPos(hdwp); } BOOL ProcessWindowMessage(HWND hwnd, UINT Msg, WPARAM wParam, LPARAM lParam, LRESULT& theResult, DWORD dwMapId) { theResult = 0; switch (Msg) { case WM_CREATE: if (!InitControls()) ::PostMessage(hwnd, WM_CLOSE, 0, 0); break; case WM_DESTROY: { ShowWindow(SW_HIDE); SaveSettings(hwnd); FreeLogs(); FreeCachedAvailableEntries(); if (IS_INSTALLED_ENUM(SelectedEnumType)) FreeInstalledAppList(); if (hImageTreeView) ImageList_Destroy(hImageTreeView); delete m_ClientPanel; PostQuitMessage(0); return 0; } case WM_COMMAND: OnCommand(wParam, lParam); break; case WM_NOTIFY: { LPNMHDR data = (LPNMHDR) lParam; switch (data->code) { case TVN_SELCHANGED: { if (data->hwndFrom == m_TreeView->m_hWnd) { switch (((LPNMTREEVIEW) lParam)->itemNew.lParam) { case IDS_INSTALLED: UpdateApplicationsList(ENUM_ALL_COMPONENTS); break; case IDS_APPLICATIONS: UpdateApplicationsList(ENUM_APPLICATIONS); break; case IDS_UPDATES: UpdateApplicationsList(ENUM_UPDATES); break; case IDS_AVAILABLEFORINST: UpdateApplicationsList(ENUM_ALL_AVAILABLE); break; case IDS_CAT_AUDIO: UpdateApplicationsList(ENUM_CAT_AUDIO); break; case IDS_CAT_DEVEL: UpdateApplicationsList(ENUM_CAT_DEVEL); break; case IDS_CAT_DRIVERS: UpdateApplicationsList(ENUM_CAT_DRIVERS); break; case IDS_CAT_EDU: UpdateApplicationsList(ENUM_CAT_EDU); break; case IDS_CAT_ENGINEER: UpdateApplicationsList(ENUM_CAT_ENGINEER); break; case IDS_CAT_FINANCE: UpdateApplicationsList(ENUM_CAT_FINANCE); break; case IDS_CAT_GAMES: UpdateApplicationsList(ENUM_CAT_GAMES); break; case IDS_CAT_GRAPHICS: UpdateApplicationsList(ENUM_CAT_GRAPHICS); break; case IDS_CAT_INTERNET: UpdateApplicationsList(ENUM_CAT_INTERNET); break; case IDS_CAT_LIBS: UpdateApplicationsList(ENUM_CAT_LIBS); break; case IDS_CAT_OFFICE: UpdateApplicationsList(ENUM_CAT_OFFICE); break; case IDS_CAT_OTHER: UpdateApplicationsList(ENUM_CAT_OTHER); break; case IDS_CAT_SCIENCE: UpdateApplicationsList(ENUM_CAT_SCIENCE); break; case IDS_CAT_TOOLS: UpdateApplicationsList(ENUM_CAT_TOOLS); break; case IDS_CAT_VIDEO: UpdateApplicationsList(ENUM_CAT_VIDEO); break; } } HMENU mainMenu = ::GetMenu(hwnd); HMENU lvwMenu = ::GetMenu(m_ListView->m_hWnd); /* Disable/enable items based on treeview selection */ if (IsSelectedNodeInstalled()) { EnableMenuItem(mainMenu, ID_REGREMOVE, MF_ENABLED); EnableMenuItem(mainMenu, ID_INSTALL, MF_GRAYED); 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->SendMessage(TB_ENABLEBUTTON, ID_REGREMOVE, TRUE); m_Toolbar->SendMessage(TB_ENABLEBUTTON, ID_INSTALL, FALSE); m_Toolbar->SendMessage(TB_ENABLEBUTTON, ID_UNINSTALL, TRUE); m_Toolbar->SendMessage(TB_ENABLEBUTTON, ID_MODIFY, TRUE); } else { EnableMenuItem(mainMenu, ID_REGREMOVE, MF_GRAYED); EnableMenuItem(mainMenu, ID_INSTALL, MF_ENABLED); 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->SendMessage(TB_ENABLEBUTTON, ID_REGREMOVE, FALSE); m_Toolbar->SendMessage(TB_ENABLEBUTTON, ID_INSTALL, TRUE); m_Toolbar->SendMessage(TB_ENABLEBUTTON, ID_UNINSTALL, FALSE); m_Toolbar->SendMessage(TB_ENABLEBUTTON, ID_MODIFY, FALSE); } } 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)) { if (IS_INSTALLED_ENUM(SelectedEnumType)) ShowInstalledAppInfo(ItemIndex); if (IS_AVAILABLE_ENUM(SelectedEnumType)) ShowAvailableAppInfo(ItemIndex); } } } 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) { if (IS_INSTALLED_ENUM(SelectedEnumType)) ShowInstalledAppInfo(-1); if (IS_AVAILABLE_ENUM(SelectedEnumType)) ShowAvailableAppInfo(-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 */ SendMessage(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 EN_LINK: OnLink((ENLINK*) lParam); break; case TTN_GETDISPINFO: m_Toolbar->OnGetDispInfo((LPTOOLTIPTEXT) lParam); break; } } break; case WM_SIZE: OnSize(hwnd, wParam, lParam); break; case WM_SIZING: { LPRECT pRect = (LPRECT) lParam; if (pRect->right - pRect->left < 565) pRect->right = pRect->left + 565; if (pRect->bottom - pRect->top < 300) pRect->bottom = pRect->top + 300; return TRUE; } case WM_SYSCOLORCHANGE: { /* Forward WM_SYSCOLORCHANGE to common controls */ m_ListView->SendMessage(WM_SYSCOLORCHANGE, 0, 0); m_TreeView->SendMessage(WM_SYSCOLORCHANGE, 0, 0); m_Toolbar->SendMessage(WM_SYSCOLORCHANGE, 0, 0); m_ListView->SendMessage(EM_SETBKGNDCOLOR, 0, GetSysColor(COLOR_BTNFACE)); } break; case WM_TIMER: if (wParam == SEARCH_TIMER_ID) { ::KillTimer(hwnd, SEARCH_TIMER_ID); UpdateApplicationsList(-1); } break; } return FALSE; } virtual VOID OnLink(ENLINK *Link) { switch (Link->msg) { case WM_LBUTTONUP: case WM_RBUTTONUP: { if (pLink) HeapFree(GetProcessHeap(), 0, pLink); pLink = (PWSTR) HeapAlloc(GetProcessHeap(), 0, (max(Link->chrg.cpMin, Link->chrg.cpMax) - min(Link->chrg.cpMin, Link->chrg.cpMax) + 1) * sizeof(WCHAR)); if (!pLink) { /* TODO: Error message */ return; } m_RichEdit->SendMessageW(EM_SETSEL, Link->chrg.cpMin, Link->chrg.cpMax); m_RichEdit->SendMessageW(EM_GETSELTEXT, 0, (LPARAM) pLink); ShowPopupMenu(m_RichEdit->m_hWnd, IDR_LINKMENU, -1); } break; } } BOOL IsSelectedNodeInstalled(void) { HTREEITEM hSelectedItem = m_TreeView->GetSelection(); TV_ITEM tItem; tItem.mask = TVIF_PARAM | TVIF_HANDLE; tItem.hItem = hSelectedItem; m_TreeView->GetItem(&tItem); switch (tItem.lParam) { case IDS_INSTALLED: case IDS_APPLICATIONS: case IDS_UPDATES: return TRUE; default: return FALSE; } } VOID OnCommand(WPARAM wParam, LPARAM lParam) { WORD wCommand = LOWORD(wParam); if (lParam == (LPARAM) m_SearchBar->m_hWnd) { WCHAR szBuf[MAX_STR_LEN]; switch (HIWORD(wParam)) { case EN_SETFOCUS: { WCHAR szWndText[MAX_STR_LEN]; LoadStringW(hInst, IDS_SEARCH_TEXT, szBuf, _countof(szBuf)); m_SearchBar->GetWindowTextW(szWndText, MAX_STR_LEN); if (wcscmp(szBuf, szWndText) == 0) { SearchEnabled = FALSE; m_SearchBar->SetWindowTextW(L""); } } break; case EN_KILLFOCUS: { m_SearchBar->GetWindowTextW(szBuf, MAX_STR_LEN); if (wcslen(szBuf) < 1) { LoadStringW(hInst, IDS_SEARCH_TEXT, szBuf, _countof(szBuf)); SearchEnabled = FALSE; m_SearchBar->SetWindowTextW(szBuf); } } break; case EN_CHANGE: { WCHAR szWndText[MAX_STR_LEN]; if (!SearchEnabled) { SearchEnabled = TRUE; break; } LoadStringW(hInst, IDS_SEARCH_TEXT, szBuf, _countof(szBuf)); m_SearchBar->GetWindowTextW(szWndText, MAX_STR_LEN); if (wcscmp(szBuf, szWndText) != 0) { StringCbCopy(szSearchPattern, sizeof(szSearchPattern), szWndText); } else { szSearchPattern[0] = UNICODE_NULL; } DWORD dwDelay; SystemParametersInfo(SPI_GETMENUSHOWDELAY, 0, &dwDelay, 0); SetTimer(SEARCH_TIMER_ID, dwDelay); } break; } return; } switch (wCommand) { case ID_OPEN_LINK: ShellExecuteW(m_hWnd, L"open", pLink, NULL, NULL, SW_SHOWNOACTIVATE); HeapFree(GetProcessHeap(), 0, pLink); break; case ID_COPY_LINK: CopyTextToClipboard(pLink); HeapFree(GetProcessHeap(), 0, pLink); break; case ID_SETTINGS: CreateSettingsDlg(m_hWnd); break; case ID_EXIT: PostMessageW(WM_CLOSE, 0, 0); break; case ID_INSTALL: if (DownloadApplication(-1)) /* TODO: Implement install dialog * if (InstallApplication(-1)) */ UpdateApplicationsList(-1); break; case ID_UNINSTALL: if (UninstallApplication(-1, FALSE)) UpdateApplicationsList(-1); break; case ID_MODIFY: if (UninstallApplication(-1, TRUE)) UpdateApplicationsList(-1); break; case ID_REGREMOVE: RemoveAppFromRegistry(-1); break; case ID_REFRESH: UpdateApplicationsList(-1); break; case ID_RESETDB: UpdateAppsDB(); UpdateApplicationsList(-1); break; case ID_HELP: MessageBoxW(L"Help not implemented yet", NULL, MB_OK); break; case ID_ABOUT: ShowAboutDialog(); break; } } VOID FreeInstalledAppList(VOID) { INT Count = ListView_GetItemCount(hListView) - 1; PINSTALLED_INFO Info; while (Count >= 0) { Info = (PINSTALLED_INFO) ListViewGetlParam(Count); if (Info) { RegCloseKey(Info->hSubKey); HeapFree(GetProcessHeap(), 0, Info); } Count--; } } static BOOL SearchPatternMatch(PCWSTR szHaystack, PCWSTR szNeedle) { if (!*szNeedle) return TRUE; /* TODO: Improve pattern search beyond a simple case-insensitive substring search. */ return StrStrIW(szHaystack, szNeedle) != NULL; } static BOOL CALLBACK s_EnumInstalledAppProc(INT ItemIndex, LPWSTR lpName, PINSTALLED_INFO Info) { PINSTALLED_INFO ItemInfo; WCHAR szText[MAX_PATH]; INT Index; if (!SearchPatternMatch(lpName, szSearchPattern)) { RegCloseKey(Info->hSubKey); return TRUE; } ItemInfo = (PINSTALLED_INFO) HeapAlloc(GetProcessHeap(), 0, sizeof(INSTALLED_INFO)); if (!ItemInfo) { RegCloseKey(Info->hSubKey); return FALSE; } RtlCopyMemory(ItemInfo, Info, sizeof(INSTALLED_INFO)); Index = ListViewAddItem(ItemIndex, 0, lpName, (LPARAM) ItemInfo); /* Get version info */ GetApplicationString(ItemInfo->hSubKey, L"DisplayVersion", szText); ListView_SetItemText(hListView, Index, 1, szText); /* Get comments */ GetApplicationString(ItemInfo->hSubKey, L"Comments", szText); ListView_SetItemText(hListView, Index, 2, szText); return TRUE; } static BOOL CALLBACK s_EnumAvailableAppProc(PAPPLICATION_INFO Info) { INT Index; if (!SearchPatternMatch(Info->szName, szSearchPattern) && !SearchPatternMatch(Info->szDesc, szSearchPattern)) { return TRUE; } /* Only add a ListView entry if... - no RegName was supplied (so we cannot determine whether the application is installed or not) or - a RegName was supplied and the application is not installed */ if (!*Info->szRegName || (!IsInstalledApplication(Info->szRegName, FALSE) && !IsInstalledApplication(Info->szRegName, TRUE))) { Index = ListViewAddItem(Info->Category, 0, Info->szName, (LPARAM) Info); ListView_SetItemText(hListView, Index, 1, Info->szVersion); ListView_SetItemText(hListView, Index, 2, Info->szDesc); } return TRUE; } VOID UpdateApplicationsList(INT EnumType) { WCHAR szBuffer1[MAX_STR_LEN], szBuffer2[MAX_STR_LEN]; HICON hIcon; HIMAGELIST hImageListView; m_ListView->SendMessage(WM_SETREDRAW, FALSE, 0); if (EnumType == -1) EnumType = SelectedEnumType; if (IS_INSTALLED_ENUM(SelectedEnumType)) FreeInstalledAppList(); (VOID) ListView_DeleteAllItems(hListView); /* Create image list */ hImageListView = ImageList_Create(LISTVIEW_ICON_SIZE, LISTVIEW_ICON_SIZE, GetSystemColorDepth() | ILC_MASK, 0, 1); hIcon = (HICON) LoadImage(hInst, MAKEINTRESOURCE(IDI_MAIN), IMAGE_ICON, LISTVIEW_ICON_SIZE, LISTVIEW_ICON_SIZE, LR_CREATEDIBSECTION); ImageList_AddIcon(hImageListView, hIcon); DestroyIcon(hIcon); if (IS_INSTALLED_ENUM(EnumType)) { /* Enum installed applications and updates */ EnumInstalledApplications(EnumType, TRUE, s_EnumInstalledAppProc); EnumInstalledApplications(EnumType, FALSE, s_EnumInstalledAppProc); } else if (IS_AVAILABLE_ENUM(EnumType)) { /* Enum availabled applications */ EnumAvailableApplications(EnumType, s_EnumAvailableAppProc); } /* Set image list for ListView */ hImageListView = ListView_SetImageList(hListView, hImageListView, LVSIL_SMALL); /* Destroy old image list */ if (hImageListView) ImageList_Destroy(hImageListView); SelectedEnumType = EnumType; LoadStringW(hInst, IDS_APPS_COUNT, szBuffer2, _countof(szBuffer2)); StringCbPrintfW(szBuffer1, sizeof(szBuffer1), szBuffer2, ListView_GetItemCount(hListView)); SetStatusBarText(szBuffer1); SetWelcomeText(); /* set automatic column width for program names if the list is not empty */ if (ListView_GetItemCount(hListView) > 0) ListView_SetColumnWidth(hListView, 0, LVSCW_AUTOSIZE); SendMessage(hListView, WM_SETREDRAW, TRUE, 0); } public: static ATL::CWndClassInfo& GetWndClassInfo() { DWORD csStyle = CS_VREDRAW |CS_HREDRAW; static ATL::CWndClassInfo wc = { { sizeof(WNDCLASSEX), csStyle, StartWindowProc, 0, 0, NULL, LoadIcon(_AtlBaseModule.GetModuleInstance(), MAKEINTRESOURCE(IDI_MAIN)), LoadCursor(NULL, IDC_ARROW), (HBRUSH) (COLOR_BTNFACE + 1), MAKEINTRESOURCE(IDR_MAINMENU), L"RAppsWnd", NULL }, NULL, NULL, IDC_ARROW, TRUE, 0, _T("") }; return wc; } HWND Create() { WCHAR szWindowName[MAX_STR_LEN]; LoadStringW(hInst, IDS_APPTITLE, szWindowName, _countof(szWindowName)); RECT r = { (SettingsInfo.bSaveWndPos ? SettingsInfo.Left : CW_USEDEFAULT), (SettingsInfo.bSaveWndPos ? SettingsInfo.Top : CW_USEDEFAULT), (SettingsInfo.bSaveWndPos ? SettingsInfo.Width : 680), (SettingsInfo.bSaveWndPos ? SettingsInfo.Height : 450) }; r.right += r.left; r.bottom += r.top; return CWindowImpl::Create(NULL, r, szWindowName, WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, WS_EX_WINDOWEDGE); } CStatusBar * GetStatusBar() { return m_StatusBar; } CAppsListView * GetListView() { return m_ListView; } CRichEdit * GetRichEdit() { return m_RichEdit; } }; CMainWindow * g_MainWindow; HWND CreateMainWindow() { g_MainWindow = new CMainWindow(); return g_MainWindow->Create(); } DWORD_PTR ListViewGetlParam(INT item) { if (item < 0) { item = g_MainWindow->GetListView()->GetSelectionMark(); } return g_MainWindow->GetListView()->GetItemData(item); } VOID SetStatusBarText(PCWSTR szText) { g_MainWindow->GetStatusBar()->SetText(szText); } INT ListViewAddItem(INT ItemIndex, INT IconIndex, PWSTR lpName, LPARAM lParam) { return g_MainWindow->GetListView()->AddItem(ItemIndex, IconIndex, lpName, lParam); } VOID NewRichEditText(PCWSTR szText, DWORD flags) { g_MainWindow->GetRichEdit()->SetText(szText, flags); } VOID InsertRichEditText(PCWSTR szText, DWORD flags) { g_MainWindow->GetRichEdit()->InsertText(szText, flags); }