[MSUTB] Implement CLangBarItemList (#6605)
authorKatayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com>
Tue, 12 Mar 2024 02:40:02 +0000 (11:40 +0900)
committerGitHub <noreply@github.com>
Tue, 12 Mar 2024 02:40:02 +0000 (11:40 +0900)
Supporting the Language bar...
JIRA issue: CORE-19363
- Define LANGBARITEMSTATE structure.
- Implement CLangBarItemList class.

dll/win32/msutb/msutb.cpp

index 912edc4..b81e2e2 100644 (file)
@@ -11,6 +11,21 @@ WINE_DEFAULT_DEBUG_CHANNEL(msutb);
 
 //#define ENABLE_DESKBAND
 
+typedef struct LANGBARITEMSTATE
+{
+    CLSID m_clsid;
+    DWORD m_dwDemoteLevel;
+    UINT_PTR m_nTimerID;
+    UINT m_uTimeOut;
+    BOOL m_bStartedIntentionally;
+    BOOL m_bDisableDemoting;
+
+    BOOL IsShown()
+    {
+        return m_dwDemoteLevel < 2;
+    }
+} LANGBARITEMSTATE, *PLANGBARITEMSTATE;
+
 HINSTANCE g_hInst = NULL;
 UINT g_wmTaskbarCreated = 0;
 UINT g_uACP = CP_ACP;
@@ -1062,6 +1077,27 @@ public:
 
 /***********************************************************************/
 
+class CLangBarItemList : public CicArray<LANGBARITEMSTATE>
+{
+public:
+    BOOL IsStartedIntentionally(REFCLSID rclsid);
+
+    LANGBARITEMSTATE *AddItem(REFCLSID rclsid);
+    void Clear();
+    BOOL SetDemoteLevel(REFCLSID rclsid, DWORD dwDemoteLevel);
+
+    LANGBARITEMSTATE *FindItem(REFCLSID rclsid);
+    LANGBARITEMSTATE *GetItemStateFromTimerId(UINT_PTR nTimerID);
+
+    void Load();
+    void SaveItem(CicRegKey *pRegKey, const LANGBARITEMSTATE *pState);
+
+    void StartDemotingTimer(REFCLSID rclsid, BOOL bIntentional);
+    UINT_PTR FindDemotingTimerId();
+};
+
+/***********************************************************************/
+
 class CTrayIconWnd
 {
 protected:
@@ -1300,6 +1336,7 @@ public:
     void AddUIObjs();
     void RemoveUIObjs();
 
+    CTipbarItem *GetItem(REFCLSID rclsid);
     void GetTextSize(BSTR bstr, LPSIZE pSize);
     void LocateItems();
     void MyMoveWnd(LONG xDelta, LONG yDelta);
@@ -1425,7 +1462,7 @@ class CTipbarWnd
     DWORD m_dwSinkCookie;
     CModalMenu *m_pModalMenu;
     CTipbarThread *m_pThread;
-    CicArray<GUID*> m_TipbarGUIDArray;
+    CLangBarItemList m_LangBarItemList;
     DWORD m_dwUnknown20;
     CUIFWndFrame *m_pWndFrame;
     CTipbarGripper *m_pTipbarGripper;
@@ -1467,6 +1504,7 @@ class CTipbarWnd
     friend class CTipbarThread;
     friend class CTipbarItem;
     friend class CLBarInatItem;
+    friend class CMainIconItem;
     friend VOID WINAPI ClosePopupTipbar(VOID);
     friend BOOL GetTipbarInternal(HWND hWnd, DWORD dwFlags, CDeskBand *pDeskBand);
     friend LONG MyWaitForInputIdle(DWORD dwThreadId, DWORD dwMilliseconds);
@@ -2786,7 +2824,7 @@ CUTBContextMenu::CUTBContextMenu(CTipbarWnd *pTipbarWnd)
     m_pTipbarWnd = pTipbarWnd;
 }
 
-/// @unimplemented
+/// @implemented
 BOOL CUTBContextMenu::Init()
 {
     m_pTipbarThread = m_pTipbarWnd->m_pFocusThread;
@@ -3121,17 +3159,19 @@ STDMETHODIMP_(BOOL) CButtonIconItem::OnDelayMsg(UINT uMsg)
  * CMainIconItem
  */
 
+/// @implemented
 CMainIconItem::CMainIconItem(CTrayIconWnd *pWnd)
     : CButtonIconItem(pWnd, 1)
 {
 }
 
+/// @implemented
 BOOL CMainIconItem::Init(HWND hWnd)
 {
     return CTrayIconItem::_Init(hWnd, WM_USER, 0, GUID_LBI_TRAYMAIN);
 }
 
-/// @unimplemented
+/// @implemented
 STDMETHODIMP_(BOOL) CMainIconItem::OnDelayMsg(UINT uMsg)
 {
     if (!CButtonIconItem::OnDelayMsg(uMsg))
@@ -3139,14 +3179,12 @@ STDMETHODIMP_(BOOL) CMainIconItem::OnDelayMsg(UINT uMsg)
 
     if (uMsg == WM_LBUTTONDBLCLK)
     {
-        //FIXME
-        //if (g_pTipbarWnd->m_dwUnknown20)
-        //    g_pTipbarWnd->m_pLangBarMgr->ShowFloating(TF_SFT_SHOWNORMAL);
+        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);
+        g_pTipbarWnd->ShowContextMenu(m_ptCursor, &m_rcMenu, uMsg == WM_RBUTTONDOWN);
     }
     return TRUE;
 }
@@ -3255,16 +3293,15 @@ HWND CTrayIconWnd::GetNotifyWnd()
     return m_hNotifyWnd;
 }
 
