From 5e4fe2cf4036499038ebdb0f33bbf64536545323 Mon Sep 17 00:00:00 2001 From: Katayama Hirofumi MZ Date: Wed, 31 Jan 2024 16:52:02 +0900 Subject: [PATCH] [MSCTF][MSUTB][SDK] Add CTrayIconWnd and CMainIconItem (#6424) Supporting Language Bar... JIRA issue: CORE-19363 - Add CTrayIconWnd, CMainIconItem, and CButtonIconItem classes. - Modify msctf.spec for TF_GetLangIcon function. - Add main icon IDI_MAINICON ("res/earth.ico"). --- dll/win32/msctf/msctf.spec | 2 +- dll/win32/msutb/lang/en-US.rc | 2 + dll/win32/msutb/msutb.cpp | 459 ++++++++++++++++++++++++++-- dll/win32/msutb/msutb.rc | 2 + dll/win32/msutb/precomp.h | 1 + dll/win32/msutb/res/earth.ico | Bin 0 -> 22382 bytes dll/win32/msutb/resource.h | 4 + sdk/include/psdk/msctf.idl | 1 + sdk/include/reactos/cicero/cicutb.h | 4 + 9 files changed, 455 insertions(+), 20 deletions(-) create mode 100644 dll/win32/msutb/res/earth.ico diff --git a/dll/win32/msctf/msctf.spec b/dll/win32/msctf/msctf.spec index 569f8701e8b..52288129e1c 100644 --- a/dll/win32/msctf/msctf.spec +++ b/dll/win32/msctf/msctf.spec @@ -18,7 +18,7 @@ @ stdcall -stub TF_DllDetachInOther() @ stdcall -stub TF_GetGlobalCompartment(ptr) @ stub TF_GetInputScope -@ stub TF_GetLangIcon +@ stdcall -stub TF_GetLangIcon(long ptr long) @ stub TF_GetMlngHKL @ stub TF_GetMlngIconIndex @ stub TF_GetThreadFlags diff --git a/dll/win32/msutb/lang/en-US.rc b/dll/win32/msutb/lang/en-US.rc index 98a9ea10f96..3c3a7a579dd 100644 --- a/dll/win32/msutb/lang/en-US.rc +++ b/dll/win32/msutb/lang/en-US.rc @@ -16,6 +16,8 @@ BEGIN IDS_IGNORE "&Ignore" IDS_YES "&Yes" IDS_NO "&No" + + IDS_RESTORELANGBAR "Restore Language Bar" IDS_MENUWND "Menu Window" IDS_LEFTCLICK "Left Click" END diff --git a/dll/win32/msutb/msutb.cpp b/dll/win32/msutb/msutb.cpp index 9a4a96e8553..26816ecb962 100644 --- a/dll/win32/msutb/msutb.cpp +++ b/dll/win32/msutb/msutb.cpp @@ -16,6 +16,10 @@ DWORD g_dwOSInfo = 0; CRITICAL_SECTION g_cs; LONG g_DllRefCount = 0; +UINT g_uTimerElapseDOACCDEFAULTACTION = 200; + +#define TIMER_ID_DOACCDEFAULTACTION 11 + EXTERN_C void __cxa_pure_virtual(void) { ERR("__cxa_pure_virtual\n"); @@ -34,6 +38,12 @@ CMsUtbModule gModule; class CCicLibMenuItem; class CTipbarAccItem; class CUTBMenuItem; +class CMainIconItem; +class CTrayIconItem; +class CTipbarWnd; +class CButtonIconItem; + +CTipbarWnd *g_pTipbarWnd = NULL; HRESULT GetGlobalCompartment(REFGUID rguid, ITfCompartment **ppComp) { @@ -98,9 +108,8 @@ void DoCloseLangbar(void) if (pLangBarMgr) { - hr = pLangBarMgr->ShowFloating(8); + hr = pLangBarMgr->ShowFloating(TF_SFT_HIDDEN); pLangBarMgr->Release(); - pLangBarMgr = NULL; } if (SUCCEEDED(hr)) @@ -453,9 +462,50 @@ public: class CTrayIconWnd { +protected: + DWORD m_dwUnknown20; + BOOL m_bBusy; + UINT m_uCallbackMessage; + UINT m_uMsg; + HWND m_hWnd; + DWORD m_dwUnknown21[2]; + HWND m_hTrayWnd; + HWND m_hNotifyWnd; + DWORD m_dwTrayWndThreadId; + DWORD m_dwUnknown22; + HWND m_hwndProgman; + DWORD m_dwProgmanThreadId; + CMainIconItem *m_pMainIconItem; + CicArray m_Items; + UINT m_uCallbackMsg; + UINT m_uNotifyIconID; + + static BOOL CALLBACK EnumChildWndProc(HWND hWnd, LPARAM lParam); + static LRESULT CALLBACK _WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + public: - //FIXME - HWND GetNotifyWnd() { return NULL; }; + CTrayIconWnd(); + ~CTrayIconWnd(); + + BOOL RegisterClass(); + HWND CreateWnd(); + void DestroyWnd(); + + BOOL SetMainIcon(HKL hKL); + BOOL SetIcon(REFGUID rguid, DWORD dwUnknown24, HICON hIcon, LPCWSTR psz); + + void RemoveAllIcon(DWORD dwFlags); + void RemoveUnusedIcons(int unknown); + + CButtonIconItem *FindIconItem(REFGUID rguid); + BOOL FindTrayEtc(); + HWND GetNotifyWnd(); + BOOL OnIconMessage(UINT uMsg, WPARAM wParam, LPARAM lParam); + + static CTrayIconWnd *GetThis(HWND hWnd); + static void SetThis(HWND hWnd, LPCREATESTRUCT pCS); + + void CallOnDelayMsg(); }; /***********************************************************************/ @@ -469,20 +519,50 @@ protected: DWORD m_dwIconAddOrModify; BOOL m_bIconAdded; CTrayIconWnd *m_pTrayIconWnd; - DWORD m_dw; + DWORD m_dwUnknown25; GUID m_guid; RECT m_rcMenu; POINT m_ptCursor; + friend class CTrayIconWnd; +public: CTrayIconItem(CTrayIconWnd *pTrayIconWnd); + virtual ~CTrayIconItem() { } BOOL _Init(HWND hWnd, UINT uCallbackMessage, UINT uNotifyIconID, const GUID& rguid); BOOL UpdateMenuRectPoint(); BOOL RemoveIcon(); - STDMETHOD_(BOOL, SetIcon)(HICON hIcon, LPCTSTR pszTip); - STDMETHOD_(LRESULT, OnMsg)(WPARAM wParam, LPARAM lParam) { return 0; }; - STDMETHOD_(LRESULT, OnDelayMsg)(LPARAM lParam) { return 0; }; + STDMETHOD_(BOOL, SetIcon)(HICON hIcon, LPCWSTR pszTip); + STDMETHOD_(BOOL, OnMsg)(WPARAM wParam, LPARAM lParam) { return FALSE; }; + STDMETHOD_(BOOL, OnDelayMsg)(UINT uMsg) { return 0; }; +}; + +/***********************************************************************/ + +class CButtonIconItem : public CTrayIconItem +{ +protected: + DWORD m_dwUnknown24; + HKL m_hKL; + friend class CTrayIconWnd; + +public: + CButtonIconItem(CTrayIconWnd *pWnd, DWORD dwUnknown24); + + STDMETHOD_(BOOL, OnMsg)(WPARAM wParam, LPARAM lParam) override; + STDMETHOD_(BOOL, OnDelayMsg)(UINT uMsg) override; +}; + +/***********************************************************************/ + +class CMainIconItem : public CButtonIconItem +{ +public: + CMainIconItem(CTrayIconWnd *pWnd); + + BOOL Init(HWND hWnd); + STDMETHOD_(BOOL, OnDelayMsg)(UINT uMsg) override; }; /*********************************************************************** @@ -780,7 +860,7 @@ STDMETHODIMP CCicLibMenu::AddMenuItem( pSubMenu->AddRef(); } - *m_MenuItems.Append(1) = pMenuItem; + m_MenuItems.Add(pMenuItem); return S_OK; } @@ -1429,8 +1509,8 @@ BOOL CUTBMenuWnd::StartDoAccDefaultActionTimer(CUTBMenuItem *pTarget) if (::IsWindow(m_hWnd)) { - ::KillTimer(m_hWnd, 11); - ::SetTimer(m_hWnd, 11, 200, NULL); + ::KillTimer(m_hWnd, TIMER_ID_DOACCDEFAULTACTION); + ::SetTimer(m_hWnd, TIMER_ID_DOACCDEFAULTACTION, g_uTimerElapseDOACCDEFAULTACTION, NULL); } return TRUE; @@ -1525,9 +1605,9 @@ CUTBMenuWnd::OnShowWindow(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) STDMETHODIMP_(void) CUTBMenuWnd::OnTimer(WPARAM wParam) { - if (wParam == 11) + if (wParam == TIMER_ID_DOACCDEFAULTACTION) { - ::KillTimer(m_hWnd, 11); + ::KillTimer(m_hWnd, TIMER_ID_DOACCDEFAULTACTION); if (m_pAccessible && m_nMenuWndID) { m_pAccessible->DoDefaultActionReal(m_nMenuWndID); @@ -1633,10 +1713,10 @@ BOOL CTrayIconItem::RemoveIcon() { if (m_dwIconAddOrModify == NIM_MODIFY) { - NOTIFYICONDATA NotifyIcon = { sizeof(NotifyIcon), m_hWnd, m_uNotifyIconID }; + NOTIFYICONDATAW NotifyIcon = { sizeof(NotifyIcon), m_hWnd, m_uNotifyIconID }; NotifyIcon.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP; NotifyIcon.uCallbackMessage = m_uCallbackMessage; - ::Shell_NotifyIcon(NIM_DELETE, &NotifyIcon); + ::Shell_NotifyIconW(NIM_DELETE, &NotifyIcon); } m_dwIconAddOrModify = NIM_ADD; @@ -1644,22 +1724,22 @@ BOOL CTrayIconItem::RemoveIcon() return TRUE; } -BOOL CTrayIconItem::SetIcon(HICON hIcon, LPCTSTR pszTip) +BOOL CTrayIconItem::SetIcon(HICON hIcon, LPCWSTR pszTip) { if (!hIcon) return FALSE; - NOTIFYICONDATA NotifyIcon = { sizeof(NotifyIcon), m_hWnd, m_uNotifyIconID }; + NOTIFYICONDATAW NotifyIcon = { sizeof(NotifyIcon), m_hWnd, m_uNotifyIconID }; NotifyIcon.uFlags = NIF_ICON | NIF_MESSAGE; NotifyIcon.uCallbackMessage = m_uCallbackMessage; NotifyIcon.hIcon = hIcon; if (pszTip) { NotifyIcon.uFlags |= NIF_TIP; - StringCchCopy(NotifyIcon.szTip, _countof(NotifyIcon.szTip), pszTip); + StringCchCopyW(NotifyIcon.szTip, _countof(NotifyIcon.szTip), pszTip); } - ::Shell_NotifyIcon(m_dwIconAddOrModify, &NotifyIcon); + ::Shell_NotifyIconW(m_dwIconAddOrModify, &NotifyIcon); m_dwIconAddOrModify = NIM_MODIFY; m_bIconAdded = NIM_MODIFY; @@ -1676,6 +1756,347 @@ BOOL CTrayIconItem::UpdateMenuRectPoint() return TRUE; } +/*********************************************************************** + * CButtonIconItem + */ + +CButtonIconItem::CButtonIconItem(CTrayIconWnd *pWnd, DWORD dwUnknown24) + : CTrayIconItem(pWnd) +{ + m_dwUnknown24 = dwUnknown24; +} + +/// @unimplemented +STDMETHODIMP_(BOOL) CButtonIconItem::OnMsg(WPARAM wParam, LPARAM lParam) +{ + switch (lParam) + { + case WM_LBUTTONDOWN: + case WM_RBUTTONDOWN: + case WM_LBUTTONDBLCLK: + case WM_RBUTTONDBLCLK: + break; + default: + return TRUE; + } + + //FIXME + return TRUE; +} + +/// @unimplemented +STDMETHODIMP_(BOOL) CButtonIconItem::OnDelayMsg(UINT uMsg) +{ + //FIXME + return FALSE; +} + +/*********************************************************************** + * CMainIconItem + */ + +CMainIconItem::CMainIconItem(CTrayIconWnd *pWnd) + : CButtonIconItem(pWnd, 1) +{ +} + +BOOL CMainIconItem::Init(HWND hWnd) +{ + return CTrayIconItem::_Init(hWnd, WM_USER, 0, GUID_LBI_TRAYMAIN); +} + +/// @unimplemented +STDMETHODIMP_(BOOL) CMainIconItem::OnDelayMsg(UINT uMsg) +{ + if (!CButtonIconItem::OnDelayMsg(uMsg)) + return 0; + + if (uMsg == WM_LBUTTONDBLCLK) + { + //FIXME + //if (g_pTipbarWnd->m_dwUnknown20) + // g_pTipbarWnd->m_pLangBarMgr->ShowFloating(TF_SFT_SHOWNORMAL); + } + else if (uMsg == WM_LBUTTONDOWN || uMsg == WM_RBUTTONDOWN) + { + //FIXME + //g_pTipbarWnd->ShowContextMenu(m_ptCursor, &m_rcClient, uMsg == WM_RBUTTONDOWN); + } + return TRUE; +} + +/*********************************************************************** + * CTrayIconWnd + */ + +CTrayIconWnd::CTrayIconWnd() +{ + m_uCallbackMsg = WM_USER + 0x1000; + m_uNotifyIconID = 0x1000; +} + +CTrayIconWnd::~CTrayIconWnd() +{ + for (size_t iItem = 0; iItem < m_Items.size(); ++iItem) + { + auto& pItem = m_Items[iItem]; + if (pItem) + { + delete pItem; + pItem = NULL; + } + } +} + +void CTrayIconWnd::CallOnDelayMsg() +{ + for (size_t iItem = 0; iItem < m_Items.size(); ++iItem) + { + auto pItem = m_Items[iItem]; + if (pItem && m_uCallbackMessage == pItem->m_uCallbackMessage) + { + pItem->OnDelayMsg(m_uMsg); + break; + } + } +} + +HWND CTrayIconWnd::CreateWnd() +{ + m_hWnd = ::CreateWindowEx(0, TEXT("CTrayIconWndClass"), NULL, WS_DISABLED, + 0, 0, 0, 0, NULL, NULL, g_hInst, this); + FindTrayEtc(); + + m_pMainIconItem = new(cicNoThrow) CMainIconItem(this); + if (m_pMainIconItem) + { + m_pMainIconItem->Init(m_hWnd); + m_Items.Add(m_pMainIconItem); + } + + return m_hWnd; +} + +void CTrayIconWnd::DestroyWnd() +{ + ::DestroyWindow(m_hWnd); + m_hWnd = NULL; +} + +BOOL CALLBACK CTrayIconWnd::EnumChildWndProc(HWND hWnd, LPARAM lParam) +{ + CTrayIconWnd *pWnd = (CTrayIconWnd *)lParam; + + TCHAR ClassName[60]; + ::GetClassName(hWnd, ClassName, _countof(ClassName)); + if (lstrcmp(ClassName, TEXT("TrayNotifyWnd")) != 0) + return TRUE; + + pWnd->m_hNotifyWnd = hWnd; + return FALSE; +} + +CButtonIconItem *CTrayIconWnd::FindIconItem(REFGUID rguid) +{ + for (size_t iItem = 0; iItem < m_Items.size(); ++iItem) + { + auto pItem = m_Items[iItem]; + if (IsEqualGUID(rguid, pItem->m_guid)) + return pItem; + } + return NULL; +} + +BOOL CTrayIconWnd::FindTrayEtc() +{ + m_hTrayWnd = ::FindWindow(TEXT("Shell_TrayWnd"), NULL); + if (!m_hTrayWnd) + return FALSE; + + ::EnumChildWindows(m_hTrayWnd, EnumChildWndProc, (LPARAM)this); + if (!m_hNotifyWnd) + return FALSE; + m_dwTrayWndThreadId = ::GetWindowThreadProcessId(m_hTrayWnd, NULL); + m_hwndProgman = FindWindow(TEXT("Progman"), NULL); + m_dwProgmanThreadId = ::GetWindowThreadProcessId(m_hwndProgman, NULL); + return TRUE; +} + +HWND CTrayIconWnd::GetNotifyWnd() +{ + if (!::IsWindow(m_hNotifyWnd)) + FindTrayEtc(); + return m_hNotifyWnd; +} + +/// @unimplemented +BOOL CTrayIconWnd::OnIconMessage(UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + //FIXME + //if (g_pTipbarWnd) + // g_pTipbarWnd->AttachFocusThread(); + + for (size_t iItem = 0; iItem < m_Items.size(); ++iItem) + { + auto pItem = m_Items[iItem]; + if (pItem) + { + if (uMsg == pItem->m_uCallbackMessage) + { + pItem->OnMsg(wParam, lParam); + return TRUE; + } + } + } + return FALSE; +} + +BOOL CTrayIconWnd::RegisterClass() +{ + WNDCLASSEX wc = { sizeof(wc) }; + wc.style = CS_HREDRAW | CS_VREDRAW; + wc.hInstance = g_hInst; + wc.hCursor = ::LoadCursor(NULL, (LPCTSTR)IDC_ARROW); + wc.lpfnWndProc = CTrayIconWnd::_WndProc; + wc.lpszClassName = TEXT("CTrayIconWndClass"); + ::RegisterClassEx(&wc); + return TRUE; +} + +void CTrayIconWnd::RemoveAllIcon(DWORD dwFlags) +{ + for (size_t iItem = 0; iItem < m_Items.size(); ++iItem) + { + auto pItem = m_Items[iItem]; + if (dwFlags & 0x1) + { + if (IsEqualGUID(pItem->m_guid, GUID_LBI_INATITEM) || + IsEqualGUID(pItem->m_guid, GUID_LBI_CTRL)) + { + continue; + } + } + + if (dwFlags & 0x2) + { + if (IsEqualGUID(pItem->m_guid, GUID_TFCAT_TIP_KEYBOARD)) + continue; + } + + if (pItem->m_uNotifyIconID < 0x1000) + continue; + + pItem->RemoveIcon(); + } +} + +/// @unimplemented +void CTrayIconWnd::RemoveUnusedIcons(int unknown) +{ + //FIXME +} + +BOOL CTrayIconWnd::SetIcon(REFGUID rguid, DWORD dwUnknown24, HICON hIcon, LPCWSTR psz) +{ + CButtonIconItem *pItem = FindIconItem(rguid); + if (!pItem) + { + if (!hIcon) + return FALSE; + pItem = new(cicNoThrow) CButtonIconItem(this, dwUnknown24); + if (!pItem) + return FALSE; + + pItem->_Init(m_hWnd, m_uCallbackMsg, m_uNotifyIconID, rguid); + m_uCallbackMsg += 2; + ++m_uNotifyIconID; + m_Items.Add(pItem); + } + + if (!hIcon) + return pItem->RemoveIcon(); + + return pItem->SetIcon(hIcon, psz); +} + +BOOL CTrayIconWnd::SetMainIcon(HKL hKL) +{ + if (!hKL) + { + m_pMainIconItem->RemoveIcon(); + m_pMainIconItem->m_hKL = NULL; + return TRUE; + } + + if (hKL != m_pMainIconItem->m_hKL) + { + WCHAR szText[64]; + HICON hIcon = TF_GetLangIcon(LOWORD(hKL), szText, _countof(szText)); + if (hIcon) + { + m_pMainIconItem->SetIcon(hIcon, szText); + ::DestroyIcon(hIcon); + } + else + { + ::LoadStringW(g_hInst, IDS_RESTORELANGBAR, szText, _countof(szText)); + hIcon = ::LoadIconW(g_hInst, MAKEINTRESOURCEW(IDI_MAINICON)); + m_pMainIconItem->SetIcon(hIcon, szText); + } + + m_pMainIconItem->m_hKL = hKL; + } + + return TRUE; +} + +CTrayIconWnd *CTrayIconWnd::GetThis(HWND hWnd) +{ + return (CTrayIconWnd *)::GetWindowLongPtr(hWnd, GWL_USERDATA); +} + +void CTrayIconWnd::SetThis(HWND hWnd, LPCREATESTRUCT pCS) +{ + if (pCS) + ::SetWindowLongPtr(hWnd, GWL_USERDATA, (LONG_PTR)pCS->lpCreateParams); + else + ::SetWindowLongPtr(hWnd, GWL_USERDATA, 0); +} + +LRESULT CALLBACK +CTrayIconWnd::_WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + CTrayIconWnd *pThis; + switch (uMsg) + { + case WM_CREATE: + CTrayIconWnd::SetThis(hWnd, (LPCREATESTRUCT)lParam); + break; + case WM_DESTROY: + ::SetWindowLongPtr(hWnd, GWL_USERDATA, 0); + break; + case WM_TIMER: + if (wParam == 100) + { + ::KillTimer(hWnd, 100); + pThis = CTrayIconWnd::GetThis(hWnd); + if (pThis) + pThis->CallOnDelayMsg(); + } + break; + default: + { + if (uMsg < WM_USER) + return ::DefWindowProc(hWnd, uMsg, wParam, lParam); + pThis = CTrayIconWnd::GetThis(hWnd); + if (pThis && pThis->OnIconMessage(uMsg, wParam, lParam)) + break; + return ::DefWindowProc(hWnd, uMsg, wParam, lParam); + } + } + return 0; +} + /*********************************************************************** * GetLibTls (MSUTB.@) * diff --git a/dll/win32/msutb/msutb.rc b/dll/win32/msutb/msutb.rc index 21964d264da..82ae823ce07 100644 --- a/dll/win32/msutb/msutb.rc +++ b/dll/win32/msutb/msutb.rc @@ -20,6 +20,8 @@ LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL #include +IDI_MAINICON ICON "res/earth.ico" + /* UTF-8 */ #pragma code_page(65001) diff --git a/dll/win32/msutb/precomp.h b/dll/win32/msutb/precomp.h index d198ded03f1..05b2a826e27 100644 --- a/dll/win32/msutb/precomp.h +++ b/dll/win32/msutb/precomp.h @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include diff --git a/dll/win32/msutb/res/earth.ico b/dll/win32/msutb/res/earth.ico new file mode 100644 index 0000000000000000000000000000000000000000..7d47171f990718f9d9ce1ea5ce817f335e840919 GIT binary patch literal 22382 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