https://git.reactos.org/?p=reactos.git;a=commitdiff;h=f8cb6458e482a797e70cae777b8510d9f8e6926b

commit f8cb6458e482a797e70cae777b8510d9f8e6926b
Author:     Katayama Hirofumi MZ <katayama.hirofumi...@gmail.com>
AuthorDate: Sun Feb 11 17:21:14 2024 +0900
Commit:     GitHub <nore...@github.com>
CommitDate: Sun Feb 11 17:21:14 2024 +0900

    [MSUTB][SDK] Add CTipbarWnd Part 4 (#6478)
    
    Supporting the Language bar...
    JIRA issue: CORE-19363
    - Add implementation to CTipbarWnd.
    - Implement GetLibTls function.
---
 dll/win32/msutb/msutb.cpp           | 770 ++++++++++++++++++++++++++++++------
 sdk/include/reactos/cicero/cicutb.h |   8 +-
 2 files changed, 663 insertions(+), 115 deletions(-)

diff --git a/dll/win32/msutb/msutb.cpp b/dll/win32/msutb/msutb.cpp
index 31b05578656..c5e3c337614 100644
--- a/dll/win32/msutb/msutb.cpp
+++ b/dll/win32/msutb/msutb.cpp
@@ -20,6 +20,7 @@ LONG g_DllRefCount = 0;
 BOOL g_bWinLogon = FALSE;
 BOOL g_fInClosePopupTipbar = FALSE;
 HWND g_hwndParent = NULL;
+LIBTHREAD g_libTLS = { NULL, NULL };
 #ifdef ENABLE_DESKBAND
 BOOL g_bEnableDeskBand = TRUE;
 #else
@@ -76,6 +77,23 @@ class CMsUtbModule : public CComModule
 
 CMsUtbModule gModule;
 
+void TFUninitLib_Thread(LIBTHREAD *libThread)
+{
+    if (!libThread)
+        return;
+
+    if (libThread->m_pUnknown1)
+    {
+        libThread->m_pUnknown1->Release();
+        libThread->m_pUnknown1 = NULL;
+    }
+    if (libThread->m_pDisplayAttrMgr)
+    {
+        libThread->m_pDisplayAttrMgr->Release();
+        libThread->m_pDisplayAttrMgr = NULL;
+    }
+}
+
 class CCicLibMenuItem;
 class CTipbarAccItem;
 class CUTBMenuItem;
@@ -1259,6 +1277,7 @@ public:
 /***********************************************************************/
 
 class CTipbarItem;
+class CTipbarBalloonItem;
 
 class CTipbarThread
 {
@@ -1306,6 +1325,18 @@ public:
     LONG _AddRef() { return ++m_cRefs; }
     LONG _Release();
 
+    /// @unimplemented
+    BOOL SetFocus(CTipbarBalloonItem *pTarget)
+    {
+        return FALSE;
+    }
+
+    /// @unimplemented
+    HRESULT CallOnUpdateHandler()
+    {
+        return E_NOTIMPL;
+    }
+
     //FIXME
 };
 
@@ -1383,6 +1414,24 @@ public:
 class CTipbarCtrlButtonHolder;
 class CDeskBand;
 
+// Flags for m_dwTipbarWndFlags
+enum
+{
+    TIPBAR_ATTACHED = 0x1,
+    TIPBAR_CHILD = 0x2,
+    TIPBAR_VERTICAL = 0x4,
+    TIPBAR_HIGHCONTRAST = 0x10,
+    TIPBAR_TRAYICON = 0x20,
+    TIPBAR_UPDATING = 0x400,
+    TIPBAR_ENSURING = 0x2000,
+    TIPBAR_NODESKBAND = 0x4000,
+    TIPBAR_TOOLBARENDED = 0x10000,
+    TIPBAR_TOPFIT = 0x40000,
+    TIPBAR_BOTTOMFIT = 0x80000,
+    TIPBAR_RIGHTFIT = 0x100000,
+    TIPBAR_LEFTFIT = 0x200000,
+};
+
 class CTipbarWnd
     : public ITfLangBarEventSink
     , public ITfLangBarEventSink_P
@@ -1434,6 +1483,7 @@ class CTipbarWnd
     friend class CTipbarGripper;
     friend class CTipbarThread;
     friend class CTipbarItem;
+    friend class CLBarInatItem;
     friend VOID WINAPI ClosePopupTipbar(VOID);
     friend BOOL GetTipbarInternal(HWND hWnd, DWORD dwFlags, CDeskBand 
*pDeskBand);
     friend LONG MyWaitForInputIdle(DWORD dwThreadId, DWORD dwMilliseconds);
@@ -1522,6 +1572,12 @@ public:
     void OnTerminateToolbar();
     HRESULT OnThreadTerminateInternal(DWORD dwThreadId);
 
+    /// @unimplemented
+    HRESULT OnThreadItemChangeInternal(DWORD dwThreadId)
+    {
+        return E_NOTIMPL;
+    }
+
     // IUnknown methods
     STDMETHOD(QueryInterface)(REFIID riid, void **ppvObj);
     STDMETHOD_(ULONG, AddRef)();
@@ -2807,7 +2863,7 @@ CUTBMenuWnd *CUTBContextMenu::CreateMenuUI(BOOL bFlag)
 
             CUTBMenuItem *pVertical = InsertItem(pMenuUI, ID_VERTICAL, 
IDS_VERTICAL);
             if (pVertical)
-                pVertical->Check(!!(m_pTipbarWnd->m_dwTipbarWndFlags & 
0x800000));
+                pVertical->Check(!!(m_pTipbarWnd->m_dwTipbarWndFlags & 
TIPBAR_VERTICAL));
         }
     }
 
@@ -2940,12 +2996,12 @@ BOOL CUTBContextMenu::SelectMenuItem(UINT nCommandId)
         }
 
         case ID_EXTRAICONS:
-            m_pTipbarWnd->m_dwTipbarWndFlags &= ~0x4000;
+            m_pTipbarWnd->m_dwTipbarWndFlags &= ~TIPBAR_NODESKBAND;
             
m_pTipbarWnd->m_pLangBarMgr->ShowFloating(TF_SFT_EXTRAICONSONMINIMIZED);
             break;
 
         case ID_NOEXTRAICONS:
-            m_pTipbarWnd->m_dwTipbarWndFlags &= ~0x4000;
+            m_pTipbarWnd->m_dwTipbarWndFlags &= ~TIPBAR_NODESKBAND;
             
m_pTipbarWnd->m_pLangBarMgr->ShowFloating(TF_SFT_NOEXTRAICONSONMINIMIZED);
             break;
 