-/// @unimplemented
+/// @implemented
 BOOL CTrayIconWnd::OnIconMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
 {
-    //FIXME
-    //if (g_pTipbarWnd)
-    //    g_pTipbarWnd->AttachFocusThread();
+    if (g_pTipbarWnd)
+        g_pTipbarWnd->AttachFocusThread();
 
     for (size_t iItem = 0; iItem < m_Items.size(); ++iItem)
     {
-        auto pItem = m_Items[iItem];
+        auto *pItem = m_Items[iItem];
         if (pItem)
         {
             if (uMsg == pItem->m_uCallbackMessage)
@@ -3893,6 +3930,229 @@ STDMETHODIMP_(BOOL) CTipbarGripper::OnSetCursor(UINT uMsg, LONG x, LONG y)
     return CUIFGripper::OnSetCursor(uMsg, x, y);
 }
 
+/***********************************************************************
+ * CLangBarItemList
+ */
+
+BOOL CLangBarItemList::IsStartedIntentionally(REFCLSID rclsid)
+{
+    auto *pItem = FindItem(rclsid);
+    if (!pItem)
+        return FALSE;
+    return pItem->m_bStartedIntentionally;
+}
+
+LANGBARITEMSTATE *CLangBarItemList::AddItem(REFCLSID rclsid)
+{
+    auto *pItem = FindItem(rclsid);
+    if (pItem)
+        return pItem;
+
+    pItem = Append(1);
+    if (!pItem)
+        return NULL;
+
+    ZeroMemory(pItem, sizeof(*pItem));
+    pItem->m_clsid = rclsid;
+    pItem->m_dwDemoteLevel = 0;
+    return pItem;
+}
+
+void CLangBarItemList::Clear()
+{
+    clear();
+
+    CicRegKey regKey;
+    LSTATUS error;
+    error = regKey.Open(HKEY_CURRENT_USER, L"SOFTWARE\\Microsoft\\CTF\\LangBar", KEY_ALL_ACCESS);
+    if (error == ERROR_SUCCESS)
+        regKey.RecurseDeleteKey(L"ItemState");
+}
+
+BOOL CLangBarItemList::SetDemoteLevel(REFCLSID rclsid, DWORD dwDemoteLevel)
+{
+    auto *pItem = AddItem(rclsid);
+    if (!pItem)
+        return TRUE;
+
+    pItem->m_dwDemoteLevel = dwDemoteLevel;
+    if (!pItem->IsShown())
+    {
+        if (pItem->m_nTimerID)
+        {
+            if (g_pTipbarWnd)
+                g_pTipbarWnd->KillTimer(pItem->m_nTimerID);
+            pItem->m_nTimerID = 0;
+            pItem->m_uTimeOut = 0;
+        }
+        pItem->m_bDisableDemoting = FALSE;
+    }
+
+    SaveItem(0, pItem);
+    return TRUE;
+}
+
+LANGBARITEMSTATE *CLangBarItemList::FindItem(REFCLSID rclsid)
+{
+    for (size_t iItem = 0; iItem < size(); ++iItem)
+    {
+        auto& item = (*this)[iItem];
+        if (IsEqualCLSID(item.m_clsid, rclsid))
+            return &item;
+    }
+    return NULL;
+}
+
+LANGBARITEMSTATE *CLangBarItemList::GetItemStateFromTimerId(UINT_PTR nTimerID)
+{
+    for (size_t iItem = 0; iItem < size(); ++iItem)
+    {
+        auto& item = (*this)[iItem];
+        if (item.m_nTimerID == nTimerID)
+            return &item;
+    }
+    return NULL;
+}
+
+void CLangBarItemList::Load()
+{
+    CicRegKey regKey;
+    LSTATUS error;
+    error = regKey.Open(HKEY_CURRENT_USER, L"SOFTWARE\\Microsoft\\CTF\\LangBar\\ItemState");
+    if (error != ERROR_SUCCESS)
+        return;
+
+    WCHAR szKeyName[MAX_PATH];
+    for (DWORD dwIndex = 0; ; ++dwIndex)
+    {
+        error = ::RegEnumKeyW(regKey, dwIndex, szKeyName, _countof(szKeyName));
+        if (error != ERROR_SUCCESS)
+            break;
+
+        CLSID clsid;
+        if (::CLSIDFromString(szKeyName, &clsid) != S_OK)
+            continue;
+
+        CicRegKey regKey2;
+        error = regKey2.Open(regKey, szKeyName);
+        if (error != ERROR_SUCCESS)
+            continue;
+
+        auto *pItem = AddItem(clsid);
+        if (!pItem)
+            continue;
+
+        DWORD Data = 0;
+        regKey2.QueryDword(L"DemoteLevel", &Data);
+        pItem->m_dwDemoteLevel = Data;
+        regKey2.QueryDword(L"DisableDemoting", &Data);
+        pItem->m_bDisableDemoting = !!Data;
+    }
+}
+
+void CLangBarItemList::SaveItem(CicRegKey *pRegKey, const LANGBARITEMSTATE *pState)
+{
+    LSTATUS error;
+    CicRegKey regKey;
+
+    if (!pRegKey)
+    {
+        error = regKey.Create(HKEY_CURRENT_USER, L"SOFTWARE\\Microsoft\\CTF\\LangBar\\ItemState");
+        if (error != ERROR_SUCCESS)
+            return;
+
+        pRegKey = &regKey;
+    }
+
+    WCHAR szSubKey[MAX_PATH];
+    ::StringFromGUID2(pState->m_clsid, szSubKey, _countof(szSubKey));
+
+    if (pState->m_dwDemoteLevel || pState->m_bDisableDemoting)
+    {
+        CicRegKey regKey2;
+        error = regKey2.Create(*pRegKey, szSubKey);
+        if (error == ERROR_SUCCESS)
+        {
+            DWORD dwDemoteLevel = pState->m_dwDemoteLevel;
+            if (dwDemoteLevel)
+                regKey2.SetDword(L"DemoteLevel", dwDemoteLevel);
+            else
+                regKey2.DeleteValue(L"DemoteLevel");
+
+            regKey2.SetDword(L"DisableDemoting", pState->m_bDisableDemoting);
+        }
+    }
+    else
+    {
+        pRegKey->RecurseDeleteKey(szSubKey);
+    }
+}
+
+void CLangBarItemList::StartDemotingTimer(REFCLSID rclsid, BOOL bIntentional)
+{
+    if (!g_bIntelliSense)
+        return;
+
+    auto *pItem = AddItem(rclsid);
+    if (!pItem || pItem->m_bDisableDemoting)
+        return;
+
+    if (pItem->m_nTimerID)
+    {
+        if (!bIntentional)
+            return;
+
+        if (g_pTipbarWnd)
+            g_pTipbarWnd->KillTimer(pItem->m_nTimerID);
+
+        pItem->m_nTimerID = 0;
+    }
+
+    pItem->m_bStartedIntentionally |= bIntentional;
+
+    UINT uTimeOut = (bIntentional ? g_uTimeOutIntentional : g_uTimeOutNonIntentional);
+    pItem->m_uTimeOut += uTimeOut;
+
+    if (pItem->m_uTimeOut < g_uTimeOutMax)
+    {
+        UINT_PTR uDemotingTimerId = FindDemotingTimerId();
+        pItem->m_nTimerID = uDemotingTimerId;
+        if (uDemotingTimerId)
+        {
+            if (g_pTipbarWnd)
+                g_pTipbarWnd->SetTimer(uDemotingTimerId, uTimeOut);
+        }
+    }
+    else
+    {
+        pItem->m_bDisableDemoting = TRUE;
+    }
+}
+
+UINT_PTR CLangBarItemList::FindDemotingTimerId()
+{
+    UINT_PTR nTimerID = 10000;
+
+    if (empty())
+        return nTimerID;
+
+    for (;;)
+    {
+        size_t iItem = 0;
+
+        while ((*this)[iItem].m_nTimerID != nTimerID)
+        {
+            ++iItem;
+            if (iItem >= size())
+                return nTimerID;
+        }
+
+        ++nTimerID;
+        if (nTimerID >= 10050)
+            return 0;
+    }
+}
+
 /***********************************************************************
  * CTipbarWnd
  */
@@ -4358,7 +4618,7 @@ BOOL CTipbarWnd::CheckExcludeCaptionButtonMode(LPRECT prc1, LPCRECT prc2)
 
 void CTipbarWnd::ClearLBItemList()
 {
-    m_TipbarGUIDArray.clear();
+    m_LangBarItemList.Clear();
     if (m_pFocusThread)
         OnThreadItemChange(m_pFocusThread->m_dwThreadId);
 }
@@ -5281,9 +5541,20 @@ STDMETHODIMP_(void) CTipbarWnd::OnTimer(WPARAM wParam)
             break;
         default:
         {
-            if ((10000 <= wParam) && (wParam <= 10049))
+            if (10000 <= wParam && wParam < 10050)
             {
-                // FIXME: CLangBarItemList
+                auto *pItem = m_LangBarItemList.GetItemStateFromTimerId(wParam);
+                if (pItem)
+                {
+                    auto& clsid = pItem->m_clsid;
+                    m_LangBarItemList.SetDemoteLevel(pItem->m_clsid, 2);
+                    if (m_pFocusThread)
+                    {
+                        auto *pThreadItem = m_pFocusThread->GetItem(clsid);
+                        if (pThreadItem)
+                            pThreadItem->AddRemoveMeToUI(FALSE);
+                    }
+                }
             }
             break;
         }
@@ -5624,6 +5895,17 @@ void CTipbarThread::RemoveUIObjs()
     RemoveAllSeparators();
 }
 
+CTipbarItem *CTipbarThread::GetItem(REFCLSID rclsid)
+{
+    for (size_t iItem = 0; iItem < m_UIObjects.size(); ++iItem)
+    {
+        auto *pItem = m_UIObjects[iItem];
+        if (pItem && IsEqualCLSID(pItem->m_ItemInfo.guidItem, rclsid))
+            return pItem;
+    }
+    return NULL;
+}
+
 void CTipbarThread::GetTextSize(BSTR bstr, LPSIZE pSize)
 {
     HWND hWnd = *m_pTipbarWnd->GetWindow();