1037 lines
30 KiB
C++
1037 lines
30 KiB
C++
// Copyright (c) 1996-1999 Microsoft Corporation
|
|
|
|
// --------------------------------------------------------------------------
|
|
//
|
|
// COMBO.CPP
|
|
//
|
|
// --------------------------------------------------------------------------
|
|
|
|
#include "oleacc_p.h"
|
|
#include <olectl.h>
|
|
#include "default.h"
|
|
#include "window.h"
|
|
#include "client.h"
|
|
#include "combo.h"
|
|
|
|
|
|
STDAPI_(LPTSTR) MyPathFindFileName(LPCTSTR pPath); // in listbox.cpp
|
|
|
|
HWND IsInComboEx(HWND hwnd); // in listbox.cpp
|
|
BOOL IsTridentControl( HWND hWnd, BOOL fCombo, BOOL fComboList ); // inlistbox.cpp
|
|
|
|
// Variation of HrGetWindowName which never uses a label
|
|
// (unlike the original HrGetWindowName which always uses a label if
|
|
// the text is an empty string - using label then is not approprite for
|
|
// combo value field.)
|
|
// Implemented near end of this file. Original is in client.cpp.
|
|
HRESULT HrGetWindowNameNoLabel(HWND hwnd, BSTR* pszName);
|
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
//
|
|
// CreateComboClient()
|
|
//
|
|
// --------------------------------------------------------------------------
|
|
HRESULT CreateComboClient(HWND hwnd, long idChildCur, REFIID riid, void** ppvCombo)
|
|
{
|
|
CCombo * pcombo;
|
|
HRESULT hr;
|
|
|
|
InitPv(ppvCombo);
|
|
|
|
pcombo = new CCombo(hwnd, idChildCur);
|
|
if (!pcombo)
|
|
return(E_OUTOFMEMORY);
|
|
|
|
hr = pcombo->QueryInterface(riid, ppvCombo);
|
|
if (!SUCCEEDED(hr))
|
|
delete pcombo;
|
|
|
|
return(hr);
|
|
}
|
|
|
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
//
|
|
// CCombo::CCombo()
|
|
//
|
|
// --------------------------------------------------------------------------
|
|
CCombo::CCombo(HWND hwnd, long idChildCur)
|
|
: CClient( CLASS_ComboClient )
|
|
{
|
|
LONG lStyle;
|
|
|
|
Initialize(hwnd, idChildCur);
|
|
|
|
m_cChildren = CCHILDREN_COMBOBOX;
|
|
m_fUseLabel = TRUE;
|
|
|
|
// If in a ComboEx, use its style, instead of our own.
|
|
// Important, because the real Combo will be DROPDOWNLIST (doesn't
|
|
// have edit) when the ComboEx is DROPDOWN (has edit) - the ComboEx
|
|
// supplies an EDIT, but the Combo doesn't know about it.
|
|
HWND hWndEx = IsInComboEx(hwnd);
|
|
if (hWndEx)
|
|
{
|
|
lStyle = GetWindowLong(hWndEx, GWL_STYLE);
|
|
}
|
|
else
|
|
{
|
|
lStyle = GetWindowLong(hwnd, GWL_STYLE);
|
|
}
|
|
|
|
switch (lStyle & CBS_DROPDOWNLIST)
|
|
{
|
|
case 0:
|
|
m_cChildren = 0; // Window not valid!
|
|
break;
|
|
|
|
case CBS_SIMPLE:
|
|
m_fHasButton = FALSE;
|
|
m_fHasEdit = TRUE;
|
|
break;
|
|
|
|
case CBS_DROPDOWN:
|
|
m_fHasButton = TRUE;
|
|
m_fHasEdit = TRUE;
|
|
break;
|
|
|
|
case CBS_DROPDOWNLIST:
|
|
m_fHasButton = TRUE;
|
|
m_fHasEdit = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
//
|
|
// CCombo::get_accChildCount()
|
|
//
|
|
// Since this is a known constant, hand directly to CAccessible. No
|
|
// need to count up fixed + window children.
|
|
//
|
|
// --------------------------------------------------------------------------
|
|
STDMETHODIMP CCombo::get_accChildCount(long* pcCount)
|
|
{
|
|
return(CAccessible::get_accChildCount(pcCount));
|
|
}
|
|
|
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
//
|
|
// CCombo::get_accChild()
|
|
//
|
|
// Succeeds for listbox, and for item if editable. This is because we
|
|
// manipulate our children by ID, since they are known.
|
|
//
|
|
// --------------------------------------------------------------------------
|
|
STDMETHODIMP CCombo::get_accChild(VARIANT varChild, IDispatch** ppdisp)
|
|
{
|
|
COMBOBOXINFO cbi;
|
|
HWND hwndChild;
|
|
|
|
InitPv(ppdisp);
|
|
|
|
//
|
|
// Validate
|
|
//
|
|
if (!ValidateChild(&varChild))
|
|
return(E_INVALIDARG);
|
|
|
|
if (!MyGetComboBoxInfo(m_hwnd, &cbi))
|
|
return(S_FALSE);
|
|
|
|
hwndChild = NULL;
|
|
|
|
switch (varChild.lVal)
|
|
{
|
|
case INDEX_COMBOBOX:
|
|
return(E_INVALIDARG);
|
|
|
|
case INDEX_COMBOBOX_ITEM:
|
|
hwndChild = cbi.hwndItem;
|
|
break;
|
|
|
|
case INDEX_COMBOBOX_LIST:
|
|
hwndChild = cbi.hwndList;
|
|
break;
|
|
}
|
|
|
|
if (!hwndChild)
|
|
return(S_FALSE);
|
|
else
|
|
return(AccessibleObjectFromWindow(hwndChild, OBJID_WINDOW, IID_IDispatch,
|
|
(void**)ppdisp));
|
|
}
|
|
|
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
//
|
|
// CCombo::get_accName()
|
|
//
|
|
// --------------------------------------------------------------------------
|
|
STDMETHODIMP CCombo::get_accName(VARIANT varChild, BSTR* pszName)
|
|
{
|
|
COMBOBOXINFO cbi;
|
|
|
|
InitPv(pszName);
|
|
|
|
//
|
|
// Validate
|
|
//
|
|
if (!ValidateChild(&varChild))
|
|
return(E_INVALIDARG);
|
|
|
|
//
|
|
// The name of the combobox, the edit inside of it, and the dropdown
|
|
// are all the same. The name of the button is Drop down/Pop up
|
|
//
|
|
if (varChild.lVal != INDEX_COMBOBOX_BUTTON)
|
|
{
|
|
HWND hwndComboEx = IsInComboEx(m_hwnd);
|
|
if( ! hwndComboEx )
|
|
{
|
|
// combo/edit/dropdown all use the name of the client itself,
|
|
// so call through with childid of CHILDID_SELF...
|
|
varChild.lVal = CHILDID_SELF;
|
|
return(CClient::get_accName(varChild, pszName));
|
|
}
|
|
else
|
|
{
|
|
// Special case if we're in a comboex - since we're one level deep,
|
|
// reach up to parent for its name...
|
|
IAccessible * pAcc;
|
|
HRESULT hr = AccessibleObjectFromWindow( hwndComboEx, OBJID_CLIENT, IID_IAccessible, (void **) & pAcc );
|
|
if( hr != S_OK )
|
|
return hr;
|
|
VARIANT varChild;
|
|
varChild.vt = VT_I4;
|
|
varChild.lVal = CHILDID_SELF;
|
|
hr = pAcc->get_accName( varChild, pszName );
|
|
pAcc->Release();
|
|
return hr;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (! MyGetComboBoxInfo(m_hwnd, &cbi))
|
|
return(S_FALSE);
|
|
|
|
if (IsWindowVisible(cbi.hwndList))
|
|
return (HrCreateString(STR_DROPDOWN_HIDE,pszName));
|
|
else
|
|
return(HrCreateString(STR_DROPDOWN_SHOW, pszName));
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
//
|
|
// CCombo::get_accValue()
|
|
//
|
|
// The value of the combobox and the combobox item is the current text of
|
|
// the thing.
|
|
//
|
|
// --------------------------------------------------------------------------
|
|
STDMETHODIMP CCombo::get_accValue(VARIANT varChild, BSTR* pszValue)
|
|
{
|
|
InitPv(pszValue);
|
|
|
|
//
|
|
// Validate
|
|
//
|
|
if (!ValidateChild(&varChild))
|
|
return(E_INVALIDARG);
|
|
|
|
switch (varChild.lVal)
|
|
{
|
|
case INDEX_COMBOBOX:
|
|
case INDEX_COMBOBOX_ITEM:
|
|
{
|
|
// HACK ALERT
|
|
// The IE4 combobox is a superclassed standard combobox,
|
|
// but if I use SendMessageA (which I do) to get the
|
|
// text, I get back garbage. They keep everything
|
|
// in Unicode. It is a bug in the Trident MSHTML
|
|
// implementation, but even if they fixed it and gave me
|
|
// back an ANSI string, I wouldn't know what code page to
|
|
// use to convert the ANSI string to Unicode - web pages
|
|
// can be in a different language than the one the user's
|
|
// computer uses! Since they already have everything in
|
|
// Unicode, we decided on a private message that will fill
|
|
// in the Unicode string, and I use that just like I would
|
|
// normally use WM_GETTEXT.
|
|
// I was going to base this on the classname of the listbox
|
|
// window, which is "Internet Explorer_TridentCmboBx", but
|
|
// the list part of a combo doesn't have a special class
|
|
// name, so instead I am going to base the special case on
|
|
// the file name of the module that owns the window.
|
|
|
|
// GetWindowModuleFileName(m_hwnd,szModuleName,ARRAYSIZE(szModuleName));
|
|
// lpszModuleName = MyPathFindFileName (szModuleName);
|
|
// if (0 == lstrcmp(lpszModuleName,TEXT("MSHTML.DLL")))
|
|
|
|
// Update: (BrendanM)
|
|
// GetWindowModuleFilename is broken on Win2k...
|
|
// IsTridentControl goes back to using classnames, and knows
|
|
// how to cope with ComboLBoxes...
|
|
|
|
if( IsTridentControl( m_hwnd, TRUE, FALSE ) )
|
|
{
|
|
OLECHAR* lpszUnicodeText = NULL;
|
|
OLECHAR* lpszLocalText = NULL;
|
|
HANDLE hProcess;
|
|
UINT cch;
|
|
|
|
cch = SendMessageINT(m_hwnd, OCM__BASE + WM_GETTEXTLENGTH, 0, 0);
|
|
|
|
lpszUnicodeText = (OLECHAR *)SharedAlloc((cch+1)*sizeof(OLECHAR),
|
|
m_hwnd,
|
|
&hProcess);
|
|
lpszLocalText = (OLECHAR*)LocalAlloc(LPTR,(cch+1)*sizeof(OLECHAR));
|
|
|
|
if (!lpszUnicodeText || !lpszLocalText)
|
|
return(E_OUTOFMEMORY);
|
|
|
|
cch = SendMessageINT(m_hwnd, OCM__BASE + WM_GETTEXT, cch, (LPARAM)lpszUnicodeText);
|
|
SharedRead (lpszUnicodeText,lpszLocalText,(cch+1)*sizeof(OLECHAR),hProcess);
|
|
|
|
*pszValue = SysAllocString(lpszLocalText);
|
|
|
|
SharedFree(lpszUnicodeText,hProcess);
|
|
LocalFree(lpszLocalText);
|
|
return (S_OK);
|
|
}
|
|
else
|
|
{
|
|
// If we're a comboex, ask the comboex instead of us...
|
|
HWND hwnd;
|
|
if( ! ( hwnd = IsInComboEx( m_hwnd ) ) )
|
|
hwnd = m_hwnd;
|
|
|
|
// uh-oh - don't want to use HrGetWindowName, since
|
|
// it will look for a label (even though we specify FALSE)
|
|
// if we are in a dialog and out text is "".
|
|
if( ! IsComboEx( hwnd ) )
|
|
{
|
|
// Regular combo - gettext works for both edit and droplist...
|
|
return HrGetWindowNameNoLabel( hwnd, pszValue);
|
|
}
|
|
else
|
|
{
|
|
// comboex - special case for droplist...
|
|
DWORD dwStyle = GetWindowLong( hwnd, GWL_STYLE );
|
|
if( ! ( dwStyle & CBS_DROPDOWNLIST ) )
|
|
{
|
|
// Not a droplist - can use normal technique...
|
|
return HrGetWindowNameNoLabel( hwnd, pszValue);
|
|
}
|
|
else
|
|
{
|
|
// Get the selected item, and get its text...
|
|
int iSel = SendMessageINT( hwnd, CB_GETCURSEL, 0, 0 );
|
|
if( iSel == CB_ERR )
|
|
return S_FALSE; // no item selected
|
|
|
|
int cch = SendMessageINT( hwnd, CB_GETLBTEXTLEN, iSel, 0);
|
|
|
|
// Some apps do not handle CB_GETTEXTLEN correctly, and
|
|
// always return a small number, like 2.
|
|
if (cch < 1024)
|
|
cch = 1024;
|
|
|
|
LPTSTR lpszText;
|
|
lpszText = (LPTSTR)LocalAlloc(LPTR, (cch+1)*sizeof(TCHAR));
|
|
if (!lpszText)
|
|
return(E_OUTOFMEMORY);
|
|
|
|
SendMessage( hwnd, CB_GETLBTEXT, iSel, (LPARAM)lpszText);
|
|
*pszValue = TCharSysAllocString(lpszText);
|
|
|
|
LocalFree((HANDLE)lpszText);
|
|
|
|
return S_OK;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return(E_NOT_APPLICABLE);
|
|
}
|
|
|
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
//
|
|
// CCombo::get_accRole()
|
|
//
|
|
// --------------------------------------------------------------------------
|
|
STDMETHODIMP CCombo::get_accRole(VARIANT varChild, VARIANT* pvarRole)
|
|
{
|
|
InitPvar(pvarRole);
|
|
|
|
//
|
|
// Validate
|
|
//
|
|
if (!ValidateChild(&varChild))
|
|
return(E_INVALIDARG);
|
|
|
|
pvarRole->vt = VT_I4;
|
|
|
|
switch (varChild.lVal)
|
|
{
|
|
case INDEX_COMBOBOX:
|
|
pvarRole->lVal = ROLE_SYSTEM_COMBOBOX;
|
|
break;
|
|
|
|
case INDEX_COMBOBOX_ITEM:
|
|
if (m_fHasEdit)
|
|
pvarRole->lVal = ROLE_SYSTEM_TEXT;
|
|
else
|
|
pvarRole->lVal = ROLE_SYSTEM_STATICTEXT;
|
|
break;
|
|
|
|
case INDEX_COMBOBOX_BUTTON:
|
|
pvarRole->lVal = ROLE_SYSTEM_PUSHBUTTON;
|
|
break;
|
|
|
|
case INDEX_COMBOBOX_LIST:
|
|
pvarRole->lVal = ROLE_SYSTEM_LIST;
|
|
break;
|
|
|
|
default:
|
|
AssertStr( TEXT("Invalid ChildID for child of combo box") );
|
|
}
|
|
|
|
return(S_OK);
|
|
}
|
|
|
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
//
|
|
// CCombo::get_accState()
|
|
//
|
|
// The state of the combo is the state of the client.
|
|
// The state of the item is the state of the edit field if present;
|
|
// read-only if static.
|
|
// The state of the button is pushed and/or hottracked.
|
|
// The state of the dropdown is floating (if not simple) and the state
|
|
// of the list window.
|
|
//
|
|
// --------------------------------------------------------------------------
|
|
STDMETHODIMP CCombo::get_accState(VARIANT varChild, VARIANT* pvarState)
|
|
{
|
|
COMBOBOXINFO cbi;
|
|
VARIANT var;
|
|
IAccessible* poleacc;
|
|
HRESULT hr;
|
|
HWND hwndActive;
|
|
|
|
InitPvar(pvarState);
|
|
|
|
//
|
|
// Validate
|
|
//
|
|
if (!ValidateChild(&varChild))
|
|
return(E_INVALIDARG);
|
|
|
|
if (!MyGetComboBoxInfo(m_hwnd, &cbi))
|
|
{
|
|
pvarState->vt = VT_I4;
|
|
pvarState->lVal = STATE_SYSTEM_INVISIBLE;
|
|
return(S_FALSE);
|
|
}
|
|
|
|
switch (varChild.lVal)
|
|
{
|
|
case INDEX_COMBOBOX:
|
|
return(CClient::get_accState(varChild, pvarState));
|
|
|
|
case INDEX_COMBOBOX_BUTTON:
|
|
pvarState->vt = VT_I4;
|
|
pvarState->lVal = cbi.stateButton;
|
|
break;
|
|
|
|
case INDEX_COMBOBOX_ITEM:
|
|
if (!cbi.hwndItem)
|
|
{
|
|
pvarState->vt = VT_I4;
|
|
pvarState->lVal = 0;
|
|
hwndActive = GetForegroundWindow();
|
|
if (hwndActive == MyGetAncestor(m_hwnd, GA_ROOT))
|
|
pvarState->lVal |= STATE_SYSTEM_FOCUSABLE;
|
|
if (MyGetFocus() == m_hwnd)
|
|
pvarState->lVal |= STATE_SYSTEM_FOCUSED;
|
|
}
|
|
else
|
|
{
|
|
// Forward state to edit field.
|
|
VariantInit(&var);
|
|
hr = GetWindowObject(cbi.hwndItem, &var);
|
|
goto AskTheChild;
|
|
}
|
|
break;
|
|
|
|
case INDEX_COMBOBOX_LIST:
|
|
// Forward state to listbox
|
|
VariantInit(&var);
|
|
hr = GetWindowObject(cbi.hwndList, &var);
|
|
|
|
AskTheChild:
|
|
if (!SUCCEEDED(hr))
|
|
return(hr);
|
|
|
|
Assert(var.vt == VT_DISPATCH);
|
|
|
|
//
|
|
// Get the child acc object
|
|
//
|
|
poleacc = NULL;
|
|
hr = var.pdispVal->QueryInterface(IID_IAccessible,
|
|
(void**)&poleacc);
|
|
var.pdispVal->Release();
|
|
|
|
if (!SUCCEEDED(hr))
|
|
return(hr);
|
|
|
|
//
|
|
// Ask the child its state
|
|
//
|
|
VariantInit(&var);
|
|
hr = poleacc->get_accState(var, pvarState);
|
|
poleacc->Release();
|
|
if (!SUCCEEDED(hr))
|
|
return(hr);
|
|
break;
|
|
}
|
|
|
|
return(S_OK);
|
|
}
|
|
|
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
//
|
|
// CCombo::get_accKeyboardShortcut()
|
|
//
|
|
// --------------------------------------------------------------------------
|
|
STDMETHODIMP CCombo::get_accKeyboardShortcut(VARIANT varChild, BSTR* pszShortcut)
|
|
{
|
|
TCHAR szKey[20];
|
|
|
|
//
|
|
// Shortcut for combo is label's hotkey.
|
|
// Shortcut for dropdown (if button) is Alt+F4.
|
|
// CWO, 12/5/96, Alt+F4? F4, by itself brings down the combo box,
|
|
// but we add "Alt" to the string. Bad! Now use
|
|
// down arrow and add Alt to it via HrMakeShortcut()
|
|
// As documented in the UI style guide.
|
|
//
|
|
// As always, shortcuts only apply if the container has "focus". In other
|
|
// words, the hotkey for the combo does nothing if the parent dialog
|
|
// isn't active. And the hotkey for the dropdown does nothing if the
|
|
// combobox/edit isn't focused.
|
|
//
|
|
|
|
InitPv(pszShortcut);
|
|
|
|
//
|
|
// Validate parameters
|
|
//
|
|
if (!ValidateChild(&varChild))
|
|
return(E_INVALIDARG);
|
|
|
|
if (varChild.lVal == INDEX_COMBOBOX || varChild.lVal == INDEX_COMBOBOX_ITEM)
|
|
{
|
|
HWND hwndComboEx = IsInComboEx(m_hwnd);
|
|
if( ! hwndComboEx )
|
|
{
|
|
// combo/edit/dropdown all use the name of the client itself,
|
|
// so call through with childid of CHILDID_SELF...
|
|
varChild.lVal = CHILDID_SELF;
|
|
return(CClient::get_accKeyboardShortcut(varChild, pszShortcut));
|
|
}
|
|
else
|
|
{
|
|
// Special case if we're in a comboex - since we're one level deep,
|
|
// reach up to parent for its name...
|
|
IAccessible * pAcc;
|
|
HRESULT hr = AccessibleObjectFromWindow( hwndComboEx, OBJID_CLIENT, IID_IAccessible, (void **) & pAcc );
|
|
if( hr != S_OK )
|
|
return hr;
|
|
VARIANT varChild;
|
|
varChild.vt = VT_I4;
|
|
varChild.lVal = CHILDID_SELF;
|
|
hr = pAcc->get_accKeyboardShortcut( varChild, pszShortcut );
|
|
pAcc->Release();
|
|
return hr;
|
|
}
|
|
}
|
|
else if (varChild.lVal == INDEX_COMBOBOX_BUTTON)
|
|
{
|
|
if (m_fHasButton)
|
|
{
|
|
LoadString(hinstResDll, STR_COMBOBOX_LIST_SHORTCUT, szKey,
|
|
ARRAYSIZE(szKey));
|
|
return(HrMakeShortcut(szKey, pszShortcut));
|
|
}
|
|
}
|
|
|
|
return(E_NOT_APPLICABLE);
|
|
}
|
|
|
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
//
|
|
// CCombo::get_accDefaultAction()
|
|
//
|
|
// --------------------------------------------------------------------------
|
|
STDMETHODIMP CCombo::get_accDefaultAction(VARIANT varChild, BSTR* pszDef)
|
|
{
|
|
COMBOBOXINFO cbi;
|
|
|
|
InitPv(pszDef);
|
|
|
|
//
|
|
// Validate parameters
|
|
//
|
|
if (!ValidateChild(&varChild))
|
|
return(E_INVALIDARG);
|
|
|
|
if ((varChild.lVal != INDEX_COMBOBOX_BUTTON) || !m_fHasButton)
|
|
return(E_NOT_APPLICABLE);
|
|
|
|
//
|
|
// Default action of button is to press it. If pressed already, pressing
|
|
// it will pop dropdown back up. If not pressed, pressing it will pop
|
|
// dropdown down.
|
|
//
|
|
if (! MyGetComboBoxInfo(m_hwnd, &cbi))
|
|
return(S_FALSE);
|
|
|
|
if (IsWindowVisible(cbi.hwndList))
|
|
return(HrCreateString(STR_DROPDOWN_HIDE, pszDef));
|
|
else
|
|
return(HrCreateString(STR_DROPDOWN_SHOW, pszDef));
|
|
}
|
|
|
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
//
|
|
// CCombo::accLocation()
|
|
//
|
|
// --------------------------------------------------------------------------
|
|
STDMETHODIMP CCombo::accLocation(long* pxLeft, long* pyTop, long* pcxWidth,
|
|
long* pcyHeight, VARIANT varChild)
|
|
{
|
|
COMBOBOXINFO cbi;
|
|
|
|
InitAccLocation(pxLeft, pyTop, pcxWidth, pcyHeight);
|
|
|
|
//
|
|
// Validate
|
|
//
|
|
if (!ValidateChild(&varChild))
|
|
return(E_INVALIDARG);
|
|
|
|
if (! varChild.lVal)
|
|
return(CClient::accLocation(pxLeft, pyTop, pcxWidth, pcyHeight, varChild));
|
|
|
|
if (! MyGetComboBoxInfo(m_hwnd, &cbi))
|
|
return(S_FALSE);
|
|
|
|
switch (varChild.lVal)
|
|
{
|
|
case INDEX_COMBOBOX_BUTTON:
|
|
if (!m_fHasButton)
|
|
return(S_FALSE);
|
|
|
|
*pcxWidth = cbi.rcButton.right - cbi.rcButton.left;
|
|
*pcyHeight = cbi.rcButton.bottom - cbi.rcButton.top;
|
|
|
|
ClientToScreen(m_hwnd, (LPPOINT)&cbi.rcButton);
|
|
|
|
*pxLeft = cbi.rcButton.left;
|
|
*pyTop = cbi.rcButton.top;
|
|
break;
|
|
|
|
case INDEX_COMBOBOX_ITEM:
|
|
*pcxWidth = cbi.rcItem.right - cbi.rcItem.left;
|
|
*pcyHeight = cbi.rcItem.bottom - cbi.rcItem.top;
|
|
|
|
ClientToScreen(m_hwnd, (LPPOINT)&cbi.rcItem);
|
|
|
|
*pxLeft = cbi.rcItem.left;
|
|
*pyTop = cbi.rcItem.top;
|
|
break;
|
|
|
|
case INDEX_COMBOBOX_LIST:
|
|
MyGetRect(cbi.hwndList, &cbi.rcItem, TRUE);
|
|
*pxLeft = cbi.rcItem.left;
|
|
*pyTop = cbi.rcItem.top;
|
|
*pcxWidth = cbi.rcItem.right - cbi.rcItem.left;
|
|
*pcyHeight = cbi.rcItem.bottom - cbi.rcItem.top;
|
|
break;
|
|
|
|
default:
|
|
AssertStr( TEXT("Invalid ChildID for child of combo box") );
|
|
}
|
|
|
|
return(S_OK);
|
|
}
|
|
|
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
//
|
|
// CCombo::accNavigate()
|
|
//
|
|
// Navigates among children of combobox.
|
|
//
|
|
// --------------------------------------------------------------------------
|
|
STDMETHODIMP CCombo::accNavigate(long dwNav, VARIANT varStart, VARIANT* pvarEnd)
|
|
{
|
|
COMBOBOXINFO cbi;
|
|
long lEnd;
|
|
|
|
InitPvar(pvarEnd);
|
|
|
|
//
|
|
// Validate parameters
|
|
//
|
|
if ((!ValidateChild(&varStart) && !ValidateHwnd(&varStart)) ||
|
|
!ValidateNavDir(dwNav, varStart.lVal))
|
|
return(E_INVALIDARG);
|
|
|
|
if (! MyGetComboBoxInfo(m_hwnd, &cbi))
|
|
return(S_FALSE);
|
|
|
|
lEnd = 0;
|
|
|
|
if (dwNav == NAVDIR_FIRSTCHILD)
|
|
{
|
|
lEnd = INDEX_COMBOBOX_ITEM;
|
|
goto GetTheChild;
|
|
}
|
|
else if (dwNav == NAVDIR_LASTCHILD)
|
|
{
|
|
dwNav = NAVDIR_PREVIOUS;
|
|
varStart.lVal = m_cChildren + 1;
|
|
}
|
|
else if (!varStart.lVal)
|
|
return(CClient::accNavigate(dwNav, varStart, pvarEnd));
|
|
|
|
//
|
|
// Map HWNDID to normal ID. We work with both (it is easier).
|
|
//
|
|
if (IsHWNDID(varStart.lVal))
|
|
{
|
|
HWND hWndTemp = HwndFromHWNDID(m_hwnd, varStart.lVal);
|
|
|
|
if (hWndTemp == cbi.hwndItem)
|
|
varStart.lVal = INDEX_COMBOBOX_ITEM;
|
|
else if (hWndTemp == cbi.hwndList)
|
|
varStart.lVal = INDEX_COMBOBOX_LIST;
|
|
else
|
|
// Don't know what the heck this is
|
|
return(S_FALSE);
|
|
}
|
|
|
|
switch (dwNav)
|
|
{
|
|
case NAVDIR_UP:
|
|
if (varStart.lVal == INDEX_COMBOBOX_LIST)
|
|
lEnd = INDEX_COMBOBOX_ITEM;
|
|
break;
|
|
|
|
case NAVDIR_DOWN:
|
|
if ((varStart.lVal != INDEX_COMBOBOX_LIST) &&
|
|
IsWindowVisible(cbi.hwndList))
|
|
{
|
|
lEnd = INDEX_COMBOBOX_LIST;
|
|
}
|
|
break;
|
|
|
|
case NAVDIR_LEFT:
|
|
if (varStart.lVal == INDEX_COMBOBOX_BUTTON)
|
|
lEnd = INDEX_COMBOBOX_ITEM;
|
|
break;
|
|
|
|
case NAVDIR_RIGHT:
|
|
if ((varStart.lVal == INDEX_COMBOBOX_ITEM) &&
|
|
!(cbi.stateButton & STATE_SYSTEM_INVISIBLE))
|
|
{
|
|
lEnd = INDEX_COMBOBOX_BUTTON;
|
|
}
|
|
break;
|
|
|
|
case NAVDIR_PREVIOUS:
|
|
lEnd = varStart.lVal - 1;
|
|
if ((lEnd == INDEX_COMBOBOX_LIST) && !IsWindowVisible(cbi.hwndList))
|
|
--lEnd;
|
|
if ((lEnd == INDEX_COMBOBOX_BUTTON) && !m_fHasButton)
|
|
--lEnd;
|
|
break;
|
|
|
|
case NAVDIR_NEXT:
|
|
lEnd = varStart.lVal + 1;
|
|
if (lEnd > m_cChildren)
|
|
lEnd = 0;
|
|
else
|
|
{
|
|
if ((lEnd == INDEX_COMBOBOX_BUTTON) && !m_fHasButton)
|
|
lEnd++;
|
|
if ((lEnd == INDEX_COMBOBOX_LIST) && !IsWindowVisible(cbi.hwndList))
|
|
lEnd = 0;
|
|
}
|
|
break;
|
|
}
|
|
|
|
GetTheChild:
|
|
if (lEnd)
|
|
{
|
|
if ((lEnd == INDEX_COMBOBOX_ITEM) && cbi.hwndItem)
|
|
return(GetWindowObject(cbi.hwndItem, pvarEnd));
|
|
else if ((lEnd == INDEX_COMBOBOX_LIST) && cbi.hwndList)
|
|
return(GetWindowObject(cbi.hwndList, pvarEnd));
|
|
|
|
pvarEnd->vt = VT_I4;
|
|
pvarEnd->lVal = lEnd;
|
|
return(S_OK);
|
|
}
|
|
|
|
return(S_FALSE);
|
|
}
|
|
|
|
|
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
//
|
|
// CCombo::accHitTest()
|
|
//
|
|
// --------------------------------------------------------------------------
|
|
STDMETHODIMP CCombo::accHitTest(long x, long y, VARIANT* pvarEnd)
|
|
{
|
|
POINT pt;
|
|
COMBOBOXINFO cbi;
|
|
RECT rc;
|
|
|
|
InitPvar(pvarEnd);
|
|
|
|
if (!MyGetComboBoxInfo(m_hwnd, &cbi))
|
|
return(S_FALSE);
|
|
|
|
pt.x = x;
|
|
pt.y = y;
|
|
|
|
// Check list first, in case it is a dropdown.
|
|
MyGetRect(cbi.hwndList, &rc, TRUE);
|
|
if (PtInRect(&rc, pt) && IsWindowVisible(cbi.hwndList))
|
|
return(GetWindowObject(cbi.hwndList, pvarEnd));
|
|
else
|
|
{
|
|
ScreenToClient(m_hwnd, &pt);
|
|
MyGetRect(m_hwnd, &rc, FALSE);
|
|
if (! PtInRect(&rc, pt))
|
|
return(S_FALSE);
|
|
|
|
if (PtInRect(&cbi.rcButton, pt))
|
|
{
|
|
pvarEnd->vt = VT_I4;
|
|
pvarEnd->lVal = INDEX_COMBOBOX_BUTTON;
|
|
}
|
|
else if (PtInRect(&cbi.rcItem, pt))
|
|
{
|
|
if (m_fHasEdit)
|
|
return(GetWindowObject(cbi.hwndItem, pvarEnd));
|
|
else
|
|
{
|
|
pvarEnd->vt = VT_I4;
|
|
pvarEnd->lVal = INDEX_COMBOBOX_ITEM;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pvarEnd->vt = VT_I4;
|
|
pvarEnd->lVal = 0;
|
|
}
|
|
}
|
|
|
|
return(S_OK);
|
|
}
|
|
|
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
//
|
|
// CCombo::accDoDefaultAction()
|
|
//
|
|
// The default action of the button is to toggle the dropdown list up or
|
|
// down. Note that we don't just pop up the listbox, we pop it up AND
|
|
// accept amu changes in the selected item..
|
|
//
|
|
// --------------------------------------------------------------------------
|
|
STDMETHODIMP CCombo::accDoDefaultAction(VARIANT varChild)
|
|
{
|
|
COMBOBOXINFO cbi;
|
|
|
|
//
|
|
// Validate
|
|
//
|
|
if (!ValidateChild(&varChild))
|
|
return(E_INVALIDARG);
|
|
|
|
if ((varChild.lVal == INDEX_COMBOBOX_BUTTON) && m_fHasButton)
|
|
{
|
|
if (!MyGetComboBoxInfo(m_hwnd, &cbi))
|
|
return(S_FALSE);
|
|
|
|
if (IsWindowVisible(cbi.hwndList))
|
|
PostMessage(m_hwnd, WM_KEYDOWN, VK_RETURN, 0);
|
|
else
|
|
PostMessage(m_hwnd, CB_SHOWDROPDOWN, TRUE, 0);
|
|
|
|
return(S_OK);
|
|
}
|
|
|
|
return(E_NOT_APPLICABLE);
|
|
}
|
|
|
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
//
|
|
// CCombo::put_accValue()
|
|
//
|
|
// This works if (1) the combo is editable or (2) the text matches a list
|
|
// item exactly.
|
|
//
|
|
// --------------------------------------------------------------------------
|
|
STDMETHODIMP CCombo::put_accValue(VARIANT varChild, BSTR szValue)
|
|
{
|
|
//
|
|
// Validate
|
|
//
|
|
if (!ValidateChild(&varChild))
|
|
return(E_INVALIDARG);
|
|
|
|
LPTSTR lpszValue;
|
|
|
|
#ifdef UNICODE
|
|
|
|
// On unicode, no conversion needed...
|
|
lpszValue = szValue;
|
|
|
|
#else
|
|
|
|
// On non-unicode, need to convert to multibyte...
|
|
|
|
// We may be dealing with DBCS chars - assume worst case where every character is
|
|
// two bytes...
|
|
UINT cchValue = SysStringLen(szValue) * 2;
|
|
lpszValue = (LPTSTR)LocalAlloc(LPTR, (cchValue+1)*sizeof(TCHAR));
|
|
if (!lpszValue)
|
|
return(E_OUTOFMEMORY);
|
|
|
|
WideCharToMultiByte(CP_ACP, 0, szValue, -1, lpszValue, cchValue+1, NULL,
|
|
NULL);
|
|
|
|
#endif
|
|
|
|
//
|
|
// If this is editable, set the text directly. If this is a dropdown
|
|
// list, select the exact match for this text.
|
|
//
|
|
if (m_fHasEdit)
|
|
SendMessage(m_hwnd, WM_SETTEXT, 0, (LPARAM)lpszValue);
|
|
else
|
|
SendMessage(m_hwnd, CB_SELECTSTRING, (UINT)-1, (LPARAM)lpszValue);
|
|
|
|
#ifndef UNICODE
|
|
// On non-unicode, free the temp string we alloc'd above...
|
|
LocalFree((HANDLE)lpszValue);
|
|
#endif
|
|
|
|
return(S_OK);
|
|
}
|
|
|
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
//
|
|
// CCombo::Next()
|
|
//
|
|
// --------------------------------------------------------------------------
|
|
STDMETHODIMP CCombo::Next(ULONG celt, VARIANT* rgvar, ULONG* pceltFetched)
|
|
{
|
|
return(CAccessible::Next(celt, rgvar, pceltFetched));
|
|
}
|
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
//
|
|
// CCombo::Skip()
|
|
//
|
|
// --------------------------------------------------------------------------
|
|
STDMETHODIMP CCombo::Skip(ULONG celt)
|
|
{
|
|
return(CAccessible::Skip(celt));
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
//
|
|
// HrGetWindowNameNoLabel()
|
|
//
|
|
// This variation of HrGetWindowName (originally from client.cpp)
|
|
// never uses a label. (HrGetWindowName would alway use a label
|
|
// if window text was "" and window was in a dialog. That's not
|
|
// appropriate for getting combo value text, though...)
|
|
//
|
|
// --------------------------------------------------------------------------
|
|
HRESULT HrGetWindowNameNoLabel(HWND hwnd, BSTR* pszName)
|
|
{
|
|
LPTSTR lpText = NULL;
|
|
|
|
if( ! IsWindow( hwnd ) )
|
|
return E_INVALIDARG;
|
|
|
|
// Look for a name property!
|
|
lpText = GetTextString( hwnd, FALSE );
|
|
if( ! lpText )
|
|
return S_FALSE;
|
|
|
|
// Strip out the mnemonic.
|
|
StripMnemonic(lpText);
|
|
|
|
// Get a BSTR
|
|
*pszName = TCharSysAllocString( lpText );
|
|
|
|
// Free our buffer
|
|
LocalFree( (HANDLE)lpText );
|
|
|
|
// Did the BSTR succeed?
|
|
if( ! *pszName )
|
|
return E_OUTOFMEMORY;
|
|
|
|
return S_OK;
|
|
}
|