@@ -2954,7 +3010,7 @@ BOOL CUTBContextMenu::SelectMenuItem(UINT nCommandId)
             break;
 
         case ID_VERTICAL:
-            m_pTipbarWnd->SetVertical((m_pTipbarWnd->m_dwTipbarWndFlags & 0x4) 
? TRUE : FALSE);
+            m_pTipbarWnd->SetVertical(!!(m_pTipbarWnd->m_dwTipbarWndFlags & 
TIPBAR_VERTICAL));
             break;
 
         case ID_ADJUSTDESKBAND:
@@ -3759,7 +3815,6 @@ STDMETHODIMP CLBarInatItem::InitMenu(ITfMenu *pMenu)
         }
     }
 
-#if 0 // FIXME: g_pTipbarWnd
     DWORD dwStatus;
     if (g_pTipbarWnd &&
         g_pTipbarWnd->m_pLangBarMgr &&
@@ -3772,7 +3827,6 @@ STDMETHODIMP CLBarInatItem::InitMenu(ITfMenu *pMenu)
         ::LoadStringW(g_hInst, IDS_RESTORELANGBAR2, szText, _countof(szText));
         LangBarInsertMenu(pMenu, 2000, szText, FALSE, NULL);
     }
-#endif
 
     return S_OK;
 }
@@ -3785,18 +3839,14 @@ STDMETHODIMP CLBarInatItem::OnMenuSelect(INT nCommandId)
     {
         if (g_pTipbarWnd)
         {
-#if 0 // FIXME: g_pTipbarWnd
             ITfLangBarMgr *pLangBarMgr = g_pTipbarWnd->m_pLangBarMgr;
             if (pLangBarMgr)
                 pLangBarMgr->ShowFloating(TF_SFT_SHOWNORMAL);
-#endif
         }
     }
     else if (TF_GetMlngHKL(nCommandId, &hKL, NULL, 0))
     {
-#if 0 // FIXME: g_pTipbarWnd
-        g_pTipbarWnd->RestoreLastFocus(0, (g_pTipbarWnd->m_dwTipbarWndFlags & 
2) != 0);
-#endif
+        g_pTipbarWnd->RestoreLastFocus(NULL, 
!!(g_pTipbarWnd->m_dwTipbarWndFlags & TIPBAR_CHILD));
         HWND hwndFore = ::GetForegroundWindow();
         if (m_dwThreadId == ::GetWindowThreadProcessId(hwndFore, NULL))
         {
@@ -3912,7 +3962,6 @@ CTipbarWnd::CTipbarWnd(DWORD style)
     m_cRefs = 1;
 }
 
-/// @unimplemented
 CTipbarWnd::~CTipbarWnd()
 {
     UnInit();
@@ -3922,23 +3971,66 @@ CTipbarWnd::~CTipbarWnd()
     if (m_hTextFont)
         ::DeleteObject(m_hTextFont);
 
-    //TFUninitLib_Thread(&g_libTLS); //FIXME
+    TFUninitLib_Thread(&g_libTLS);
 }
 
 /// @unimplemented
 void CTipbarWnd::Init(BOOL bChild, CDeskBand *pDeskBand)
 {
+    if (bChild)
+        m_dwTipbarWndFlags |= TIPBAR_CHILD;
+    else
+        m_dwTipbarWndFlags &= ~TIPBAR_CHILD;
+
+    if (m_dwTipbarWndFlags & TIPBAR_CHILD)
+        m_dwTipbarWndFlags &= TIPBAR_HIGHCONTRAST;
+
+    m_pDeskBand = pDeskBand;
+
+    RECT rc = { 0, 0, 0, 0 };
+
+    if (g_bNewLook && !m_pWndFrame && (m_style & 0x20000000))
+    {
+        CUIFWndFrame *pWndFrame = new(cicNoThrow) CUIFWndFrame(GetWindow(), 
&rc, 0);
+        if (pWndFrame)
+        {
+            pWndFrame->Initialize();
+            AddUIObj(m_pWndFrame);
+        }
+    }
+
+    if (!m_pTipbarGripper && !(m_dwTipbarWndFlags & TIPBAR_CHILD))
+    {
+        m_pTipbarGripper =
+            new(cicNoThrow) CTipbarGripper(this, &rc, !!(m_dwTipbarWndFlags & 
TIPBAR_VERTICAL));
+        if (m_pTipbarGripper)
+        {
+            m_pTipbarGripper->Initialize();
+            AddUIObj(m_pTipbarGripper);
+        }
+    }
+
+    //FIXME: CTipbarCtrlButtonHolder
+
+    if (m_dwTipbarWndFlags & TIPBAR_VERTICAL)
+    {
+        Move(m_nLeft, m_nTop, GetTipbarHeight(), 0);
+    }
+    else
+    {
+        Move(m_nLeft, m_nTop, 0, GetTipbarHeight());
+    }
 }
 
 void CTipbarWnd::InitHighContrast()
 {
-    m_dwTipbarWndFlags &= ~0x10;
+    m_dwTipbarWndFlags &= ~TIPBAR_HIGHCONTRAST;
 
     HIGHCONTRAST HiCon = { sizeof(HiCon) };
     if (::SystemParametersInfo(SPI_GETHIGHCONTRAST, sizeof(HiCon), &HiCon, 0))
     {
         if (HiCon.dwFlags & HCF_HIGHCONTRASTON)
-            m_dwTipbarWndFlags |= 0x10;
+            m_dwTipbarWndFlags |= TIPBAR_HIGHCONTRAST;
     }
 }
 
@@ -3999,9 +4091,29 @@ void CTipbarWnd::InitThemeMargins()
     theme.CloseThemeData();
 }
 
-/// @unimplemented
 void CTipbarWnd::UnInit()
 {
+    SetFocusThread(NULL);
+    for (size_t iItem = 0; iItem < m_Threads.size(); ++iItem)
+    {
+        CTipbarThread* pThread = m_Threads[iItem];
+        if (pThread)
+        {
+            pThread->_UninitItemList(TRUE);
+            pThread->m_pTipbarWnd = NULL;
+            pThread->_Release();
+        }
+    }
+    m_Threads.clear();
+
+    if (m_pLangBarMgr)
+        m_pLangBarMgr->UnAdviseEventSink(m_dwSinkCookie);
+
+    if (m_pLangBarMgr)
+    {
+        m_pLangBarMgr->Release();
+        m_pLangBarMgr = NULL;
+    }
 }
 
 BOOL CTipbarWnd::IsFullScreenWindow(HWND hWnd)
@@ -4029,10 +4141,11 @@ BOOL CTipbarWnd::IsHKLToSkipRedrawOnNoItem()
     return IsSkipRedrawHKL(hKL);
 }
 
