Something went wrong. Try again.
Reactos
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613/* * PROJECT: ReactOS Explorer * LICENSE: LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later) * PURPOSE: AppBar implementation * COPYRIGHT: Copyright 2008 Vincent Povirk for CodeWeavers * Copyright 2025 Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com) */
#include "precomp.h"#include "appbar.h"
CAppBarManager::CAppBarManager() : m_hAppBarDPA(NULL) , m_ahwndAutoHideBars { 0 }{}
CAppBarManager::~CAppBarManager(){ DestroyAppBarDPA();}
PAPPBAR CAppBarManager::FindAppBar(_In_ HWND hwndAppBar) const{ if (!m_hAppBarDPA) return NULL;
INT nItems = DPA_GetPtrCount(m_hAppBarDPA); while (--nItems >= 0) { PAPPBAR pAppBar = (PAPPBAR)DPA_GetPtr(m_hAppBarDPA, nItems); if (pAppBar && hwndAppBar == pAppBar->hWnd) return pAppBar; }
return NULL;}
void CAppBarManager::EliminateAppBar(_In_ INT iItem){ LocalFree(DPA_GetPtr(m_hAppBarDPA, iItem)); DPA_DeletePtr(m_hAppBarDPA, iItem);}
void CAppBarManager::DestroyAppBarDPA(){ if (!m_hAppBarDPA) return;
INT nItems = DPA_GetPtrCount(m_hAppBarDPA); while (--nItems >= 0) { ::LocalFree(DPA_GetPtr(m_hAppBarDPA, nItems)); }
DPA_Destroy(m_hAppBarDPA); m_hAppBarDPA = NULL;}
// ABM_NEWBOOL CAppBarManager::OnAppBarNew(_In_ const APPBAR_COMMAND *pData){ HWND hWnd = (HWND)UlongToHandle(pData->abd.hWnd32);
if (m_hAppBarDPA) { if (FindAppBar(hWnd)) { ERR("Already exists: %p\n", hWnd); return FALSE; } } else { const UINT c_nGrow = 4; m_hAppBarDPA = DPA_Create(c_nGrow); if (!m_hAppBarDPA) { ERR("Out of memory\n"); return FALSE; } }
PAPPBAR pAppBar = (PAPPBAR)::LocalAlloc(LPTR, sizeof(*pAppBar)); if (pAppBar) { pAppBar->hWnd = hWnd; pAppBar->uEdge = UINT_MAX; pAppBar->uCallbackMessage = pData->abd.uCallbackMessage; if (DPA_InsertPtr(m_hAppBarDPA, INT_MAX, pAppBar) >= 0) return TRUE; // Success!
::LocalFree(pAppBar); }
ERR("Out of memory\n"); return FALSE;}
// ABM_REMOVEvoid CAppBarManager::OnAppBarRemove(_In_ const APPBAR_COMMAND *pData){ if (!m_hAppBarDPA) return;
HWND hWnd = (HWND)UlongToHandle(pData->abd.hWnd32); INT nItems = DPA_GetPtrCount(m_hAppBarDPA); while (--nItems >= 0) { PAPPBAR pAppBar = (PAPPBAR)DPA_GetPtr(m_hAppBarDPA, nItems); if (!pAppBar) continue;
if (pAppBar->hWnd == hWnd) { RECT rcOld = pAppBar->rc; EliminateAppBar(nItems); StuckAppChange(hWnd, &rcOld, NULL, FALSE); } }}
// ABM_QUERYPOSvoid CAppBarManager::OnAppBarQueryPos(_Inout_ PAPPBAR_COMMAND pData){ HWND hWnd = (HWND)UlongToHandle(pData->abd.hWnd32); PAPPBAR pAppBar1 = FindAppBar(hWnd); if (!pAppBar1) { ERR("Not found: %p\n", hWnd); return; }
PAPPBARDATAINTEROP pOutput = AppBar_LockOutput(pData); if (!pOutput) { ERR("!pOutput: %d\n", pData->dwProcessId); return; } pOutput->rc = pData->abd.rc;
if (::IsRectEmpty(&pOutput->rc)) ERR("IsRectEmpty\n");
HMONITOR hMon1 = ::MonitorFromRect(&pOutput->rc, MONITOR_DEFAULTTOPRIMARY); ASSERT(hMon1 != NULL);
// Subtract tray rectangle from pOutput->rc if necessary if (hMon1 == GetMonitor() && !IsAutoHideState()) { APPBAR dummyAppBar; dummyAppBar.uEdge = GetPosition(); GetDockedRect(&dummyAppBar.rc); AppBarSubtractRect(&dummyAppBar, &pOutput->rc); }
// Subtract area from pOutput->rc UINT uEdge = pData->abd.uEdge; INT nItems = DPA_GetPtrCount(m_hAppBarDPA); while (--nItems >= 0) { PAPPBAR pAppBar2 = (PAPPBAR)DPA_GetPtr(m_hAppBarDPA, nItems); if (!pAppBar2 || pAppBar1->hWnd == pAppBar2->hWnd) continue;
if ((Edge_IsVertical(uEdge) || !Edge_IsVertical(pAppBar2->uEdge)) && (pAppBar1->uEdge != uEdge || !AppBarOutsideOf(pAppBar1, pAppBar2))) { if (pAppBar1->uEdge == uEdge || pAppBar2->uEdge != uEdge) continue; }
HMONITOR hMon2 = ::MonitorFromRect(&pAppBar2->rc, MONITOR_DEFAULTTONULL); if (hMon1 == hMon2) AppBarSubtractRect(pAppBar2, &pOutput->rc); }
AppBar_UnLockOutput(pOutput);}
// ABM_SETPOSvoid CAppBarManager::OnAppBarSetPos(_Inout_ PAPPBAR_COMMAND pData){ HWND hWnd = (HWND)UlongToHandle(pData->abd.hWnd32); PAPPBAR pAppBar = FindAppBar(hWnd); if (!pAppBar) return;
OnAppBarQueryPos(pData);
PAPPBARDATAINTEROP pOutput = AppBar_LockOutput(pData); if (!pOutput) return;
RECT rcOld = pAppBar->rc, rcNew = pData->abd.rc; BOOL bChanged = !::EqualRect(&rcOld, &rcNew);
pAppBar->rc = rcNew; pAppBar->uEdge = pData->abd.uEdge;
AppBar_UnLockOutput(pOutput);
if (bChanged) StuckAppChange(hWnd, &rcOld, &rcNew, FALSE);}
void CAppBarManager::OnAppBarNotifyAll( _In_opt_ HMONITOR hMon, _In_opt_ HWND hwndIgnore, _In_ DWORD dwNotify, _In_opt_ LPARAM lParam){ TRACE("%p, %p, 0x%X, %p\n", hMon, hwndIgnore, dwNotify, lParam);
if (!m_hAppBarDPA) return;
INT nItems = DPA_GetPtrCount(m_hAppBarDPA); while (--nItems >= 0) { PAPPBAR pAppBar = (PAPPBAR)DPA_GetPtr(m_hAppBarDPA, nItems); if (!pAppBar || pAppBar->hWnd == hwndIgnore) continue;
HWND hwndAppBar = pAppBar->hWnd; if (!::IsWindow(hwndAppBar)) { EliminateAppBar(nItems); continue; }
if (!hMon || hMon == ::MonitorFromWindow(hwndAppBar, MONITOR_DEFAULTTONULL)) ::PostMessageW(hwndAppBar, pAppBar->uCallbackMessage, dwNotify, lParam); }}
/// @param pAppBar The target AppBar to subtract./// @param prc The rectangle to be subtracted.void CAppBarManager::AppBarSubtractRect(_In_ PAPPBAR pAppBar, _Inout_ PRECT prc){ switch (pAppBar->uEdge) { case ABE_LEFT: prc->left = max(prc->left, pAppBar->rc.right); break; case ABE_TOP: prc->top = max(prc->top, pAppBar->rc.bottom); break; case ABE_RIGHT: prc->right = min(prc->right, pAppBar->rc.left); break; case ABE_BOTTOM: prc->bottom = min(prc->bottom, pAppBar->rc.top); break; default: ASSERT(FALSE); break; }}
// Is pAppBar1 outside of pAppBar2?BOOL CAppBarManager::AppBarOutsideOf( _In_ const APPBAR *pAppBar1, _In_ const APPBAR *pAppBar2){ if (pAppBar1->uEdge != pAppBar2->uEdge) return FALSE;
switch (pAppBar2->uEdge) { case ABE_LEFT: return pAppBar1->rc.left >= pAppBar2->rc.left; case ABE_TOP: return pAppBar1->rc.top >= pAppBar2->rc.top; case ABE_RIGHT: return pAppBar1->rc.right <= pAppBar2->rc.right; case ABE_BOTTOM: return pAppBar1->rc.bottom <= pAppBar2->rc.bottom; default: ASSERT(FALSE); return FALSE; }}
/// Get rectangle of the tray window./// @param prcDocked The pointer to the rectangle to be received.void CAppBarManager::GetDockedRect(_Out_ PRECT prcDocked){ *prcDocked = *GetTrayRect(); if (IsAutoHideState() && IsHidingState()) ComputeHiddenRect(prcDocked, GetPosition());}
/// Compute the position and size of the hidden TaskBar./// @param prc The rectangle before hiding TaskBar./// @param uSide The side of TaskBar (ABE_...).void CAppBarManager::ComputeHiddenRect(_Inout_ PRECT prc, _In_ UINT uSide){ MONITORINFO mi = { sizeof(mi) }; HMONITOR hMonitor = ::MonitorFromRect(prc, MONITOR_DEFAULTTONULL); if (!::GetMonitorInfoW(hMonitor, &mi)) return; RECT rcMon = mi.rcMonitor;
INT cxy = Edge_IsVertical(uSide) ? (prc->bottom - prc->top) : (prc->right - prc->left); switch (uSide) { case ABE_LEFT: prc->right = rcMon.left + GetSystemMetrics(SM_CXFRAME) / 2; prc->left = prc->right - cxy; break; case ABE_TOP: prc->bottom = rcMon.top + GetSystemMetrics(SM_CYFRAME) / 2; prc->top = prc->bottom - cxy; break; case ABE_RIGHT: prc->left = rcMon.right - GetSystemMetrics(SM_CXFRAME) / 2; prc->right = prc->left + cxy; break; case ABE_BOTTOM: prc->top = rcMon.bottom - GetSystemMetrics(SM_CYFRAME) / 2; prc->bottom = prc->top + cxy; break; default: ASSERT(FALSE); break; }}
/// This function is called when AppBar and/or TaskBar is being moved, removed, and/or updated./// @param hwndTarget The target window. Optional./// @param prcOld The old position and size. Optional./// @param prcNew The new position and size. Optional./// @param bTray TRUE if the tray is being moved.voidCAppBarManager::StuckAppChange( _In_opt_ HWND hwndTarget, _In_opt_ const RECT *prcOld, _In_opt_ const RECT *prcNew, _In_ BOOL bTray){ RECT rcWorkArea1, rcWorkArea2; HMONITOR hMon1 = NULL; UINT flags = 0; enum { SET_WORKAREA_1 = 1, SET_WORKAREA_2 = 2, NEED_SIZING = 4 }; // for flags
if (prcOld) { hMon1 = (bTray ? GetPreviousMonitor() : ::MonitorFromRect(prcOld, MONITOR_DEFAULTTONEAREST)); if (hMon1) { WORKAREA_TYPE type1 = RecomputeWorkArea(GetTrayRect(), hMon1, &rcWorkArea1); if (type1 == WORKAREA_IS_NOT_MONITOR) flags = SET_WORKAREA_1; if (type1 == WORKAREA_SAME_AS_MONITOR) flags = NEED_SIZING; } }
if (prcNew) { HMONITOR hMon2 = ::MonitorFromRect(prcNew, MONITOR_DEFAULTTONULL); if (hMon2 && hMon2 != hMon1) { WORKAREA_TYPE type2 = RecomputeWorkArea(GetTrayRect(), hMon2, &rcWorkArea2); if (type2 == WORKAREA_IS_NOT_MONITOR) flags |= SET_WORKAREA_2; else if (type2 == WORKAREA_SAME_AS_MONITOR && !flags) flags = NEED_SIZING; } }
if (flags & SET_WORKAREA_1) { UINT fWinIni = ((flags == SET_WORKAREA_1 && GetDesktopWnd()) ? SPIF_SENDCHANGE : 0); ::SystemParametersInfoW(SPI_SETWORKAREA, TRUE, &rcWorkArea1, fWinIni); RedrawDesktop(GetDesktopWnd(), &rcWorkArea1); }
if (flags & SET_WORKAREA_2) { UINT fWinIni = (GetDesktopWnd() ? SPIF_SENDCHANGE : 0); ::SystemParametersInfoW(SPI_SETWORKAREA, TRUE, &rcWorkArea2, fWinIni); RedrawDesktop(GetDesktopWnd(), &rcWorkArea2); }
if (bTray || flags == NEED_SIZING) ::SendMessageW(GetDesktopWnd(), WM_SIZE, 0, 0);
// Post ABN_POSCHANGED messages to AppBar windows OnAppBarNotifyAll(NULL, hwndTarget, ABN_POSCHANGED, TRUE);}
void CAppBarManager::RedrawDesktop(_In_ HWND hwndDesktop, _Inout_ PRECT prc){ if (!hwndDesktop) return; ::MapWindowPoints(NULL, hwndDesktop, (POINT*)prc, sizeof(*prc) / sizeof(POINT)); ::RedrawWindow(hwndDesktop, prc, 0, RDW_ALLCHILDREN | RDW_ERASE | RDW_INVALIDATE);}
/// Re-compute the work area./// @param prcTray The position and size of the tray window/// @param hMonitor The monitor of the work area to re-compute./// @param prcWorkArea The work area to be re-computed.WORKAREA_TYPECAppBarManager::RecomputeWorkArea( _In_ const RECT *prcTray, _In_ HMONITOR hMonitor, _Out_ PRECT prcWorkArea){ MONITORINFO mi = { sizeof(mi) }; if (!::GetMonitorInfoW(hMonitor, &mi)) return WORKAREA_NO_TRAY_AREA;
if (IsAutoHideState()) *prcWorkArea = mi.rcMonitor; else ::SubtractRect(prcWorkArea, &mi.rcMonitor, prcTray);
if (m_hAppBarDPA) { INT nItems = DPA_GetPtrCount(m_hAppBarDPA); while (--nItems >= 0) { PAPPBAR pAppBar = (PAPPBAR)DPA_GetPtr(m_hAppBarDPA, nItems); if (pAppBar && hMonitor == ::MonitorFromRect(&pAppBar->rc, MONITOR_DEFAULTTONULL)) AppBarSubtractRect(pAppBar, prcWorkArea); } }
if (!::EqualRect(prcWorkArea, &mi.rcWork)) return WORKAREA_IS_NOT_MONITOR;
if (IsAutoHideState() || ::IsRectEmpty(prcTray)) return WORKAREA_NO_TRAY_AREA;
return WORKAREA_SAME_AS_MONITOR;}
BOOL CALLBACKCAppBarManager::MonitorEnumProc( _In_ HMONITOR hMonitor, _In_ HDC hDC, _In_ LPRECT prc, _Inout_ LPARAM lParam){ CAppBarManager *pThis = (CAppBarManager *)lParam; UNREFERENCED_PARAMETER(hDC);
RECT rcWorkArea; if (pThis->RecomputeWorkArea(prc, hMonitor, &rcWorkArea) != WORKAREA_IS_NOT_MONITOR) return TRUE;
HWND hwndDesktop = pThis->GetDesktopWnd(); ::SystemParametersInfoW(SPI_SETWORKAREA, 0, &rcWorkArea, hwndDesktop ? SPIF_SENDCHANGE : 0); pThis->RedrawDesktop(hwndDesktop, &rcWorkArea); return TRUE;}
void CAppBarManager::RecomputeAllWorkareas(){ ::EnumDisplayMonitors(NULL, NULL, CAppBarManager::MonitorEnumProc, (LPARAM)this);}
BOOL CAppBarManager::SetAutoHideBar(_In_ HWND hwndTarget, _In_ BOOL bSetOrReset, _In_ UINT uSide){ ATLASSERT(uSide < _countof(m_ahwndAutoHideBars)); HWND *phwndAutoHide = &m_ahwndAutoHideBars[uSide]; if (!IsWindow(*phwndAutoHide)) *phwndAutoHide = NULL;
if (bSetOrReset) // Set? { if (!*phwndAutoHide) *phwndAutoHide = hwndTarget; return *phwndAutoHide == hwndTarget; } else // Reset { if (*phwndAutoHide == hwndTarget) *phwndAutoHide = NULL; return TRUE; }}
void CAppBarManager::OnAppBarActivationChange2(_In_ HWND hwndNewAutoHide, _In_ UINT uSide){ HWND hwndAutoHideBar = OnAppBarGetAutoHideBar(uSide); if (hwndAutoHideBar && hwndAutoHideBar != hwndNewAutoHide) ::PostMessageW(GetTrayWnd(), TWM_SETZORDER, (WPARAM)hwndAutoHideBar, uSide);}
PAPPBAR_COMMANDCAppBarManager::GetAppBarMessage(_Inout_ PCOPYDATASTRUCT pCopyData){ PAPPBAR_COMMAND pData = (PAPPBAR_COMMAND)pCopyData->lpData;
if (pCopyData->cbData != sizeof(*pData) || pData->abd.cbSize != sizeof(pData->abd)) { ERR("Invalid AppBar message\n"); return NULL; }
return pData;}
// ABM_GETSTATEUINT CAppBarManager::OnAppBarGetState(){ return (IsAutoHideState() ? ABS_AUTOHIDE : 0) | (IsAlwaysOnTop() ? ABS_ALWAYSONTOP : 0);}
// ABM_GETTASKBARPOSBOOL CAppBarManager::OnAppBarGetTaskbarPos(_Inout_ PAPPBAR_COMMAND pData){ PAPPBARDATAINTEROP pOutput = AppBar_LockOutput(pData); if (!pOutput) { ERR("!pOutput: %d\n", pData->dwProcessId); return FALSE; }
pOutput->rc = *GetTrayRect(); pOutput->uEdge = GetPosition();
AppBar_UnLockOutput(pOutput); return TRUE;}
// ABM_ACTIVATE, ABM_WINDOWPOSCHANGEDvoid CAppBarManager::OnAppBarActivationChange(_In_ const APPBAR_COMMAND *pData){ HWND hWnd = (HWND)UlongToHandle(pData->abd.hWnd32); PAPPBAR pAppBar = FindAppBar(hWnd); if (!pAppBar) { ERR("Not found: %p\n", hWnd); return; }
HWND hwndAppBar = pAppBar->hWnd; for (UINT uSide = ABE_LEFT; uSide <= ABE_BOTTOM; ++uSide) { if (m_ahwndAutoHideBars[uSide] == hwndAppBar && uSide != pAppBar->uEdge) return; }
OnAppBarActivationChange2(hwndAppBar, pAppBar->uEdge);}
// ABM_GETAUTOHIDEBARHWND CAppBarManager::OnAppBarGetAutoHideBar(_In_ UINT uSide){ if (uSide >= _countof(m_ahwndAutoHideBars)) return NULL;
if (!::IsWindow(m_ahwndAutoHideBars[uSide])) m_ahwndAutoHideBars[uSide] = NULL; return m_ahwndAutoHideBars[uSide];}
// ABM_SETAUTOHIDEBARBOOL CAppBarManager::OnAppBarSetAutoHideBar(_In_ const APPBAR_COMMAND *pData){ if (pData->abd.uEdge >= _countof(m_ahwndAutoHideBars)) return FALSE; HWND hwndTarget = (HWND)UlongToHandle(pData->abd.hWnd32); return SetAutoHideBar(hwndTarget, (BOOL)pData->abd.lParam64, pData->abd.uEdge);}
// ABM_SETSTATEvoid CAppBarManager::OnAppBarSetState(_In_ UINT uState){ if ((uState & ~(ABS_AUTOHIDE | ABS_ALWAYSONTOP))) return;
SetAutoHideState(!!(uState & ABS_AUTOHIDE)); UpdateAlwaysOnTop(!!(uState & ABS_ALWAYSONTOP));}
// WM_COPYDATA TABDMC_APPBARLRESULT CAppBarManager::OnAppBarMessage(_Inout_ PCOPYDATASTRUCT pCopyData){ PAPPBAR_COMMAND pData = GetAppBarMessage(pCopyData); if (!pData) return 0;
switch (pData->dwMessage) { case ABM_NEW: return OnAppBarNew(pData); case ABM_REMOVE: OnAppBarRemove(pData); break; case ABM_QUERYPOS: OnAppBarQueryPos(pData); break; case ABM_SETPOS: OnAppBarSetPos(pData); break; case ABM_GETSTATE: return OnAppBarGetState(); case ABM_GETTASKBARPOS: return OnAppBarGetTaskbarPos(pData); case ABM_ACTIVATE: case ABM_WINDOWPOSCHANGED: OnAppBarActivationChange(pData); break; case ABM_GETAUTOHIDEBAR: return (LRESULT)OnAppBarGetAutoHideBar(pData->abd.uEdge); case ABM_SETAUTOHIDEBAR: return OnAppBarSetAutoHideBar(pData); case ABM_SETSTATE: OnAppBarSetState((UINT)pData->abd.lParam64); break; default: { FIXME("0x%X\n", pData->dwMessage); return FALSE; } } return TRUE;}