// Copyright (c) 1996-1999 Microsoft Corporation // -------------------------------------------------------------------------- // // SCROLL.CPP // // Scroll bar class. // // OUTSTANDING ISSUES: // Internationalize scrollbar placement for RtoL languages in window. // // -------------------------------------------------------------------------- #include "oleacc_p.h" #include "default.h" #include "window.h" #include "client.h" #include "scroll.h" #include "propmgr_util.h" ///////////////////////////////////////////////////////////////////////////// // // SCROLLBAR (in a Window) // ///////////////////////////////////////////////////////////////////////////// // -------------------------------------------------------------------------- // // CreateScrollBarObject() // // -------------------------------------------------------------------------- HRESULT CreateScrollBarObject(HWND hwnd, long idObject, REFIID riid, void** ppvScroll) { return(CreateScrollBarThing(hwnd, idObject, 0, riid, ppvScroll)); } // -------------------------------------------------------------------------- // // CreateScrollBarThing() // // -------------------------------------------------------------------------- HRESULT CreateScrollBarThing(HWND hwnd, long idObject, long iItem, REFIID riid, void** ppvScroll) { CScrollBar * pscroll; HRESULT hr; InitPv(ppvScroll); pscroll = new CScrollBar(); if (pscroll) { if (! pscroll->FInitialize(hwnd, idObject, iItem)) { delete pscroll; return(E_FAIL); } } else return(E_OUTOFMEMORY); hr = pscroll->QueryInterface(riid, ppvScroll); if (!SUCCEEDED(hr)) delete pscroll; return(hr); } // -------------------------------------------------------------------------- // // CScrollBar::Clone() // // -------------------------------------------------------------------------- STDMETHODIMP CScrollBar::Clone(IEnumVARIANT** ppenum) { return(CreateScrollBarThing(m_hwnd, (m_fVertical ? OBJID_VSCROLL : OBJID_HSCROLL), m_idChildCur, IID_IEnumVARIANT, (void**)ppenum)); } // -------------------------------------------------------------------------- // // CScrollBar::FInitialize() // // -------------------------------------------------------------------------- BOOL CScrollBar::FInitialize(HWND hwndScrollBar, LONG idObject, LONG iChildCur) { if (! IsWindow(hwndScrollBar)) return(FALSE); m_hwnd = hwndScrollBar; m_cChildren = CCHILDREN_SCROLLBAR; m_idChildCur = iChildCur; m_fVertical = (idObject == OBJID_VSCROLL); return(TRUE); } // -------------------------------------------------------------------------- // // GetScrollMask() // // Gets present elements (may or may not be offscreen) // // -------------------------------------------------------------------------- void FixUpScrollBarInfo(LPSCROLLBARINFO lpsbi) { if (lpsbi->rgstate[INDEX_SCROLLBAR_SELF] & STATE_SYSTEM_UNAVAILABLE) { lpsbi->rgstate[INDEX_SCROLLBAR_UPPAGE] |= STATE_SYSTEM_INVISIBLE; lpsbi->rgstate[INDEX_SCROLLBAR_THUMB] |= STATE_SYSTEM_INVISIBLE; lpsbi->rgstate[INDEX_SCROLLBAR_DOWNPAGE] |= STATE_SYSTEM_INVISIBLE; } } // -------------------------------------------------------------------------- // // CScrollBar::get_accName() // // -------------------------------------------------------------------------- STDMETHODIMP CScrollBar::get_accName(VARIANT varChild, BSTR* pszName) { InitPv(pszName); if (! ValidateChild(&varChild)) return(E_INVALIDARG); return(HrCreateString(STR_SCROLLBAR_NAME + varChild.lVal + (m_fVertical ? 0 : INDEX_SCROLLBAR_HORIZONTAL), pszName)); } // -------------------------------------------------------------------------- // // CScrollBar::get_accValue() // // -------------------------------------------------------------------------- STDMETHODIMP CScrollBar::get_accValue(VARIANT varChild, BSTR* pszValue) { long lPos; InitPv(pszValue); if (! ValidateChild(&varChild)) return(E_INVALIDARG); if (varChild.lVal) return(E_NOT_APPLICABLE); // // The value is the position. // lPos = GetScrollPos(m_hwnd, (m_fVertical ? SB_VERT : SB_HORZ)); int Min, Max; GetScrollRange( m_hwnd, (m_fVertical ? SB_VERT : SB_HORZ), & Min, & Max ); // work out a percent value... if( Min != Max ) lPos = ( ( lPos - Min ) * 100 ) / ( Max - Min ); else lPos = 0; // Prevent div-by-0 return(VarBstrFromI4(lPos, 0, 0, pszValue)); } // -------------------------------------------------------------------------- // // CScrollBar::get_accDescription() // // -------------------------------------------------------------------------- STDMETHODIMP CScrollBar::get_accDescription(VARIANT varChild, BSTR* pszDesc) { InitPv(pszDesc); // // Validate the params // if (! ValidateChild(&varChild)) return(E_INVALIDARG); return(HrCreateString(STR_SCROLLBAR_DESCRIPTION + varChild.lVal + (m_fVertical ? 0 : INDEX_SCROLLBAR_HORIZONTAL), pszDesc)); } // -------------------------------------------------------------------------- // // CScrollBar::get_accRole() // // -------------------------------------------------------------------------- STDMETHODIMP CScrollBar::get_accRole(VARIANT varChild, VARIANT* pvarRole) { InitPvar(pvarRole); if (! ValidateChild(&varChild)) return(E_INVALIDARG); pvarRole->vt = VT_I4; switch (varChild.lVal) { case INDEX_SCROLLBAR_SELF: pvarRole->lVal = ROLE_SYSTEM_SCROLLBAR; break; case INDEX_SCROLLBAR_UP: case INDEX_SCROLLBAR_DOWN: case INDEX_SCROLLBAR_UPPAGE: case INDEX_SCROLLBAR_DOWNPAGE: pvarRole->lVal = ROLE_SYSTEM_PUSHBUTTON; break; case INDEX_SCROLLBAR_THUMB: pvarRole->lVal = ROLE_SYSTEM_INDICATOR; break; default: AssertStr( TEXT("Invalid ChildID for child of scroll bar") ); } return(S_OK); } // -------------------------------------------------------------------------- // // CScrollBar::get_accState() // // -------------------------------------------------------------------------- STDMETHODIMP CScrollBar::get_accState(VARIANT varChild, VARIANT* pvarState) { SCROLLBARINFO sbi; InitPvar(pvarState); // // Validate parameters // if (! ValidateChild(&varChild)) return(E_INVALIDARG); pvarState->vt = VT_I4; pvarState->lVal = 0; // // Get our information // if (! MyGetScrollBarInfo(m_hwnd, (m_fVertical ? OBJID_VSCROLL : OBJID_HSCROLL), &sbi) || (sbi.rgstate[INDEX_SCROLLBAR_SELF] & STATE_SYSTEM_INVISIBLE)) { // // If scrollbar isn't there period, fail. // pvarState->lVal |= STATE_SYSTEM_INVISIBLE; return(S_OK); } // // If unavailable or offscreen, everything is. // FixUpScrollBarInfo(&sbi); pvarState->lVal |= sbi.rgstate[INDEX_SCROLLBAR_SELF]; pvarState->lVal |= sbi.rgstate[varChild.lVal]; return(S_OK); } // -------------------------------------------------------------------------- // // CScrollBar::get_accDefaultAction() // // -------------------------------------------------------------------------- STDMETHODIMP CScrollBar::get_accDefaultAction(VARIANT varChild, BSTR * pszDefAction) { InitPv(pszDefAction); // // Validate the params // if (! ValidateChild(&varChild)) return(E_INVALIDARG); switch (varChild.lVal) { case INDEX_SCROLLBAR_UP: case INDEX_SCROLLBAR_UPPAGE: case INDEX_SCROLLBAR_DOWNPAGE: case INDEX_SCROLLBAR_DOWN: return(HrCreateString(STR_BUTTON_PUSH, pszDefAction)); } return(E_NOT_APPLICABLE); } // -------------------------------------------------------------------------- // // CScrollBar::accLocation() // // -------------------------------------------------------------------------- STDMETHODIMP CScrollBar::accLocation(long* pxLeft, long* pyTop, long* pcxWidth, long* pcyHeight, VARIANT varChild) { SCROLLBARINFO sbi; int dxyButton; InitAccLocation(pxLeft, pyTop, pcxWidth, pcyHeight); // // Validate params // if (! ValidateChild(&varChild)) return(E_INVALIDARG); if (! MyGetScrollBarInfo(m_hwnd, (m_fVertical ? OBJID_VSCROLL : OBJID_HSCROLL), &sbi) || (sbi.rgstate[INDEX_TITLEBAR_SELF] & (STATE_SYSTEM_INVISIBLE | STATE_SYSTEM_OFFSCREEN))) { return(S_FALSE); } FixUpScrollBarInfo(&sbi); if (sbi.rgstate[varChild.lVal] & (STATE_SYSTEM_INVISIBLE | STATE_SYSTEM_OFFSCREEN)) return(S_FALSE); if (m_fVertical) dxyButton = sbi.rcScrollBar.right - sbi.rcScrollBar.left; else dxyButton = sbi.rcScrollBar.bottom - sbi.rcScrollBar.top; switch (varChild.lVal) { case INDEX_SCROLLBAR_SELF: *pxLeft = sbi.rcScrollBar.left; *pyTop = sbi.rcScrollBar.top; *pcxWidth = sbi.rcScrollBar.right - sbi.rcScrollBar.left; *pcyHeight = sbi.rcScrollBar.bottom - sbi.rcScrollBar.top; break; case INDEX_SCROLLBAR_UP: case INDEX_SCROLLBAR_DOWN: if (m_fVertical) { *pxLeft = sbi.rcScrollBar.left; *pcxWidth = dxyButton; *pcyHeight = sbi.dxyLineButton; if (varChild.lVal == INDEX_SCROLLBAR_UP) *pyTop = sbi.rcScrollBar.top; else *pyTop = sbi.rcScrollBar.bottom - *pcyHeight; } else { *pyTop = sbi.rcScrollBar.top; *pcyHeight = dxyButton; *pcxWidth = sbi.dxyLineButton; if (varChild.lVal == INDEX_SCROLLBAR_UP) *pxLeft = sbi.rcScrollBar.left; else *pxLeft = sbi.rcScrollBar.right - *pcxWidth; } break; case INDEX_SCROLLBAR_UPPAGE: if (m_fVertical) { *pxLeft = sbi.rcScrollBar.left; *pcxWidth = dxyButton; *pyTop = sbi.rcScrollBar.top + sbi.dxyLineButton; *pcyHeight = sbi.xyThumbTop - sbi.dxyLineButton; } else { *pyTop = sbi.rcScrollBar.top; *pcyHeight = dxyButton; *pxLeft = sbi.rcScrollBar.left + sbi.dxyLineButton; *pcxWidth = sbi.xyThumbTop - sbi.dxyLineButton; } break; case INDEX_SCROLLBAR_DOWNPAGE: if (m_fVertical) { *pxLeft = sbi.rcScrollBar.left; *pcxWidth = dxyButton; *pyTop = sbi.rcScrollBar.top + sbi.xyThumbBottom; *pcyHeight = (sbi.rcScrollBar.bottom - sbi.rcScrollBar.top) - sbi.xyThumbBottom - sbi.dxyLineButton; } else { *pyTop = sbi.rcScrollBar.top; *pcyHeight = dxyButton; *pxLeft = sbi.rcScrollBar.left + sbi.xyThumbBottom; *pcxWidth = (sbi.rcScrollBar.right - sbi.rcScrollBar.left) - sbi.xyThumbBottom - sbi.dxyLineButton; } break; case INDEX_SCROLLBAR_THUMB: if (m_fVertical) { *pxLeft = sbi.rcScrollBar.left; *pcxWidth = dxyButton; *pyTop = sbi.rcScrollBar.top + sbi.xyThumbTop; *pcyHeight = sbi.xyThumbBottom - sbi.xyThumbTop; } else { *pyTop = sbi.rcScrollBar.top; *pcyHeight = dxyButton; *pxLeft = sbi.rcScrollBar.left + sbi.xyThumbTop; *pcxWidth = sbi.xyThumbBottom - sbi.xyThumbTop; } break; default: AssertStr( TEXT("Invalid ChildID for child of scroll bar") ); } return(S_OK); } // -------------------------------------------------------------------------- // // CScrollBar::accNavigate() // // -------------------------------------------------------------------------- STDMETHODIMP CScrollBar::accNavigate(long dwNavDir, VARIANT varStart, VARIANT * pvarEnd) { long lEndUp = 0; SCROLLBARINFO sbi; InitPvar(pvarEnd); // // Validate params // if (! ValidateChild(&varStart) || ! ValidateNavDir(dwNavDir, varStart.lVal)) return(E_INVALIDARG); if (! MyGetScrollBarInfo(m_hwnd, (m_fVertical ? OBJID_VSCROLL : OBJID_HSCROLL), &sbi)) { return(S_FALSE); } if (dwNavDir == NAVDIR_FIRSTCHILD) { dwNavDir = NAVDIR_NEXT; } else if (dwNavDir == NAVDIR_LASTCHILD) { dwNavDir = NAVDIR_PREVIOUS; varStart.lVal = m_cChildren + 1; } else if (varStart.lVal == INDEX_SCROLLBAR_SELF) return(GetParentToNavigate((m_fVertical ? OBJID_VSCROLL : OBJID_HSCROLL), m_hwnd, OBJID_WINDOW, dwNavDir, pvarEnd)); FixUpScrollBarInfo(&sbi); switch (dwNavDir) { case NAVDIR_NEXT: FindNext: lEndUp = varStart.lVal; while (++lEndUp <= INDEX_SCROLLBAR_MAC) { if (!(sbi.rgstate[lEndUp] & STATE_SYSTEM_INVISIBLE)) break; } if (lEndUp > INDEX_SCROLLBAR_MAC) lEndUp = 0; break; case NAVDIR_PREVIOUS: FindPrevious: lEndUp = varStart.lVal; while (--lEndUp >= INDEX_SCROLLBAR_MIC) { if (!(sbi.rgstate[lEndUp] & STATE_SYSTEM_INVISIBLE)) break; } if (lEndUp < INDEX_SCROLLBAR_MIC) lEndUp = 0; break; case NAVDIR_UP: lEndUp = 0; if (m_fVertical) goto FindPrevious; break; case NAVDIR_LEFT: lEndUp = 0; if (!m_fVertical) goto FindPrevious; break; case NAVDIR_DOWN: lEndUp = 0; if (m_fVertical) goto FindNext; break; case NAVDIR_RIGHT: lEndUp = 0; if (!m_fVertical) goto FindNext; break; default: AssertStr( TEXT("Invalid NavDir") ); } if (lEndUp != INDEX_SCROLLBAR_SELF) { pvarEnd->vt = VT_I4; pvarEnd->lVal = lEndUp; return(S_OK); } else return(S_FALSE); } // -------------------------------------------------------------------------- // // CScrollBar::accHitTest() // // -------------------------------------------------------------------------- STDMETHODIMP CScrollBar::accHitTest(long xLeft, long yTop, VARIANT * pvarChild) { POINT pt; SCROLLBARINFO sbi; int xyPtAxis; int xyScrollEnd; long lHit; InitPvar(pvarChild); if (! MyGetScrollBarInfo(m_hwnd, (m_fVertical ? OBJID_VSCROLL : OBJID_HSCROLL), &sbi) || (sbi.rgstate[INDEX_SCROLLBAR_SELF] & (STATE_SYSTEM_OFFSCREEN | STATE_SYSTEM_INVISIBLE))) { return(S_FALSE); } pt.x = xLeft; pt.y = yTop; if (! PtInRect(&sbi.rcScrollBar, pt)) return(S_FALSE); FixUpScrollBarInfo(&sbi); // // Convert to scrollbar coords. // if (m_fVertical) { xyPtAxis = yTop - sbi.rcScrollBar.top; xyScrollEnd = sbi.rcScrollBar.bottom - sbi.rcScrollBar.top; } else { xyPtAxis = xLeft - sbi.rcScrollBar.left; xyScrollEnd = sbi.rcScrollBar.right - sbi.rcScrollBar.left; } lHit = INDEX_SCROLLBAR_SELF; if (xyPtAxis < sbi.dxyLineButton) { Assert(!(sbi.rgstate[INDEX_SCROLLBAR_UP] & STATE_SYSTEM_INVISIBLE)); lHit = INDEX_SCROLLBAR_UP; } else if (xyPtAxis >= xyScrollEnd - sbi.dxyLineButton) { Assert(!(sbi.rgstate[INDEX_SCROLLBAR_DOWN] & STATE_SYSTEM_INVISIBLE)); lHit = INDEX_SCROLLBAR_DOWN; } else if (!(sbi.rgstate[INDEX_SCROLLBAR_SELF] & STATE_SYSTEM_UNAVAILABLE)) { if (xyPtAxis < sbi.xyThumbTop) { Assert(!(sbi.rgstate[INDEX_SCROLLBAR_UPPAGE] & STATE_SYSTEM_INVISIBLE)); lHit = INDEX_SCROLLBAR_UPPAGE; } else if (xyPtAxis >= sbi.xyThumbBottom) { Assert(!(sbi.rgstate[INDEX_SCROLLBAR_DOWNPAGE] & STATE_SYSTEM_INVISIBLE)); lHit = INDEX_SCROLLBAR_DOWNPAGE; } else { Assert(!(sbi.rgstate[INDEX_SCROLLBAR_THUMB] & STATE_SYSTEM_INVISIBLE)); lHit = INDEX_SCROLLBAR_THUMB; } } pvarChild->vt = VT_I4; pvarChild->lVal = lHit; return(S_OK); } // -------------------------------------------------------------------------- // // CScrollBar::accDoDefaultAction() // // Only works if the element is visible and available! // // -------------------------------------------------------------------------- STDMETHODIMP CScrollBar::accDoDefaultAction(VARIANT varChild) { WPARAM wpAction; SCROLLBARINFO sbi; // // Validate params // if (! ValidateChild(&varChild)) return(E_INVALIDARG); // // Is child available and present? // if (!MyGetScrollBarInfo(m_hwnd, (m_fVertical ? OBJID_VSCROLL : OBJID_HSCROLL), &sbi) || (sbi.rgstate[INDEX_SCROLLBAR_SELF] & (STATE_SYSTEM_INVISIBLE | STATE_SYSTEM_UNAVAILABLE))) { return(S_FALSE); } FixUpScrollBarInfo(&sbi); if (sbi.rgstate[varChild.lVal] & STATE_SYSTEM_UNAVAILABLE) return(S_FALSE); switch (varChild.lVal) { case INDEX_SCROLLBAR_UP: wpAction = SB_LINEUP; break; case INDEX_SCROLLBAR_UPPAGE: wpAction = SB_PAGEUP; break; case INDEX_SCROLLBAR_DOWNPAGE: wpAction = SB_PAGEDOWN; break; case INDEX_SCROLLBAR_DOWN: wpAction = SB_LINEDOWN; break; default: return(E_NOT_APPLICABLE); } PostMessage(m_hwnd, (m_fVertical ? WM_VSCROLL : WM_HSCROLL), wpAction, (LPARAM)m_hwnd); return(S_OK); } // -------------------------------------------------------------------------- // // CScrollBar::put_accValue() // // CALLER frees the string // // -------------------------------------------------------------------------- STDMETHODIMP CScrollBar::put_accValue(VARIANT varChild, BSTR szValue) { long lPos; HRESULT hr; if (! ValidateChild(&varChild)) return(E_INVALIDARG); if (varChild.lVal) return(E_NOT_APPLICABLE); hr = VarI4FromStr(szValue, 0, 0, &lPos); if (!SUCCEEDED(hr)) return(hr); // Verify that we've got a valid percent value if( lPos < 0 || lPos > 100 ) return E_INVALIDARG; int Min, Max; GetScrollRange( m_hwnd, SB_CTL, & Min, & Max ); // work out value from percentage... lPos = Min + ( ( Max - Min ) * lPos ) / 100; SetScrollPos(m_hwnd, (m_fVertical ? SB_VERT : SB_HORZ), lPos, TRUE); return(S_OK); } STDMETHODIMP CScrollBar::GetIdentityString ( DWORD dwIDChild, BYTE ** ppIDString, DWORD * pdwIDStringLen ) { // Unlike the other HWND-based proxies, this one has two associated // objids - OBJID_VSCROLL or OBJID_HSCROLL, dpending on whether it represents // the horizontal or vertival non-client scrollbar. // Because of this, the default implementation of GetIdentityString in // the CAccessible base class can't handle this for us, since it can't // determin that this is a CScrollBar nor check what our m_fVertical flag is. // // Instead, we override GetIdentityString, and implement it here where we // have the information we need. // TODO - should probably verify that idChild is valid. // (we can do this for some classes - eg. where the number of children is // fixed and known - but it may not be preactical to do it in general.) *ppIDString = NULL; *pdwIDStringLen = 0; BYTE * pKeyData = (BYTE *) CoTaskMemAlloc( HWNDKEYSIZE ); if( ! pKeyData ) { return E_OUTOFMEMORY; } DWORD idObject = m_fVertical ? OBJID_VSCROLL : OBJID_HSCROLL; MakeHwndKey( pKeyData, m_hwnd, idObject, dwIDChild ); *ppIDString = pKeyData; *pdwIDStringLen = HWNDKEYSIZE; return S_OK; } ///////////////////////////////////////////////////////////////////////////// // // GRIP // ///////////////////////////////////////////////////////////////////////////// // -------------------------------------------------------------------------- // // CreateSizeGripObject() // // EXTERNAL // // -------------------------------------------------------------------------- HRESULT CreateSizeGripObject(HWND hwnd, long idObject, REFIID riid, void** ppvGrip) { return(CreateSizeGripThing(hwnd, idObject, riid, ppvGrip)); } // -------------------------------------------------------------------------- // // CreateSizeGripThing() // // INTERNAL // // -------------------------------------------------------------------------- HRESULT CreateSizeGripThing(HWND hwnd, long idObject, REFIID riid, void** ppvGrip) { CSizeGrip * psizegrip; HRESULT hr; UNUSED(idObject); InitPv(ppvGrip); psizegrip = new CSizeGrip(); if (psizegrip) { if (! psizegrip->FInitialize(hwnd)) { delete psizegrip; return(E_FAIL); } } else return(E_OUTOFMEMORY); hr = psizegrip->QueryInterface(riid, ppvGrip); if (!SUCCEEDED(hr)) delete psizegrip; return(hr); } // -------------------------------------------------------------------------- // // CSizeGrip::FInitialize() // // -------------------------------------------------------------------------- BOOL CSizeGrip::FInitialize(HWND hwnd) { m_hwnd = hwnd; return(IsWindow(hwnd)); } // -------------------------------------------------------------------------- // // CSizeGrip::IsActive() // // Returns TRUE if the size grip can actually be used to size a window. // Sometimes size grips are present, but do not allow sizing - eg. // present on a control because horiz+vert scrollbars are present, but the // control is fixed in a dialog. // // See ntuser\rtl\winmgr.c:SizeBoxHwnd() for the corresponding USER code // that does this. // // -------------------------------------------------------------------------- BOOL CSizeGrip::IsActive() { // sizable if: // size grip is present... WINDOWINFO wi; if( ! MyGetWindowInfo( m_hwnd, & wi ) || ! ( wi.dwStyle & WS_VSCROLL ) || ! ( wi.dwStyle & WS_HSCROLL ) ) { return FALSE; } // Find first ancestor or self that is sizable and non-maximized... HWND hwndSizable = m_hwnd; HWND hwndDesktop = GetDesktopWindow(); for( ; ; ) { DWORD dwStyle = GetWindowLong( hwndSizable, GWL_STYLE ); if( dwStyle & WS_THICKFRAME ) { // Got it! break; } // try next level up... hwndSizable = MyGetAncestor( hwndSizable, GA_PARENT ); if( ! hwndSizable || hwndSizable == hwndDesktop ) { // Didn't find any sizable ancestors - so not active. return FALSE; } } // We should really do this check along with checking the THICKFRAME // abive - ie. "find the first sizable and non-zoomed window"; instead of // this which is "find the first sizable window, and then check that it // is non-zoomed". // This version is consistent with USER's behavior, which disallows a // maximized MDI child from sizing its parent. if( IsZoomed( hwndSizable ) ) { return FALSE; } // If the window we're sizing is different than this window, then check // that the size grip is within SM_C[X|Y]EDGE of the sizable window... if( hwndSizable != m_hwnd ) { // sizable window's scrollbars must NOT be showing... if( ! MyGetWindowInfo( hwndSizable, & wi ) || ( wi.dwStyle & WS_VSCROLL ) || ( wi.dwStyle & WS_HSCROLL ) ) { return FALSE; } // check if bottom-right aligns with top-level window... RECT rcInner; GetWindowRect( m_hwnd, & rcInner ); RECT rcTopLevel; GetClientRect( hwndSizable, & rcTopLevel ); MapWindowPoints( NULL, hwndSizable, (POINT *) & rcInner, 2 ); if( rcInner.right < rcTopLevel.right - GetSystemMetrics( SM_CXEDGE ) || rcInner.bottom < rcTopLevel.bottom - GetSystemMetrics( SM_CYEDGE ) ) { return FALSE; } } return TRUE; } // -------------------------------------------------------------------------- // // CSizeGrip::get_accName() // // -------------------------------------------------------------------------- STDMETHODIMP CSizeGrip::get_accName(VARIANT varChild, BSTR * pszName) { InitPv(pszName); // // Validate parameters // if (! ValidateChild(&varChild)) return(E_INVALIDARG); if( IsActive() ) { return HrCreateString( STR_SCROLLBAR_NAME + INDEX_SCROLLBAR_GRIP, pszName ); } else { return S_FALSE; } } // -------------------------------------------------------------------------- // // CSizeGrip::get_accDescription() // // -------------------------------------------------------------------------- STDMETHODIMP CSizeGrip::get_accDescription(VARIANT varChild, BSTR * pszDesc) { InitPv(pszDesc); if (! ValidateChild(&varChild)) return(E_INVALIDARG); if( IsActive() ) { return HrCreateString( STR_SCROLLBAR_DESCRIPTION + INDEX_SCROLLBAR_GRIP, pszDesc ); } else { return S_FALSE; } } // -------------------------------------------------------------------------- // // CSizeGrip::get_accRole() // // -------------------------------------------------------------------------- STDMETHODIMP CSizeGrip::get_accRole(VARIANT varChild, VARIANT * pvarRole) { InitPvar(pvarRole); // // Validate parameters // if (! ValidateChild(&varChild)) return(E_INVALIDARG); pvarRole->vt = VT_I4; pvarRole->lVal = ROLE_SYSTEM_GRIP; return(S_OK); } // -------------------------------------------------------------------------- // // CSizeGrip::get_accState() // // -------------------------------------------------------------------------- STDMETHODIMP CSizeGrip::get_accState(VARIANT varChild, VARIANT * pvarState) { WINDOWINFO wi; InitPvar(pvarState); // // Validate parameters // if (! ValidateChild(&varChild)) return(E_INVALIDARG); pvarState->vt = VT_I4; pvarState->lVal = 0; // // We are only visible if both scrollbars are present. // if (! MyGetWindowInfo(m_hwnd, &wi) || !(wi.dwStyle & WS_VSCROLL) || !(wi.dwStyle & WS_HSCROLL)) { pvarState->lVal |= STATE_SYSTEM_INVISIBLE | STATE_SYSTEM_UNAVAILABLE; } else if( ! IsActive() ) { pvarState->lVal |= STATE_SYSTEM_UNAVAILABLE; } return S_OK; } // -------------------------------------------------------------------------- // // CSizeGrip::accLocation() // // -------------------------------------------------------------------------- STDMETHODIMP CSizeGrip::accLocation(long* pxLeft, long* pyTop, long* pcxWidth, long* pcyHeight, VARIANT varChild) { WINDOWINFO wi; InitAccLocation(pxLeft, pyTop, pcxWidth, pcyHeight); // // Validate parameters // if (! ValidateChild(&varChild)) return(E_INVALIDARG); if (MyGetWindowInfo(m_hwnd, &wi) && (wi.dwStyle & WS_VSCROLL) && (wi.dwStyle & WS_HSCROLL)) { *pxLeft = wi.rcClient.right; *pyTop = wi.rcClient.bottom; *pcxWidth = GetSystemMetrics(SM_CXVSCROLL); *pcyHeight = GetSystemMetrics(SM_CYHSCROLL); } else return(S_FALSE); return(S_OK); } // -------------------------------------------------------------------------- // // CSizeGrip::accHitTest() // // -------------------------------------------------------------------------- STDMETHODIMP CSizeGrip::accHitTest(long xLeft, long yTop, VARIANT * pvarChild) { WINDOWINFO wi; InitPvar(pvarChild); if (MyGetWindowInfo(m_hwnd, &wi) && (wi.dwStyle & WS_VSCROLL) && (wi.dwStyle & WS_HSCROLL)) { if ((xLeft >= wi.rcClient.right) && (xLeft < wi.rcClient.right + GetSystemMetrics(SM_CXVSCROLL)) && (yTop >= wi.rcClient.bottom) && (yTop < wi.rcClient.bottom + GetSystemMetrics(SM_CYHSCROLL))) { pvarChild->vt = VT_I4; pvarChild->lVal = CHILDID_SELF; return(S_OK); } } return(S_FALSE); } // -------------------------------------------------------------------------- // // CSizeGrip::accNavigate() // // -------------------------------------------------------------------------- STDMETHODIMP CSizeGrip::accNavigate(long dwNavFlags, VARIANT varStart, VARIANT * pvarEnd) { InitPvar(pvarEnd); // // Validate parameters // if (! ValidateChild(&varStart) || ! ValidateNavDir(dwNavFlags, varStart.lVal)) return(E_INVALIDARG); if (dwNavFlags >= NAVDIR_FIRSTCHILD) return(S_FALSE); // // Navigation among peers only // return(GetParentToNavigate(OBJID_SIZEGRIP, m_hwnd, OBJID_WINDOW, dwNavFlags, pvarEnd)); } // -------------------------------------------------------------------------- // // CSizeGrip::Clone() // // -------------------------------------------------------------------------- STDMETHODIMP CSizeGrip::Clone(IEnumVARIANT** ppenum) { return(CreateSizeGripThing(m_hwnd, OBJID_SIZEGRIP, IID_IEnumVARIANT, (void**)ppenum)); } ///////////////////////////////////////////////////////////////////////////// // // SCROLL CONTROL (Can be bar or grip) // ///////////////////////////////////////////////////////////////////////////// // -------------------------------------------------------------------------- // // CreateScrollBarClient() // // Called from CClient creation // // -------------------------------------------------------------------------- HRESULT CreateScrollBarClient(HWND hwnd, long idChildCur, REFIID riid, void** ppvScroll) { CScrollCtl * pscroll; HRESULT hr; InitPv(ppvScroll); pscroll = new CScrollCtl(hwnd, idChildCur); if (!pscroll) return(E_OUTOFMEMORY); hr = pscroll->QueryInterface(riid, ppvScroll); if (!SUCCEEDED(hr)) delete pscroll; return(hr); } // -------------------------------------------------------------------------- // // CScrollCtl::CScrollCtl // // -------------------------------------------------------------------------- CScrollCtl::CScrollCtl(HWND hwnd, long idChildCur) : CClient( CLASS_ScrollBarClient ) { long lStyle; Initialize(hwnd, idChildCur); lStyle = GetWindowLong(hwnd, GWL_STYLE); if (lStyle & (SBS_SIZEBOX| SBS_SIZEGRIP)) { m_fGrip = TRUE; m_cChildren = 0; } else { m_fUseLabel = TRUE; m_cChildren = CCHILDREN_SCROLLBAR; if (lStyle & SBS_VERT) m_fVertical = TRUE; } } // -------------------------------------------------------------------------- // // CScrollCtl::get_accName() // // -------------------------------------------------------------------------- STDMETHODIMP CScrollCtl::get_accName(VARIANT varChild, BSTR* pszName) { InitPv(pszName); if (!ValidateChild(&varChild)) return(E_INVALIDARG); if (!varChild.lVal) return(CClient::get_accName(varChild, pszName)); Assert(!m_fGrip); return(HrCreateString(STR_SCROLLBAR_NAME + varChild.lVal + (m_fVertical ? 0 : INDEX_SCROLLBAR_HORIZONTAL), pszName)); } // -------------------------------------------------------------------------- // // CScrollCtl::get_accValue() // // -------------------------------------------------------------------------- STDMETHODIMP CScrollCtl::get_accValue(VARIANT varChild, BSTR* pszValue) { long lPos; InitPv(pszValue); if (!ValidateChild(&varChild)) return(E_INVALIDARG); if (varChild.lVal || m_fGrip) return(E_NOT_APPLICABLE); lPos = GetScrollPos(m_hwnd, SB_CTL); int Min, Max; GetScrollRange( m_hwnd, SB_CTL, & Min, & Max ); // work out a percent value... if( Min != Max ) lPos = ( ( lPos - Min ) * 100 ) / ( Max - Min ); else lPos = 0; // Prevent div-by-0 return(VarBstrFromI4(lPos, 0, 0, pszValue)); } // -------------------------------------------------------------------------- // // CScrollCtl::get_accDescription() // // -------------------------------------------------------------------------- STDMETHODIMP CScrollCtl::get_accDescription(VARIANT varChild, BSTR* pszDesc) { InitPv(pszDesc); if (!ValidateChild(&varChild)) return(E_INVALIDARG); if (!varChild.lVal) return(CClient::get_accDescription(varChild, pszDesc)); Assert(!m_fGrip); return(HrCreateString(STR_SCROLLBAR_DESCRIPTION + varChild.lVal + (m_fVertical ? 0 : INDEX_SCROLLBAR_HORIZONTAL), pszDesc)); } // -------------------------------------------------------------------------- // // CScrollCtl::get_accRole() // // -------------------------------------------------------------------------- STDMETHODIMP CScrollCtl::get_accRole(VARIANT varChild, VARIANT* pvarRole) { InitPvar(pvarRole); if (!ValidateChild(&varChild)) return(E_INVALIDARG); pvarRole->vt = VT_I4; switch (varChild.lVal) { case 0: if (m_fGrip) pvarRole->lVal = ROLE_SYSTEM_GRIP; else pvarRole->lVal = ROLE_SYSTEM_SCROLLBAR; break; case INDEX_SCROLLBAR_UP: case INDEX_SCROLLBAR_DOWN: case INDEX_SCROLLBAR_UPPAGE: case INDEX_SCROLLBAR_DOWNPAGE: pvarRole->lVal = ROLE_SYSTEM_PUSHBUTTON; break; case INDEX_SCROLLBAR_THUMB: pvarRole->lVal = ROLE_SYSTEM_INDICATOR; break; default: AssertStr( TEXT("Invalid ChildID for child of scroll bar") ); } return(S_OK); } // -------------------------------------------------------------------------- // // CScrollCtl::get_accState() // // -------------------------------------------------------------------------- STDMETHODIMP CScrollCtl::get_accState(VARIANT varChild, VARIANT* pvarState) { SCROLLBARINFO sbi; InitPvar(pvarState); if (!ValidateChild(&varChild)) return(E_INVALIDARG); if (!varChild.lVal) return(CClient::get_accState(varChild, pvarState)); Assert(!m_fGrip); pvarState->vt = VT_I4; pvarState->lVal = 0; if (!MyGetScrollBarInfo(m_hwnd, OBJID_CLIENT, &sbi)) { pvarState->lVal |= STATE_SYSTEM_INVISIBLE; return(S_OK); } FixUpScrollBarInfo(&sbi); pvarState->lVal |= sbi.rgstate[INDEX_SCROLLBAR_SELF]; pvarState->lVal |= sbi.rgstate[varChild.lVal]; if ((varChild.lVal == INDEX_SCROLLBAR_THUMB) && (MyGetFocus() == m_hwnd)) pvarState->lVal |= STATE_SYSTEM_FOCUSED; return(S_OK); } // -------------------------------------------------------------------------- // // CScrollCtl::get_accDefaultAction() // // -------------------------------------------------------------------------- STDMETHODIMP CScrollCtl::get_accDefaultAction(VARIANT varChild, BSTR* pszDefA) { InitPv(pszDefA); if (!ValidateChild(&varChild)) return(E_INVALIDARG); switch (varChild.lVal) { case INDEX_SCROLLBAR_UP: case INDEX_SCROLLBAR_UPPAGE: case INDEX_SCROLLBAR_DOWNPAGE: case INDEX_SCROLLBAR_DOWN: return(HrCreateString(STR_BUTTON_PUSH, pszDefA)); } return(E_NOT_APPLICABLE); } // -------------------------------------------------------------------------- // // CScrollCtl::accLocation() // // -------------------------------------------------------------------------- STDMETHODIMP CScrollCtl::accLocation(long* pxLeft, long* pyTop, long* pcxWidth, long* pcyHeight, VARIANT varChild) { SCROLLBARINFO sbi; int dxyButton; InitAccLocation(pxLeft, pyTop, pcxWidth, pcyHeight); if (!ValidateChild(&varChild)) return(E_INVALIDARG); if (!varChild.lVal) return(CClient::accLocation(pxLeft, pyTop, pcxWidth, pcyHeight, varChild)); Assert(!m_fGrip); if (!MyGetScrollBarInfo(m_hwnd, OBJID_CLIENT, &sbi)) return(S_FALSE); FixUpScrollBarInfo(&sbi); if (sbi.rgstate[varChild.lVal] & (STATE_SYSTEM_INVISIBLE | STATE_SYSTEM_OFFSCREEN)) return(S_FALSE); if (m_fVertical) dxyButton = sbi.rcScrollBar.right - sbi.rcScrollBar.left; else dxyButton = sbi.rcScrollBar.bottom - sbi.rcScrollBar.top; switch (varChild.lVal) { case INDEX_SCROLLBAR_UP: case INDEX_SCROLLBAR_DOWN: if (m_fVertical) { *pxLeft = sbi.rcScrollBar.left; *pcxWidth = dxyButton; *pcyHeight = sbi.dxyLineButton; if (varChild.lVal == INDEX_SCROLLBAR_UP) *pyTop = sbi.rcScrollBar.top; else *pyTop = sbi.rcScrollBar.bottom - *pcyHeight; } else { *pyTop = sbi.rcScrollBar.top; *pcyHeight = dxyButton; *pcxWidth = sbi.dxyLineButton; if (varChild.lVal == INDEX_SCROLLBAR_UP) *pxLeft = sbi.rcScrollBar.left; else *pxLeft = sbi.rcScrollBar.right - *pcxWidth; } break; case INDEX_SCROLLBAR_UPPAGE: if (m_fVertical) { *pxLeft = sbi.rcScrollBar.left; *pcxWidth = dxyButton; *pyTop = sbi.rcScrollBar.top + sbi.dxyLineButton; *pcyHeight = sbi.xyThumbTop - sbi.dxyLineButton; } else { *pyTop = sbi.rcScrollBar.top; *pcyHeight = dxyButton; *pxLeft = sbi.rcScrollBar.left + sbi.dxyLineButton; *pcxWidth = sbi.xyThumbTop - sbi.dxyLineButton; } break; case INDEX_SCROLLBAR_DOWNPAGE: if (m_fVertical) { *pxLeft = sbi.rcScrollBar.left; *pcxWidth = dxyButton; *pyTop = sbi.rcScrollBar.top + sbi.xyThumbBottom; *pcyHeight = (sbi.rcScrollBar.bottom - sbi.rcScrollBar.top) - sbi.xyThumbBottom - sbi.dxyLineButton; } else { *pyTop = sbi.rcScrollBar.top; *pcyHeight = dxyButton; *pxLeft = sbi.rcScrollBar.left + sbi.xyThumbBottom; *pcxWidth = (sbi.rcScrollBar.right - sbi.rcScrollBar.left) - sbi.xyThumbBottom - sbi.dxyLineButton; } break; case INDEX_SCROLLBAR_THUMB: if (m_fVertical) { *pxLeft = sbi.rcScrollBar.left; *pcxWidth = dxyButton; *pyTop = sbi.rcScrollBar.top + sbi.xyThumbTop; *pcyHeight = sbi.xyThumbBottom - sbi.xyThumbTop; } else { *pyTop = sbi.rcScrollBar.top; *pcyHeight = dxyButton; *pxLeft = sbi.rcScrollBar.left + sbi.xyThumbTop; *pcxWidth = sbi.xyThumbBottom - sbi.xyThumbTop; } break; default: AssertStr( TEXT("Invalid ChildID for child of scroll bar") ); } return(S_OK); } // -------------------------------------------------------------------------- // // CScrollCtl::accNavigate() // // -------------------------------------------------------------------------- STDMETHODIMP CScrollCtl::accNavigate(long dwNavDir, VARIANT varStart, VARIANT* pvarEnd) { long lEndUp = 0; SCROLLBARINFO sbi; InitPvar(pvarEnd); if (!ValidateChild(&varStart) || !ValidateNavDir(dwNavDir, varStart.lVal)) return(E_INVALIDARG); if (!varStart.lVal) { if (dwNavDir < NAVDIR_FIRSTCHILD) return(CClient::accNavigate(dwNavDir, varStart, pvarEnd)); if (!m_fGrip) return(S_FALSE); if (dwNavDir == NAVDIR_FIRSTCHILD) dwNavDir = NAVDIR_NEXT; else { dwNavDir = NAVDIR_PREVIOUS; varStart.lVal = m_cChildren + 1; } } Assert(!m_fGrip); if (!MyGetScrollBarInfo(m_hwnd, OBJID_CLIENT, &sbi)) return(S_FALSE); FixUpScrollBarInfo(&sbi); switch (dwNavDir) { case NAVDIR_NEXT: FindNext: lEndUp = varStart.lVal; while (++lEndUp <= INDEX_SCROLLBAR_MAC) { if (!(sbi.rgstate[lEndUp] & STATE_SYSTEM_INVISIBLE)) break; } if (lEndUp > INDEX_SCROLLBAR_MAC) lEndUp = 0; break; case NAVDIR_PREVIOUS: FindPrevious: lEndUp = varStart.lVal; while (--lEndUp >= INDEX_SCROLLBAR_MIC) { if (!(sbi.rgstate[lEndUp] & STATE_SYSTEM_INVISIBLE)) break; } if (lEndUp < INDEX_SCROLLBAR_MIC) lEndUp = 0; break; case NAVDIR_UP: lEndUp = 0; if (m_fVertical) goto FindPrevious; break; case NAVDIR_LEFT: lEndUp = 0; if (!m_fVertical) goto FindPrevious; break; case NAVDIR_DOWN: lEndUp = 0; if (m_fVertical) goto FindNext; break; case NAVDIR_RIGHT: lEndUp = 0; if (!m_fVertical) goto FindNext; break; default: AssertStr( TEXT("Invalid NavDir") ); } if (lEndUp != INDEX_SCROLLBAR_SELF) { pvarEnd->vt = VT_I4; pvarEnd->lVal = lEndUp; return(S_OK); } else return(S_FALSE); } // -------------------------------------------------------------------------- // // CScrollCtl::accHitTest() // // -------------------------------------------------------------------------- STDMETHODIMP CScrollCtl::accHitTest(long xLeft, long yTop, VARIANT* pvarHit) { HRESULT hr; SCROLLBARINFO sbi; int xyPtAxis; int xyScrollEnd; // // Is this in our client area at all? // hr = CClient::accHitTest(xLeft, yTop, pvarHit); // #11150, CWO, 1/27/97, Replaced !SUCCEEDED with !S_OK if ((hr != S_OK) || (pvarHit->vt != VT_I4) || (pvarHit->lVal != 0) || m_fGrip) return(hr); // // We only get here if this is a scrollbar control (not a grip) // if (!MyGetScrollBarInfo(m_hwnd, OBJID_CLIENT, &sbi)) return(S_OK); FixUpScrollBarInfo(&sbi); // // Convert to scrollbar coords. // if (m_fVertical) { xyPtAxis = yTop - sbi.rcScrollBar.top; xyScrollEnd = sbi.rcScrollBar.bottom - sbi.rcScrollBar.top; } else { xyPtAxis = xLeft - sbi.rcScrollBar.left; xyScrollEnd = sbi.rcScrollBar.right - sbi.rcScrollBar.left; } if (xyPtAxis < sbi.dxyLineButton) { Assert(!(sbi.rgstate[INDEX_SCROLLBAR_UP] & STATE_SYSTEM_INVISIBLE)); pvarHit->lVal = INDEX_SCROLLBAR_UP; } else if (xyPtAxis >= xyScrollEnd - sbi.dxyLineButton) { Assert(!(sbi.rgstate[INDEX_SCROLLBAR_DOWN] & STATE_SYSTEM_INVISIBLE)); pvarHit->lVal = INDEX_SCROLLBAR_DOWN; } else if (!(sbi.rgstate[INDEX_SCROLLBAR_SELF] & STATE_SYSTEM_UNAVAILABLE)) { if (xyPtAxis < sbi.xyThumbTop) { Assert(!(sbi.rgstate[INDEX_SCROLLBAR_UPPAGE] & STATE_SYSTEM_INVISIBLE)); pvarHit->lVal = INDEX_SCROLLBAR_UPPAGE; } else if (xyPtAxis >= sbi.xyThumbBottom) { Assert(!(sbi.rgstate[INDEX_SCROLLBAR_DOWNPAGE] & STATE_SYSTEM_INVISIBLE)); pvarHit->lVal = INDEX_SCROLLBAR_DOWNPAGE; } else { Assert(!(sbi.rgstate[INDEX_SCROLLBAR_THUMB] & STATE_SYSTEM_INVISIBLE)); pvarHit->lVal = INDEX_SCROLLBAR_THUMB; } } return(S_OK); } // -------------------------------------------------------------------------- // // CScrollCtl::accDoDefaultAction() // // -------------------------------------------------------------------------- STDMETHODIMP CScrollCtl::accDoDefaultAction(VARIANT varChild) { WPARAM wpAction = 0; SCROLLBARINFO sbi; if (!ValidateChild(&varChild)) return(E_INVALIDARG); if (!varChild.lVal || (varChild.lVal == INDEX_SCROLLBAR_THUMB)) return(E_NOT_APPLICABLE); Assert(!m_fGrip); if (!MyGetScrollBarInfo(m_hwnd, OBJID_CLIENT, &sbi) || (sbi.rgstate[INDEX_SCROLLBAR_SELF] & (STATE_SYSTEM_INVISIBLE | STATE_SYSTEM_UNAVAILABLE))) { return(S_FALSE); } FixUpScrollBarInfo(&sbi); if (sbi.rgstate[varChild.lVal] & STATE_SYSTEM_UNAVAILABLE) return(S_FALSE); switch (varChild.lVal) { case INDEX_SCROLLBAR_UP: wpAction = SB_LINEUP; break; case INDEX_SCROLLBAR_UPPAGE: wpAction = SB_PAGEUP; break; case INDEX_SCROLLBAR_DOWNPAGE: wpAction = SB_PAGEDOWN; break; case INDEX_SCROLLBAR_DOWN: wpAction = SB_LINEDOWN; break; default: AssertStr( TEXT("Invalid ChildID for child of scroll bar") ); } SendMessage(GetParent(m_hwnd), (m_fVertical ? WM_VSCROLL : WM_HSCROLL), wpAction, (LPARAM)m_hwnd); return(S_OK); } // -------------------------------------------------------------------------- // // CScrollCtl::put_accValue() // // -------------------------------------------------------------------------- STDMETHODIMP CScrollCtl::put_accValue(VARIANT varChild, BSTR szValue) { long lPos; HRESULT hr; if (!ValidateChild(&varChild)) return(E_INVALIDARG); if (varChild.lVal || m_fGrip) return(E_NOT_APPLICABLE); hr = VarI4FromStr(szValue, 0, 0, &lPos); if (!SUCCEEDED(hr)) return(hr); // Verify that we've got a valid percent value if( lPos < 0 || lPos > 100 ) return E_INVALIDARG; int Min, Max; GetScrollRange( m_hwnd, SB_CTL, & Min, & Max ); // work out value from percentage... lPos = Min + ( ( Max - Min ) * lPos ) / 100; SetScrollPos(m_hwnd, SB_CTL, lPos, TRUE); return(S_OK); }