-/// @unimplemented
 BOOL CTipbarWnd::IsInItemChangeOrDirty(CTipbarThread *pTarget)
 {
-    return FALSE;
+    if (pTarget->m_dwThreadId == m_dwChangingThreadId)
+        return TRUE;
+    return pTarget->IsDirtyItem();
 }
 
 void CTipbarWnd::AddThreadToThreadCreatingList(CTipbarThread *pThread)
@@ -4047,9 +4160,30 @@ void 
CTipbarWnd::RemoveThredFromThreadCreatingList(CTipbarThread *pTarget)
         m_ThreadCreatingList.Remove(iItem);
 }
 
-/// @unimplemented
 void CTipbarWnd::MoveToStub(BOOL bFlag)
 {
+    m_dwTipbarWndFlags |= 0x40;
+
+    RECT rcWorkArea;
+    ::SystemParametersInfo(SPI_GETWORKAREA, 0, &rcWorkArea, 0);
+
+    if (bFlag)
+    {
+        m_nLeft = rcWorkArea.right - 38;
+        m_dwTipbarWndFlags &= ~0x80;
+    }
+    else
+    {
+        RECT Rect;
+        ::GetWindowRect(m_hWnd, &Rect);
+        m_nLeft = rcWorkArea.right + Rect.left - Rect.right;
+        m_dwTipbarWndFlags |= 0x80;
+    }
+
+    m_nTop = rcWorkArea.bottom - m_cyDlgFrameX2 - GetTipbarHeight();
+
+    if (m_pFocusThread)
+        m_pFocusThread->MyMoveWnd(0, 0);
 }
 
 void CTipbarWnd::RestoreFromStub()
@@ -4099,10 +4233,23 @@ INT CTipbarWnd::GetTipbarHeight()
     return m_cySmallIcon + cy + (2 * size.cy);
 }
 
-/// @unimplemented
 BOOL CTipbarWnd::AutoAdjustDeskBandSize()
 {
-    return FALSE;
+    if ((m_dwTipbarWndFlags & TIPBAR_NODESKBAND) ||
+        !m_pFocusThread ||
+        (m_pFocusThread->m_dwFlags1 & 0x800))
+    {
+        return FALSE;
+    }
+
+    DWORD dwOldWndFlags = m_dwTipbarWndFlags;
+    m_dwTipbarWndFlags &= ~0x8000;
+
+    if (!AdjustDeskBandSize(!(dwOldWndFlags & 0x8000)))
+        return FALSE;
+
+    m_dwTipbarWndFlags |= TIPBAR_NODESKBAND;
+    return TRUE;
 }
 
 /// @unimplemented
@@ -4124,13 +4271,13 @@ void CTipbarWnd::AdjustPosOnDisplayChange()
         return;
 
     INT x = m_nLeft, y = m_nTop;
-    if (m_dwTipbarWndFlags & 0x200000)
+    if (m_dwTipbarWndFlags & TIPBAR_LEFTFIT)
         x = rcWorkArea.left;
-    if (m_dwTipbarWndFlags & 0x40000)
+    if (m_dwTipbarWndFlags & TIPBAR_TOPFIT)
         y = rcWorkArea.top;
-    if (m_dwTipbarWndFlags & 0x100000)
+    if (m_dwTipbarWndFlags & TIPBAR_RIGHTFIT)
         x = rcWorkArea.right - m_nWidth;
-    if (m_dwTipbarWndFlags & 0x80000)
+    if (m_dwTipbarWndFlags & TIPBAR_BOTTOMFIT)
         y = rcWorkArea.bottom - m_nHeight;
     if (x != m_nLeft || y != m_nTop)
         Move(x, y, m_nWidth, m_nHeight);
@@ -4139,9 +4286,9 @@ void CTipbarWnd::AdjustPosOnDisplayChange()
 void CTipbarWnd::SetVertical(BOOL bVertical)
 {
     if (bVertical)
-        m_dwTipbarWndFlags |= 0x4;
+        m_dwTipbarWndFlags |= TIPBAR_VERTICAL;
     else
-        m_dwTipbarWndFlags &= ~0x4;
+        m_dwTipbarWndFlags &= ~TIPBAR_VERTICAL;
 
     if (m_pTipbarGripper)
     {
@@ -4154,11 +4301,11 @@ void CTipbarWnd::SetVertical(BOOL bVertical)
     }
 
     if (g_fTaskbarTheme)
-        SetActiveTheme(L"TASKBAR", !!(m_dwTipbarWndFlags & 0x4), 1);
+        SetActiveTheme(L"TASKBAR", !!(m_dwTipbarWndFlags & TIPBAR_VERTICAL), 
1);
 
-    if (!(m_dwTipbarWndFlags & 2))
+    if (!(m_dwTipbarWndFlags & TIPBAR_CHILD))
     {
-        if (m_dwTipbarWndFlags & 4)
+        if (m_dwTipbarWndFlags & TIPBAR_VERTICAL)
         {
             Move(m_nLeft, m_nTop, GetTipbarHeight(), 0);
         }
@@ -4177,37 +4324,48 @@ void CTipbarWnd::SetVertical(BOOL bVertical)
 
 void CTipbarWnd::UpdatePosFlags()
 {
-    if (m_dwTipbarWndFlags & 0x2)
+    if (m_dwTipbarWndFlags & TIPBAR_CHILD)
         return;
 
     RECT rc = { m_nLeft, m_nTop, m_nLeft + m_nWidth, m_nTop + m_nHeight }, 
rcWorkArea;
     if (!GetWorkArea(&rc, &rcWorkArea))
         return;
 
-    if (m_nLeft > rcWorkArea.left + 2)
-        m_dwTipbarWndFlags &= ~0x200000;
+    if (rcWorkArea.left + 2 < m_nLeft)
+        m_dwTipbarWndFlags &= ~TIPBAR_LEFTFIT;
     else
-        m_dwTipbarWndFlags |= 0x200000;
+        m_dwTipbarWndFlags |= TIPBAR_LEFTFIT;
 
-    if ( m_nTop> rcWorkArea.top + 2 )
-        m_dwTipbarWndFlags &= ~0x40000;
+    if (rcWorkArea.top + 2 < m_nTop)
+        m_dwTipbarWndFlags &= ~TIPBAR_TOPFIT;
     else
-        m_dwTipbarWndFlags |= 0x40000;
+        m_dwTipbarWndFlags |= TIPBAR_TOPFIT;
 
     if (m_nLeft + m_nWidth < rcWorkArea.right - 2)
-        m_dwTipbarWndFlags &= ~0x100000;
+        m_dwTipbarWndFlags &= ~TIPBAR_RIGHTFIT;
     else
-        m_dwTipbarWndFlags |= 0x100000;
+        m_dwTipbarWndFlags |= TIPBAR_RIGHTFIT;
 
     if (m_nTop + m_nHeight < rcWorkArea.bottom - 2)
-        m_dwTipbarWndFlags &= ~0x80000;
+        m_dwTipbarWndFlags &= ~TIPBAR_BOTTOMFIT;
     else
-        m_dwTipbarWndFlags |= 0x80000;
+        m_dwTipbarWndFlags |= TIPBAR_BOTTOMFIT;
 }
 
-/// @unimplemented
 void CTipbarWnd::CancelMenu()
 {
+    if (!m_pThread)
+        return;
+
+    CTipbarWnd *pTipbarWnd = m_pThread->m_pTipbarWnd;
+    if (pTipbarWnd)
+    {
+        if (pTipbarWnd->m_pLangBarMgr)
+            pTipbarWnd->StartModalInput(NULL, m_pThread->m_dwThreadId);
+    }
+
+    m_pModalMenu->CancelMenu();
+    StartBackToAlphaTimer();
 }
 
 BOOL CTipbarWnd::CheckExcludeCaptionButtonMode(LPRECT prc1, LPCRECT prc2)
@@ -4215,9 +4373,11 @@ BOOL CTipbarWnd::CheckExcludeCaptionButtonMode(LPRECT 
prc1, LPCRECT prc2)
     return (prc1->top < prc2->top + 5) && (prc2->right <= prc1->right + (5 * 
m_ButtonWidth));
 }
 
-/// @unimplemented
 void CTipbarWnd::ClearLBItemList()
 {
+    m_TipbarGUIDArray.clear();
+    if (m_pFocusThread)
+        OnThreadItemChange(m_pFocusThread->m_dwThreadId);
 }
 
 HFONT CTipbarWnd::CreateVerticalFont()
@@ -4253,7 +4413,7 @@ HFONT CTipbarWnd::CreateVerticalFont()
 
 void CTipbarWnd::UpdateVerticalFont()
 {
-    if (m_dwTipbarWndFlags & 4)
+    if (m_dwTipbarWndFlags & TIPBAR_VERTICAL)
     {
         if (m_hTextFont)
         {
@@ -4273,6 +4433,7 @@ void CTipbarWnd::UpdateVerticalFont()
 /// @unimplemented
 void CTipbarWnd::ShowOverScreenSizeBalloon()
 {
+    //FIXME: CTipbarCtrlButtonHolder
 }
 
 void CTipbarWnd::DestroyOverScreenSizeBalloon()
@@ -4292,15 +4453,26 @@ void CTipbarWnd::DestroyWnd()
         ::DestroyWindow(m_hWnd);
 }
 
-/// @unimplemented
 HKL CTipbarWnd::GetFocusKeyboardLayout()
 {
-    return NULL;
+    DWORD dwThreadId = 0;
+    if (m_pFocusThread)
+        dwThreadId = m_pFocusThread->m_dwThreadId;
+    return ::GetKeyboardLayout(dwThreadId);
 }
 
-/// @unimplemented
 void CTipbarWnd::KillOnTheadItemChangeTimer()
 {
+    DWORD dwChangingThreadId = m_dwChangingThreadId;
+    m_dwChangingThreadId = 0;
+    KillTimer(4);
+
+    if (dwChangingThreadId)
+    {
+        CTipbarThread *pThread = _FindThread(dwChangingThreadId);
+        if (pThread)
+            pThread->m_dwUnknown34 |= 0x1;
+    }
 }
 
 UINT_PTR CTipbarWnd::SetTimer(UINT_PTR nIDEvent, UINT uElapse)
@@ -4347,7 +4519,7 @@ void CTipbarWnd::SavePosition()
         ::ClientToScreen(m_hWnd, &pt);
         regKey.SetDword(TEXT("Left"), pt.x);
         regKey.SetDword(TEXT("Top"), pt.y);
-        regKey.SetDword(TEXT("Vertical"), !!(m_dwTipbarWndFlags & 4));
+        regKey.SetDword(TEXT("Vertical"), !!(m_dwTipbarWndFlags & 
TIPBAR_VERTICAL));
     }
 }
 
@@ -4379,13 +4551,10 @@ BOOL CTipbarWnd::SetLangBand(BOOL bDeskBand, BOOL 
bFlag2)
         ret = FALSE;
     }
 
-    if (!(m_dwTipbarWndFlags & 2))
+    if (!(m_dwTipbarWndFlags & TIPBAR_CHILD) && bDeskBand)
     {
-        if (bDeskBand)
-        {
-            KillTimer(7);
-            SetTimer(7, g_uTimerElapseSYSCOLORCHANGED);
-        }
+        KillTimer(7);
+        SetTimer(7, g_uTimerElapseSYSCOLORCHANGED);
     }
 
     return ret;
@@ -4393,7 +4562,7 @@ BOOL CTipbarWnd::SetLangBand(BOOL bDeskBand, BOOL bFlag2)
 
 void CTipbarWnd::SetMoveRect(INT X, INT Y, INT nWidth, INT nHeight)
 {
-    if (m_dwTipbarWndFlags & 0x2)
+    if (m_dwTipbarWndFlags & TIPBAR_CHILD)
     {
         m_nWidth = nWidth;
         m_nHeight = nHeight;
@@ -4402,7 +4571,7 @@ void CTipbarWnd::SetMoveRect(INT X, INT Y, INT nWidth, 
INT nHeight)
 
     ++m_bInCallOn;
 
-    m_dwTipbarWndFlags |= 0x400;
+    m_dwTipbarWndFlags |= TIPBAR_UPDATING;
 
     m_X = X;
     m_Y = Y;
@@ -4420,7 +4589,7 @@ void CTipbarWnd::SetMoveRect(INT X, INT Y, INT nWidth, 
INT nHeight)
 
     if (m_pTipbarGripper)
     {
-        if (m_dwTipbarWndFlags & 4)
+        if (m_dwTipbarWndFlags & TIPBAR_VERTICAL)
         {
             INT GripperWidth = GetGripperWidth();
             ::SetRect(&rc, size.cx, size.cy, nWidth - m_cxDlgFrameX2 - 
size.cx, size.cy + GripperWidth);
@@ -4437,19 +4606,27 @@ void CTipbarWnd::SetMoveRect(INT X, INT Y, INT nWidth, 
INT nHeight)
     --m_bInCallOn;
 }
 
-/// @unimplemented
 void CTipbarWnd::SetShowText(BOOL bShow)
 {
+    if (bShow)
+        m_dwTipbarWndFlags |= TIPBAR_HIGHCONTRAST;
+    else
+        m_dwTipbarWndFlags &= ~TIPBAR_HIGHCONTRAST;
+
+    if (m_pFocusThread)
+        OnThreadItemChange(m_pFocusThread->m_dwThreadId);
+
+    TerminateAllThreads(FALSE);
 }
 
 void CTipbarWnd::SetShowTrayIcon(BOOL bShow)
 {
-    if (m_dwTipbarWndFlags & 0x20)
-        m_dwTipbarWndFlags &= ~0x20;
+    if (m_dwTipbarWndFlags & TIPBAR_TRAYICON)
+        m_dwTipbarWndFlags &= ~TIPBAR_TRAYICON;
     else
-        m_dwTipbarWndFlags |= 0x20;
+        m_dwTipbarWndFlags |= TIPBAR_TRAYICON;
 
-    if ((m_dwTipbarWndFlags & 0x20) && m_pFocusThread)
+    if ((m_dwTipbarWndFlags & TIPBAR_TRAYICON) && m_pFocusThread)
     {
         KillTimer(10);
         SetTimer(10, g_uTimerElapseMOVETOTRAY);
@@ -4461,9 +4638,45 @@ void CTipbarWnd::SetShowTrayIcon(BOOL bShow)
     }
 }
 
-/// @unimplemented
 void CTipbarWnd::ShowContextMenu(POINT pt, LPCRECT prc, BOOL bFlag)
 {
+    AddRef();
+
+    RECT rc;
+    if (!prc)
+    {
+        rc = { pt.x, pt.y, pt.x, pt.y };
+        prc = &rc;
+    }
+
+    if (m_pFocusThread)
+    {
+        CUTBContextMenu *pContextMenu = new(cicNoThrow) CUTBContextMenu(this);
+        if (pContextMenu)
+        {
+            if (pContextMenu->Init())
+            {
+                m_pThread = m_pFocusThread;
+                StartModalInput(this, m_pFocusThread->m_dwThreadId);
+
+                m_pModalMenu = pContextMenu;
+                DWORD dwCommandId = pContextMenu->ShowPopup(GetWindow(), pt, 
prc, bFlag);
+                m_pModalMenu = NULL;
+
+                if (m_pThread)
+                    StopModalInput(m_pThread->m_dwThreadId);
+
+                m_pThread = NULL;
+
+                if (dwCommandId != (DWORD)-1)
+                    pContextMenu->SelectMenuItem(dwCommandId);
+            }
+
+            delete pContextMenu;
+        }
+    }
+
+    Release();
 }
 
 void CTipbarWnd::StartBackToAlphaTimer()
@@ -4472,10 +4685,17 @@ void CTipbarWnd::StartBackToAlphaTimer()
     ::SetTimer(m_hWnd, 3, 3 * uTime, NULL);
 }
 
-/// @unimplemented
 BOOL CTipbarWnd::StartDoAccDefaultActionTimer(CTipbarItem *pTarget)
 {
-    return FALSE;
+    if (!m_pTipbarAccessible)
+        return FALSE;
+    INT IDOfItem = m_pTipbarAccessible->GetIDOfItem(pTarget);
+    m_nID = IDOfItem;
+    if (!IDOfItem || IDOfItem == -1)
+        return FALSE;
+    KillTimer(11);
+    SetTimer(11, g_uTimerElapseDOACCDEFAULTACTION);
+    return TRUE;
 }
 
 void CTipbarWnd::StartModalInput(ITfLangBarEventSink *pSink, DWORD dwThreadId)
@@ -4504,27 +4724,87 @@ void CTipbarWnd::StopModalInput(DWORD dwThreadId)
     m_pLangBarMgr->SetModalInput(NULL, dwCurThreadId, 0);
 }
 
-/// @unimplemented
+LONG MyWaitForInputIdle(DWORD dwThreadId, DWORD dwMilliseconds)
+{
+    if (g_pTipbarWnd && (g_pTipbarWnd->m_dwShowType & TF_SFT_DESKBAND))
+        return 0;
+
+    if (TF_IsInMarshaling(dwThreadId))
+        return STATUS_TIMEOUT;
+
+    DWORD dwFlags1 = 0, dwFlags2 = 0;
+    if (!TF_GetThreadFlags(dwThreadId, &dwFlags1, &dwFlags2, NULL) && dwFlags2)
+        return -1;
+
+    return TF_CheckThreadInputIdle(dwThreadId, dwMilliseconds);
+}
+
 CTipbarThread *CTipbarWnd::_CreateThread(DWORD dwThreadId)
 {
-    return NULL;
+    CTipbarThread *pTarget = _FindThread(dwThreadId);
+    if (pTarget)
+        return pTarget;
+
+    MyWaitForInputIdle(dwThreadId, 2000);
+
+    pTarget = new(cicNoThrow) CTipbarThread(this);
+    if (!pTarget)
+        return NULL;
+
+    AddThreadToThreadCreatingList(pTarget);
+
+    HRESULT hr = pTarget->Init(dwThreadId);
+
+    RemoveThredFromThreadCreatingList(pTarget);
+
+    if (SUCCEEDED(hr) && !m_Threads.Add(pTarget))
+    {
+        pTarget->_UninitItemList(TRUE);
+        pTarget->m_pTipbarWnd = NULL;
+        pTarget->_Release();
+        return NULL;
+    }
+
+    return pTarget;
 }
 
-/// @unimplemented
 CTipbarThread *CTipbarWnd::_FindThread(DWORD dwThreadId)
 {
-    return NULL;
+    if (g_bWinLogon)
+        return NULL;
+
+    CTipbarThread *pTarget = NULL;
+    for (size_t iItem = 0; iItem < m_Threads.size(); ++iItem)
+    {
+        CTipbarThread *pThread = m_Threads[iItem];
+        if (pThread && pThread->m_dwThreadId == dwThreadId)
+        {
+            pTarget = pThread;
+            break;
+        }
+    }
+
+    if (!pTarget)
+        return NULL;
+
+    DWORD dwFlags1, dwFlags2, dwFlags3;
+    TF_GetThreadFlags(dwThreadId, &dwFlags1, &dwFlags2, &dwFlags3);
+
+    if (!dwFlags2 || (dwFlags2 != pTarget->m_dwFlags2) || (dwFlags3 != 
pTarget->m_dwFlags3))
+    {
+        OnThreadTerminateInternal(dwThreadId);
+        return NULL;
+    }
+
+    return pTarget;
 }
 
 void CTipbarWnd::EnsureFocusThread()
 {
-    if (m_pFocusThread)
-        return;
-
-    if (m_dwTipbarWndFlags & 0x12000)
+    if (m_pFocusThread || (m_dwTipbarWndFlags & (TIPBAR_TOOLBARENDED | 
TIPBAR_ENSURING)))
         return;
 
-    m_dwTipbarWndFlags |= 0x2000;
+    m_dwTipbarWndFlags |= TIPBAR_ENSURING;
 
     HWND hwndFore = ::GetForegroundWindow();
     if (!hwndFore)
@@ -4534,19 +4814,41 @@ void CTipbarWnd::EnsureFocusThread()
     if (dwThreadId)
         OnSetFocus(dwThreadId);
 
-    m_dwTipbarWndFlags &= ~0x2000;
+    m_dwTipbarWndFlags &= ~TIPBAR_ENSURING;
 }
 
-/// @unimplemented
 HRESULT CTipbarWnd::SetFocusThread(CTipbarThread *pFocusThread)
 {
-    return E_NOTIMPL;
+    if (pFocusThread == m_pFocusThread)
+        return S_OK;
+
+    DWORD dwThreadId = ::GetCurrentThreadId();
+    DestroyOverScreenSizeBalloon();
+
+    if (m_pFocusThread)
+    {
+        m_pFocusThread->SetFocus(NULL);
+        ::AttachThreadInput(dwThreadId, m_pFocusThread->m_dwThreadId, FALSE);
+    }
+
+    m_dwTipbarWndFlags &= ~TIPBAR_ATTACHED;
+    m_pFocusThread = pFocusThread;
+    return S_OK;
 }
 
-/// @unimplemented
 HRESULT CTipbarWnd::AttachFocusThread()
 {
-    return E_NOTIMPL;
+    if (m_dwTipbarWndFlags & TIPBAR_ATTACHED)
+        return S_FALSE;
+
+    if (m_pFocusThread)
+    {
+        DWORD dwThreadId = ::GetCurrentThreadId();
+        ::AttachThreadInput(dwThreadId, m_pFocusThread->m_dwThreadId, TRUE);
+        m_dwTipbarWndFlags |= TIPBAR_ATTACHED;
+    }
+
+    return S_OK;
 }
 
 void CTipbarWnd::RestoreLastFocus(DWORD *pdwThreadId, BOOL fPrev)
@@ -4574,9 +4876,31 @@ void CTipbarWnd::CleanUpThreadPointer(CTipbarThread 
*pThread, BOOL bRemove)
         m_pUnknownThread = NULL;
 }
 
-/// @unimplemented
 void CTipbarWnd::TerminateAllThreads(BOOL bFlag)
 {
+    const size_t cItems = m_Threads.size();
+
+    DWORD *pdwThreadIds = new(cicNoThrow) DWORD[cItems];
+    if (!pdwThreadIds)
+        return;
+
+    for (size_t iItem = 0; iItem < cItems; ++iItem)
+    {
+        pdwThreadIds[iItem] = 0;
+        CTipbarThread* pThread = m_Threads[iItem];
+        if (pThread && (bFlag || (pThread != m_pFocusThread)))
+        {
+            pdwThreadIds[iItem] = pThread->m_dwThreadId;
+        }
+    }
+
+    for (size_t iItem = 0; iItem < cItems; ++iItem)
+    {
+        if (pdwThreadIds[iItem])
+            OnThreadTerminateInternal(pdwThreadIds[iItem]);
+    }
+
+    delete[] pdwThreadIds;
 }
 
 STDMETHODIMP CTipbarWnd::QueryInterface(REFIID riid, void **ppvObj)
@@ -4632,16 +4956,61 @@ STDMETHODIMP CTipbarWnd::OnThreadTerminate(DWORD 
dwThreadId)
     return hr;
 }
 
-/// @unimplemented
 HRESULT CTipbarWnd::OnThreadTerminateInternal(DWORD dwThreadId)
 {
-    return E_NOTIMPL;
+    for (size_t iItem = 0; iItem < m_Threads.size(); ++iItem)
+    {
+        CTipbarThread *pThread = m_Threads[iItem];
+        if (pThread && pThread->m_dwThreadId == dwThreadId)
+        {
+            m_Threads.Remove(iItem);
+            pThread->RemoveUIObjs();
+            CleanUpThreadPointer(pThread, FALSE);
+            pThread->_UninitItemList(TRUE);
+            pThread->m_pTipbarWnd = NULL;
+            pThread->_Release();
+            break;
+        }
+    }
+
+    return S_OK;
 }
 
-/// @unimplemented
 STDMETHODIMP CTipbarWnd::OnThreadItemChange(DWORD dwThreadId)
 {
-    return E_NOTIMPL;
+    if (m_dwTipbarWndFlags & TIPBAR_TOOLBARENDED)
+        return S_OK;
+    if (!(m_dwTipbarWndFlags & TIPBAR_CHILD) && (m_dwShowType & 
TF_SFT_DESKBAND))
+        return S_OK;
+
+    CTipbarThread *pThread = _FindThread(dwThreadId);
+    if (pThread)
+    {
+        if ((!m_dwUnknown23 || m_dwUnknown23 == dwThreadId) && pThread == 
m_pFocusThread)
+        {
+            KillOnTheadItemChangeTimer();
+            m_dwChangingThreadId = dwThreadId;
+            KillTimer(6);
+            SetTimer(4, g_uTimerElapseONTHREADITEMCHANGE);
+        }
+        else
+        {
+            pThread->m_dwUnknown34 |= 0x1;
+        }
+    }
+    else
+    {
+        for (size_t iItem = 0; iItem < m_ThreadCreatingList.size(); ++iItem)
+        {
+            CTipbarThread *pItem = m_ThreadCreatingList[iItem];
+            if (pItem && pItem->m_dwThreadId == dwThreadId)
+            {
+                pItem->m_dwUnknown34 |= 0x1;
+            }
+        }
+    }
+
+    return S_OK;
 }
 
 STDMETHODIMP CTipbarWnd::OnModalInput(DWORD dwThreadId, UINT uMsg, WPARAM 
wParam, LPARAM lParam)
@@ -4700,10 +5069,28 @@ STDMETHODIMP CTipbarWnd::ShowFloating(DWORD dwFlags)
     return E_NOTIMPL;
 }
 
-/// @unimplemented
 STDMETHODIMP CTipbarWnd::GetItemFloatingRect(DWORD dwThreadId, REFGUID rguid, 
RECT *prc)
 {
-    return E_NOTIMPL;
+    if (m_dwTipbarWndFlags & TIPBAR_TRAYICON)
+        return E_UNEXPECTED;
+
+    if (!m_pFocusThread || (m_pFocusThread->m_dwThreadId != dwThreadId))
+        return E_FAIL;
+
+    for (size_t iItem = 0; iItem < m_pFocusThread->m_UIObjects.size(); ++iItem)
+    {
+        CTipbarItem* pItem = m_pFocusThread->m_UIObjects[iItem];
+        if (pItem)
+        {
+            if ((pItem->m_dwItemFlags & 0x8) && 
IsEqualGUID(pItem->m_ItemInfo.guidItem, rguid))
+            {
+                pItem->OnUnknown57(prc);
+                return S_OK;
+            }
+        }
+    }
+
+    return E_FAIL;
 }
 
 /// @unimplemented
@@ -4724,9 +5111,20 @@ STDMETHODIMP_(void) CTipbarWnd::GetAccLocation(LPRECT 
lprc)
     GetRect(lprc);
 }
 
-/// @unimplemented
 STDMETHODIMP_(void) CTipbarWnd::PaintObject(HDC hDC, LPCRECT prc)
 {
+    if (m_dwTipbarWndFlags & TIPBAR_UPDATING)
+    {
+        Move(m_X, m_Y, m_CX, m_CY);
+        m_dwTipbarWndFlags &= ~TIPBAR_UPDATING;
+    }
+
+    if (!m_pFocusThread || !m_pFocusThread->IsDirtyItem())
+    {
+        m_pFocusThread->CallOnUpdateHandler();
+        if (g_pTipbarWnd)
+            CUIFWindow::PaintObject(hDC, prc);
+    }
 }
 
 STDMETHODIMP_(DWORD) CTipbarWnd::GetWndStyle()
@@ -4775,6 +5173,140 @@ STDMETHODIMP_(void) CTipbarWnd::OnDestroy(HWND hWnd)
 /// @unimplemented
 STDMETHODIMP_(void) CTipbarWnd::OnTimer(WPARAM wParam)
 {
+    AddRef();
+    switch (wParam)
+    {
+        case 1:
+            KillTimer(1);
+            MoveToStub(FALSE);
+            break;
+        case 2:
+            KillTimer(2);
+            MoveToStub(TRUE);
+            break;
+        case 3:
+            KillTimer(3);
+            SetAlpha((BYTE)m_dwAlphaValue, TRUE);
+            break;
+        case 4:
+        {
+            LONG status = MyWaitForInputIdle(m_dwChangingThreadId, 2000);
+            if (status)
+            {
+                if (status != STATUS_TIMEOUT)
+                {
+                    KillTimer(4);
+                    m_dwChangingThreadId = 0;
+                }
+            }
+            else if (!m_pThread)
+            {
+                KillTimer(4);
+                DWORD dwOldThreadId = m_dwChangingThreadId;
+                m_dwChangingThreadId = 0;
+                OnThreadItemChangeInternal(dwOldThreadId);
+            }
+            break;
+        }
+        case 5:
+            KillTimer(5);
+            ::SetWindowPos(m_hWnd, NULL, 0, 0, 0, 0, SWP_NOACTIVATE | 
SWP_NOMOVE | SWP_NOSIZE);
+            break;
+        case 6:
+            KillTimer(6);
+            if (m_pFocusThread)
+            {
+                if (m_pFocusThread->m_dwThreadId != m_dwChangingThreadId &&
+                    !m_pFocusThread->CallOnUpdateHandler())
+                {
+                    if (m_pFocusThread)
+                        OnThreadItemChange(m_pFocusThread->m_dwThreadId);
+                }
+            }
+            break;
+        case 7:
+        {
+            DWORD dwThreadId = 0;
+            if (KillTimer(7))
+            {
+                if (m_pFocusThread)
+                    dwThreadId = m_pFocusThread->m_dwThreadId;
+
+                TerminateAllThreads(TRUE);
+                UpdateVerticalFont();
+
+                if (dwThreadId)
+                    OnSetFocus(dwThreadId);
+
+                InitMetrics();
+                // FIXME: CTipbarCtrlButtonHolder
+
+                InitHighContrast();
+                SetAlpha(0xFF, TRUE);
+                ::RedrawWindow(m_hWnd, NULL, NULL, (RDW_FRAME | RDW_UPDATENOW 
| RDW_INVALIDATE));
+            }
+            break;
+        }
+        case 8:
+            KillTimer(8);
+            UpdateUI(NULL);
+            break;
+        case 9:
+            KillTimer(9);
+            //FIXME
+            if (m_pUnknownThread == m_pFocusThread)
+                Show(!!(m_dwUnknown23_5[3] & 0x80000000));
+            m_pUnknownThread = NULL;
+            if ((m_dwUnknown23_5[3] & 0x2))
+                ShowOverScreenSizeBalloon();
+            break;
+        case 10:
+            KillTimer(10);
+            MoveToTray();
+            break;
+        case 11:
+            KillTimer(11);
+            if (m_pTipbarAccessible)
+            {
+                if (m_nID)
+                {
+                    m_pTipbarAccessible->DoDefaultActionReal(m_nID);
+                    m_nID = 0;
+                }
+            }
+            break;
+        case 12:
+            KillTimer(12);
+            AdjustPosOnDisplayChange();
+            break;
+        case 13:
+#ifdef ENABLE_DESKBAND
+            if (!m_pDeskBand || !m_pDeskBand->m_dwUnknown19)
+            {
+                KillTimer(13);
+                if (!m_pFocusThread)
+                EnsureFocusThread();
+            }
+#endif
+            break;
+        case 14:
+            if (SetLangBand(TRUE, TRUE))
+            {
+                m_dwShowType = TF_SFT_DESKBAND;
+                KillTimer(14);
+            }
+            break;
+        default:
+        {
+            if ((10000 <= wParam) && (wParam <= 10049))
+            {
+                // FIXME: CLangBarItemList
+            }
+            break;
+        }
+    }
+
+    Release();
 }
 
 STDMETHODIMP_(void) CTipbarWnd::OnSysColorChange()
@@ -4785,10 +5317,10 @@ STDMETHODIMP_(void) CTipbarWnd::OnSysColorChange()
 
 void CTipbarWnd::OnTerminateToolbar()
 {
-    m_dwTipbarWndFlags |= 0x10000;
+    m_dwTipbarWndFlags |= TIPBAR_TOOLBARENDED;
     DestroyOverScreenSizeBalloon();
     TerminateAllThreads(TRUE);
-    if (!(m_dwTipbarWndFlags & 0x2))
+    if (!(m_dwTipbarWndFlags & TIPBAR_CHILD))
         SavePosition();
 }
 
@@ -4829,11 +5361,20 @@ STDMETHODIMP_(void) CTipbarWnd::OnUser(HWND hWnd, UINT 
uMsg, WPARAM wParam, LPAR
     }
 }
 
-/// @unimplemented
 STDMETHODIMP_(LRESULT)
 CTipbarWnd::OnWindowPosChanged(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM 
lParam)
 {
-    return 0;
+    if (m_pFocusThread)
+    {
+        for (size_t iItem = 0; iItem < m_pFocusThread->m_UIObjects.size(); 
++iItem)
+        {
+            CTipbarItem *pItem = m_pFocusThread->m_UIObjects[iItem];
+            if (pItem)
+                pItem->OnUnknown44();
+        }
+    }
+
+    return ::DefWindowProc(hWnd, uMsg, wParam, lParam);
 }
 
 STDMETHODIMP_(LRESULT)
@@ -4880,7 +5421,7 @@ CTipbarWnd::OnSettingChange(HWND hWnd, UINT uMsg, WPARAM 
wParam, LPARAM lParam)
 STDMETHODIMP_(LRESULT)
 CTipbarWnd::OnDisplayChange(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
 {
-    if (!(m_dwTipbarWndFlags & 2))
+    if (!(m_dwTipbarWndFlags & TIPBAR_CHILD))
     {
         KillTimer(12);
         SetTimer(12, g_uTimerElapseDISPLAYCHANGE);
@@ -4927,9 +5468,25 @@ STDMETHODIMP_(void) CTipbarWnd::OnThemeChanged(HWND 
hWnd, WPARAM wParam, LPARAM
     CUIFWindow::OnThemeChanged(hWnd, wParam, lParam);
 }
 
-/// @unimplemented
 STDMETHODIMP_(void) CTipbarWnd::UpdateUI(LPCRECT prc)
 {
+    KillTimer(8);
+
+    if (m_dwChangingThreadId || m_bInCallOn || (m_pFocusThread && 
m_pFocusThread->IsDirtyItem()))
+    {
+        SetTimer(8, g_uTimerElapseUPDATEUI);
+        return;
+    }
+
+    if (m_dwTipbarWndFlags & TIPBAR_UPDATING)
+    {
+        ++m_bInCallOn;
+        Move(m_X, m_Y, m_CX, m_CY);
+        m_dwTipbarWndFlags &= ~TIPBAR_UPDATING;
+        --m_bInCallOn;
+    }
+
+    CUIFWindow::UpdateUI(NULL);
 }
 
 /// @unimplemented
@@ -4984,21 +5541,6 @@ HRESULT CTipbarThread::InitItemList()
     return E_NOTIMPL;
 }
 
-LONG MyWaitForInputIdle(DWORD dwThreadId, DWORD dwMilliseconds)
-{
-    if (g_pTipbarWnd && (g_pTipbarWnd->m_dwShowType & TF_SFT_DESKBAND))
-        return 0;
-
-    if (TF_IsInMarshaling(dwThreadId))
-        return STATUS_TIMEOUT;
-
-    DWORD dwFlags1 = 0, dwFlags2 = 0;
-    if (!TF_GetThreadFlags(dwThreadId, &dwFlags1, &dwFlags2, NULL) && dwFlags2)
-        return -1;
-
-    return TF_CheckThreadInputIdle(dwThreadId, dwMilliseconds);
-}
-
 HRESULT CTipbarThread::_UninitItemList(BOOL bUnAdvise)
 {
     for (size_t iItem = 0; iItem < m_UIObjects.size(); ++iItem)
@@ -5175,7 +5717,7 @@ BOOL CTipbarThread::IsVertical()
 {
     if (!m_pTipbarWnd)
         return FALSE;
-    return !!(m_pTipbarWnd->m_dwTipbarWndFlags & 0x20000000);
+    return !!(m_pTipbarWnd->m_dwTipbarWndFlags & TIPBAR_VERTICAL);
 }
 
 /// @unimplemented
@@ -5522,7 +6064,7 @@ BOOL GetTipbarInternal(HWND hWnd, DWORD dwFlags, 
CDeskBand *pDeskBand)
     g_pTipbarWnd->ShowFloating(dwStatus);
 
     if (!bParent && (dwOldStatus & TF_SFT_DESKBAND))
-        g_pTipbarWnd->m_dwTipbarWndFlags |= 0x4000;
+        g_pTipbarWnd->m_dwTipbarWndFlags |= TIPBAR_NODESKBAND;
 
     g_hwndParent = hWnd;
     return TRUE;
@@ -5531,13 +6073,13 @@ BOOL GetTipbarInternal(HWND hWnd, DWORD dwFlags, 
CDeskBand *pDeskBand)
 /***********************************************************************
  *              GetLibTls (MSUTB.@)
  *
- * @unimplemented
+ * @implemented
  */
-EXTERN_C LPVOID WINAPI
+EXTERN_C PLIBTHREAD WINAPI
 GetLibTls(VOID)
 {
-    FIXME("stub:()\n");
-    return NULL;
+    TRACE("()\n");
+    return &g_libTLS;
 }
 
 /***********************************************************************
diff --git a/sdk/include/reactos/cicero/cicutb.h 
b/sdk/include/reactos/cicero/cicutb.h
index 774be036cb4..abda6e7baff 100644
--- a/sdk/include/reactos/cicero/cicutb.h
+++ b/sdk/include/reactos/cicero/cicutb.h
@@ -18,7 +18,13 @@ DEFINE_GUID(IID_ITfLangBarEventSink_P,         0x7A460360, 
0xDA21, 0x4B09, 0xA8,
 DEFINE_GUID(CLSID_MSUTBDeskBand,               0x540D8A8B, 0x1C3F, 0x4E32, 
0x81, 0x32, 0x53, 0x0F, 0x6A, 0x50, 0x20, 0x90);
 DEFINE_GUID(CATID_DeskBand,                    0x00021492, 0x0000, 0x0000, 
0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46);
 
-EXTERN_C LPVOID WINAPI GetLibTls(VOID);
+typedef struct LIBTHREAD
+{
+    IUnknown *m_pUnknown1;
+    ITfDisplayAttributeMgr *m_pDisplayAttrMgr;
+} LIBTHREAD, *PLIBTHREAD;
+
+EXTERN_C PLIBTHREAD WINAPI GetLibTls(VOID);
 EXTERN_C BOOL WINAPI GetPopupTipbar(HWND hWnd, BOOL fWinLogon);
 EXTERN_C HRESULT WINAPI SetRegisterLangBand(BOOL bRegister);
 EXTERN_C VOID WINAPI ClosePopupTipbar(VOID);

Reply via email to