Windows2003-3790/inetcore/mshtml/iextag/select.cxx
2020-09-30 16:53:55 +02:00

9018 lines
226 KiB
C++

//+------------------------------------------------------------------
//
// Microsoft IEPEERS
// Copyright (C) Microsoft Corporation, 1999.
//
// File: iextags\select.cxx
//
// Contents: The SELECT control.
//
// Classes: CIESelectElement
//
// Interfaces: IHTMLSelectElement3
// IPrivateSelect
//
//-------------------------------------------------------------------
#include "headers.h"
#pragma MARK_DATA(__FILE__)
#pragma MARK_CODE(__FILE__)
#pragma MARK_CONST(__FILE__)
#ifndef __X_IEXTAG_HXX_
#define __X_IEXTAG_HXX_
#include "iextag.h"
#endif
#ifndef __X_UTILS_HXX_
#define __X_UTILS_HXX_
#include "utils.hxx"
#endif
#ifndef __X_SELECT_HXX_
#define __X_SELECT_HXX_
#include "select.hxx"
#endif
#ifndef __X_SELITEM_HXX_
#define __X_SELITEM_HXX_
#include "selitem.hxx"
#endif
// TODO: This is a magic number right now, should be based on font
#define SELECT_OPTIONWIDTH 4
#define SELECT_OPTIONHEIGHT 16
#define SELECT_MAXOPTIONS 12
#define SELECT_SCROLLWAIT 250
#define SELECT_TIMERVLWAIT 10
const CBaseCtl::PROPDESC CIESelectElement::s_propdesc[] =
{
{_T("size"), VT_I4, -1},
{_T("selectedIndex"), VT_I4, -1},
{_T("multiple"), VT_I4, -1},
{_T("name"), VT_BSTR, NULL, NULL},
{_T("length"), VT_I4, 0},
{_T("type"), VT_BSTR, NULL, NULL},
NULL
};
enum
{
eSize = 0,
eSelectedIndex = 1,
eMultiple = 2,
eName = 3,
eLength = 4,
eType = 5,
};
/////////////////////////////////////////////////////////////////////////////
//
// CIESelectElement
//
/////////////////////////////////////////////////////////////////////////////
BOOL CIESelectElement::_bSavedCtrl = FALSE;
UINT_PTR CIESelectElement::_iTimerID = 0;
CIESelectElement *CIESelectElement::_pTimerSelect = NULL;
POINT CIESelectElement::_ptSavedPoint;
#ifdef SELECT_TIMERVL
UINT_PTR CIESelectElement::_iTimerVL = 0;
LONG CIESelectElement::_lTimerVLRef = 0;
CRITICAL_SECTION CIESelectElement::_lockTimerVL;
CIESelectElement::SELECT_TIMERVL_LINKELEM *CIESelectElement::_queueTimerVL = NULL;
#endif
//+------------------------------------------------------------------
//
// Member: CIESelectElement::CIESelectElement()
//
// Synopsis: Initializes member variables.
//
// Arguments: None
//
// Returns: None.
//
//-------------------------------------------------------------------
CIESelectElement::CIESelectElement()
{
_lShiftAnchor = -1;
_lTopChanged = -1;
_lBottomChanged = -1;
_pDispDocLink = NULL ;
#ifdef SELECT_GETSIZE
_fLayoutDirty = TRUE;
#endif
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::~CIESelectElement()
//
// Synopsis: Releases the event sink.
//
// Arguments: None
//
// Returns: None.
//
//-------------------------------------------------------------------
CIESelectElement::~CIESelectElement()
{
if (_pSinkPopup)
{
delete _pSinkPopup;
}
if (_pSinkVL)
{
delete _pSinkVL;
}
if (_pSinkButton)
{
delete _pSinkButton;
}
}
/////////////////////////////////////////////////////////////////////////////
//
// IElementBehavior overrides
//
/////////////////////////////////////////////////////////////////////////////
//+------------------------------------------------------------------
//
// Member: CIESelectElement::Detach()
//
// Synopsis: Releases interfaces.
//
// Arguments: None
//
// Returns: S_OK
//
//-------------------------------------------------------------------
HRESULT
CIESelectElement::Detach()
{
HRESULT hr = S_OK;
VARIANT_BOOL bOpen;
ClearInterface(&_pDispDocLink);
ClearInterface(&_pLastSelected);
ClearInterface(&_pLastHighlight);
ClearInterface(&_pSelectInPopup);
ClearInterface(&_pElemDisplay);
ClearInterface(&_pStyleButton);
if (_pPopup && !SELECT_ISINPOPUP(_fFlavor))
{
hr = _pPopup->get_isOpen(&bOpen);
if (FAILED(hr))
goto Cleanup;
if (bOpen)
{
hr = HidePopup();
if (FAILED(hr))
goto Cleanup;
}
ClearInterface(&_pPopup);
}
Cleanup:
return hr;
}
/////////////////////////////////////////////////////////////////////////////
//
// IElementBehaviorSubmit overrides and helpers
//
/////////////////////////////////////////////////////////////////////////////
//+------------------------------------------------------------------
//
// Member: CIESelectElement::Reset()
//
// Synopsis: Resets the selection status to the original status.
//
// Arguments: None
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT
CIESelectElement::Reset()
{
HRESULT hr;
long cItems;
long iItem;
IHTMLElementCollection *pItems = NULL;
IDispatch *pItem = NULL;
IPrivateOption *pOption = NULL;
CVariant var, var2;
VARIANT_BOOL bOn;
long lLastSelected = -1;
CContextAccess a(_pSite);
hr = a.Open(CA_ELEM);
if (FAILED(hr))
goto Cleanup;
//
// Get the children and length
//
hr = a.Elem()->get_children((IDispatch**)&pItems);
if (FAILED(hr))
goto Cleanup;
hr = pItems->get_length(&cItems);
if (FAILED(hr))
goto Cleanup;
//
// Go through all of the children and
// run a reset on them.
//
V_VT(&var) = VT_I4;
for (iItem = 0; iItem < cItems; iItem++)
{
V_I4(&var) = iItem;
hr = pItems->item(var, var2, &pItem);
if (FAILED(hr))
goto Cleanup;
hr = pItem->QueryInterface(IID_IPrivateOption, (void**)&pOption);
if (FAILED(hr))
{
ClearInterface(&pItem);
continue;
}
// Reset the option's selection status
hr = pOption->Reset();
if (FAILED(hr))
goto Cleanup;
hr = pOption->GetHighlight(&bOn);
if (FAILED(hr))
goto Cleanup;
if (bOn)
{
lLastSelected = iItem;
ClearInterface(&_pLastHighlight);
_pLastHighlight = pOption;
_pLastHighlight->AddRef();
}
ClearInterface(&pOption);
ClearInterface(&pItem);
}
if (lLastSelected == -1)
{
lLastSelected = 0;
}
if (SELECT_ISDROPBOX(_fFlavor) && (cItems > 0))
{
hr = GetIndex(lLastSelected, &pOption);
if (FAILED(hr))
goto Cleanup;
if (pOption)
{
DropDownSelect(pOption);
}
}
hr = CommitSelection(NULL);
if (FAILED(hr))
goto Cleanup;
Cleanup:
ReleaseInterface(pOption);
ReleaseInterface(pItem);
ReleaseInterface(pItems);
return hr;
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::GetSubmitInfo()
//
// Synopsis: Sends the information for a forms submission.
//
// Arguments: IHTMLSubmitData *pSubmitData - submit interface
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT
CIESelectElement::GetSubmitInfo(IHTMLSubmitData *pSubmitData)
{
HRESULT hr;
CComBSTR bstrName;
CContextAccess a(_pSite);
hr = a.Open(CA_ELEM);
if (FAILED(hr))
goto Cleanup;
// Get this control's name
hr = get_name(&bstrName);
if (FAILED(hr))
goto Cleanup;
if ((bstrName == NULL) || (bstrName[0] == 0))
{
//
// If the control does not have a name,
// then the operation failed.
//
hr = S_FALSE;
goto Cleanup;
}
//
// Append the submit info
//
if (SELECT_ISMULTIPLE(_fFlavor))
{
hr = GetMultipleSubmitInfo(pSubmitData, bstrName);
if (FAILED(hr))
goto Cleanup;
}
else
{
hr = GetSingleSubmitInfo(pSubmitData, bstrName);
if (FAILED(hr))
goto Cleanup;
}
Cleanup:
return hr;
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::GetSingleSubmitInfo()
//
// Synopsis: Sends the information for a forms submission when
// the select is a single select.
//
// Arguments: IHTMLSubmitData *pSubmitData - submit interface
// CComBSTR *pbstrName - the name of the select
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT
CIESelectElement::GetSingleSubmitInfo(IHTMLSubmitData *pSubmitData, CComBSTR bstrName)
{
HRESULT hr;
long lIndex;
CComBSTR bstrVal;
IHTMLOptionElement2 *pOption = NULL;
hr = get_selectedIndex(&lIndex);
if (FAILED(hr))
goto Cleanup;
if (lIndex < 0)
{
// There is no selection, so stop
goto Cleanup;
}
hr = GetIndex(lIndex, &pOption);
if (FAILED(hr) || !pOption)
{
// If the option does not exist,
// ignore the error and stop
hr = S_OK;
goto Cleanup;
}
// Get the value to submit
hr = pOption->get_value(&bstrVal);
if (FAILED(hr))
goto Cleanup;
// Append the data
hr = pSubmitData->appendNameValuePair(bstrName, bstrVal);
if (FAILED(hr))
goto Cleanup;
Cleanup:
ReleaseInterface(pOption);
return hr;
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::GetMultipleSubmitInfo()
//
// Synopsis: Sends the information for a forms submission when
// the select is a multiple select.
//
// Arguments: IHTMLSubmitData *pSubmitData - submit interface
// CComBSTR *pbstrName - the name of the select
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT
CIESelectElement::GetMultipleSubmitInfo(IHTMLSubmitData *pSubmitData, CComBSTR bstrName)
{
HRESULT hr;
long cItems;
long iItem;
VARIANT_BOOL bSelected;
CComBSTR bstrVal;
CVariant var, var2;
BOOL bNeedSeparator = FALSE;
IHTMLElementCollection *pItems = NULL;
IDispatch *pItem = NULL;
IHTMLOptionElement2 *pOption = NULL;
CContextAccess a(_pSite);
hr = a.Open(CA_ELEM);
if (FAILED(hr))
goto Cleanup;
hr = a.Elem()->get_children((IDispatch**)&pItems);
if (FAILED(hr))
goto Cleanup;
hr = pItems->get_length(&cItems);
if (FAILED(hr))
goto Cleanup;
//
// Go through all the children, find the selected ones, and submit their info
//
V_VT(&var) = VT_I4;
for (iItem = 0; iItem < cItems; iItem++)
{
V_I4(&var) = iItem;
hr = pItems->item(var, var2, &pItem);
if (FAILED(hr))
goto Cleanup;
hr = pItem->QueryInterface(IID_IHTMLOptionElement2, (void**)&pOption);
if (FAILED(hr))
{
ClearInterface(&pItem);
continue;
}
hr = pOption->get_selected(&bSelected);
if (FAILED(hr))
goto Cleanup;
if (bSelected)
{
//
// The option is selected, so submit its value
//
if (bNeedSeparator)
{
// Separate the item from the previous item
hr = pSubmitData->appendItemSeparator();
if (FAILED(hr))
goto Cleanup;
}
else
{
bNeedSeparator = TRUE;
}
hr = pOption->get_value(&bstrVal);
if (FAILED(hr))
goto Cleanup;
hr = pSubmitData->appendNameValuePair(bstrName, bstrVal);
if (FAILED(hr))
goto Cleanup;
}
ClearInterface(&pOption);
ClearInterface(&pItem);
}
Cleanup:
ReleaseInterface(pOption);
ReleaseInterface(pItem);
ReleaseInterface(pItems);
return hr;
}
/////////////////////////////////////////////////////////////////////////////
//
// IElementBehaviorFocus overrides
//
/////////////////////////////////////////////////////////////////////////////
//+------------------------------------------------------------------
//
// Member: CIESelectElement::GetFocusRect()
//
// Synopsis: Sets pRect to the rectangle of the focus rect.
//
// Arguments: pRect - Receives the focus rectangle
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT
CIESelectElement::GetFocusRect(RECT * pRect)
{
HRESULT hr;
long lNumOptions;
IHTMLElement *pElem = NULL;
IDispatch *pDispatch = NULL;
VARIANT_BOOL bOpen;
long lTopIndex;
long lBottomIndex;
CContextAccess a(_pSite);
RECT rect;
long lIndex;
Assert(pRect);
hr = GetNumOptions(&lNumOptions);
if (FAILED(hr))
goto Cleanup;
lIndex = _lFocusIndex;
if (SELECT_ISLISTBOX(_fFlavor))
{
hr = a.Open(CA_ELEM2);
if (FAILED(hr))
goto Cleanup;
if (lNumOptions == 0)
{
if (!SELECT_ISINPOPUP(_fFlavor))
{
pRect->top = 0;
pRect->left = 0;
#ifdef SELECT_GETSIZE
pRect->right = _sizeOption.cx;
pRect->bottom = _sizeOption.cy;
#else
pRect->bottom = SELECT_OPTIONHEIGHT;
hr = a.Elem2()->get_clientWidth(&pRect->right);
if (FAILED(hr))
goto Cleanup;
#endif
}
}
else
{
hr = GetTopVisibleOptionIndex(&lTopIndex);
if (FAILED(hr))
goto Cleanup;
hr = GetBottomVisibleOptionIndex(&lBottomIndex);
if (FAILED(hr))
goto Cleanup;
// Make sure the option is onscreen
if ((_lFocusIndex >= lTopIndex) && (_lFocusIndex < lBottomIndex))
{
// If the writingmode is "tb-rl", the index is got with counting from left to right.
// Hence, it needs to be changed to the actual index so that the index is as from
// right to left.
if (_fWritingModeTBRL)
{
lIndex = lNumOptions-_lFocusIndex-1;
}
hr = GetIndex(lIndex, &pDispatch);
if (FAILED(hr))
goto Cleanup;
hr = pDispatch->QueryInterface(IID_IHTMLElement, (void**)&pElem);
if (FAILED(hr))
goto Cleanup;
hr = pElem->get_offsetTop(&rect.top);
if (FAILED(hr))
goto Cleanup;
hr = pElem->get_offsetLeft(&rect.left);
if (FAILED(hr))
goto Cleanup;
// If the writingmode is "tb-rl", then, the top and left values are interchaged to
// present the options in right to left manner.
if (_fWritingModeTBRL)
{
pRect->left = rect.top;
pRect->top = rect.left;
}
else
{
pRect->top = rect.top;
pRect->left = rect.left;
}
#ifdef SELECT_GETSIZE
if (_fWritingModeTBRL)
{
// The rectangle now changes to present the options text in vertical direction
// and hence the changes in width and height.
hr = a.Elem2()->get_clientHeight(&pRect->right);
if (FAILED(hr))
goto Cleanup;
pRect->right += pRect->left;
pRect->bottom = pRect->top + _sizeOption.cy;
}
else
{
hr = a.Elem2()->get_clientWidth(&pRect->right);
if (FAILED(hr))
goto Cleanup;
pRect->bottom = pRect->top + _sizeOption.cy;
}
#else
hr = pElem->get_offsetWidth(&pRect->right);
if (FAILED(hr))
goto Cleanup;
hr = pElem->get_offsetHeight(&pRect->bottom);
if (FAILED(hr))
goto Cleanup;
pRect->right += pRect->left;
pRect->bottom += pRect->top;
#endif
}
}
}
else if (SELECT_ISDROPBOX(_fFlavor) && (lNumOptions > 0))
{
hr = _pPopup->get_isOpen(&bOpen);
if (FAILED(hr))
goto Cleanup;
if (!bOpen)
{
#ifdef SELECT_GETSIZE
// TODO: Handle with real borders
pRect->left = 1;
pRect->top = 1;
pRect->right = pRect->left + _sizeOption.cx;
pRect->bottom = pRect->top + _sizeOption.cy;
#else
hr = _pElemDisplay->get_offsetTop(&rect.top);
if (FAILED(hr))
goto Cleanup;
hr = _pElemDisplay->get_offsetLeft(&rect.left);
if (FAILED(hr))
goto Cleanup;
hr = _pElemDisplay->get_offsetWidth(&rect.bottom);
if (FAILED(hr))
goto Cleanup;
hr = _pElemDisplay->get_offsetHeight(&rect.right);
if (FAILED(hr))
goto Cleanup;
*pRect = rect;
if (_fWritingModeTBRL)
{
pRect->top = rect.left;
pRect->left = rect.top;
}
else
{
pRect->right = rect.bottom;
pRect->bottom = rect.right;
}
pRect->right += pRect->left;
pRect->bottom += pRect->top;
#endif
}
}
Cleanup:
ReleaseInterface(pDispatch);
ReleaseInterface(pElem);
return hr;
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::RefreshFocusRect()
//
// Synopsis: Refreshes the focus rectangle.
//
// Arguments: None
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT
CIESelectElement::RefreshFocusRect()
{
HRESULT hr;
CContextAccess a(_pSite);
hr = a.Open(CA_ELEM3);
if (FAILED(hr))
goto Cleanup;
hr = a.Elem3()->put_hideFocus(VARIANT_TRUE);
if (FAILED(hr))
goto Cleanup;
hr = a.Elem3()->put_hideFocus(VARIANT_FALSE);
if (FAILED(hr))
goto Cleanup;
Cleanup:
return hr;
}
#ifdef SELECT_GETSIZE
/////////////////////////////////////////////////////////////////////////////
//
// IElementBehaviorLayout overrides
//
/////////////////////////////////////////////////////////////////////////////
//+------------------------------------------------------------------
//
// Member: CIESelectElement::GetLayoutInfo()
//
// Synopsis: Returns the type of layout we want.
//
// Arguments: LONG *plLayoutInfo - Returns the layout info
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT
CIESelectElement::GetLayoutInfo(LONG *plLayoutInfo)
{
Assert(plLayoutInfo);
*plLayoutInfo = BEHAVIORLAYOUTINFO_MODIFYNATURAL;
return S_OK;
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::GetSize()
//
// Synopsis: Returns the size we want.
//
// Arguments: LONG dwFlags - Request type
// SIZE sizeContent - Content size
// POINT *pptTranslate - Translation point
// POINT *pptTopLeft - Top left point
// SIZE *psizeProposed - Returns our proposed size
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT
CIESelectElement::GetSize(LONG dwFlags,
SIZE sizeContent,
POINT *pptTranslate,
POINT *pptTopLeft,
SIZE *psizeProposed)
{
HRESULT hr = S_OK;
BOOL bWidthSet;
BOOL bHeightSet;
long lButtonWidth = GetSystemMetrics(SM_CXVSCROLL);
// if this is not a normal sizing pass, or if this is the second
// call due to percent sized children, just leave.
if (! (dwFlags & BEHAVIORLAYOUTMODE_NATURAL)
|| dwFlags & BEHAVIORLAYOUTMODE_FINAL_PERCENT)
goto Cleanup;
hr = IsWidthHeightSet(&bWidthSet, &bHeightSet);
if (FAILED(hr))
goto Cleanup;
if (!_fLayoutDirty && (bWidthSet || bHeightSet))
{
// Check to see if the layout really is dirty
_fLayoutDirty = (psizeProposed->cx != _sizeSelect.cx) ||
(psizeProposed->cy != _sizeSelect.cy);
}
if (!_fAllOptionsSized)
{
// If we have zero options, then all the options have been sized
// This doesn't get set correctly because OnOptionSized will never get
// called when there are no options.
long lNumOptions;
hr = GetNumOptions(&lNumOptions);
if (FAILED(hr))
goto Cleanup;
_fAllOptionsSized = lNumOptions == 0;
}
if (_fLayoutDirty && _fContentReady && _fAllOptionsSized)
{
_fLayoutDirty = FALSE;
if (SELECT_ISDROPBOX(_fFlavor) && _fVLEngaged)
{
_sizeContent = _sizeOption; // the forced option size
// TODO: +2 for padding
_sizeContent.cx += lButtonWidth + 2;
_sizeContent.cy += 2;
_sizeSelect = _sizeContent;
}
else
{
long lSize;
hr = get_size(&lSize);
if (FAILED(hr))
goto Cleanup;
// If we have no default option size, then set to a default size
if (_sizeOption.cx == 0)
{
_sizeOption.cx = SELECT_OPTIONWIDTH;
if (!_fNeedScrollBar)
{
_sizeOption.cx += lButtonWidth;
}
}
if (_sizeOption.cy == 0)
{
_sizeOption.cy = SELECT_OPTIONHEIGHT;
}
if (_fNeedScrollBar)
{
_sizeContent.cx = _sizeOption.cx;
}
else if(_sizeContent.cx < _sizeOption.cx)
{
_sizeContent.cx = _sizeOption.cx;
}
if (_fNeedScrollBar)
{
_sizeContent.cx += lButtonWidth;
}
_sizeContent.cy = _sizeOption.cy * lSize;
_sizeSelect = _sizeContent;
}
// Add in the border and padding
// TODO: Replace with real border
if (SELECT_ISDROPBOX(_fFlavor))
{
_sizeSelect.cx += 4;
_sizeSelect.cy += 4;
}
else if (!SELECT_ISINPOPUP(_fFlavor))
{
_sizeSelect.cx += 6;
_sizeSelect.cy += 6;
}
if (bHeightSet)
{
_sizeSelect.cy = psizeProposed->cy;
// TODO: Replace with real border
_sizeContent.cy = _sizeSelect.cy - 6;
if (SELECT_ISLISTBOX(_fFlavor))
{
// Make sure that the content height is a multiple of the height of the options
_sizeContent.cy -= _sizeContent.cy % _sizeOption.cy;
// TODO: Replace with real border
_sizeSelect.cy = _sizeContent.cy + 6;
}
}
if (bWidthSet)
{
_sizeSelect.cx = psizeProposed->cx;
// TODO: Replace with real border
_sizeContent.cx = _sizeSelect.cx - 6;
// _sizeContent.cx -= 8;
if (SELECT_ISLISTBOX(_fFlavor))
{
long lNumOptions;
hr = GetNumOptions(&lNumOptions);
if (FAILED(hr))
goto Cleanup;
_sizeOption.cx = _sizeContent.cx;
if (lNumOptions > (_sizeContent.cy / _sizeOption.cy))
{
// Scrollbars are needed
_sizeOption.cx -= lButtonWidth;
}
}
if (SELECT_ISDROPBOX(_fFlavor))
{
_sizeSelect.cx += lButtonWidth + 2;
}
}
// TODO: Remove _lMaxHeight everywhere
_lMaxHeight = _sizeOption.cy;
}
*psizeProposed = _sizeSelect;
Cleanup:
return hr;
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::IsWidthHeightSet()
//
// Synopsis: Returns whether the width and/or height was set in the style.
//
// Arguments: BOOL *pbWidthSet - Returns if the width was set
// BOOL *pbHeightSet - Returns if the height was set
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT
CIESelectElement::IsWidthHeightSet(BOOL *pbWidthSet, BOOL *pbHeightSet)
{
HRESULT hr;
CVariant var;
CContextAccess a(_pSite);
hr = a.Open(CA_STYLE);
if (FAILED(hr))
goto Cleanup;
if (pbWidthSet)
{
hr = a.Style()->get_width(&var);
if (FAILED(hr))
goto Cleanup;
*pbWidthSet = !(var.IsEmpty() || ((V_VT(&var) == VT_BSTR) && (V_BSTR(&var) == NULL)));
}
if (pbHeightSet)
{
hr = a.Style()->get_height(&var);
if (FAILED(hr))
goto Cleanup;
*pbHeightSet = !(var.IsEmpty() || ((V_VT(&var) == VT_BSTR) && (V_BSTR(&var) == NULL)));
}
Cleanup:
return hr;
}
#endif
/////////////////////////////////////////////////////////////////////////////
//
// CBaseCtl overrides
//
/////////////////////////////////////////////////////////////////////////////
//+------------------------------------------------------------------
//
// Member: CIESelectElement::Init()
//
// Synopsis: Attaches events and sets the default style.
//
// Arguments: None
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT
CIESelectElement::Init()
{
HRESULT hr;
CComBSTR bstr;
CContextAccess a(_pSite);
hr = a.Open(CA_ELEM | CA_DEFAULTS | CA_SITEOM | CA_STYLE);
if (FAILED(hr))
goto Cleanup;
// Register the behavior name
hr = a.SiteOM()->RegisterName(_T("select"));
if (FAILED(hr))
goto Cleanup;
//
// Register for events
//
hr = AttachEvent(EVENT_ONFOCUS, &a);
if (FAILED(hr))
goto Cleanup;
hr = AttachEvent(EVENT_ONBLUR, &a);
if (FAILED(hr))
goto Cleanup;
hr = AttachEvent(EVENT_ONMOUSEDOWN, &a);
if (FAILED(hr))
goto Cleanup;
hr = AttachEvent(EVENT_ONMOUSEUP, &a);
if (FAILED(hr))
goto Cleanup;
hr = AttachEvent(EVENT_ONMOUSEOVER, &a);
if (FAILED(hr))
goto Cleanup;
hr = AttachEvent(EVENT_ONMOUSEOUT, &a);
if (FAILED(hr))
goto Cleanup;
hr = AttachEvent(EVENT_ONMOUSEMOVE, &a);
if (FAILED(hr))
goto Cleanup;
hr = AttachEvent(EVENT_ONKEYDOWN, &a);
if (FAILED(hr))
goto Cleanup;
hr = AttachEvent(EVENT_ONKEYUP, &a);
if (FAILED(hr))
goto Cleanup;
hr = AttachEvent(EVENT_ONKEYPRESS, &a);
if (FAILED(hr))
goto Cleanup;
hr = AttachEvent(EVENT_ONSELECTSTART, &a);
if (FAILED(hr))
goto Cleanup;
hr = AttachEvent(EVENT_ONSCROLL, &a);
if (FAILED(hr))
goto Cleanup;
hr = AttachEvent(EVENT_ONCONTEXTMENU, &a);
if (FAILED(hr))
goto Cleanup;
hr = AttachEvent(EVENT_ONPROPERTYCHANGE, &a);
if (FAILED(hr))
goto Cleanup;
bstr = _T("onchange");
hr = RegisterEvent(a.SiteOM(), bstr, &_lOnChangeCookie);
if (FAILED(hr))
goto Cleanup;
bstr = _T("onmousedown");
hr = RegisterEvent(a.SiteOM(), bstr, &_lOnMouseDownCookie, BEHAVIOREVENTFLAGS_STANDARDADDITIVE | BEHAVIOREVENTFLAGS_BUBBLE);
if (FAILED(hr))
goto Cleanup;
bstr = _T("onmouseup");
hr = RegisterEvent(a.SiteOM(), bstr, &_lOnMouseUpCookie, BEHAVIOREVENTFLAGS_STANDARDADDITIVE | BEHAVIOREVENTFLAGS_BUBBLE);
if (FAILED(hr))
goto Cleanup;
bstr = _T("onclick");
hr = RegisterEvent(a.SiteOM(), bstr, &_lOnClickCookie, BEHAVIOREVENTFLAGS_STANDARDADDITIVE | BEHAVIOREVENTFLAGS_BUBBLE);
if (FAILED(hr))
goto Cleanup;
bstr = _T("onkeydown");
hr = RegisterEvent(a.SiteOM(), bstr, &_lOnKeyDownCookie, BEHAVIOREVENTFLAGS_STANDARDADDITIVE | BEHAVIOREVENTFLAGS_BUBBLE);
if (FAILED(hr))
goto Cleanup;
bstr = _T("onkeyup");
hr = RegisterEvent(a.SiteOM(), bstr, &_lOnKeyUpCookie, BEHAVIOREVENTFLAGS_STANDARDADDITIVE | BEHAVIOREVENTFLAGS_BUBBLE);
if (FAILED(hr))
goto Cleanup;
bstr = _T("onkeypress");
hr = RegisterEvent(a.SiteOM(), bstr, &_lOnKeyPressedCookie, BEHAVIOREVENTFLAGS_STANDARDADDITIVE | BEHAVIOREVENTFLAGS_BUBBLE);
if (FAILED(hr))
goto Cleanup;
// Be able to receive keyboard focus
hr = a.Defaults()->put_tabStop(VB_TRUE);
if (FAILED(hr))
goto Cleanup;
hr = a.Defaults()->put_viewMasterTab(VARIANT_TRUE);
if (FAILED(hr))
goto Cleanup;
// Force a layout
hr = a.Style()->put_display(_T("inline-block"));
if (FAILED(hr))
goto Cleanup;
hr = SetupDefaultStyle();
if (FAILED(hr))
goto Cleanup;
Cleanup:
return hr;
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::SetupDefaultStyle()
//
// Synopsis: Sets the default style for the select control.
//
// Arguments: None
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT
CIESelectElement::SetupDefaultStyle()
{
HRESULT hr;
CComBSTR bstrHidden(_T("hidden"));
CComBSTR bstrBorder(_T("3 window-inset"));
CComBSTR bstrDefault(_T("default"));
CComBSTR bstrFont(_T("MS Sans Serif"));
CComBSTR bstrFontSize(_T("10pt"));
CComBSTR bstrWindow(_T("window"));
CComBSTR bstrColor(_T("windowtext"));
CComBSTR bstrVert(_T("text-bottom"));
CVariant var;
CContextAccess a(_pSite);
//
// TODO: Can this be made into a table?
//
hr = a.Open(CA_DEFSTYLE);
if (FAILED(hr))
goto Cleanup;
// Setup hidden
hr = a.DefStyle()->put_visibility(bstrHidden);
if (FAILED(hr))
goto Cleanup;
// Setup border
hr = a.DefStyle()->put_border(bstrBorder);
if (FAILED(hr))
goto Cleanup;
// Setup mouse
hr = a.DefStyle()->put_cursor(bstrDefault);
if (FAILED(hr))
goto Cleanup;
// Setup overflow
hr = a.DefStyle()->put_overflow(bstrHidden);
if (FAILED(hr))
goto Cleanup;
// Setup font
hr = a.DefStyle()->put_fontFamily(bstrFont);
if (FAILED(hr))
goto Cleanup;
// Setup the font size
V_VT(&var) = VT_BSTR;
V_BSTR(&var) = bstrFontSize;
hr = a.DefStyle()->put_fontSize(var);
if (FAILED(hr))
goto Cleanup;
// Setup background
V_VT(&var) = VT_BSTR;
V_BSTR(&var) = (BSTR)bstrWindow;
hr = a.DefStyle()->put_backgroundColor(var);
if (FAILED(hr))
goto Cleanup;
// Setup the foreground color
V_VT(&var) = VT_BSTR;
V_BSTR(&var) = (BSTR)bstrColor;
hr = a.DefStyle()->put_color(var);
if (FAILED(hr))
goto Cleanup;
// Setup the vertical alignment
V_VT(&var) = VT_BSTR;
V_BSTR(&var) = bstrVert;
hr = a.DefStyle()->put_verticalAlign(var);
if (FAILED(hr))
goto Cleanup;
// Set a top margin
V_VT(&var) = VT_I4;
V_I4(&var) = 1;
hr = a.DefStyle()->put_marginTop(var);
if (FAILED(hr))
goto Cleanup;
V_VT(&var) = VT_I4;
V_I4(&var) = 7;
hr = a.DefStyle()->put_width(var);
if (FAILED(hr))
goto Cleanup;
Cleanup:
return hr;
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::OnContentReady()
//
// Synopsis: Initializes the flavor of the control and the flavor
// of the OPTION elements.
//
// Arguments: CEventObjectAccess *pEvent - Event information
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT
CIESelectElement::OnContentReady()
{
return InitContent();
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::InitContent()
//
// Synopsis: Initializes the flavor of the control and the flavor
// of the OPTION elements.
//
// Arguments: None
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT
CIESelectElement::InitContent()
{
HRESULT hr;
VARIANT_BOOL bMult;
long lSize;
hr = SetWritingModeFlag();
if (FAILED(hr))
goto Cleanup;
//
// Retrieve properties
//
hr = get_multiple(&bMult);
if (FAILED(hr))
goto Cleanup;
if (hr == S_FALSE)
{
// The attribute was not set correctly, default to VARIANT_FALSE
bMult = VARIANT_FALSE;
hr = put_multiple(bMult);
if (FAILED(hr))
goto Cleanup;
}
hr = get_size(&lSize);
if (FAILED(hr))
goto Cleanup;
if ((hr == S_FALSE) || (lSize < 1))
{
// The size was not set correctly, correct it
lSize = bMult ? 4 : 1;
hr = GetProps()[eSize].Set(lSize);
if (FAILED(hr))
goto Cleanup;
}
//
// Setup the flavor (default is single select listbox)
//
_fFlavor = _fFlavor & SELECT_INPOPUP; // If this was set, then keep it
if (bMult)
{
// Multiple select
_fFlavor |= SELECT_MULTIPLE;
}
else if (lSize == 1)
{
// Single select dropbox.
_fFlavor |= SELECT_DROPBOX;
}
//
// Based on the flavor, setup the control
//
if (SELECT_ISLISTBOX(_fFlavor))
{
#ifdef SELECT_GETSIZE
_fLayoutDirty = TRUE;
#endif
hr = SetupListBox();
if (FAILED(hr))
goto Cleanup;
}
else if (SELECT_ISDROPBOX(_fFlavor))
{
hr = SetupDropBox();
if (FAILED(hr))
goto Cleanup;
}
else
{
hr = E_FAIL;
goto Cleanup;
}
//
// Initialize the options
//
hr = InitOptions();
if (FAILED(hr))
goto Cleanup;
#ifndef SELECT_GETSIZE
//
// Make the control visible
//
hr = MakeVisible(TRUE);
if (FAILED(hr))
goto Cleanup;
#endif
#ifdef SELECT_GETSIZE
_fContentReady = TRUE;
#endif
Cleanup:
return hr;
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::MakeVisible()
//
// Synopsis: Makes the control visible or hidden.
//
// Arguments: BOOL bShow - True to make visible
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT
CIESelectElement::MakeVisible(BOOL bShow /* = TRUE */)
{
HRESULT hr;
CComBSTR bstrVisible;
CContextAccess a(_pSite);
hr = a.Open(CA_DEFSTYLE);
if (FAILED(hr))
goto Cleanup;
if (bShow)
{
bstrVisible = _T("visible");
}
else
{
bstrVisible = _T("hidden");
}
// Display the control
hr = a.DefStyle()->put_visibility(bstrVisible);
if (FAILED(hr))
goto Cleanup;
Cleanup:
return hr;
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::SetupListBox()
//
// Synopsis: Sets up the listbox control.
//
// Arguments: None
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT
CIESelectElement::SetupListBox()
{
HRESULT hr;
long lSize;
long lNumOptions;
CContextAccess a(_pSite);
hr = a.Open(CA_DEFSTYLE | CA_DEFSTYLE2 | CA_DEFAULTS);
if (FAILED(hr))
goto Cleanup;
hr = get_size(&lSize);
if (FAILED(hr))
goto Cleanup;
#ifndef SELECT_GETSIZE
hr = SetDimensions(lSize);
if (FAILED(hr))
goto Cleanup;
#endif
hr = GetNumOptions(&lNumOptions);
if (FAILED(hr))
goto Cleanup;
_fNeedScrollBar = (lNumOptions > lSize);
if (_fWritingModeTBRL)
{
hr = SetPixelHeightWidth();
if (FAILED(hr))
goto Cleanup;
}
//
// Setup the scroll segment
//
hr = a.Defaults()->put_scrollSegmentY(lNumOptions);
if (FAILED(hr))
goto Cleanup;
#ifdef SELECT_GETSIZE
if (!_fContentReady)
{
// The border is different on the listbox (we need this in case we were dynamically
// changed from a dropbox to a listbox).
hr = a.DefStyle()->put_border(CComBSTR(_T("3 window-inset")));
if (FAILED(hr))
goto Cleanup;
hr = a.DefStyle2()->put_overflowY(CComBSTR(_T("auto")));
if (FAILED(hr))
goto Cleanup;
//
// Make the control visible
//
hr = MakeVisible(TRUE);
if (FAILED(hr))
goto Cleanup;
}
#endif
Cleanup:
return hr;
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::SetupDropBox()
//
// Synopsis: Sets up the dropbox control.
//
// Arguments: None
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT
CIESelectElement::SetupDropBox()
{
HRESULT hr;
hr = SetupDropControl();
if (FAILED(hr))
goto Cleanup;
hr = SetupPopup();
if (FAILED(hr))
goto Cleanup;
Cleanup:
return hr;
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::SetupDropControl()
//
// Synopsis: Sets up the dropbox control portion.
//
// Arguments: None
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT
CIESelectElement::SetupDropControl()
{
HRESULT hr = S_OK;
CComBSTR bstrHidden(_T("hidden"));
CComBSTR bstrBorder(_T("2 inset"));
CComBSTR bstrWritingMode(_T(""));
CContextAccess a(_pSite);
long lTemp;
#ifndef SELECT_GETSIZE
IHTMLDocument2 *pDocThis = NULL;
IDispatch *pDispDocLink = NULL;
IHTMLDocument2 *pDocLink = NULL;
IHTMLDocument3 *pDocLink3 = NULL;
IHTMLElement *pElemLink = NULL;
IHTMLDOMNode *pNodeLink = NULL;
IHTMLElement *pElemDisplay = NULL;
IHTMLDOMNode *pNodeDisplay = NULL;
IHTMLElement *pElemButton = NULL;
IHTMLDOMNode *pNodeButton = NULL;
CComBSTR bstr;
CVariant var;
IDispatch *pDispatch = NULL;
IHTMLElement *pElem = NULL;
IHTMLElement2 *pElem2 = NULL;
IHTMLStyle *pStyle = NULL;
IHTMLStyle2 *pStyle2 = NULL;
long lHeight;
long lWidth;
long lButtonWidth;
long lNumOptions;
long lDiff;
#endif
#ifdef SELECT_GETSIZE
hr = a.Open(CA_DEFSTYLE);
if (FAILED(hr))
goto Cleanup;
#else
hr = a.Open(CA_ELEM | CA_ELEM2 | CA_ELEM3 | CA_PELEM | CA_DEFSTYLE);
if (FAILED(hr))
goto Cleanup;
#endif
// Set overflow hidden for the drop control
hr = a.DefStyle()->put_overflow(bstrHidden);
if (FAILED(hr))
goto Cleanup;
// The border is different on the drop control
hr = a.DefStyle()->put_border(bstrBorder);
if (FAILED(hr))
goto Cleanup;
#ifndef SELECT_GETSIZE
hr = GetNumOptions(&lNumOptions);
if (FAILED(hr))
goto Cleanup;
// Set the dimensions of the control
if (lNumOptions > 0)
{
// TODO: Change this to the real maximum number of options
if (lNumOptions > SELECT_MAXOPTIONS)
{
lNumOptions = SELECT_MAXOPTIONS;
}
hr = SetDimensions(lNumOptions);
if (FAILED(hr))
goto Cleanup;
}
else
{
hr = SetDimensions(1);
if (FAILED(hr))
goto Cleanup;
}
hr = a.Elem2()->get_clientHeight(&_lPopupSize.cy);
if (FAILED(hr))
goto Cleanup;
hr = a.DefStyle()->get_pixelWidth(&lWidth);
if (FAILED(hr))
goto Cleanup;
_lPopupSize.cx = lWidth;
if (lNumOptions > 0)
{
hr = GetIndex(0, &pElem);
if (FAILED(hr))
goto Cleanup;
hr = pElem->get_offsetHeight(&lHeight);
if (FAILED(hr))
goto Cleanup;
lDiff = _lPopupSize.cy - lHeight;
hr = a.DefStyle()->get_pixelHeight(&lHeight);
if (FAILED(hr))
goto Cleanup;
lHeight -= lDiff;
ClearInterface(&pElem);
}
else
{
hr = a.DefStyle()->get_pixelHeight(&lHeight);
if (FAILED(hr))
goto Cleanup;
}
if (_fWritingModeTBRL)
{
lTemp = lWidth;
lWidth = lHeight;
lHeight = lTemp;
}
hr = a.DefStyle()->put_pixelHeight(lHeight + 2);
if (FAILED(hr))
goto Cleanup;
hr = a.DefStyle()->put_pixelWidth(lWidth + 2);
if (FAILED(hr))
goto Cleanup;
// Get the client area's width
hr = a.Elem2()->get_clientWidth(&lWidth);
if (FAILED(hr))
goto Cleanup;
// Get the client area's height
hr = a.Elem2()->get_clientHeight(&lHeight);
if (FAILED(hr))
goto Cleanup;
// Get the width of the button
lButtonWidth = GetSystemMetrics(SM_CXVSCROLL);
// Get the document
hr = a.Elem()->get_document((IDispatch**)&pDocThis);
if (FAILED(hr))
goto Cleanup;
//
// Create a superflous slave element for now
//
bstr = _T("SPAN");
hr = pDocThis->createElement(bstr, &pElemLink);
if (FAILED(hr))
goto Cleanup;
hr = pElemLink->QueryInterface(IID_IHTMLDOMNode, (void**)&pNodeLink);
if (FAILED(hr))
goto Cleanup;
//
// Create the display element
//
bstr = _T("SPAN");
hr = pDocThis->createElement(bstr, &pElemDisplay);
if (FAILED(hr))
goto Cleanup;
_pElemDisplay = pElemDisplay;
_pElemDisplay->AddRef();
//
// Insert the display
//
hr = pElemDisplay->QueryInterface(IID_IHTMLDOMNode, (void**)&pNodeDisplay);
if (FAILED(hr))
goto Cleanup;
hr = pNodeLink->appendChild(pNodeDisplay, NULL);
if (FAILED(hr))
goto Cleanup;
//
// Create the button
//
bstr = _T("SPAN");
hr = pDocThis->createElement(bstr, &pElemButton);
if (FAILED(hr))
goto Cleanup;
//
// Insert the button
//
hr = pElemButton->QueryInterface(IID_IHTMLDOMNode, (void**)&pNodeButton);
if (FAILED(hr))
goto Cleanup;
hr = pNodeLink->appendChild(pNodeButton, NULL);
if (FAILED(hr))
goto Cleanup;
// Remove the superflous Link element now
hr = pNodeLink->removeNode(VB_FALSE, NULL);
if (FAILED(hr))
goto Cleanup;
// Get hold of the Link document
if ( _pDispDocLink )
{
pDispDocLink = _pDispDocLink;
_pDispDocLink->AddRef();
}
else
{
hr = pElemDisplay->get_document(&pDispDocLink);
if (FAILED(hr))
goto Cleanup;
}
hr = pDispDocLink->QueryInterface(IID_IHTMLDocument2, (void**)&pDocLink);
if (FAILED(hr))
goto Cleanup;
//
// Create the viewLink
//
hr = a.PElem()->putref_viewLink(pDocLink);
if (FAILED(hr))
goto Cleanup;
//
// Set the display's style
//
hr = pElemDisplay->get_style(&pStyle);
if (FAILED(hr))
goto Cleanup;
hr = pStyle->QueryInterface(IID_IHTMLStyle2, (void**)&pStyle2);
if (FAILED(hr))
goto Cleanup;
// Set the mouse cursor be an arrow for the display
bstr = _T("default");
hr = pStyle->put_cursor(bstr);
if (FAILED(hr))
goto Cleanup;
// Set overflow hidden for the display
bstr = _T("hidden");
hr = pStyle->put_overflow(bstr);
if (FAILED(hr))
goto Cleanup;
if (_fWritingModeTBRL)
{
lTemp = lWidth;
lWidth = lHeight;
lHeight = lWidth;
}
// Set the width of the display
hr = pStyle->put_pixelWidth(lWidth - lButtonWidth);
if (FAILED(hr))
goto Cleanup;
// Set the height of the display
bstr = _T("100%");
V_VT(&var) = VT_BSTR;
V_BSTR(&var) = bstr;
hr = pStyle->put_height(var);
if (FAILED(hr))
goto Cleanup;
// Set the vertical alignment of the display to be top
bstr = _T("top");
V_VT(&var) = VT_BSTR;
V_BSTR(&var) = bstr;
hr = pStyle->put_verticalAlign(var);
if (FAILED(hr))
goto Cleanup;
// Set the font for the display
bstr = _T("MS Sans Serif");
hr = pStyle->put_fontFamily(bstr);
if (FAILED(hr))
goto Cleanup;
// Set the font size for the display
bstr = _T("10pt");
V_VT(&var) = VT_BSTR;
V_BSTR(&var) = bstr;
hr = pStyle->put_fontSize(var);
if (FAILED(hr))
goto Cleanup;
// Set the left padding on the display
bstr = _T("3px");
V_VT(&var) = VT_BSTR;
V_BSTR(&var) = bstr;
hr = pStyle->put_paddingLeft(var);
if (FAILED(hr))
goto Cleanup;
// Set the top padding on the display
bstr = _T("1px");
V_VT(&var) = VT_BSTR;
V_BSTR(&var) = bstr;
hr = pStyle->put_paddingTop(var);
if (FAILED(hr))
goto Cleanup;
ClearInterface(&pStyle);
ClearInterface(&pStyle2);
//
// Set the button's style
//
hr = pElemButton->get_style(&pStyle);
if (FAILED(hr))
goto Cleanup;
hr = pStyle->QueryInterface(IID_IHTMLStyle2, (void**)&pStyle2);
if (FAILED(hr))
goto Cleanup;
_pStyleButton = pStyle;
_pStyleButton->AddRef();
// Set the mouse cursor be an arrow for the button
bstr = _T("default");
hr = pStyle->put_cursor(bstr);
if (FAILED(hr))
goto Cleanup;
// Set overflow hidden for the button
bstr = _T("hidden");
hr = pStyle->put_overflow(bstr);
if (FAILED(hr))
goto Cleanup;
if (_fWritingModeTBRL)
{
hr = pStyle->put_pixelHeight(lButtonWidth);
if (FAILED(hr))
goto Cleanup;
}
else
{
// Set the width of the button
hr = pStyle->put_pixelWidth(lButtonWidth);
if (FAILED(hr))
goto Cleanup;
}
// Set the height of the button
bstr = _T("100%");
V_VT(&var) = VT_BSTR;
V_BSTR(&var) = bstr;
if (_fWritingModeTBRL)
{
hr = pStyle->put_width(var);
if (FAILED(hr))
goto Cleanup;
}
else
{
hr = pStyle->put_height(var);
if (FAILED(hr))
goto Cleanup;
}
// Set the background color for the button
bstr = _T("buttonface");
hr = pStyle->put_background(bstr);
if (FAILED(hr))
goto Cleanup;
// Set the border for the button
bstr = _T("2 outset");
hr = pStyle->put_border(bstr);
if (FAILED(hr))
goto Cleanup;
// Set the font family of the button
bstr = _T("Marlett");
hr = pStyle->put_fontFamily(bstr);
if (FAILED(hr))
goto Cleanup;
// Set the font size of the button
V_VT(&var) = VT_I4;
V_I4(&var) = lButtonWidth - 4;
hr = pStyle->put_fontSize(var);
if (FAILED(hr))
goto Cleanup;
// Set the text to be the down arrow
bstr = _T("u");
hr = pElemButton->put_innerText(bstr);
if (FAILED(hr))
goto Cleanup;
// Set the alignment on the arrow to be centered
bstr = _T("center");
hr = pStyle->put_textAlign(bstr);
if (FAILED(hr))
goto Cleanup;
// The layout-grid-line is supposed to vertically
// center the arrow, but it doesn't. The padding
// is there to push the arrow up one pixel for IE5 compat.
// Set the layout-grid-line of the button to be 100%
bstr = _T("100%");
V_VT(&var) = VT_BSTR;
V_BSTR(&var) = bstr;
hr = pStyle2->put_layoutGridLine(var);
if (FAILED(hr))
goto Cleanup;
// Set the bottom padding of the button
bstr = _T("2px");
V_VT(&var) = VT_BSTR;
V_BSTR(&var) = bstr;
hr = pStyle->put_paddingBottom(var);
if (FAILED(hr))
goto Cleanup;
ClearInterface(&pStyle);
ClearInterface(&pStyle2);
//
// Attach an event sink to listen for events within the view link
//
hr = pDocLink->QueryInterface(IID_IHTMLDocument3, (void**)&pDocLink3);
if (FAILED(hr))
goto Cleanup;
if ( !_pSinkVL )
{
_pSinkVL = new CEventSink(this, SELECTES_VIEWLINK);
if (!_pSinkVL)
{
hr = E_OUTOFMEMORY;
goto Cleanup;
}
bstr = _T("onmousedown");
hr = AttachEventToSink(pDocLink3, bstr, _pSinkVL);
if (FAILED(hr))
goto Cleanup;
bstr = _T("onmouseup");
hr = AttachEventToSink(pDocLink3, bstr, _pSinkVL);
if (FAILED(hr))
goto Cleanup;
bstr = _T("onclick");
hr = AttachEventToSink(pDocLink3, bstr, _pSinkVL);
if (FAILED(hr))
goto Cleanup;
bstr = _T("onselectstart");
hr = AttachEventToSink(pDocLink3, bstr, _pSinkVL);
if (FAILED(hr))
goto Cleanup;
bstr = _T("oncontextmenu");
hr = AttachEventToSink(pDocLink3, bstr, _pSinkVL);
if (FAILED(hr))
goto Cleanup;
bstr = _T("onbeforefocusenter");
hr = AttachEventToSink(pDocLink3, bstr, _pSinkVL);
if (FAILED(hr))
goto Cleanup;
}
if ( !_pSinkButton )
{
_pSinkButton = new CEventSink(this, SELECTES_BUTTON);
if (!_pSinkButton)
{
hr = E_OUTOFMEMORY;
goto Cleanup;
}
hr = pElemDisplay->QueryInterface(IID_IHTMLElement2, (void**)&pElem2);
if (FAILED(hr))
goto Cleanup;
bstr = _T("onbeforefocusenter");
hr = AttachEventToSink(pElem2, bstr, _pSinkVL);
if (FAILED(hr))
goto Cleanup;
ClearInterface(&pElem2);
hr = pElemButton->QueryInterface(IID_IHTMLElement2, (void**)&pElem2);
if (FAILED(hr))
goto Cleanup;
bstr = _T("onmousedown");
hr = AttachEventToSink(pElem2, bstr, _pSinkButton);
if (FAILED(hr))
goto Cleanup;
bstr = _T("onmouseup");
hr = AttachEventToSink(pElem2, bstr, _pSinkButton);
if (FAILED(hr))
goto Cleanup;
bstr = _T("onmouseout");
hr = AttachEventToSink(pElem2, bstr, _pSinkButton);
if (FAILED(hr))
goto Cleanup;
bstr = _T("onbeforefocusenter");
hr = AttachEventToSink(pElem2, bstr, _pSinkButton);
if (FAILED(hr))
goto Cleanup;
bstr = _T("onbeforefocusenter");
hr = AttachEventToSink(pElem2, bstr, _pSinkVL);
if (FAILED(hr))
goto Cleanup;
}
#endif
Cleanup:
#ifndef SELECT_GETSIZE
ReleaseInterface(pDocThis);
ReleaseInterface(pDispDocLink);
ReleaseInterface(pDocLink);
ReleaseInterface(pDocLink3);
ReleaseInterface(pElemLink);
ReleaseInterface(pNodeLink);
ReleaseInterface(pElemDisplay);
ReleaseInterface(pNodeDisplay);
ReleaseInterface(pElemButton);
ReleaseInterface(pNodeButton);
ReleaseInterface(pDispatch);
ReleaseInterface(pElem);
ReleaseInterface(pElem2);
ReleaseInterface(pStyle);
ReleaseInterface(pStyle2);
#endif
return hr;
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::AttachEventToSink()
//
// Synopsis: Attaches an element's event to a given sink.
//
// Arguments: IHTMLElement2 *pElem - The element to listen to
// CComBSTR& bstr - The name of the event
// CEventSink* pSink - The event sink
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT
CIESelectElement::AttachEventToSink(IHTMLElement2 *pElem, CComBSTR& bstr, CEventSink* pSink)
{
HRESULT hr;
VARIANT_BOOL vSuccess;
hr = pElem->attachEvent(bstr, (IDispatch*)pSink, &vSuccess);
if (FAILED(hr))
goto Cleanup;
if (vSuccess != VARIANT_TRUE)
{
hr = E_FAIL;
goto Cleanup;
}
Cleanup:
return hr;
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::AttachEventToSink()
//
// Synopsis: Attaches a document's event to a given sink.
//
// Arguments: IHTMLDocument3 *pDoc - The document to listen to
// CComBSTR& bstr - The name of the event
// CEventSink* pSink - The event sink
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT
CIESelectElement::AttachEventToSink(IHTMLDocument3 *pDoc, CComBSTR& bstr, CEventSink* pSink)
{
HRESULT hr;
VARIANT_BOOL vSuccess;
hr = pDoc->attachEvent(bstr, (IDispatch*)pSink, &vSuccess);
if (FAILED(hr))
goto Cleanup;
if (vSuccess != VARIANT_TRUE)
{
hr = E_FAIL;
goto Cleanup;
}
Cleanup:
return hr;
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::SetupPopup()
//
// Synopsis: Sets up the popup window for dropbox.
//
// Arguments: None
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT
CIESelectElement::SetupPopup()
{
HRESULT hr;
IDispatch *pDocMainD = NULL;
IHTMLDocument2 *pDocMain = NULL;
IHTMLWindow2 *pWinMain = NULL;
IHTMLWindow4 *pWinMain4 = NULL;
IHTMLDocument *pDocThis = NULL;
IHTMLDocument2 *pDoc2 = NULL;
IDispatch *pPopupDisp = NULL;
IHTMLElement *pElemHTML = NULL;
IHTMLElement *pElemBODY = NULL;
IHTMLElement *pElemSELECT = NULL;
IHTMLElementCollection *pItems = NULL;
IDispatch *pDispatchItem = NULL;
IHTMLElement2 *pElem2 = NULL;
IHTMLStyle *pStyleBody = NULL;
#ifndef SELECT_GETSIZE
IHTMLStyle *pStyle = NULL;
IHTMLElement *pElem = NULL;
#endif
long lSize;
CVariant var, var2;
CComBSTR bstr, bstrHTML, bstrTemp;
CComBSTR bstrImport = _T("<?IMPORT namespace=IE implementation='#default'>");
TCHAR strNum[5];
CContextAccess a(_pSite);
hr = a.Open(CA_ELEM);
if (FAILED(hr))
goto Cleanup;
hr = a.Elem()->get_document(&pDocMainD);
if (FAILED(hr))
goto Cleanup;
hr = pDocMainD->QueryInterface(IID_IHTMLDocument2, (void **)&pDocMain);
if (FAILED(hr))
goto Cleanup;
hr = pDocMain->get_parentWindow(&pWinMain);
if (FAILED(hr))
goto Cleanup;
hr = pWinMain->QueryInterface(IID_IHTMLWindow4, (void **)&pWinMain4);
if (FAILED(hr))
goto Cleanup;
V_VT(&var) = VT_EMPTY;
hr = pWinMain4->createPopup(&var, &pPopupDisp);
if (FAILED(hr))
goto Cleanup;
hr = pPopupDisp->QueryInterface(IID_IHTMLPopup, (void **)&_pPopup);
if (FAILED(hr))
goto Cleanup;
//
// Get the popup's document
//
hr = _pPopup->get_document(&pDocThis);
if (FAILED(hr))
goto Cleanup;
//
// Get the popup's document
//
pDocThis->QueryInterface(IID_IHTMLDocument2, (void**)&pDoc2);
if (FAILED(hr))
goto Cleanup;
hr = pDoc2->get_body(&pElemBODY);
if (FAILED(hr))
goto Cleanup;
hr = UpdatePopup();
if (FAILED(hr))
goto Cleanup;
hr = pElemBODY->get_children((IDispatch**)&pItems);
if (FAILED(hr))
goto Cleanup;
hr = pItems->item(var, var2, &pDispatchItem);
if (FAILED(hr))
goto Cleanup;
if (!pDispatchItem)
{
hr = E_FAIL;
goto Cleanup;
}
//
// Attach an event sink to listen for events within the popup
//
hr = pDispatchItem->QueryInterface(IID_IHTMLElement2, (void**)&pElem2);
if (FAILED(hr))
goto Cleanup;
if ( !_pSinkPopup )
{
_pSinkPopup = new CEventSink(this, SELECTES_POPUP);
if (!_pSinkPopup)
{
hr = E_OUTOFMEMORY;
goto Cleanup;
}
bstr = _T("onchange");
hr = AttachEventToSink(pElem2, bstr, _pSinkPopup);
if (FAILED(hr))
goto Cleanup;
bstr = _T("onkeydown");
hr = AttachEventToSink(pElem2, bstr, _pSinkPopup);
if (FAILED(hr))
goto Cleanup;
bstr = _T("onkeyup");
hr = AttachEventToSink(pElem2, bstr, _pSinkPopup);
if (FAILED(hr))
goto Cleanup;
bstr = _T("onkeypress");
hr = AttachEventToSink(pElem2, bstr, _pSinkPopup);
if (FAILED(hr))
goto Cleanup;
}
Cleanup:
ReleaseInterface(pDocMainD);
ReleaseInterface(pDocMain);
ReleaseInterface(pWinMain);
ReleaseInterface(pWinMain4);
ReleaseInterface(pPopupDisp);
ReleaseInterface(pElemHTML);
ReleaseInterface(pElemBODY);
ReleaseInterface(pElemSELECT);
ReleaseInterface(pDocThis);
ReleaseInterface(pDoc2);
ReleaseInterface(pItems);
ReleaseInterface(pDispatchItem);
ReleaseInterface(pElem2);
ReleaseInterface(pStyleBody);
#ifndef SELECT_GETSIZE
ReleaseInterface(pStyle);
ReleaseInterface(pElem);
#endif
return hr;
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::UpdatePopup()
//
// Synopsis: Update the popup window for dropbox.
// The viewlink has just the dropdown button and selected text. The _pPopup has
// the list of options stored in its associated document. Each time an option is
// is added to the dropdown, it is actually added to the document associated with
// the _pPopup. The popup of the dropdown, itself doesn't get updated. This leads
// to wrong display of the list of options. To fix this, this function is added.
//
// Arguments: None
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT
CIESelectElement::UpdatePopup()
{
HRESULT hr;
IHTMLDocument *pDocThis = NULL;
IHTMLDocument2 *pDoc2 = NULL;
IHTMLElement *pElemBODY = NULL;
IHTMLElementCollection *pItems = NULL;
IDispatch *pDispatchItem = NULL;
IHTMLStyle *pStyleBody = NULL;
#ifndef SELECT_GETSIZE
IHTMLStyle *pStyle = NULL;
IHTMLElement *pElem = NULL;
#endif
long lSize;
CVariant var, var2;
CComBSTR bstr, bstrHTML, bstrTemp;
CComBSTR bstrImport = _T("<?IMPORT namespace=IE implementation='#default'>");
TCHAR strNum[5];
CContextAccess a(_pSite);
CComBSTR bstrWritingMode(_T(""));
hr = a.Open(CA_ELEM);
if (FAILED(hr))
goto Cleanup;
//
// Get the popup's document
//
hr = _pPopup->get_document(&pDocThis);
if (FAILED(hr))
goto Cleanup;
//
// Get the popup's document
//
pDocThis->QueryInterface(IID_IHTMLDocument2, (void**)&pDoc2);
if (FAILED(hr))
goto Cleanup;
hr = pDoc2->get_body(&pElemBODY);
if (FAILED(hr))
goto Cleanup;
//
// Set the style on the popup
//
hr = pElemBODY->get_style(&pStyleBody);
if (FAILED(hr))
goto Cleanup;
hr = pStyleBody->put_border(CComBSTR(_T("1 solid")));
if (FAILED(hr))
goto Cleanup;
if (_pSelectInPopup)
{
if (_fWritingModeTBRL)
{
bstrWritingMode = _T("tb-rl");
}
hr = _pSelectInPopup->SetWritingMode(bstrWritingMode);
goto Cleanup;
}
#ifdef NEVER
hr = pStyleBody->put_margin(CComBSTR(_T("0")));
if (FAILED(hr))
goto Cleanup;
hr = pStyleBody->put_padding(CComBSTR(_T("0")));
if (FAILED(hr))
goto Cleanup;
hr = pStyleBody->put_overflow(CComBSTR(_T("hidden")));
if (FAILED(hr))
goto Cleanup;
#endif
//
// Create a SELECT element
//
hr = GetNumOptions(&lSize);
if (FAILED(hr))
goto Cleanup;
if (lSize <= 1)
{
lSize = 2;
}
_ltot(min(lSize, SELECT_MAXOPTIONS), strNum, 10);
hr = a.Elem()->get_innerHTML(&bstrHTML);
if (FAILED(hr))
goto Cleanup;
bstr = bstrImport;
bstrTemp = _T("<IE:select size=");
bstr += bstrTemp;
bstrTemp = strNum;
bstr += bstrTemp;
bstrTemp = _T(" style='border:none;margin:0px'>");
bstr += bstrTemp;
bstr += bstrHTML;
bstrTemp = _T("</IE:select>");
bstr += bstrTemp;
hr = pElemBODY->put_innerHTML(bstr);
if (FAILED(hr))
goto Cleanup;
hr = pElemBODY->get_children((IDispatch**)&pItems);
if (FAILED(hr))
goto Cleanup;
hr = pItems->item(var, var2, &pDispatchItem);
if (FAILED(hr))
goto Cleanup;
if (!pDispatchItem)
{
hr = E_FAIL;
goto Cleanup;
}
hr = pDispatchItem->QueryInterface(IID_IPrivateSelect, (void**)&_pSelectInPopup);
if (FAILED(hr))
goto Cleanup;
hr = _pSelectInPopup->SetInPopup(_pPopup);
if (FAILED(hr))
goto Cleanup;
hr = _pSelectInPopup->InitOptions();
if (FAILED(hr))
goto Cleanup;
#ifndef SELECT_GETSIZE
hr = pDispatchItem->QueryInterface(IID_IHTMLElement, (void**)&pElem);
if (FAILED(hr))
goto Cleanup;
//
// Set the width/height of the popup's select
//
hr = pElem->get_style(&pStyle);
if (FAILED(hr))
goto Cleanup;
V_VT(&var) = VT_I4;
if (_fWritingModeTBRL)
{
V_I4(&var) = _lPopupSize.cy;
}
else
{
V_I4(&var) = _lPopupSize.cx;
}
hr = pStyle->put_width(var);
if (FAILED(hr))
goto Cleanup;
if (_fWritingModeTBRL)
{
V_I4(&var) = _lPopupSize.cx;
}
else
{
V_I4(&var) = _lPopupSize.cy;
}
hr = pStyle->put_height(var);
if (FAILED(hr))
goto Cleanup;
_lPopupSize.cx += 2;
_lPopupSize.cy += 2;
#endif
Cleanup:
ReleaseInterface(pElemBODY);
ReleaseInterface(pDocThis);
ReleaseInterface(pDoc2);
ReleaseInterface(pItems);
ReleaseInterface(pDispatchItem);
ReleaseInterface(pStyleBody);
#ifndef SELECT_GETSIZE
ReleaseInterface(pStyle);
ReleaseInterface(pElem);
#endif
return hr;
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::OnFocus()
//
// Synopsis: Update focus information.
//
// Arguments: CEventObjectAccess *pEvent - Event information
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT
CIESelectElement::OnFocus(CEventObjectAccess *pEvent)
{
if (SELECT_ISDROPBOX(_fFlavor))
{
return SetDisplayHighlight(TRUE);
}
return S_OK;
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::OnBlur()
//
// Synopsis: When SELECT loses focus, the focused OPTION should
// lose focus as well.
//
// Arguments: CEventObjectAccess *pEvent - Event information
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT
CIESelectElement::OnBlur(CEventObjectAccess *pEvent)
{
HRESULT hr = S_OK;
if (SELECT_ISDROPBOX(_fFlavor))
{
hr = HidePopup();
if (FAILED(hr))
goto Cleanup;
hr = SetDisplayHighlight(FALSE);
if (FAILED(hr))
goto Cleanup;
}
Cleanup:
return hr;
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::SetDisplayHighlight()
//
// Synopsis: Sets the highlighted state of the dropbox display.
//
// Arguments: BOOL bOn - Indicates whether the display should be
// highlighted.
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT
CIESelectElement::SetDisplayHighlight(BOOL bOn)
{
HRESULT hr;
CComBSTR bstrBack;
CComBSTR bstrText;
CVariant var;
long lNumOptions;
VARIANT_BOOL bOpen;
IHTMLStyle *pStyle = NULL;
hr = GetNumOptions(&lNumOptions);
if (FAILED(hr))
goto Cleanup;
if ((lNumOptions > 0) && _pPopup && _pElemDisplay)
{
hr = _pPopup->get_isOpen(&bOpen);
if (FAILED(hr))
goto Cleanup;
if (bOn && !bOpen)
{
bstrBack = _T("highlight");
bstrText = _T("highlighttext");
}
else
{
bstrBack = _T("");
bstrText = _T("");
}
hr = _pElemDisplay->get_style(&pStyle);
if (FAILED(hr))
goto Cleanup;
hr = pStyle->put_background(bstrBack);
if (FAILED(hr))
goto Cleanup;
V_VT(&var) = VT_BSTR;
V_BSTR(&var) = (BSTR)bstrText;
hr = pStyle->put_color(var);
if (FAILED(hr))
goto Cleanup;
}
Cleanup:
ReleaseInterface(pStyle);
return hr;
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::OnMouseDown()
//
// Synopsis: Start a drag in listboxes.
// Toggles the popup open/closed in a dropbox.
//
// Arguments: CEventObjectAccess *pEvent - Event information
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT
CIESelectElement::OnMouseDown(CEventObjectAccess *pEvent)
{
HRESULT hr;
VARIANT_BOOL bDisabled;
long lMouseButtons = NULL;
hr = GetDisabled(&bDisabled);
if (FAILED(hr) || bDisabled)
goto Cleanup;
hr = pEvent->GetMouseButtons(&lMouseButtons);
if (FAILED(hr))
goto Cleanup;
if (lMouseButtons & EVENT_LEFTBUTTON)
{
if (SELECT_ISDROPBOX(_fFlavor))
{
hr = TogglePopup();
if (FAILED(hr))
goto Cleanup;
hr = SetDisplayHighlight(TRUE);
if (FAILED(hr))
goto Cleanup;
}
else if (!SELECT_ISINPOPUP(_fFlavor))
{
// Just in case a previous drag was not completed
hr = FinishDrag();
if (FAILED(hr))
goto Cleanup;
// Start the new drag
hr = StartDrag();
if (FAILED(hr))
goto Cleanup;
}
}
Cleanup:
return hr;
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::OnMouseOver()
//
// Synopsis: Used to hang onto the current popup state. Mouse events out of
// our control can shut the popup without our knowledge. This gives
// us that knowledge.
//
// Arguments: CEventObjectAccess *pEvent - Event information
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT
CIESelectElement::OnMouseOver(CEventObjectAccess *pEvent)
{
HRESULT hr = S_OK;
if (_pPopup)
{
hr = _pPopup->get_isOpen(&_bPopupOpen);
if (FAILED(hr))
goto Cleanup;
}
Cleanup:
return hr;
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::OnMouseOut()
//
// Synopsis: Used to hang onto the current popup state. Mouse events out of
// our control can shut the popup without our knowledge. This gives
// us that knowledge.
//
// Arguments: CEventObjectAccess *pEvent - Event information
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT
CIESelectElement::OnMouseOut(CEventObjectAccess *pEvent)
{
HRESULT hr = S_OK;
if (_pPopup)
{
hr = _pPopup->get_isOpen(&_bPopupOpen);
if (FAILED(hr))
goto Cleanup;
}
Cleanup:
return hr;
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::OnMouseUp()
//
// Synopsis: Finish a drag and commit all changes.
//
// Arguments: CEventObjectAccess *pEvent - Event information
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT
CIESelectElement::OnMouseUp(CEventObjectAccess *pEvent)
{
HRESULT hr;
VARIANT_BOOL bDisabled;
long lKeyboardStatus = NULL;
POINT ptElem;
hr = GetDisabled(&bDisabled);
if (FAILED(hr) || bDisabled)
goto Cleanup;
if (_fDragMode)
{
// Stop the drag
hr = FinishDrag();
if (FAILED(hr))
goto Cleanup;
hr = pEvent->GetParentCoordinates(&ptElem);
if (FAILED(hr))
goto Cleanup;
hr = pEvent->GetKeyboardStatus(&lKeyboardStatus);
if (FAILED(hr))
goto Cleanup;
// Make final selection changes
hr = HandleDownXY(ptElem, lKeyboardStatus & EVENT_CTRLKEY);
if (FAILED(hr))
goto Cleanup;
}
// Commit all selection changes
hr = FireOnChange();
if (FAILED(hr))
goto Cleanup;
Cleanup:
return hr;
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::OnMouseMove()
//
// Synopsis: Handle drag select.
//
// Arguments: CEventObjectAccess *pEvent - Event information
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT
CIESelectElement::OnMouseMove(CEventObjectAccess *pEvent)
{
HRESULT hr = S_OK;
long lMouseButtons = NULL;
long lKeyboardStatus = NULL;
POINT ptElem;
if (!_fDragMode)
goto Cleanup;
hr = ClearScrollTimeout();
if (FAILED(hr))
goto Cleanup;
hr = pEvent->GetMouseButtons(&lMouseButtons);
if (FAILED(hr))
goto Cleanup;
if (lMouseButtons & EVENT_LEFTBUTTON)
{
hr = pEvent->GetParentCoordinates(&ptElem);
if (FAILED(hr))
goto Cleanup;
hr = pEvent->GetKeyboardStatus(&lKeyboardStatus);
if (FAILED(hr))
goto Cleanup;
// Make selection changes
hr = HandleDownXY(ptElem, lKeyboardStatus & EVENT_CTRLKEY);
if (FAILED(hr))
goto Cleanup;
}
else
{
// A different mouse button is down, finish this drag
hr = FinishDrag();
if (FAILED(hr))
goto Cleanup;
}
Cleanup:
return hr;
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::StartDrag()
//
// Synopsis: Capture the mouse, get focus, engage drag mode.
//
// Arguments: None
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT
CIESelectElement::StartDrag()
{
HRESULT hr;
CContextAccess a(_pSite);
if (_fDragMode)
{
hr = S_OK;
goto Cleanup;
}
hr = a.Open(CA_ELEM2);
if (FAILED(hr))
goto Cleanup;
hr = a.Elem2()->setCapture();
if (FAILED(hr))
goto Cleanup;
hr = a.Elem2()->focus();
if (FAILED(hr))
goto Cleanup;
_fDragMode = TRUE;
Cleanup:
return hr;
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::FinishDrag()
//
// Synopsis: Release the mouse, kill scroll timer, disengage drag mode.
//
// Arguments: None
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT
CIESelectElement::FinishDrag()
{
HRESULT hr;
CContextAccess a(_pSite);
if (!_fDragMode)
{
hr = S_OK;
goto Cleanup;
}
hr = ClearScrollTimeout();
if (FAILED(hr))
goto Cleanup;
hr = a.Open(CA_ELEM2);
if (FAILED(hr))
goto Cleanup;
hr = a.Elem2()->releaseCapture();
if (FAILED(hr))
goto Cleanup;
_fDragMode = FALSE;
Cleanup:
return hr;
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::HandleDownXY()
//
// Synopsis: Performs special "clicks" during a drag.
//
// Arguments: POINT pt - Location of the mouse
// BOOL bCtrlKey - TRUE if the CTRL key is down
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT
CIESelectElement::HandleDownXY(POINT pt, BOOL bCtrlKey)
{
HRESULT hr;
long lIndex;
BOOL bNeedTimer;
long lXY;
Assert(!SELECT_ISINPOPUP(_fFlavor));
if (_fWritingModeTBRL)
{
lXY = pt.x;
}
else
{
lXY = pt.y;
}
hr = GetOptionIndexFromY(lXY, &lIndex, &bNeedTimer);
if (FAILED(hr))
goto Cleanup;
if ((hr == S_FALSE) || (_lFocusIndex == lIndex))
{
// No need to do any selecting, so stop
hr = S_OK;
goto Cleanup;
}
// Select the option
hr = OnOptionSelected(VARIANT_TRUE, lIndex, SELECT_EXTEND);
if (FAILED(hr))
goto Cleanup;
// Check for drag mode since this could be the last call
if (bNeedTimer && _fDragMode)
{
// The mouse is either above or below the control,
// so set a timer to get the control to scroll.
SetScrollTimeout(pt, bCtrlKey);
if (FAILED(hr))
goto Cleanup;
}
Cleanup:
return hr;
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::GetOptionIndexFromY()
//
// Synopsis: Determines the index of the option based on lY.
//
// Arguments: long lY - The Y coordinate
// long *plIndex - Returns the index
// BOOL *pbNeedTimer - Returns whether Y is
// above/below the control
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT
CIESelectElement::GetOptionIndexFromY(long lY, long *plIndex, BOOL *pbNeedTimer)
{
HRESULT hr;
long lTopIndex;
long lBottomIndex;
long lNumOptions;
long lIndex;
long lTop;
long lScrollTop;
long lClientTop;
long lLeft;
long lScrollLeft;
long lClientLeft;
IHTMLElement *pElemParent = NULL;
IHTMLElement *pElem = NULL;
IHTMLElement2 *pElem2 = NULL;
CContextAccess a(_pSite);
Assert(plIndex && pbNeedTimer);
*pbNeedTimer = FALSE;
hr = a.Open(CA_ELEM);
if (FAILED(hr))
goto Cleanup;
//
// Convert the mouse points relative to the upper left
// corner of the select's client rectangle.
//
pElem = a.Elem();
pElem->AddRef();
hr = pElem->QueryInterface(IID_IHTMLElement2, (void**)&pElem2);
if (FAILED(hr))
goto Cleanup;
// Go through all the parents to calculate the correct offset
while (true)
{
if (_fWritingModeTBRL)
{
hr = pElem->get_offsetLeft(&lLeft);
if (FAILED(hr))
goto Cleanup;
hr = pElem2->get_clientLeft(&lClientLeft);
if (FAILED(hr))
goto Cleanup;
lY -= lLeft + lClientLeft;
}
else
{
hr = pElem->get_offsetTop(&lTop);
if (FAILED(hr))
goto Cleanup;
hr = pElem2->get_clientTop(&lClientTop);
if (FAILED(hr))
goto Cleanup;
lY -= lTop + lClientTop;
}
hr = pElem->get_offsetParent(&pElemParent);
if (FAILED(hr))
goto Cleanup;
if (pElemParent == NULL)
{
break;
}
ClearInterface(&pElem);
ClearInterface(&pElem2);
pElem = pElemParent;
pElemParent = NULL;
hr = pElem->QueryInterface(IID_IHTMLElement2, (void**)&pElem2);
if (FAILED(hr))
goto Cleanup;
if (_fWritingModeTBRL)
{
hr = pElem2->get_scrollLeft(&lScrollLeft);
if (FAILED(hr))
goto Cleanup;
lY += lScrollLeft;
}
else
{
hr = pElem2->get_scrollTop(&lScrollTop);
if (FAILED(hr))
goto Cleanup;
lY += lScrollTop;
}
}
//
// Calculate the clicked index
//
hr = GetTopVisibleOptionIndex(&lTopIndex);
if (FAILED(hr))
goto Cleanup;
if (lY < 0)
{
//
// If lY is negative, we need to subtract an extra height
// so that the match will come out right when the division
// truncates the remainder.
//
// We also want to restrict the index to only one option
// beyond the visible range of the control.
//
lY = -_lMaxHeight;
*pbNeedTimer = TRUE;
}
lIndex = (lY / _lMaxHeight) + lTopIndex;
hr = GetBottomVisibleOptionIndex(&lBottomIndex);
if (FAILED(hr))
goto Cleanup;
// If the writingmode is "tb-rl", the count which is made from left has to be transformed to the
// count from right.
if (_fWritingModeTBRL)
{
lIndex = lBottomIndex - lIndex -1;
}
if (lIndex >= lBottomIndex)
{
//
// We want to restrict the index to only one option
// beyond the visible range of the control.
//
lIndex = lBottomIndex;
*pbNeedTimer = TRUE;
}
//
// Make sure the index is valid
//
hr = GetNumOptions(&lNumOptions);
if (FAILED(hr))
goto Cleanup;
if ((lIndex >= 0) && (lIndex < lNumOptions))
{
// Success!
*plIndex = lIndex;
hr = S_OK;
}
else
{
// Invalid index
hr = S_FALSE;
}
Cleanup:
ReleaseInterface(pElem);
ReleaseInterface(pElem2);
ReleaseInterface(pElemParent);
return hr;
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::OnKeyDown()
//
// Synopsis: Changes the selection based on the key pressed.
//
// Arguments: CEventObjectAccess *pEvent - Event information
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT
CIESelectElement::OnKeyDown(CEventObjectAccess *pEvent)
{
HRESULT hr;
DWORD dwFlags = 0;
long lNumOptions;
long lSize;
long lKeyboardStatus = 0;
long lKeyCode;
VARIANT_BOOL bDisabled;
VARIANT_BOOL bSelected;
VARIANT_BOOL bIsOpen;
BOOL bCancelEvent = FALSE;
IPrivateOption *pOption = NULL;
hr = GetDisabled(&bDisabled);
if (FAILED(hr) || bDisabled)
goto Cleanup;
hr = GetNumOptions(&lNumOptions);
if (FAILED(hr) || (lNumOptions == 0)) // No need to do anything if no options
goto Cleanup;
hr = pEvent->GetKeyboardStatus(&lKeyboardStatus);
if (FAILED(hr))
goto Cleanup;
dwFlags |= (lKeyboardStatus & EVENT_ALTKEY) ? SELECT_ALT : 0;
dwFlags |= (lKeyboardStatus & EVENT_SHIFTKEY) ? SELECT_SHIFT : 0;
dwFlags |= (lKeyboardStatus & EVENT_CTRLKEY) ? SELECT_CTRL : 0;
hr = pEvent->GetKeyCode(&lKeyCode);
if (FAILED(hr))
goto Cleanup;
switch (lKeyCode)
{
case VK_UP:
case VK_DOWN:
if (SELECT_ISDROPBOX(_fFlavor))
{
Assert(_pPopup && _pSelectInPopup);
hr = _pPopup->get_isOpen(&bIsOpen);
if (FAILED(hr))
goto Cleanup;
if (lKeyboardStatus & EVENT_ALTKEY)
{
if (bIsOpen)
{
hr = _pSelectInPopup->SelectCurrentOption(dwFlags);
if (FAILED(hr))
goto Cleanup;
}
else
{
hr = ShowPopup();
if (FAILED(hr))
goto Cleanup;
}
}
else
{
BOOL bChanged;
hr = _pSelectInPopup->MoveFocusByOne(lKeyCode == VK_UP, dwFlags | (!bIsOpen ? SELECT_FIREEVENT : 0));
if (FAILED(hr))
goto Cleanup;
hr = _pSelectInPopup->CommitSelection(&bChanged);
if (FAILED(hr))
goto Cleanup;
if (bChanged)
{
hr = SynchSelWithPopup();
if (FAILED(hr))
goto Cleanup;
}
}
}
else if ( SELECT_ISINPOPUP(_fFlavor))
{
VARIANT_BOOL bOpen;
long lSize;
long lPrevIndex;
long lTopOptionIndex;
long lBottomOptionIndex;
Assert(_pPopup);
Assert(_pLastHighlight);
hr = _pLastHighlight->GetIndex(&lPrevIndex);
if (FAILED(hr))
goto Cleanup;
hr = get_size(&lSize);
if (FAILED(hr))
goto Cleanup;
hr = GetTopVisibleOptionIndex(&lTopOptionIndex);
if (FAILED(hr))
goto Cleanup;
hr = GetBottomVisibleOptionIndex(&lBottomOptionIndex);
if (FAILED(hr))
goto Cleanup;
hr = _pLastHighlight->SetHighlight(VARIANT_FALSE);
if (FAILED(hr))
goto Cleanup;
if ( lKeyCode == VK_DOWN)
{
_lFocusIndex = (lNumOptions == (lPrevIndex +1)) ? lPrevIndex : lPrevIndex +1 ;
}
else
{
_lFocusIndex = ( lPrevIndex == 0 ) ? lPrevIndex : lPrevIndex -1 ;
}
hr = GetIndex(_lFocusIndex , &pOption);
if (FAILED(hr) || !pOption)
goto Cleanup;
hr = pOption->SetIndex(VARIANT_TRUE);
if (FAILED(hr))
goto Cleanup;
// Highlight the item
hr = pOption->SetHighlight(VARIANT_TRUE);
if (FAILED(hr))
goto Cleanup;
if (_pLastHighlight)
{
ClearInterface(&_pLastHighlight);
}
_pLastHighlight = pOption;
_pLastHighlight->AddRef();
hr = OnOptionFocus(_lFocusIndex, VARIANT_TRUE);
if (FAILED(hr))
goto Cleanup;
hr = _pLastHighlight -> SetSelected(VARIANT_TRUE);
if (FAILED(hr))
goto Cleanup;
hr = _pLastHighlight -> SetIndex(_lFocusIndex);
if (FAILED(hr))
goto Cleanup;
if ( ((lKeyCode == VK_UP) && ((lBottomOptionIndex - lPrevIndex) == lSize)) ||
((lKeyCode == VK_DOWN) && ((lPrevIndex - lTopOptionIndex) == lSize - 1 )) )
{
hr = MakeOptionVisible(pOption);
if (FAILED(hr))
goto Cleanup;
}
}
else if (!(lKeyboardStatus & EVENT_ALTKEY))
{
hr = MoveFocusByOne(lKeyCode == VK_UP, dwFlags | SELECT_FIREEVENT);
if (FAILED(hr))
goto Cleanup;
}
bCancelEvent = TRUE;
break;
case VK_SPACE:
if (SELECT_ISMULTIPLE(_fFlavor))
{
// Select the current OPTION
hr = OnOptionClicked(_lFocusIndex, dwFlags | SELECT_FIREEVENT);
if (FAILED(hr))
goto Cleanup;
}
bCancelEvent = TRUE;
break;
case VK_RETURN:
if (SELECT_ISDROPBOX(_fFlavor))
{
// Select the current OPTION
Assert(_pSelectInPopup);
hr = _pSelectInPopup->SelectCurrentOption(dwFlags);
if (FAILED(hr))
goto Cleanup;
}
else if ( SELECT_ISINPOPUP(_fFlavor))
{
hr = OnOptionClicked(_lFocusIndex, dwFlags | SELECT_FIREEVENT);
if (FAILED(hr))
goto Cleanup;
}
bCancelEvent = TRUE;
break;
case VK_HOME:
hr = OnOptionClicked(0, dwFlags | SELECT_FIREEVENT);
if (FAILED(hr))
goto Cleanup;
bCancelEvent = TRUE;
break;
case VK_END:
hr = OnOptionClicked(lNumOptions - 1, dwFlags | SELECT_FIREEVENT);
if (FAILED(hr))
goto Cleanup;
bCancelEvent = TRUE;
break;
case VK_PRIOR:
hr = PushFocusToExtreme(FALSE);
if (FAILED(hr))
goto Cleanup;
hr = SelectVisibleIndex(0);
if (FAILED(hr))
goto Cleanup;
bCancelEvent = TRUE;
break;
case VK_NEXT:
hr = PushFocusToExtreme(TRUE);
if (FAILED(hr))
goto Cleanup;
hr = get_size(&lSize);
if (FAILED(hr))
goto Cleanup;
hr = SelectVisibleIndex(lSize - 1);
if (FAILED(hr))
goto Cleanup;
bCancelEvent = TRUE;
break;
case VK_ESCAPE:
if (SELECT_ISDROPBOX(_fFlavor))
{
hr = SetDisplayHighlight(TRUE);
if (FAILED(hr))
goto Cleanup;
}
bCancelEvent = TRUE;
break;
default:
//
// If the keypressed can be displayed on screen,
// then look for it in the options.
//
if (_istgraph((TCHAR)lKeyCode))
{
hr = SelectByKey(lKeyCode);
if (FAILED(hr))
goto Cleanup;
}
// Forces WM_CHAR to be generated when handling OnKeyDown
hr = S_FALSE;
break;
}
if (bCancelEvent)
{
hr = CancelEvent(pEvent);
if (FAILED(hr))
goto Cleanup;
}
Cleanup:
if ( pOption )
ReleaseInterface(pOption);
return hr;
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::MoveFocusByOne()
//
// Synopsis: Moves the focus up/down by one.
//
// Arguments: BOOL bUp - TRUE if moving up
// DWORD dwFlags - Keyboard status flags
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT STDMETHODCALLTYPE
CIESelectElement::MoveFocusByOne(BOOL bUp, DWORD dwFlags)
{
HRESULT hr;
long lNumOptions;
long lDir = bUp ? -1 : 1;
IPrivateOption *pOption = NULL;
// No options, nothing to do
hr = GetNumOptions(&lNumOptions);
if (FAILED(hr))
goto Cleanup;
// Trying to move beyond an extreme, stop
if ((bUp && (_lFocusIndex <= 0)) ||
(!bUp && (_lFocusIndex >= (lNumOptions - 1))))
goto Cleanup;
if (SELECT_ISMULTIPLE(_fFlavor) && (dwFlags & SELECT_CTRL))
{
// Move the focus
hr = OnOptionFocus(_lFocusIndex + lDir);
if (FAILED(hr))
goto Cleanup;
hr = GetIndex(_lFocusIndex, &pOption);
if (FAILED(hr))
goto Cleanup;
hr = MakeOptionVisible(pOption);
if (FAILED(hr))
goto Cleanup;
}
else
{
// Select the option
hr = OnOptionClicked(_lFocusIndex + lDir, dwFlags);
if (FAILED(hr))
goto Cleanup;
}
Cleanup:
ReleaseInterface(pOption);
return hr;
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::SelectCurrentOption()
//
// Synopsis: Selects the option that currently has focus.
//
// Arguments: DWORD dwFlags - Keyboard status flags
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT STDMETHODCALLTYPE
CIESelectElement::SelectCurrentOption(DWORD dwFlags)
{
return OnOptionClicked(_lFocusIndex, dwFlags | SELECT_FIREEVENT);
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::SelectByKey()
//
// Synopsis: Selects the next option that begins with the given key.
// Loops around, if the end is reached.
//
// Arguments: long lKey - The character to look for
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT
CIESelectElement::SelectByKey(long lKey)
{
HRESULT hr;
long lNumOptions;
long lStart;
long lIndex;
IPrivateOption *pOption = NULL;
hr = GetNumOptions(&lNumOptions);
if (FAILED(hr))
goto Cleanup;
lStart = _lFocusIndex + 1;
if (lStart >= lNumOptions)
{
lStart = 0;
}
// Search from the start index to the last option
hr = SearchForKey(lKey, lStart, lNumOptions - 1, &lIndex);
if (FAILED(hr))
goto Cleanup;
if ((hr == S_FALSE) && (lStart > 0))
{
//
// If the previous search did not find anything,
// then search from index 0 to just before the
// previous start index.
//
hr = SearchForKey(lKey, 0, lStart - 1, &lIndex);
if (FAILED(hr))
goto Cleanup;
}
if (hr == S_OK)
{
// Select the found option
hr = OnOptionClicked(lIndex, SELECT_FIREEVENT);
if (FAILED(hr))
goto Cleanup;
}
Cleanup:
ReleaseInterface(pOption);
return hr;
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::SelectByKey()
//
// Synopsis: Selects the next option that begins with the given key.
// Loops around, if the end is reached.
//
// Arguments: long lKey - The character to look for
// long lStart - The first index to check
// long lEnd - The last index to check
// long *plIndex - The index of the found option (or -1)
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT
CIESelectElement::SearchForKey(long lKey, long lStart, long lEnd, long *plIndex)
{
HRESULT hr;
IHTMLElementCollection *pItems = NULL;
IDispatch *pItem = NULL;
IHTMLElement *pOption = NULL;
long iItem;
CComBSTR bstr;
CVariant var, var2;
CContextAccess a(_pSite);
Assert(plIndex);
hr = a.Open(CA_ELEM);
if (FAILED(hr))
goto Cleanup;
hr = a.Elem()->get_children((IDispatch**)&pItems);
if (FAILED(hr))
goto Cleanup;
V_VT(&var) = VT_I4;
for (iItem = lStart; iItem <= lEnd; iItem++)
{
V_I4(&var) = iItem;
hr = pItems->item(var, var2, &pItem);
if (FAILED(hr))
goto Cleanup;
hr = pItem->QueryInterface(IID_IHTMLElement, (void**)&pOption);
if (FAILED(hr))
{
ClearInterface(&pItem);
continue;
}
hr = pOption->get_innerText(&bstr);
if (FAILED(hr))
goto Cleanup;
if ( bstr.m_str
&& bstr.m_str[0]
&& _totupper((TCHAR)bstr[0]) == _totupper((TCHAR)lKey))
{
// An option is found
*plIndex = iItem;
hr = S_OK;
goto Cleanup;
}
ClearInterface(&pOption);
ClearInterface(&pItem);
}
// No option found
hr = S_FALSE;
Cleanup:
ReleaseInterface(pOption);
ReleaseInterface(pItem);
ReleaseInterface(pItems);
return hr;
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::OnPropertyChange()
//
// Synopsis: Allows setting of a global variable about the writingmode.
//
// Arguments: CEventObjectAccess *pEvent - Event info
// BSTR bstr
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT
CIESelectElement::OnPropertyChange(CEventObjectAccess *pEvent, BSTR bstr)
{
HRESULT hr = S_OK;
if (!StrCmpICW(bstr, L"style.writingMode"))
{
hr = SetWritingModeFlag();
if (FAILED(hr))
goto Cleanup;
hr = RefreshView();
if (FAILED(hr))
goto Cleanup;
}
Cleanup:
return S_OK;
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::SetWritingModeFlag()
//
// Synopsis: The _fWritingModeTBRL flag is set properly based on
// the writingmode.
//
// Arguments: None.
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT
CIESelectElement:: SetWritingModeFlag()
{
CContextAccess a(_pSite);
HRESULT hr = S_OK;
CComBSTR bstrWritingMode(_T(""));
hr = a.Open(CA_STYLE3);
if (FAILED(hr))
goto Cleanup;
hr = a.Style3()->get_writingMode(&bstrWritingMode);
if (FAILED(hr))
goto Cleanup;
if(!bstrWritingMode || StrCmpICW(bstrWritingMode, L"tb-rl"))
{
_fWritingModeTBRL = FALSE;
}
else
{
_fWritingModeTBRL = TRUE;
}
Cleanup:
return hr;
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::SetPixelHeightWidth()
//
// Synopsis: The pixelheight and pixelwidth are set accordingly
// based on the writingmode.
//
// Arguments: None.
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT
CIESelectElement:: SetPixelHeightWidth()
{
CContextAccess a(_pSite);
HRESULT hr = S_OK;
long lWidth;
long lHeight;
long lButtonWidth;
hr = a.Open(CA_DEFSTYLE);
if (FAILED(hr))
goto Cleanup;
// Get the width of the button
lButtonWidth = GetSystemMetrics(SM_CXVSCROLL);
if (_fWritingModeTBRL)
{
lWidth = _sizeOptionReported.cy + 2;
lHeight = lButtonWidth;
}
else
{
lWidth = lButtonWidth;
lHeight = _sizeOptionReported.cy + 2;
}
// Set the height
hr = a.DefStyle()->put_pixelHeight(lHeight);
if (FAILED(hr))
goto Cleanup;
// Set the width
hr = a.DefStyle()->put_pixelWidth(lWidth);
if (FAILED(hr))
goto Cleanup;
Cleanup:
return hr;
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::RefreshView()
//
// Synopsis: The view has to be changed after the writing mode is changed.
//
// Arguments: None.
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT
CIESelectElement:: RefreshView()
{
HRESULT hr = S_OK;
CContextAccess a(_pSite);
CComBSTR bstrWritingMode(_T(""));
CVariant var;
long lWidth;
long lHeight;
long lButtonWidth;
hr = a.Open(CA_DEFSTYLE);
if (FAILED(hr))
goto Cleanup;
if(SELECT_ISLISTBOX(_fFlavor))
{
if (_fWritingModeTBRL)
{
hr = SetPixelHeightWidth();
if (FAILED(hr))
goto Cleanup;
}
}
if (SELECT_ISDROPBOX(_fFlavor))
{
if (_fWritingModeTBRL)
{
bstrWritingMode = _T("tb-rl");
}
hr = SetWritingMode(bstrWritingMode);
if (FAILED(hr))
goto Cleanup;
hr = UpdatePopup();
if (FAILED(hr))
goto Cleanup;
if (_pElemDisplay)
{
hr = SetupDisplay(_pElemDisplay);
if (FAILED(hr))
goto Cleanup;
_pElemDisplay->Release();
hr = SetupDropControlDimensions();
if (FAILED(hr))
goto Cleanup;
}
}
Cleanup:
return hr;
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::OnSelectStart()
//
// Synopsis: Cancels selection.
//
// Arguments: CEventObjectAccess *pEvent - Event information
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT
CIESelectElement::OnSelectStart(CEventObjectAccess *pEvent)
{
return CancelEvent(pEvent);
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::OnScroll()
//
// Synopsis: Cancels scroll.
//
// Arguments: CEventObjectAccess *pEvent - Event information
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT
CIESelectElement::OnScroll(CEventObjectAccess *pEvent)
{
return RefreshFocusRect();
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::OnContextMenu()
//
// Synopsis: Cancels menu.
//
// Arguments: CEventObjectAccess *pEvent - Event information
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT
CIESelectElement::OnContextMenu(CEventObjectAccess *pEvent)
{
return CancelEvent(pEvent);
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::CancelEvent()
//
// Synopsis: Cancels the event.
//
// Arguments: CEventObjectAccess *pEvent - Event information
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT
CIESelectElement::CancelEvent(CEventObjectAccess *pEvent)
{
HRESULT hr;
CVariant var;
V_VT(&var) = VT_BOOL;
V_BOOL(&var) = VARIANT_FALSE;
hr = pEvent->EventObj()->put_returnValue(var);
if (FAILED(hr))
goto Cleanup;
Cleanup:
return hr;
}
/////////////////////////////////////////////////////////////////////////////
//
// IHTMLSelectElement3 overrides
//
/////////////////////////////////////////////////////////////////////////////
//+------------------------------------------------------------------
//
// Member: CIESelectElement::clearSelection()
//
// Synopsis: Deselects all of the OPTION elements.
//
// Arguments: None
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT
CIESelectElement::clearSelection()
{
HRESULT hr = S_OK;
if (SELECT_ISMULTIPLE(_fFlavor))
{
hr = SetAllSelected(VARIANT_FALSE, SELECT_FIREEVENT);
if (FAILED(hr))
goto Cleanup;
}
// Don't allow single-select to deselect
Cleanup:
return hr;
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::selectAll()
//
// Synopsis: Selects all of the OPTION elements.
//
// Arguments: None
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT
CIESelectElement::selectAll()
{
HRESULT hr;
long lNumOptions;
if (SELECT_ISMULTIPLE(_fFlavor))
{
hr = SetAllSelected(VARIANT_TRUE, SELECT_FIREEVENT);
if (FAILED(hr))
goto Cleanup;
}
else
{
// Single select selects the last element
hr = GetNumOptions(&lNumOptions);
if (FAILED(hr) || (lNumOptions == 0))
goto Cleanup;
hr = OnOptionSelected(VARIANT_TRUE, lNumOptions - 1, SELECT_FIREEVENT);
if (FAILED(hr))
goto Cleanup;
}
Cleanup:
return hr;
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::put_name()
//
// Synopsis: Sets the name of the select.
//
// Arguments: BSTR bstrName - The new name
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT
CIESelectElement::put_name(BSTR bstrName)
{
return GetProps()[eName].Set(bstrName);
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::get_name()
//
// Synopsis: Retrieves the name of the select.
//
// Arguments: BSTR *pbstrName - Receives the value
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT
CIESelectElement::get_name(BSTR *pbstrName)
{
return GetProps()[eName].Get(pbstrName);
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::put_size()
//
// Synopsis: Sets the size of the SELECT.
//
// Arguments: long lSize - The new size
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT
CIESelectElement::put_size(long lSize)
{
HRESULT hr = S_OK;
VARIANT_BOOL bMult;
CVariant var;
long lPrevSize;
CContextAccess a(_pSite);
if (lSize < 1)
{
return E_INVALIDARG;
}
else
{
get_size(&lPrevSize);
if (lPrevSize == lSize)
{
// No need to change.
return S_OK;
}
hr = GetProps()[eSize].Set(lSize);
if (FAILED(hr))
goto Cleanup;
#ifdef SELECT_GETSIZE
hr = get_multiple(&bMult);
if (FAILED(hr))
goto Cleanup;
// Detect major change
if ((lSize == 1) && SELECT_ISLISTBOX(_fFlavor) && !bMult)
{
hr = BecomeDropBox();
if (FAILED(hr))
goto Cleanup;
}
else if ((lSize > 1) && SELECT_ISDROPBOX(_fFlavor))
{
hr = BecomeListBox();
if (FAILED(hr))
goto Cleanup;
}
else
{
// Reset the height --> CalcSize gets called
hr = a.Open(CA_DEFSTYLE);
if (FAILED(hr))
goto Cleanup;
V_VT(&var) = VT_I4;
V_I4(&var) = _sizeOptionReported.cy * lSize;
hr = a.DefStyle()->put_height(var);
if (FAILED(hr))
goto Cleanup;
hr = RefreshListBox();
if (FAILED(hr))
goto Cleanup;
}
#endif
}
Cleanup:
return hr;
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::BecomeListBox()
//
// Synopsis: Changes a dropbox into a listbox.
//
// Arguments: None
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT
CIESelectElement::BecomeListBox()
{
HRESULT hr;
_sizeOption = _sizeOptionReported;
hr = DeInitViewLink();
if (FAILED(hr))
goto Cleanup;
_fContentReady = FALSE;
hr = InitContent();
if (FAILED(hr))
goto Cleanup;
Cleanup:
return hr;
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::BecomeDropBox()
//
// Synopsis: Changes a listbox into a dropbox.
//
// Arguments: None
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT
CIESelectElement::BecomeDropBox()
{
HRESULT hr;
_sizeOption = _sizeOptionReported;
hr = InitContent();
if (FAILED(hr))
goto Cleanup;
hr = InitViewLink();
if (FAILED(hr))
goto Cleanup;
Cleanup:
return hr;
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::RefreshListBox()
//
// Synopsis: Refreshes content settings on a listbox.
//
// Arguments: None
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT
CIESelectElement::RefreshListBox()
{
HRESULT hr;
_sizeOption = _sizeOptionReported;
hr = InitContent();
if (FAILED(hr))
goto Cleanup;
if ( !_fNeedScrollBar)
{
AdjustSizeForScrollbar(&_sizeOption);
}
Cleanup:
return hr;
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::get_size()
//
// Synopsis: Gets the size of the SELECT.
//
// Arguments: long *plSize - Receives the size
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT
CIESelectElement::get_size(long *plSize)
{
return GetProps()[eSize].Get(plSize);
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::put_selectedIndex()
//
// Synopsis: Sets the index to be selected.
// Any currently selected options are unselected.
//
// Arguments: long lIndex - The index to select
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT
CIESelectElement::put_selectedIndex(long lIndex)
{
HRESULT hr;
IHTMLOptionElement2 *pOption = NULL;
hr = clearSelection();
if (FAILED(hr))
goto Cleanup;
hr = GetIndex(lIndex, &pOption);
if (FAILED(hr))
goto Cleanup;
hr = pOption->put_selected(VARIANT_TRUE);
if (FAILED(hr))
goto Cleanup;
Cleanup:
ReleaseInterface(pOption);
return hr;
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::get_selectedIndex()
//
// Synopsis: Gets the last index to be selected.
//
// Arguments: long *plIndex - Receives the index
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT
CIESelectElement::get_selectedIndex(long *plIndex)
{
if (SELECT_ISMULTIPLE(_fFlavor))
{
return GetFirstSelected(plIndex);
}
else
{
return GetProps()[eSelectedIndex].Get(plIndex);
}
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::put_multiple()
//
// Synopsis: Turns multiple selection on/off.
//
// Arguments: VARIANT_BOOL bMult - VARIANT_TRUE=on VARIANT_FALSE=off
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT
CIESelectElement::put_multiple(VARIANT_BOOL bMult)
{
HRESULT hr;
VARIANT_BOOL bPrevMult;
long lMult = bMult ? 0 : -1;
hr = get_multiple(&bPrevMult);
if (FAILED(hr))
goto Cleanup;
hr = GetProps()[eMultiple].Set(lMult);
if (FAILED(hr))
goto Cleanup;
if (bPrevMult == bMult)
{
// Done
goto Cleanup;
}
if (bMult && SELECT_ISDROPBOX(_fFlavor))
{
hr = BecomeListBox();
if (FAILED(hr))
goto Cleanup;
}
else
{
long lSize;
hr = get_size(&lSize);
if (FAILED(hr))
goto Cleanup;
if (!bMult && (lSize == 1) && SELECT_ISLISTBOX(_fFlavor))
{
hr = BecomeDropBox();
if (FAILED(hr))
goto Cleanup;
}
else
{
hr = RefreshListBox();
if (FAILED(hr))
goto Cleanup;
}
}
Cleanup:
return hr;
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::get_multiple()
//
// Synopsis: Gets if the SELECT is a multiple select.
//
// Arguments: VARIANT_BOOL *p - Receives if it is a multiple select
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT
CIESelectElement::get_multiple(VARIANT_BOOL * p)
{
HRESULT hr;
long lMult;
hr = GetProps()[eMultiple].Get(&lMult);
if (FAILED(hr))
goto Cleanup;
*p = (lMult == -1) ? VARIANT_FALSE : VARIANT_TRUE;
Cleanup:
return hr;
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::get_length()
//
// Synopsis: Retrieves the number of options.
//
// Arguments: long *plLength - Receives the value
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT
CIESelectElement::get_length(long *plLength)
{
return GetNumOptions(plLength);
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::get_type()
//
// Synopsis: Retrieves the name of the select.
//
// Arguments: BSTR *pbstrType - Receives the value
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT
CIESelectElement::get_type(BSTR *pbstrType)
{
CComBSTR bstr;
if (!pbstrType)
{
return E_NOTIMPL;
}
Assert(pbstrType);
if (SELECT_ISMULTIPLE(_fFlavor))
{
bstr = _T("select-multiple");
}
else
{
bstr = _T("select-one");
}
*pbstrType = bstr;
return S_OK;
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::get_options()
//
// Synopsis: Maps to children.
//
// Arguments: IDispatch **ppOptions - receives children collection
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT
CIESelectElement::get_options(IDispatch ** ppOptions)
{
HRESULT hr;
CComPtr<IDispatch> pItems;
CContextAccess a(_pSite);
if (!ppOptions)
{
hr = E_INVALIDARG;
goto Cleanup;
}
//
// Restoring the old way of handling the options collection until
// we can sort out the DISPID issue.
//
// This version simply returns the IHTMLElementCollection of children which doesn't
// support Add/Remove, etc. The new way will again return the select element's
// dispatch interface and support the standard collection interface methods directly.
//
hr = a.Open(CA_ELEM);
if (FAILED(hr))
goto Cleanup;
hr = a.Elem()->get_children((IDispatch**) &pItems);
if (FAILED(hr))
goto Cleanup;
hr = pItems->QueryInterface(IID_IDispatch, (void **) ppOptions);
if (FAILED(hr))
goto Cleanup;
Cleanup:
return hr;
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::add()
//
// Synopsis: Adds the element to the options collection
//
// Arguments: IDispatch *pElement - option element to be added
// long lIndex - position in collection where the element is to be added
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT
CIESelectElement::add(IDispatch *pElement, VARIANT varIndex)
{
HRESULT hr;
CComPtr<IHTMLOptionElement2> pOption;
CComPtr<IHTMLElement> pHtml, pParent;
CVariant var;
long lIndex;
Assert(pElement);
hr = pElement->QueryInterface(__uuidof(IHTMLOptionElement2), (void **) &pOption);
if (FAILED(hr))
goto Cleanup;
hr = var.CoerceVariantArg(&varIndex, VT_I4);
if (FAILED(hr))
goto Cleanup;
lIndex = var.IsEmpty() ? -1 : V_I4(&var);
if (lIndex < -1)
{
hr = E_INVALIDARG;
goto Cleanup;
}
//
// Check to see if this has already been added and is part of this document
// by checking to see if it has a parent element already
//
hr = pElement->QueryInterface( __uuidof(IHTMLElement), (void **) &pHtml);
if (FAILED(hr))
goto Cleanup;
hr = pHtml->get_parentElement(&pParent);
if (FAILED(hr))
goto Cleanup;
if(pParent)
{
hr = E_INVALIDARG;
goto Cleanup;
}
hr = AddOptionHelper(pOption, lIndex);
if (FAILED(hr))
goto Cleanup;
if (_pSelectInPopup)
{
ClearInterface(&_pSelectInPopup) ;
UpdatePopup();
}
Cleanup:
return hr;
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::remove()
//
// Synopsis: Removes the element from the options collection
//
// Arguments: long lIndex - position in collection of the element that is to be removed
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT
CIESelectElement::remove(long lIndex)
{
HRESULT hr;
if (lIndex < 0)
{
hr = E_INVALIDARG;
goto Cleanup;
}
hr = RemoveOptionHelper(lIndex);
Cleanup:
return hr;
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::AddOptionHelper()
//
// Synopsis: Inserts the option element into the select's child collection
//
// Arguments: IHTMLOptionElement2 *pOption - option element to be added
// long lIndex - position in collection where the element is to be added
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT
CIESelectElement::AddOptionHelper(IHTMLOptionElement2 *pOption, long lIndex)
{
HRESULT hr;
CComPtr<IHTMLElementCollection> pItems;
CComPtr<IHTMLOptionElement2> pPrevOption;
CComPtr<IHTMLDOMNode> pElementNode, pOptionNode, pPrevNode;
CComVariant vDispatch;
CContextAccess a(_pSite);
long cOptions;
Assert( lIndex >= -1 );
hr = a.Open(CA_ELEM);
if (FAILED(hr))
goto Cleanup;
// Get number of options
hr = get_length(&cOptions);
if (FAILED(hr))
goto Cleanup;
// Calculate the position of the added item within the child collection
if ( lIndex == -1 || lIndex >= cOptions) // append
{
pPrevNode = NULL;
}
else
{
// Get the adjacent option if one exists
hr = GetIndex(lIndex, &pPrevOption);
if (FAILED(hr))
goto Cleanup;
hr = pPrevOption->QueryInterface( __uuidof(IHTMLDOMNode), (void **) &pPrevNode);
if (FAILED(hr))
goto Cleanup;
}
// Insert the option
hr = a.Elem()->QueryInterface( __uuidof(IHTMLDOMNode), (void **) &pElementNode);
if (FAILED(hr))
goto Cleanup;
hr = pOption->QueryInterface( __uuidof(IHTMLDOMNode), (void **) &pOptionNode);
if (FAILED(hr))
goto Cleanup;
vDispatch = pPrevNode;
hr = pElementNode->insertBefore(pOptionNode, vDispatch, NULL);
if (FAILED(hr))
goto Cleanup;
Cleanup:
return ResetIndexes();
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::remove()
//
// Synopsis: Removes the option element from the select's child collection
//
// Arguments: long lIndex - position in collection where the element is to be added
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT
CIESelectElement::RemoveOptionHelper(long lIndex)
{
HRESULT hr = S_OK;
CComPtr<IHTMLElementCollection> pItems;
CComPtr<IHTMLOptionElement2> pOption;
CComPtr<IHTMLDOMNode> pElementNode, pOptionNode;
CContextAccess a(_pSite);
long cOptions;
Assert( lIndex >= -1 );
hr = a.Open(CA_ELEM);
if (FAILED(hr))
goto Cleanup;
// Get number of options
hr = get_length(&cOptions);
if (FAILED(hr))
goto Cleanup;
// Get the option at the given index
if(lIndex >= cOptions)
goto Cleanup;
hr = GetIndex(lIndex, &pOption);
if (FAILED(hr))
goto Cleanup;
// Remove it
hr = a.Elem()->QueryInterface( __uuidof(IHTMLDOMNode), (void **) &pElementNode);
if (FAILED(hr))
goto Cleanup;
hr = pOption->QueryInterface( __uuidof(IHTMLDOMNode), (void **) &pOptionNode);
if (FAILED(hr))
goto Cleanup;
hr = pElementNode->removeChild(pOptionNode, NULL);
if (FAILED(hr))
goto Cleanup;
Cleanup:
return ResetIndexes();
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::ParseChildren()
//
// Synopsis: Walks through the list of children looking for any elements that
// do not support IPrivateOption (and are therefor not ie:option elements)
// and removes them from the document.
//
// Arguments: None
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT
CIESelectElement::ParseChildren()
{
HRESULT hr;
CContextAccess a(_pSite);
CComVariant name, index;
CComPtr<IHTMLElementCollection> pItems;
CComPtr<IDispatch> pDispatch;
CComPtr<IPrivateOption> pOption;
CComPtr<IHTMLDOMNode> pElementNode, pOptionNode;
long i, cItems;
//
// Get all the children
//
hr = a.Open(CA_ELEM);
if (FAILED(hr))
goto Cleanup;
hr = a.Elem()->get_children((IDispatch**) &pItems);
if (FAILED(hr))
goto Cleanup;
hr = pItems->get_length(&cItems);
if (FAILED(hr))
goto Cleanup;
hr = a.Elem()->QueryInterface( __uuidof(IHTMLDOMNode), (void **) &pElementNode);
if (FAILED(hr))
goto Cleanup;
//
// Iterator through the collection looking for those who don't belong
//
for (i = cItems - 1; i >= 0; i--)
{
name = i;
hr = pItems->item(name, index, &pDispatch);
if (FAILED(hr))
goto Cleanup;
//
// If it's not an option element, remove it; it doesn't belong
//
hr = pDispatch->QueryInterface(IID_IPrivateOption, (void **) &pOption);
if (FAILED(hr))
{
hr = pDispatch->QueryInterface( __uuidof(IHTMLDOMNode), (void **) &pOptionNode);
if (FAILED(hr))
goto Cleanup;
hr = pElementNode->removeChild(pOptionNode, NULL);
if (FAILED(hr))
goto Cleanup;
pOptionNode.Release();
}
pDispatch.Release();
pOption.Release();
}
Cleanup:
return hr;
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::ResetIndexes()
//
// Synopsis: Sets the index of each child to the current position in the
// child collection.
//
// Note: this is only here because of the fact that the option elements
// hold onto the index. This should be removed since it makes dealing with
// the dynamic collection difficult.
//
// Arguments: None
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT
CIESelectElement::ResetIndexes()
{
HRESULT hr = S_OK;
CComPtr<IHTMLElementCollection> pItems;
CComPtr<IDispatch> pDispatch;
CComPtr<IPrivateOption> pPrivOption;
CComVariant name, index;
long i;
long cOptions;
// get the option collection and iterate through it
hr = get_length(&cOptions);
if (FAILED(hr))
goto Cleanup;
for(i = 0; i < cOptions; i++)
{
name = i;
hr = item(name, index, &pDispatch);
if (FAILED(hr))
goto Cleanup;
hr = pDispatch->QueryInterface(IID_IPrivateOption, (void **) &pPrivOption);
if (FAILED(hr))
goto Cleanup;
hr = pPrivOption->SetIndex(i);
if (FAILED(hr))
goto Cleanup;
pDispatch.Release();
pPrivOption.Release();
}
Cleanup:
return hr;
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::get__newEnum()
//
// Synopsis: Standard get__newEnum collection iterator
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT
CIESelectElement::get__newEnum(IUnknown ** p)
{
HRESULT hr = S_OK;
CContextAccess a(_pSite);
CComPtr<IHTMLElementCollection> pItems;
hr = a.Open(CA_ELEM);
if (FAILED(hr))
goto Cleanup;
hr = a.Elem()->get_children((IDispatch**) &pItems);
if (FAILED(hr))
goto Cleanup;
hr = pItems->get__newEnum(p);
if(FAILED(hr))
goto Cleanup;
Cleanup:
return hr;
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::item()
//
// Synopsis: Gets an item from the options collection.
//
// Arguments: VARIANT name -
// VARIANT index -
// IDispatch ** pdisp - the selected item
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT
CIESelectElement::item(VARIANT name, VARIANT index, IDispatch ** pdisp)
{
HRESULT hr = S_OK;
CContextAccess a(_pSite);
CComPtr<IHTMLElementCollection> pItems;
hr = a.Open(CA_ELEM);
if (FAILED(hr))
goto Cleanup;
hr = a.Elem()->get_children((IDispatch**) &pItems);
if (FAILED(hr))
goto Cleanup;
hr = pItems->item(name, index, pdisp);
if(FAILED(hr))
goto Cleanup;
Cleanup:
return hr;
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::tags()
//
// Synopsis: Commits the highlighted options to being selected.
//
// Arguments: BOOL *pbChanged - Returns whether anything changed
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT
CIESelectElement::tags(VARIANT tagName, IDispatch ** pdisp)
{
HRESULT hr = S_OK;
CContextAccess a(_pSite);
CComPtr<IHTMLElementCollection> pItems;
hr = a.Open(CA_ELEM);
if (FAILED(hr))
goto Cleanup;
hr = a.Elem()->get_children((IDispatch**) &pItems);
if (FAILED(hr))
goto Cleanup;
hr = pItems->tags(tagName, pdisp);
if(FAILED(hr))
goto Cleanup;
Cleanup:
return hr;
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::CommitSingleSelection()
//
// Synopsis: Commits the highlighted options to being selected.
//
// Arguments: BOOL *pbChanged - Returns whether anything changed
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT
CIESelectElement::urns(VARIANT urn, IDispatch ** pdisp)
{
HRESULT hr = S_OK;
CContextAccess a(_pSite);
CComPtr<IHTMLElementCollection> pItems;
CComPtr<IHTMLElementCollection2> pItems2;
hr = a.Open(CA_ELEM);
if (FAILED(hr))
goto Cleanup;
hr = a.Elem()->get_children((IDispatch**) &pItems);
if (FAILED(hr))
goto Cleanup;
hr = pItems->QueryInterface( __uuidof(IHTMLElementCollection2), (void **) &pItems2);
if (FAILED(hr))
goto Cleanup;
hr = pItems2->urns(urn, pdisp);
if(FAILED(hr))
goto Cleanup;
Cleanup:
return hr;
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::CommitSingleSelection()
//
// Synopsis: Commits the highlighted options to being selected.
//
// Arguments: BOOL *pbChanged - Returns whether anything changed
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT
CIESelectElement::CommitSingleSelection(BOOL *pbChanged)
{
HRESULT hr = S_OK;
VARIANT_BOOL bOn;
long lIndex = -1;
Assert(pbChanged);
*pbChanged = FALSE;
if (_pLastSelected)
{
hr = _pLastSelected->GetHighlight(&bOn);
if (FAILED(hr))
goto Cleanup;
if (!bOn)
{
// If we're not highlighted, then our selection status changed
hr = _pLastSelected->SetSelected(VARIANT_FALSE);
if (FAILED(hr))
goto Cleanup;
*pbChanged = TRUE;
}
ClearInterface(&_pLastSelected);
}
if (_pLastHighlight)
{
hr = _pLastHighlight->GetSelected(&bOn);
if (FAILED(hr))
goto Cleanup;
if (!bOn)
{
// If we are not selected, then our selection status changed
hr = _pLastHighlight->SetSelected(VARIANT_TRUE);
if (FAILED(hr))
goto Cleanup;
*pbChanged = TRUE;
}
hr = _pLastHighlight->GetIndex(&lIndex);
if (FAILED(hr))
goto Cleanup;
_pLastSelected = _pLastHighlight;
_pLastSelected->AddRef();
}
hr = GetProps()[eSelectedIndex].Set(lIndex);
if (FAILED(hr))
goto Cleanup;
Cleanup:
return hr;
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::CommitMultipleSelection()
//
// Synopsis: Commits the highlighted options to being selected.
//
// Arguments: BOOL *pbChanged - Returns whether anything changed
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT
CIESelectElement::CommitMultipleSelection(BOOL *pbChanged)
{
HRESULT hr;
long cItems;
long iItem;
VARIANT_BOOL bSelected;
VARIANT_BOOL bHighlight;
IHTMLElementCollection *pItems = NULL;
IDispatch *pDispatchItem = NULL;
IPrivateOption *pPrivOption = NULL;
CContextAccess a(_pSite);
Assert(pbChanged);
*pbChanged = FALSE;
hr = a.Open(CA_ELEM);
if (FAILED(hr))
goto Cleanup;
hr = a.Elem()->get_children((IDispatch**)&pItems);
if (FAILED(hr))
goto Cleanup;
hr = pItems->get_length(&cItems);
if (FAILED(hr))
goto Cleanup;
if ( (_lTopChanged < 0) || (_lTopChanged >= cItems) ||
(_lBottomChanged < 0) || (_lBottomChanged >= cItems) )
{
hr = S_OK;
goto Cleanup;
}
for (iItem = _lTopChanged; iItem <= _lBottomChanged; iItem++)
{
CVariant var, var2;
//
// Get a child
//
V_VT(&var) = VT_I4;
V_I4(&var) = iItem;
hr = pItems->item(var, var2, &pDispatchItem);
if (FAILED(hr))
goto Cleanup;
hr = pDispatchItem->QueryInterface(IID_IPrivateOption, (void**)&pPrivOption);
if (FAILED(hr))
{
ClearInterface(&pDispatchItem);
continue;
}
hr = pPrivOption->GetSelected(&bSelected);
if (FAILED(hr))
goto Cleanup;
hr = pPrivOption->GetHighlight(&bHighlight);
if (FAILED(hr))
goto Cleanup;
if (bSelected != bHighlight)
{
hr = pPrivOption->SetSelected(bHighlight);
if (FAILED(hr))
goto Cleanup;
*pbChanged = TRUE;
}
ClearInterface(&pPrivOption);
ClearInterface(&pDispatchItem);
}
Cleanup:
ReleaseInterface(pPrivOption);
ReleaseInterface(pDispatchItem);
ReleaseInterface(pItems);
return hr;
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::CommitSelection()
//
// Synopsis: Commits the highlighted options to being selected.
//
// Arguments: BOOL *pbChanged - Returns whether anything changed
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT STDMETHODCALLTYPE
CIESelectElement::CommitSelection(BOOL *pbChanged)
{
HRESULT hr = S_OK;
BOOL bChanged;
if (SELECT_ISMULTIPLE(_fFlavor))
{
hr = CommitMultipleSelection(&bChanged);
if (FAILED(hr))
goto Cleanup;
}
else
{
hr = CommitSingleSelection(&bChanged);
if (FAILED(hr))
goto Cleanup;
}
if (pbChanged)
{
*pbChanged = bChanged;
}
Cleanup:
return hr;
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::SetWritingMode
//
// Synopsis: Commits the writing mode.
//
// Arguments: BSTR bstrName : the writing mode.
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT STDMETHODCALLTYPE
CIESelectElement::SetWritingMode(BSTR bstrName)
{
HRESULT hr = S_OK;
CContextAccess a(_pSite);
hr = a.Open(CA_STYLE3);
if (FAILED(hr))
goto Cleanup;
hr = a.Style3()->put_writingMode(bstrName);
if (FAILED(hr))
goto Cleanup;
Cleanup:
return hr;
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::FireOnChange()
//
// Synopsis: Commits the highlighted options to being selected.
// Then, if anything changed, fires onchange.
//
// Arguments: BOOL *pbChanged - Returns whether anything changed
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT
CIESelectElement::FireOnChange()
{
HRESULT hr = S_OK;
BOOL bChanged;
hr = CommitSelection(&bChanged);
if (FAILED(hr))
goto Cleanup;
if (bChanged || SELECT_ISINPOPUP(_fFlavor))
{
hr = FireEvent(_lOnChangeCookie, NULL, CComBSTR("change"));
if (FAILED(hr))
goto Cleanup;
}
Cleanup:
return hr;
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::AdjustChangedRange()
//
// Synopsis: Ajdusts the change range to include the given index.
//
// Arguments: long lIndex - The index to include
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
void
CIESelectElement::AdjustChangedRange(long lIndex)
{
if (SELECT_ISMULTIPLE(_fFlavor))
{
if ((lIndex < _lTopChanged) || (_lTopChanged == -1))
{
_lTopChanged = lIndex;
}
if (lIndex > _lBottomChanged)
{
_lBottomChanged = lIndex;
}
}
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::OnOptionClicked()
//
// Synopsis: Called when an option is clicked (or simulated click).
//
// Arguments: long lIndex - The index of the option
// DWORD dwFlags - Selection flags (fire event, keyboard)
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT STDMETHODCALLTYPE
CIESelectElement::OnOptionClicked(long lIndex, DWORD dwFlags)
{
VARIANT_BOOL bSelected = VARIANT_TRUE;
DWORD dwSelFlags = dwFlags & (SELECT_FIREEVENT);
if (SELECT_ISMULTIPLE(_fFlavor))
{
if (dwFlags & SELECT_SHIFT)
{
dwSelFlags |= SELECT_EXTEND;
}
else if (dwFlags & SELECT_CTRL)
{
dwSelFlags |= SELECT_TOGGLE;
}
else
{
dwSelFlags |= SELECT_CLEARPREV;
}
}
else
{
dwSelFlags |= SELECT_CLEARPREV;
}
return OnOptionSelected(bSelected, lIndex, dwSelFlags);
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::OnOptionSelected()
//
// Synopsis: Called when an option is selected/deselected.
//
// Arguments: VARIANT_BOOL bSelected - Whether the option is selected
// long lIndex - The index of the option
// DWORD dwFlags - Selection flags (fire event, keyboard)
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT STDMETHODCALLTYPE
CIESelectElement::OnOptionSelected(VARIANT_BOOL bSelected, long lIndex, DWORD dwFlags)
{
HRESULT hr;
IPrivateOption *pOption = NULL;
BOOL bSetShiftAnchor = TRUE;
BOOL bRefreshFocus = TRUE;
BOOL bNeedToSelect = TRUE;
long lPrevIndex;
if (!SELECT_ISMULTIPLE(_fFlavor))
{
//
// Do some checking for single-select
//
if (!bSelected)
{
// Don't allow de-select in single-select
hr = S_OK;
goto Cleanup;
}
// Require this flag in single-select
dwFlags |= SELECT_CLEARPREV;
}
hr = GetIndex(lIndex, &pOption);
if (FAILED(hr) || !pOption)
goto Cleanup;
AdjustChangedRange(lIndex);
//
// Remove the previously selected items only if selecting
//
if (bSelected)
{
if (SELECT_ISMULTIPLE(_fFlavor))
{
// Multiple select doesn't have to de-highlight previous items
if (dwFlags & SELECT_CLEARPREV)
{
hr = SetAllSelected(VARIANT_FALSE, NULL);
if (FAILED(hr))
goto Cleanup;
}
}
else
{
// Single select must de-highlight the previous item
if (_pLastHighlight)
{
hr = _pLastHighlight->GetIndex(&lPrevIndex);
if (FAILED(hr))
goto Cleanup;
// If highlighting the previous item, then don't do anything
if (lPrevIndex != lIndex)
{
// De-highlight the previous item
hr = _pLastHighlight->SetHighlight(VARIANT_FALSE);
if (FAILED(hr))
goto Cleanup;
}
}
if (SELECT_ISINPOPUP(_fFlavor) && _pLastHighlight)
{
// In the popup, un-highlight the currently highlighted option.
hr = _pLastHighlight->SetHighlight(VARIANT_FALSE);
if (FAILED(hr))
goto Cleanup;
}
}
}
//
// Highlight the new item
//
if (SELECT_ISDROPBOX(_fFlavor))
{
hr = DropDownSelect(pOption);
if (FAILED(hr))
goto Cleanup;
Assert(_pSelectInPopup);
hr = _pSelectInPopup->OnOptionSelected(bSelected, lIndex, dwFlags & (~SELECT_FIREEVENT));
if (FAILED(hr))
goto Cleanup;
bNeedToSelect = FALSE;
}
else if (SELECT_ISMULTIPLE(_fFlavor))
{
if ((dwFlags & SELECT_EXTEND) && (_lShiftAnchor != -1))
{
hr = SelectRange(_lShiftAnchor, _lFocusIndex, lIndex);
if (FAILED(hr))
goto Cleanup;
bSetShiftAnchor = FALSE;
bNeedToSelect = FALSE;
}
else
{
if (dwFlags & SELECT_TOGGLE)
{
hr = pOption->GetSelected(&bSelected);
if (FAILED(hr))
goto Cleanup;
bSelected = bSelected ? VARIANT_FALSE : VARIANT_TRUE;
}
}
}
else if (SELECT_ISINPOPUP(_fFlavor))
{
VARIANT_BOOL bOpen;
Assert(_pPopup);
hr = _pPopup->get_isOpen(&bOpen);
if (FAILED(hr))
goto Cleanup;
if (bOpen)
{
bRefreshFocus = !(dwFlags & SELECT_FIREEVENT);
}
else
{
bRefreshFocus = FALSE;
}
}
if (bNeedToSelect)
{
// Highlight the item
hr = pOption->SetHighlight(bSelected);
if (FAILED(hr))
goto Cleanup;
}
if (bSelected || (dwFlags & SELECT_CLEARPREV))
{
if (_pLastHighlight)
{
ClearInterface(&_pLastHighlight);
}
if (bSelected)
{
_pLastHighlight = pOption;
_pLastHighlight->AddRef();
if (SELECT_ISLISTBOX(_fFlavor))
{
hr = MakeOptionVisible(_pLastHighlight);
if (FAILED(hr))
goto Cleanup;
}
}
}
if (bSetShiftAnchor)
{
_lShiftAnchor = lIndex;
}
hr = OnOptionFocus(lIndex, (VARIANT_BOOL)bRefreshFocus);
if (FAILED(hr))
goto Cleanup;
if (dwFlags & SELECT_FIREEVENT)
{
// Commit changes to selection (highlight -> selected)
hr = FireOnChange();
if (FAILED(hr))
goto Cleanup;
}
Cleanup:
ReleaseInterface(pOption);
return hr;
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::OnOptionHighlighted()
//
// Synopsis: Called back when an option is highlighted
//
// Arguments: long lIndex - Index of the option
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT STDMETHODCALLTYPE
CIESelectElement::OnOptionHighlighted(long lIndex)
{
HRESULT hr;
if (_pLastHighlight)
{
hr = _pLastHighlight->SetHighlight(VARIANT_FALSE);
if (FAILED(hr))
goto Cleanup;
ClearInterface(&_pLastHighlight);
}
hr = GetIndex(lIndex, &_pLastHighlight);
if (FAILED(hr))
goto Cleanup;
hr = _pLastHighlight->SetHighlight(VARIANT_TRUE);
if (FAILED(hr))
goto Cleanup;
Cleanup:
return hr;
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::OnOptionFocus()
//
// Synopsis: Called back when an option is focused
//
// Arguments: long lIndex - Index of the option
// VARIANT_BOOL bRequireRefresh - Force a refresh
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT STDMETHODCALLTYPE
CIESelectElement::OnOptionFocus(long lIndex, VARIANT_BOOL bRequireRefresh /* = VARIANT_TRUE */)
{
HRESULT hr = S_OK ;
_lFocusIndex = lIndex;
if (bRequireRefresh || !SELECT_ISINPOPUP(_fFlavor))
{
hr = RefreshFocusRect();
if (FAILED(hr))
goto Cleanup;
}
Cleanup:
return hr;
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::InitOptions()
//
// Synopsis: Sets option flags based on the select's flavor.
// Initializes default selections.
//
// Arguments: None
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT STDMETHODCALLTYPE
CIESelectElement::InitOptions()
{
HRESULT hr;
long cItems;
long iItem;
long lStart, lEnd;
long lDirection;
BOOL bTurnOff = FALSE;
IHTMLElementCollection *pItems = NULL;
IDispatch *pDispatchItem = NULL;
IPrivateOption *pPrivOption = NULL;
IHTMLOptionElement2 *pOption = NULL;
CVariant var, var2;
VARIANT_BOOL bSelected;
CContextAccess a(_pSite);
hr = a.Open(CA_ELEM | CA_STYLE);
if (FAILED(hr))
goto Cleanup;
hr = ParseChildren();
if (FAILED(hr))
goto Cleanup;
hr = a.Elem()->get_children((IDispatch**)&pItems);
if (FAILED(hr))
goto Cleanup;
hr = pItems->get_length(&cItems);
if (FAILED(hr))
goto Cleanup;
V_VT(&var) = VT_I4;
for (iItem = 0; iItem < cItems; iItem++)
{
V_I4(&var) = iItem;
hr = pItems->item(var, var2, &pDispatchItem);
if (FAILED(hr))
goto Cleanup;
hr = pDispatchItem->QueryInterface(IID_IPrivateOption, (void**)&pPrivOption);
if (FAILED(hr))
{
ClearInterface(&pDispatchItem);
continue;
}
hr = pPrivOption->SetIndex(iItem);
if (FAILED(hr))
goto Cleanup;
if (SELECT_ISINPOPUP(_fFlavor))
{
//
// If the listbox is in a popup,
// options should only be selected on onmouseup events.
// The options should also highlight on onmouseover events.
//
pPrivOption->SetSelectOnMouseDown(VARIANT_FALSE);
pPrivOption->SetHighlightOnMouseOver(VARIANT_TRUE);
}
else
{
pPrivOption->SetSelectOnMouseDown(VARIANT_TRUE);
pPrivOption->SetHighlightOnMouseOver(VARIANT_FALSE);
}
ClearInterface(&pPrivOption);
ClearInterface(&pDispatchItem);
}
//
// Deal with default selected options
//
// Setup the begin and end values and the direction.
// we're doing this since the code we want to run is the same,
// just in different directions depending on the flavor.
if (SELECT_ISMULTIPLE(_fFlavor))
{
lStart = 0;
lEnd = cItems;
lDirection = 1;
}
else
{
lStart = cItems - 1;
lEnd = -1;
lDirection = -1;
}
for (iItem = lStart; iItem != lEnd; iItem += lDirection)
{
V_I4(&var) = iItem;
hr = pItems->item(var, var2, &pDispatchItem);
if (FAILED(hr))
goto Cleanup;
hr = pDispatchItem->QueryInterface(IID_IPrivateOption, (void**)&pPrivOption);
if (FAILED(hr))
{
ClearInterface(&pDispatchItem);
continue;
}
if (bTurnOff)
{
hr = pPrivOption->SetSelected(VARIANT_FALSE);
if (FAILED(hr))
goto Cleanup;
hr = pPrivOption->SetInitSelected(VARIANT_FALSE);
if (FAILED(hr))
goto Cleanup;
}
else
{
hr = pPrivOption->GetSelected(&bSelected);
if (FAILED(hr))
goto Cleanup;
if (bSelected)
{
hr = OnOptionSelected(VARIANT_TRUE, iItem, 0);
if (FAILED(hr))
goto Cleanup;
if (SELECT_ISMULTIPLE(_fFlavor))
{
// Multiple select is done. So, break out
iItem = lEnd - lDirection; // Causes us to break out
}
else
{
// Single select should turn the rest off
bTurnOff = TRUE;
}
}
}
ClearInterface(&pPrivOption);
ClearInterface(&pDispatchItem);
}
hr = CommitSelection(NULL);
if (FAILED(hr))
goto Cleanup;
if (SELECT_ISDROPBOX(_fFlavor) && (cItems > 0))
{
// Initialize the selection on dropdowns
long lSelIndex;
hr = get_selectedIndex(&lSelIndex);
if (FAILED(hr))
goto Cleanup;
if (((lSelIndex == -1) || (hr == S_FALSE)) && (cItems > 0))
{
V_I4(&var) = 0;
hr = pItems->item(var, var2, &pDispatchItem);
if (FAILED(hr))
goto Cleanup;
hr = pDispatchItem->QueryInterface(IID_IPrivateOption, (void**)&pPrivOption);
if (SUCCEEDED(hr))
{
hr = pPrivOption->SetInitSelected(VARIANT_TRUE);
if (FAILED(hr))
goto Cleanup;
} // added
// There was no slection, so select the first one
OnOptionSelected(VARIANT_TRUE, 0, 0);
// Ignore the return value
hr = CommitSelection(NULL);
if (FAILED(hr))
goto Cleanup;
}
}
Cleanup:
ReleaseInterface(pOption);
ReleaseInterface(pPrivOption);
ReleaseInterface(pDispatchItem);
ReleaseInterface(pItems);
return hr;
}
#ifdef SELECT_GETSIZE
//+------------------------------------------------------------------
//
// Member: CIESelectElement::OnOptionSized()
//
// Synopsis: Called by the options when they are sized.
//
// Arguments: SIZE* psizeOption - The option's size (and returns it)
// BOOL bNew - TRUE if this is the first time it's calling back
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT STDMETHODCALLTYPE
CIESelectElement::OnOptionSized(SIZE* psizeOption, BOOL bNew, BOOL bAdjust)
{
HRESULT hr = S_OK;
BOOL bWidthSet, bHeightSet;
if (bNew)
{
// If the option is reporting a new size, then compare it
// with the current largest option.
_lNumOptionsReported++;
}
if (bNew || bAdjust)
{
if (psizeOption->cx > _sizeOptionReported.cx)
{
_sizeOptionReported.cx = psizeOption->cx;
_fLayoutDirty = TRUE;
}
if(_lNumOptionsReported == 1) {
if (psizeOption->cy > _sizeOptionReported.cy)
{
_sizeOptionReported.cy = psizeOption->cy;
_fLayoutDirty = TRUE;
}
}
}
hr = IsWidthHeightSet(&bWidthSet, &bHeightSet);
if (FAILED(hr))
goto Cleanup;
//
// Only adjust for scrollbars if the width hasn't been
// set explicitly. The code to handle explicitly sizing
// accounts for the scrollbar.
//
// Set the return value for the option
*psizeOption = _sizeOptionReported;
if(!bWidthSet)
{
AdjustSizeForScrollbar(psizeOption);
}
if (!_fAllOptionsSized)
{
long lNumOptions;
hr = GetNumOptions(&lNumOptions);
if (FAILED(hr))
goto Cleanup;
if (lNumOptions == _lNumOptionsReported)
{
// All options have reported in at least once
// so they have all been sized.
_fAllOptionsSized = TRUE;
}
}
if (_fAllOptionsSized)
{
// All options have been sized, so setup the select's version
// of an option size.
_sizeOption = *psizeOption;
#ifdef SELECT_TIMERVL
if (SELECT_ISDROPBOX(_fFlavor) &&
_fContentReady && !_fVLEngaged)
{
// If ViewLink has not been turned on, then turn it on.
AddSelectToTimerVLQueue(this);
}
#endif
}
Cleanup:
return hr;
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::AdjustSizeForScrollbar()
//
// Synopsis: Inflates the size to account for any scrollbars.
//
// Arguments: SIZE *pSize - The size to adjust
//
// Returns: None.
//
//-------------------------------------------------------------------
VOID
CIESelectElement::AdjustSizeForScrollbar(SIZE *pSize)
{
long lButtonWidth = GetSystemMetrics(SM_CXVSCROLL);
if (_fContentReady)
{
if ( !_fNeedScrollBar && SELECT_ISLISTBOX(_fFlavor))
{
// If the select does not need a scroll bar, then
// the option needs to be wider.
pSize->cx += lButtonWidth;
}
if (SELECT_ISINPOPUP(_fFlavor))
{
// This is padding added in the drop control that
// affects the width of the popup, affecting the
// width of the option.
pSize->cx += 4;
}
}
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::SetupDropControlDimensions()
//
// Synopsis: Setup the drop control's elements' dimensions.
//
// Arguments: SIZE sizeOption - The option's size
// BOOL bNew - TRUE if this is the first time it's calling back
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT
CIESelectElement::SetupDropControlDimensions()
{
HRESULT hr;
long lButtonWidth;
IHTMLStyle *pStyle = NULL;
SIZE sizeLogical;
long lWidth;
long lHeight;
Assert(_pElemDisplay);
// Get the width of the button
lButtonWidth = GetSystemMetrics(SM_CXVSCROLL);
if (_fWritingModeTBRL)
{
sizeLogical.cx = _sizeOptionReported.cy;
sizeLogical.cy = _sizeOptionReported.cx;
lWidth = _sizeOptionReported.cy + 2;
lHeight = lButtonWidth;
}
else
{
sizeLogical = _sizeOptionReported;
lWidth = lButtonWidth;
lHeight = _sizeOptionReported.cy + 2;
}
hr = _pElemDisplay->get_style(&pStyle);
if (FAILED(hr))
goto Cleanup;
// Set the width of the display
hr = pStyle->put_pixelWidth(sizeLogical.cx);
if (FAILED(hr))
goto Cleanup;
// Set the height of the display
hr = pStyle->put_pixelHeight(sizeLogical.cy);
if (FAILED(hr))
goto Cleanup;
// Set the width of the button
hr = _pStyleButton->put_pixelWidth(lWidth);
if (FAILED(hr))
goto Cleanup;
// Set the height of the button (+2 for border)
hr = _pStyleButton->put_pixelHeight(lHeight);
if (FAILED(hr))
goto Cleanup;
Cleanup:
ReleaseInterface(pStyle);
return hr;
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::SetupButton()
//
// Synopsis: Setup the drop control's button.
//
// Arguments: IHTMLElement *pButton - The button
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT
CIESelectElement::SetupButton(IHTMLElement *pButton)
{
HRESULT hr;
IHTMLStyle *pStyle = NULL;
CComBSTR bstrDefault(_T("default"));
CComBSTR bstrHidden(_T("hidden"));
CComBSTR bstrButtonFace(_T("buttonface"));
CComBSTR bstrBorder(_T("2 outset"));
CComBSTR bstrFont(_T("Marlett"));
CComBSTR bstrButtonText(_T("u"));
CComBSTR bstrCenter(_T("center"));
CComBSTR bstrMiddle(_T("middle"));
CVariant var;
long lButtonWidth;
// Get the width of the button
lButtonWidth = GetSystemMetrics(SM_CXVSCROLL);
hr = pButton->get_style(&pStyle);
if (FAILED(hr))
goto Cleanup;
// Store for later use
_pStyleButton = pStyle;
_pStyleButton->AddRef();
// Set the mouse cursor be an arrow for the button
hr = pStyle->put_cursor(bstrDefault);
if (FAILED(hr))
goto Cleanup;
// Set overflow hidden for the button
hr = pStyle->put_overflow(bstrHidden);
if (FAILED(hr))
goto Cleanup;
// Set the background color for the button
hr = pStyle->put_background(bstrButtonFace);
if (FAILED(hr))
goto Cleanup;
// Set the border for the button
hr = pStyle->put_border(bstrBorder);
if (FAILED(hr))
goto Cleanup;
// Set the font family of the button
hr = pStyle->put_fontFamily(bstrFont);
if (FAILED(hr))
goto Cleanup;
// Set the font size of the button
V_VT(&var) = VT_I4;
V_I4(&var) = lButtonWidth - 4;
hr = pStyle->put_fontSize(var);
if (FAILED(hr))
goto Cleanup;
// Set the text to be the down arrow
hr = pButton->put_innerText(bstrButtonText);
if (FAILED(hr))
goto Cleanup;
// Set the alignment on the arrow to be centered
hr = pStyle->put_textAlign(bstrCenter);
if (FAILED(hr))
goto Cleanup;
// Align the button to the middle
V_VT(&var) = VT_BSTR;
V_BSTR(&var) = bstrMiddle;
hr = pStyle->put_verticalAlign(var);
if (FAILED(hr))
goto Cleanup;
Cleanup:
ReleaseInterface(pStyle);
return hr;
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::SetupDisplay()
//
// Synopsis: Setup the drop control's display.
//
// Arguments: IHTMLElement *pDisplay - The display
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT
CIESelectElement::SetupDisplay(IHTMLElement *pDisplay)
{
HRESULT hr;
IHTMLStyle *pStyle = NULL;
IHTMLStyle3 *pStyle3 = NULL;
CComBSTR bstrDefault(_T("default"));
CComBSTR bstrHidden(_T("hidden"));
CComBSTR bstrMiddle(_T("middle"));
CComBSTR bstr2px(_T("2px"));
CVariant var;
long lButtonWidth;
CComBSTR bstrWritingMode(_T(""));
_pElemDisplay = pDisplay;
_pElemDisplay->AddRef();
// Get the width of the button
lButtonWidth = GetSystemMetrics(SM_CXVSCROLL);
//
// Set the display's style
//
hr = pDisplay->get_style(&pStyle);
if (FAILED(hr))
goto Cleanup;
hr = pStyle->QueryInterface(IID_IHTMLStyle3, (void**)&pStyle3);
if (FAILED(hr))
goto Cleanup;
if (_fWritingModeTBRL)
{
bstrWritingMode = _T("tb-rl");
}
hr = pStyle3->put_writingMode(bstrWritingMode);
if (FAILED(hr))
goto Cleanup;
// Set the mouse cursor be an arrow for the display
hr = pStyle->put_cursor(bstrDefault);
if (FAILED(hr))
goto Cleanup;
// Set overflow hidden for the display
hr = pStyle->put_overflow(bstrHidden);
if (FAILED(hr))
goto Cleanup;
// Set the vertical alignment of the display to be middle
V_VT(&var) = VT_BSTR;
V_BSTR(&var) = bstrMiddle;
hr = pStyle->put_verticalAlign(var);
if (FAILED(hr))
goto Cleanup;
// Set the left padding on the display
V_VT(&var) = VT_BSTR;
V_BSTR(&var) = bstr2px;
hr = pStyle->put_paddingLeft(var);
if (FAILED(hr))
goto Cleanup;
// Set the margin on the display
V_VT(&var) = VT_I4;
V_I4(&var) = 1;
hr = pStyle->put_marginLeft(var);
if (FAILED(hr))
goto Cleanup;
hr = pStyle->put_marginRight(var);
if (FAILED(hr))
goto Cleanup;
hr = pStyle->put_marginTop(var);
if (FAILED(hr))
goto Cleanup;
Cleanup:
ReleaseInterface(pStyle);
ClearInterface(&pStyle3);
return hr;
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::InitViewLink()
//
// Synopsis: Engages viewlink, sets up the drop control's dimensions
// and makes the drop control visible.
//
// Arguments: None
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT
CIESelectElement::InitViewLink()
{
HRESULT hr;
long lIndex;
IPrivateOption *pOption = NULL;
long lSize;
hr = get_size(&lSize);
if (FAILED(hr))
goto Cleanup;
if ( lSize >1 )
goto Cleanup;
// Engage the viewlink
hr = EngageViewLink();
if (FAILED(hr))
goto Cleanup;
// Put the selected option into the display
hr = get_selectedIndex(&lIndex);
if (FAILED(hr))
goto Cleanup;
if (lIndex >= 0)
{
hr = GetIndex(lIndex, &pOption);
if (SUCCEEDED(hr) && pOption)
{
hr = DropDownSelect(pOption);
if (FAILED(hr))
goto Cleanup;
}
}
// Resize the elements in the viewlink
hr = SetupDropControlDimensions();
if (FAILED(hr))
goto Cleanup;
// Make the control visible
hr = MakeVisible(TRUE);
if (FAILED(hr))
goto Cleanup;
_fLayoutDirty = TRUE;
Cleanup:
ReleaseInterface(pOption);
return hr;
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::DeInitViewLink()
//
// Synopsis: Turns off ViewLink.
//
// Arguments: None
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT
CIESelectElement::DeInitViewLink()
{
HRESULT hr = E_FAIL;
VARIANT_BOOL bOpen;
CContextAccess a(_pSite);
if (!_fVLEngaged)
{
goto Cleanup;
}
else
{
_fVLEngaged = FALSE;
}
//
// Get a hold of the Link document
//
ReleaseInterface(_pDispDocLink);
hr = _pElemDisplay->get_document(&_pDispDocLink);
if (FAILED(hr))
goto Cleanup;
ClearInterface(&_pElemDisplay);
ClearInterface(&_pStyleButton);
ClearInterface(&_pSelectInPopup);
hr = a.Open(CA_DEFAULTS);
if (FAILED(hr))
goto Cleanup;
// Remove the viewLink
hr = a.Defaults()->putref_viewLink(NULL);
if (FAILED(hr))
goto Cleanup;
if (_pPopup && !SELECT_ISINPOPUP(_fFlavor))
{
hr = _pPopup->get_isOpen(&bOpen);
if (FAILED(hr))
goto Cleanup;
if (bOpen)
{
hr = HidePopup();
if (FAILED(hr))
goto Cleanup;
}
ClearInterface(&_pPopup);
}
Cleanup:
return hr;
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::EngageViewLink()
//
// Synopsis: Turns on ViewLink.
//
// Arguments: None
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT
CIESelectElement::EngageViewLink()
{
HRESULT hr = S_OK;
IHTMLDocument2 *pDocThis = NULL;
IHTMLDocument2 *pDocLink = NULL;
IHTMLDocument3 *pDocLink3 = NULL;
IDispatch *pDispDocLink = NULL;
IHTMLElement *pElemLink = NULL;
IHTMLDOMNode *pNodeLink = NULL;
IHTMLElement *pElemNOBR = NULL;
IHTMLDOMNode *pNodeNOBR = NULL;
IHTMLElement *pElemDisplay = NULL;
IHTMLDOMNode *pNodeDisplay = NULL;
IHTMLElement *pElemButton = NULL;
IHTMLDOMNode *pNodeButton = NULL;
IHTMLElement2 *pElem2 = NULL;
CComBSTR bstrSpan(_T("SPAN"));
CComBSTR bstrNOBR(_T("NOBR"));
CComBSTR bstr;
CContextAccess a(_pSite);
if (_fVLEngaged)
{
goto Cleanup;
}
else
{
_fVLEngaged = TRUE;
}
hr = a.Open(CA_ELEM | CA_DEFAULTS);
if (FAILED(hr))
goto Cleanup;
// Get the document
hr = a.Elem()->get_document((IDispatch**)&pDocThis);
if (FAILED(hr))
goto Cleanup;
//
// Create a superflous slave element for now
//
hr = pDocThis->createElement(bstrSpan, &pElemLink);
if (FAILED(hr))
goto Cleanup;
hr = pElemLink->QueryInterface(IID_IHTMLDOMNode, (void**)&pNodeLink);
if (FAILED(hr))
goto Cleanup;
//
// Create and insert a NOBR element
//
hr = pDocThis->createElement(bstrNOBR, &pElemNOBR);
if (FAILED(hr))
goto Cleanup;
hr = pElemNOBR->QueryInterface(IID_IHTMLDOMNode, (void**)&pNodeNOBR);
if (FAILED(hr))
goto Cleanup;
hr = pNodeLink->appendChild(pNodeNOBR, NULL);
if (FAILED(hr))
goto Cleanup;
//
// Create and insert the display element
//
hr = pDocThis->createElement(bstrSpan, &pElemDisplay);
if (FAILED(hr))
goto Cleanup;
hr = pElemDisplay->QueryInterface(IID_IHTMLDOMNode, (void**)&pNodeDisplay);
if (FAILED(hr))
goto Cleanup;
hr = pNodeNOBR->appendChild(pNodeDisplay, NULL);
if (FAILED(hr))
goto Cleanup;
//
// Create and insert the button
//
hr = pDocThis->createElement(bstrSpan, &pElemButton);
if (FAILED(hr))
goto Cleanup;
hr = pElemButton->QueryInterface(IID_IHTMLDOMNode, (void**)&pNodeButton);
if (FAILED(hr))
goto Cleanup;
hr = pNodeNOBR->appendChild(pNodeButton, NULL);
if (FAILED(hr))
goto Cleanup;
//
// Remove the superflous Link element now
//
hr = pNodeLink->removeNode(VB_FALSE, NULL);
if (FAILED(hr))
goto Cleanup;
//
// Get a hold of the Link document
//
if (_pDispDocLink)
{
ClearInterface(&_pDispDocLink);
}
hr = pElemDisplay->get_document(&pDispDocLink);
if (FAILED(hr))
goto Cleanup;
_pDispDocLink = pDispDocLink;
_pDispDocLink->AddRef();
hr = pDispDocLink->QueryInterface(IID_IHTMLDocument2, (void**)&pDocLink);
if (FAILED(hr))
goto Cleanup;
//
// Create the viewLink
//
hr = a.Defaults()->putref_viewLink(pDocLink);
if (FAILED(hr))
goto Cleanup;
//
// Setup styles and sizes
//
hr = SetupDisplay(pElemDisplay);
if (FAILED(hr))
goto Cleanup;
hr = SetupButton(pElemButton);
if (FAILED(hr))
goto Cleanup;
hr = pDocLink->QueryInterface(IID_IHTMLDocument3, (void**)&pDocLink3);
if (FAILED(hr))
goto Cleanup;
//
// Attach an event sink to listen for events within the view link
//
if ( !_pSinkVL )
{
_pSinkVL = new CEventSink(this, SELECTES_VIEWLINK);
if (!_pSinkVL)
{
hr = E_OUTOFMEMORY;
goto Cleanup;
}
bstr = _T("onmousedown");
hr = AttachEventToSink(pDocLink3, bstr, _pSinkVL);
if (FAILED(hr))
goto Cleanup;
bstr = _T("onmouseup");
hr = AttachEventToSink(pDocLink3, bstr, _pSinkVL);
if (FAILED(hr))
goto Cleanup;
bstr = _T("onclick");
hr = AttachEventToSink(pDocLink3, bstr, _pSinkVL);
if (FAILED(hr))
goto Cleanup;
bstr = _T("onselectstart");
hr = AttachEventToSink(pDocLink3, bstr, _pSinkVL);
if (FAILED(hr))
goto Cleanup;
bstr = _T("oncontextmenu");
hr = AttachEventToSink(pDocLink3, bstr, _pSinkVL);
if (FAILED(hr))
goto Cleanup;
bstr = _T("onbeforefocusenter");
hr = AttachEventToSink(pDocLink3, bstr, _pSinkVL);
if (FAILED(hr))
goto Cleanup;
}
if ( !_pSinkButton )
{
_pSinkButton = new CEventSink(this, SELECTES_BUTTON);
if (!_pSinkButton)
{
hr = E_OUTOFMEMORY;
goto Cleanup;
}
hr = pElemDisplay->QueryInterface(IID_IHTMLElement2, (void**)&pElem2);
if (FAILED(hr))
goto Cleanup;
bstr = _T("onbeforefocusenter");
hr = AttachEventToSink(pElem2, bstr, _pSinkVL);
if (FAILED(hr))
goto Cleanup;
ClearInterface(&pElem2);
hr = pElemButton->QueryInterface(IID_IHTMLElement2, (void**)&pElem2);
if (FAILED(hr))
goto Cleanup;
bstr = _T("onmousedown");
hr = AttachEventToSink(pElem2, bstr, _pSinkButton);
if (FAILED(hr))
goto Cleanup;
bstr = _T("onmouseup");
hr = AttachEventToSink(pElem2, bstr, _pSinkButton);
if (FAILED(hr))
goto Cleanup;
bstr = _T("onmouseout");
hr = AttachEventToSink(pElem2, bstr, _pSinkButton);
if (FAILED(hr))
goto Cleanup;
bstr = _T("onbeforefocusenter");
hr = AttachEventToSink(pElem2, bstr, _pSinkButton);
if (FAILED(hr))
goto Cleanup;
if ( _pSinkVL )
bstr = _T("onbeforefocusenter");
hr = AttachEventToSink(pElem2, bstr, _pSinkVL);
if (FAILED(hr))
goto Cleanup;
}
Cleanup:
ReleaseInterface(pDocThis);
ReleaseInterface(pDispDocLink);
ReleaseInterface(pDocLink);
ReleaseInterface(pDocLink3);
ReleaseInterface(pElemLink);
ReleaseInterface(pNodeLink);
ReleaseInterface(pElemNOBR);
ReleaseInterface(pNodeNOBR);
ReleaseInterface(pElemDisplay);
ReleaseInterface(pNodeDisplay);
ReleaseInterface(pElemButton);
ReleaseInterface(pNodeButton);
ReleaseInterface(pElem2);
return hr;
}
#endif
#ifndef SELECT_GETSIZE
//+------------------------------------------------------------------
//
// Member: CIESelectElement::SetDimensions()
//
// Synopsis: Sets the dimensions for the select and option controls.
//
// Arguments: None
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT STDMETHODCALLTYPE
CIESelectElement::SetDimensions()
{
HRESULT hr;
long lSize;
hr = get_size(&lSize);
if (FAILED(hr))
goto Cleanup;
hr = SetDimensions(lSize);
if (FAILED(hr))
goto Cleanup;
Cleanup:
return hr;
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::SetDimensions()
//
// Synopsis: Sets the dimensions for the select and option controls.
//
// Arguments: long lSize The size of the control.
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT
CIESelectElement::SetDimensions(long lSize)
{
HRESULT hr;
IHTMLElementCollection *pItems = NULL;
IDispatch *pDispatchItem = NULL;
IHTMLElement2 *pElem2 = NULL;
IPrivateOption *pOption = NULL;
IHTMLRect *pRect = NULL;
long cNumItems;
long iCurRow;
long lHeight = 0;
long lWidth = 0;
long lScrollWidth;
long lVertBorder;
long lHorizBorder;
long lMaxWidth = 0;
long lMaxHeight = 0;
long lClient;
long lOffset;
long lItemTop;
long lItemLeft;
long lItemHeight;
long lItemWidth;
long lTemp;
CVariant var, var2;
CContextAccess a(_pSite);
hr = a.Open(CA_ELEM | CA_ELEM2 | CA_DEFSTYLE);
if (FAILED(hr))
goto Cleanup;
//
// Get the scroll bar width
//
lScrollWidth = GetSystemMetrics(SM_CXVSCROLL);
//
// Calculate the vertical padding and border
//
hr = a.Elem()->get_offsetHeight(&lOffset);
if (FAILED(hr))
goto Cleanup;
hr = a.Elem2()->get_clientHeight(&lClient);
if (FAILED(hr))
goto Cleanup;
lVertBorder = lOffset - lClient;
//
// Calculate the horizontal padding and border
//
hr = a.Elem()->get_offsetWidth(&lOffset);
if (FAILED(hr))
goto Cleanup;
hr = a.Elem2()->get_clientWidth(&lClient);
if (FAILED(hr))
goto Cleanup;
lHorizBorder = lOffset - lClient;
//
// Get the children
//
hr = a.Elem()->get_children((IDispatch**)&pItems);
if (FAILED(hr))
goto Cleanup;
//
// Set the scroll range
//
hr = pItems->get_length(&cNumItems);
if (FAILED(hr))
goto Cleanup;
for (iCurRow = 0; iCurRow < cNumItems; iCurRow++)
{
// Get the row
V_VT(&var) = VT_I4;
V_I4(&var) = iCurRow;
hr = pItems->item(var, var2, &pDispatchItem);
if (FAILED(hr))
goto Cleanup;
// Get the IHTMLElement interface
hr = pDispatchItem->QueryInterface(IID_IHTMLElement2, (void**)&pElem2);
if (FAILED(hr))
goto Cleanup;
//
// Get the width and height of the option
//
hr = pElem2->getBoundingClientRect(&pRect);
if (FAILED(hr))
goto Cleanup;
hr = pRect->get_top(&lItemTop);
if (FAILED(hr))
goto Cleanup;
hr = pRect->get_left(&lItemLeft);
if (FAILED(hr))
goto Cleanup;
hr = pRect->get_right(&lItemWidth);
if (FAILED(hr))
goto Cleanup;
hr = pRect->get_bottom(&lItemHeight);
if (FAILED(hr))
goto Cleanup;
lItemWidth -= lItemLeft;
lItemHeight -= lItemTop;
ClearInterface(&pRect);
ClearInterface(&pElem2);
//
// If the width is greater than the maximum,
// then set the max to the option's width.
//
if (lItemWidth > lMaxWidth)
{
lMaxWidth = lItemWidth;
}
//
// If the width is greater than the maximum,
// then set the max to the option's width.
//
if (lItemHeight > lMaxHeight)
{
lMaxHeight = lItemHeight;
}
hr = pDispatchItem->QueryInterface(IID_IPrivateOption, (void**)&pOption);
if (FAILED(hr))
goto Cleanup;
hr = pOption->SetFinalDefaultStyles();
if (FAILED(hr))
goto Cleanup;
ClearInterface(&pOption);
ClearInterface(&pDispatchItem);
}
//
// If we have no default option height,
// then set to a default size.
//
if (lMaxHeight == 0)
{
lMaxHeight = SELECT_OPTIONHEIGHT;
}
//
// Calculate the height of the control
//
lHeight = lMaxHeight * lSize;
// Add in the border
lHeight += lVertBorder;
// Add in the border and the scroll bar + padding
// for IE5 compat, there is 2 pixels padding at left and right
lWidth = lMaxWidth + lHorizBorder + lScrollWidth + 4;
if (_fWritingModeTBRL)
{
lTemp = lWidth;
lWidth = lHeight;
lHeight = lTemp;
}
// Set the height
hr = a.DefStyle()->put_pixelHeight(lHeight);
if (FAILED(hr))
goto Cleanup;
// Set the width
hr = a.DefStyle()->put_pixelWidth(lWidth);
if (FAILED(hr))
goto Cleanup;
//
// Take care of some last minute styles
// Force fixed height
//
_lMaxHeight = lMaxHeight;
for (iCurRow = 0; iCurRow < cNumItems; iCurRow++)
{
// Get the row
V_VT(&var) = VT_I4;
V_I4(&var) = iCurRow;
hr = pItems->item(var, var2, &pDispatchItem);
if (FAILED(hr))
goto Cleanup;
hr = pDispatchItem->QueryInterface(IID_IPrivateOption, (void**)&pOption);
if (FAILED(hr))
goto Cleanup;
hr = pOption->SetFinalHeight(lMaxHeight);
if (FAILED(hr))
goto Cleanup;
ClearInterface(&pDispatchItem);
ClearInterface(&pOption);
}
Cleanup:
ReleaseInterface(pDispatchItem);
ReleaseInterface(pItems);
ReleaseInterface(pElem2);
ReleaseInterface(pRect);
ReleaseInterface(pOption);
return hr;
}
#endif
//+------------------------------------------------------------------
//
// Member: CIESelectElement::GetSelectedIndex()
//
// Synopsis: Returns the currently selected index.
//
// Arguments: long *plIndex - Returns the index.
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT STDMETHODCALLTYPE
CIESelectElement::GetSelectedIndex(long *plIndex)
{
return get_selectedIndex(plIndex);
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::GetDisabled()
//
// Synopsis: Returns if the control is disabled.
//
// Arguments: VARIANT_BOOL *pbDisabled - Returns VARIANT_TRUE if disabled.
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT STDMETHODCALLTYPE
CIESelectElement::GetDisabled(VARIANT_BOOL *pbDisabled)
{
HRESULT hr;
CContextAccess a(_pSite);
Assert(pbDisabled);
hr = a.Open(CA_ELEM3);
if (FAILED(hr))
goto Cleanup;
hr = a.Elem3()->get_disabled(pbDisabled);
if (FAILED(hr))
goto Cleanup;
Cleanup:
return hr;
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::SetAllSelected()
//
// Synopsis: Sets all of the options to the given selection status.
//
// Arguments: VARIANT_BOOL bSelected - Select/Deselect elements
// DWORD dwFlags - Selection flags (fire event)
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT
CIESelectElement::SetAllSelected(VARIANT_BOOL bSelected, DWORD dwFlags)
{
HRESULT hr;
long cItems;
long iItem;
VARIANT_BOOL bPrev;
IHTMLElementCollection *pItems = NULL;
IDispatch *pDispatchItem = NULL;
IPrivateOption *pPrivOption = NULL;
CVariant var, var2;
CContextAccess a(_pSite);
Assert(SELECT_ISMULTIPLE(_fFlavor));
hr = a.Open(CA_ELEM);
if (FAILED(hr))
goto Cleanup;
hr = a.Elem()->get_children((IDispatch**)&pItems);
if (FAILED(hr))
goto Cleanup;
hr = pItems->get_length(&cItems);
if (FAILED(hr))
goto Cleanup;
V_VT(&var) = VT_I4;
for (iItem = 0; iItem < cItems; iItem++)
{
if ((dwFlags & SELECT_FIREEVENT) && (iItem == (cItems - 1)))
{
hr = OnOptionSelected(bSelected, iItem, SELECT_FIREEVENT);
if (FAILED(hr))
goto Cleanup;
}
else
{
V_I4(&var) = iItem;
hr = pItems->item(var, var2, &pDispatchItem);
if (FAILED(hr))
{
ClearInterface(&pDispatchItem);
continue;
}
hr = pDispatchItem->QueryInterface(IID_IPrivateOption, (void**)&pPrivOption);
if (FAILED(hr))
goto Cleanup;
hr = pPrivOption->GetHighlight(&bPrev);
if (FAILED(hr))
goto Cleanup;
if (bPrev != bSelected)
{
hr = pPrivOption->SetHighlight(bSelected);
if (FAILED(hr))
goto Cleanup;
AdjustChangedRange(iItem);
}
}
ClearInterface(&pPrivOption);
ClearInterface(&pDispatchItem);
}
Cleanup:
ReleaseInterface(pPrivOption);
ReleaseInterface(pDispatchItem);
ReleaseInterface(pItems);
return hr;
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::GetNumOptions()
//
// Synopsis: Returns the number of options in the select.
//
// Arguments: long *plItems - Returns the number of options
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT
CIESelectElement::GetNumOptions(long *plItems)
{
HRESULT hr;
IHTMLElementCollection *pItems = NULL;
CContextAccess a(_pSite);
Assert(plItems);
hr = a.Open(CA_ELEM);
if (FAILED(hr))
goto Cleanup;
hr = a.Elem()->get_children((IDispatch**)&pItems);
if (FAILED(hr))
goto Cleanup;
hr = pItems->get_length(plItems);
if (FAILED(hr))
goto Cleanup;
Cleanup:
ReleaseInterface(pItems);
return hr;
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::GetScrollTopBottom()
//
// Synopsis: Returns the scroll top and bottom values.
//
// Arguments: long *plScrollTop - The top value
// long *plScrollBottom - The bottom value
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT
CIESelectElement::GetScrollTopBottom(long *plScrollTop, long *plScrollBottom)
{
HRESULT hr;
long lHeight;
CContextAccess a(_pSite);
Assert(plScrollTop);
Assert(plScrollBottom);
hr = a.Open(CA_ELEM2);
if (FAILED(hr))
goto Cleanup;
//
// Calculate the visible range
//
hr = a.Elem2()->get_scrollTop(plScrollTop);
if (FAILED(hr))
goto Cleanup;
#ifdef SELECT_GETSIZE
// TODO: Just use this instead of lHeight
lHeight = _sizeContent.cy;
#else
hr = a.Elem2()->get_clientHeight(&lHeight);
if (FAILED(hr))
goto Cleanup;
#endif
*plScrollBottom = *plScrollTop + lHeight;
Cleanup:
return hr;
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::GetTopVisibleOptionIndex()
//
// Synopsis: Returns the index of the first visible option.
//
// Arguments: long *plItems - Returns the index
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT
CIESelectElement::GetTopVisibleOptionIndex(long *plIndex)
{
HRESULT hr = S_OK;
long lScrollTop;
long lScrollBottom;
Assert(plIndex);
if (_lMaxHeight > 0)
{
//
// Calculate the visible range
//
hr = GetScrollTopBottom(&lScrollTop, &lScrollBottom);
if (FAILED(hr))
goto Cleanup;
//
// Assuming fixed height to calc index
//
*plIndex = lScrollTop / _lMaxHeight;
}
else
{
*plIndex = 0;
}
Cleanup:
return hr;
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::GetBottomVisibleOptionIndex()
//
// Synopsis: Returns the index of the last visible option (+1)
//
// Arguments: long *plItems - Returns the index
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT
CIESelectElement::GetBottomVisibleOptionIndex(long *plIndex)
{
HRESULT hr = S_OK;
long lScrollTop;
long lScrollBottom;
Assert(plIndex);
if (_lMaxHeight > 0)
{
//
// Calculate the visible range
//
hr = GetScrollTopBottom(&lScrollTop, &lScrollBottom);
if (FAILED(hr))
goto Cleanup;
//
// Assuming fixed height to calc index
//
*plIndex = lScrollBottom / _lMaxHeight;
}
else
{
*plIndex = 0;
}
Cleanup:
return hr;
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::GetFirstSelected()
//
// Synopsis: Returns the index of the first option that's selected.
//
// Arguments: long *plItems - Returns the index of the option
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT
CIESelectElement::GetFirstSelected(long *plIndex)
{
HRESULT hr;
long cItems;
long iItem;
VARIANT_BOOL bSelected;
IHTMLElementCollection *pItems = NULL;
IDispatch *pDispatchItem = NULL;
IPrivateOption *pOption = NULL;
CVariant var, var2;
CContextAccess a(_pSite);
Assert(plIndex);
*plIndex = -1;
hr = a.Open(CA_ELEM);
if (FAILED(hr))
goto Cleanup;
hr = a.Elem()->get_children((IDispatch**)&pItems);
if (FAILED(hr))
goto Cleanup;
hr = pItems->get_length(&cItems);
if (FAILED(hr))
goto Cleanup;
V_VT(&var) = VT_I4;
for (iItem = 0; iItem < cItems; iItem++)
{
V_I4(&var) = iItem;
hr = pItems->item(var, var2, &pDispatchItem);
if (FAILED(hr))
goto Cleanup;
hr = pDispatchItem->QueryInterface(IID_IPrivateOption, (void**)&pOption);
if (FAILED(hr))
{
ClearInterface(&pDispatchItem);
continue;
}
hr = pOption->GetSelected(&bSelected);
if (FAILED(hr))
goto Cleanup;
if (bSelected)
{
// Found the first selected option
*plIndex = iItem;
iItem = cItems; // Causes us to break out
}
ClearInterface(&pOption);
ClearInterface(&pDispatchItem);
}
Cleanup:
ReleaseInterface(pOption);
ReleaseInterface(pDispatchItem);
ReleaseInterface(pItems);
return hr;
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::Select(long lIndex)
//
// Synopsis: Selects the lIndex-th Option.
// Note: Unlike put_selectedIndex, this method does *not* clear
// the currently selected option(s),
//
// Arguments: long lItems - Index of the option
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT
CIESelectElement::Select(long lIndex)
{
HRESULT hr;
IHTMLOptionElement2 *pOption = NULL;
hr = GetIndex(lIndex, &pOption);
if (FAILED(hr))
goto Cleanup;
hr = pOption->put_selected(VARIANT_TRUE);
if (FAILED(hr))
goto Cleanup;
Cleanup:
ReleaseInterface(pOption);
return hr;
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::SelectVisibleIndex()
//
// Synopsis: Selects the option that's lIndex down from the top
// visible option.
//
// Arguments: long lItems - Index of the option
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT
CIESelectElement::SelectVisibleIndex(long lIndex)
{
HRESULT hr = S_OK;
IHTMLOptionElement2 *pOption = NULL;
VARIANT_BOOL bSelected;
long lTopIndex;
long lNewIndex;
long lNumOptions;
hr = GetNumOptions(&lNumOptions);
if (FAILED(hr))
goto Cleanup;
hr = GetTopVisibleOptionIndex(&lTopIndex);
if (FAILED(hr))
goto Cleanup;
lNewIndex = lTopIndex + lIndex;
if (lNewIndex >= lNumOptions)
{
lNewIndex = lNumOptions - 1;
}
hr = GetIndex(lNewIndex, &pOption);
if (FAILED(hr) || !pOption)
{
hr = S_OK;
goto Cleanup;
}
hr = pOption->get_selected(&bSelected);
if (FAILED(hr))
goto Cleanup;
if (!bSelected)
{
hr = OnOptionClicked(lNewIndex, SELECT_FIREEVENT);
if (FAILED(hr))
goto Cleanup;
}
Cleanup:
ReleaseInterface(pOption);
return hr;
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::PushFocusToExtreme()
//
// Synopsis: Pushes the focused option to the top or bottom.
//
// Arguments: BOOL bTop - TRUE to push to the top, FALSE to the bottom
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT
CIESelectElement::PushFocusToExtreme(BOOL bTop)
{
HRESULT hr = S_OK;
IHTMLElement *pElem = NULL;
long lScrollTop;
long lScrollBottom;
long lScrollHeight;
long lHeight;
long lTop;
long lBottom;
long lNewTop;
long lNewBottom;
CContextAccess a(_pSite);
if (_lFocusIndex < 0)
goto Cleanup;
hr = a.Open(CA_ELEM2);
if (FAILED(hr))
goto Cleanup;
//
// Calculate the visible range
//
hr = GetScrollTopBottom(&lScrollTop, &lScrollBottom);
if (FAILED(hr))
goto Cleanup;
a.Elem2()->get_scrollHeight(&lScrollHeight);
if (FAILED(hr))
goto Cleanup;
//
// Get the option's range
//
hr = GetIndex(_lFocusIndex, &pElem);
if (FAILED(hr))
goto Cleanup;
hr = pElem->get_offsetTop(&lTop);
if (FAILED(hr))
goto Cleanup;
hr = pElem->get_offsetHeight(&lHeight);
if (FAILED(hr))
goto Cleanup;
lBottom = lTop + lHeight;
//
// Realign the scroll range
//
if (bTop)
{
lNewTop = lTop;
lNewBottom = lScrollBottom + (lTop - lScrollTop);
}
else
{
lNewTop = lScrollTop + (lBottom - lScrollBottom);
lNewBottom = lBottom;
}
if (lNewBottom > lScrollHeight)
{
lNewTop -= (lNewBottom - lScrollHeight);
lNewBottom = lScrollHeight;
}
if (lNewTop < 0)
{
lNewBottom -= lNewTop;
lNewTop = 0;
}
//
// If there is a change, then change the scroll top
//
if (lNewTop != lScrollTop)
{
hr = a.Elem2()->put_scrollTop(lNewTop);
if (FAILED(hr))
goto Cleanup;
}
Cleanup:
ReleaseInterface(pElem);
return hr;
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::MakeOptionVisible()
//
// Synopsis: Makes the given option visible.
//
// Arguments: IPrivateOption *pOption - The option to make visible
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT
CIESelectElement::MakeOptionVisible(IPrivateOption *pOption)
{
HRESULT hr;
IHTMLElement *pElem = NULL;
long lScrollTop;
long lScrollBottom;
long lHeight;
long lTop;
long lBottom;
long lNewTop;
CContextAccess a(_pSite);
#ifdef SELECT_GETSIZE
if (SELECT_ISDROPBOX(_fFlavor) || !(_fContentReady && _fAllOptionsSized))
#else
if (SELECT_ISDROPBOX(_fFlavor))
#endif
{
hr = S_OK;
goto Cleanup;
}
hr = a.Open(CA_ELEM2);
if (FAILED(hr))
goto Cleanup;
//
// Calculate the visible range
//
hr = GetScrollTopBottom(&lScrollTop, &lScrollBottom);
if (FAILED(hr))
goto Cleanup;
//
// Get the option's range
//
hr = pOption->QueryInterface(IID_IHTMLElement, (void**)&pElem);
if (FAILED(hr))
goto Cleanup;
hr = pElem->get_offsetTop(&lTop);
if (FAILED(hr))
goto Cleanup;
hr = pElem->get_offsetHeight(&lHeight);
if (FAILED(hr))
goto Cleanup;
lBottom = lTop + lHeight;
//
// Make sure the option is not already visible
//
if ((lTop < lScrollTop) || (lBottom > lScrollBottom))
{
//
// First, make sure that the option isn't just bigger than the control
//
if ((lTop < lScrollTop) && (lBottom > lScrollBottom))
goto Cleanup;
//
// Determine if we're coming from the top or bottom
//
if (lTop < lScrollTop)
{
// From top
lNewTop = lTop;
}
else
{
// From bottom
lNewTop = lBottom - (lScrollBottom - lScrollTop);
}
hr = a.Elem2()->put_scrollTop(lNewTop);
if (FAILED(hr))
goto Cleanup;
}
Cleanup:
ReleaseInterface(pElem);
return hr;
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::GetIndex()
//
// Synopsis: Returns a pointer to the given index.
//
// Arguments: long lIndex - Index of the option
// IHTMLOptionElement2 **ppOption - Returns a pointer to the option
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT
CIESelectElement::GetIndex(long lIndex, IHTMLOptionElement2 **ppOption)
{
HRESULT hr;
IDispatch *pDispatch = NULL;
Assert(ppOption);
hr = GetIndex(lIndex, &pDispatch);
if (FAILED(hr))
goto Cleanup;
hr = pDispatch->QueryInterface(IID_IHTMLOptionElement2, (void**)ppOption);
if (FAILED(hr))
goto Cleanup;
Cleanup:
ReleaseInterface(pDispatch);
return hr;
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::GetIndex()
//
// Synopsis: Returns a pointer to the given index.
//
// Arguments: long lIndex - Index of the option
// IPrivateOption **ppOption - Returns a pointer to the option
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT
CIESelectElement::GetIndex(long lIndex, IPrivateOption **ppOption)
{
HRESULT hr;
IDispatch *pDispatch = NULL;
Assert(ppOption);
hr = GetIndex(lIndex, &pDispatch);
if (FAILED(hr))
goto Cleanup;
hr = pDispatch->QueryInterface(IID_IPrivateOption, (void**)ppOption);
if (FAILED(hr))
goto Cleanup;
Cleanup:
ReleaseInterface(pDispatch);
return hr;
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::GetIndex()
//
// Synopsis: Returns a pointer to the given index.
//
// Arguments: long lIndex - Index of the option
// IHTMLElement **ppOption - Returns a pointer to the option
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT
CIESelectElement::GetIndex(long lIndex, IHTMLElement **ppElem)
{
HRESULT hr;
IDispatch *pDispatch = NULL;
Assert(ppElem);
hr = GetIndex(lIndex, &pDispatch);
if (FAILED(hr))
goto Cleanup;
hr = pDispatch->QueryInterface(IID_IHTMLElement, (void**)ppElem);
if (FAILED(hr))
goto Cleanup;
Cleanup:
ReleaseInterface(pDispatch);
return hr;
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::GetIndex()
//
// Synopsis: Returns a pointer to the given index.
//
// Arguments: long lIndex - Index of the option
// IDispatch **ppOption - Returns a pointer to the option
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT
CIESelectElement::GetIndex(long lIndex, IDispatch **ppOption)
{
HRESULT hr;
IHTMLElementCollection *pItems = NULL;
CVariant var, var2;
CContextAccess a(_pSite);
Assert(ppOption);
hr = a.Open(CA_ELEM);
if (FAILED(hr))
goto Cleanup;
hr = a.Elem()->get_children((IDispatch**)&pItems);
if (FAILED(hr))
goto Cleanup;
V_VT(&var) = VT_I4;
V_I4(&var) = lIndex;
hr = pItems->item(var, var2, ppOption);
if (FAILED(hr))
goto Cleanup;
if (!*ppOption)
{
hr = E_FAIL;
goto Cleanup;
}
Cleanup:
ReleaseInterface(pItems);
return hr;
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::SelectRange()
//
// Synopsis: Selects a range of elements.
// lAnchor is one end of the range. lNew is the new
// end of the range. lLast is the old end of the range.
// From these values, we can tell if we're selecting or
// deselecting.
//
// Arguments: long lAnchor - Beginning of the range
// long lLast - Old end of the range
// long lNew - New end of the range
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT
CIESelectElement::SelectRange(long lAnchor, long lLast, long lNew)
{
HRESULT hr;
long lIndexStart, lIndexEnd;
long lRangeLast, lRangeNew;
long iItem;
IHTMLElementCollection *pItems = NULL;
IDispatch *pDispatchItem = NULL;
IPrivateOption *pOption = NULL;
IPrivateOption *pAnchor = NULL;
VARIANT_BOOL bAnchorHighlight;
CVariant var, var2;
BOOL bUndo;
CContextAccess a(_pSite);
#if DBG == 1 // For Assert
long cItems;
#endif
if (lNew == lLast)
{
// No change, so exit
hr = S_OK;
goto Cleanup;
}
if (((lNew < lAnchor) && (lAnchor < lLast)) ||
((lNew > lAnchor) && (lAnchor > lLast)))
{
//
// If we flip sides around the anchor,
// then do one side before the other
//
hr = SelectRange(lAnchor, lLast, lAnchor);
if (FAILED(hr))
goto Cleanup;
hr = SelectRange(lAnchor, lAnchor, lNew);
// Done
goto Cleanup;
}
bUndo = (abs(lNew - lAnchor) < abs(lLast - lAnchor));
// Skip over the option that does not need to change
lRangeLast = lLast;
lRangeNew = lNew;
if (bUndo)
{
if (lLast < lNew)
{
lRangeNew--;
}
else
{
lRangeNew++;
}
}
else
{
if (lLast < lNew)
{
lRangeLast++;
}
else
{
lRangeLast--;
}
}
if (lRangeLast < lRangeNew)
{
lIndexStart = lRangeLast;
lIndexEnd = lRangeNew;
}
else
{
lIndexStart = lRangeNew;
lIndexEnd = lRangeLast;
}
hr = a.Open(CA_ELEM);
if (FAILED(hr))
goto Cleanup;
hr = a.Elem()->get_children((IDispatch**)&pItems);
if (FAILED(hr))
goto Cleanup;
#if DBG == 1 // For Assert Check
hr = pItems->get_length(&cItems);
if (FAILED(hr))
goto Cleanup;
#endif
Assert( (lIndexStart >= 0) && (lIndexEnd >= 0) &&
(lIndexStart < cItems) && (lIndexEnd < cItems) );
hr = GetIndex(lAnchor, &pAnchor);
if (FAILED(hr))
goto Cleanup;
hr = pAnchor->GetHighlight(&bAnchorHighlight);
if (FAILED(hr))
goto Cleanup;
V_VT(&var) = VT_I4;
for (iItem = lIndexStart; iItem <= lIndexEnd; iItem++)
{
V_I4(&var) = iItem;
hr = pItems->item(var, var2, &pDispatchItem);
if (FAILED(hr))
goto Cleanup;
hr = pDispatchItem->QueryInterface(IID_IPrivateOption, (void**)&pOption);
if (FAILED(hr))
goto Cleanup;
if (_fDragMode)
{
if (bUndo)
{
hr = pOption->SetPrevHighlight();
if (FAILED(hr))
goto Cleanup;
}
else
{
hr = pOption->SetHighlight(bAnchorHighlight);
if (FAILED(hr))
goto Cleanup;
}
}
else
{
hr = pOption->SetHighlight(bUndo ? VARIANT_FALSE : VARIANT_TRUE);
if (FAILED(hr))
goto Cleanup;
}
AdjustChangedRange(iItem);
ClearInterface(&pDispatchItem);
ClearInterface(&pOption);
}
Cleanup:
ReleaseInterface(pItems);
ReleaseInterface(pDispatchItem);
ReleaseInterface(pOption);
ReleaseInterface(pAnchor);
return hr;
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::DropDownSelect()
//
// Synopsis: Puts the text from the option into the display.
//
// Arguments: IPrivateOption *pOption - Option to select
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT
CIESelectElement::DropDownSelect(IPrivateOption *pOption)
{
HRESULT hr;
CComBSTR bstr;
IHTMLElement *pOptionElem = NULL;
if (!_pElemDisplay)
{
return S_OK;
}
hr = pOption->QueryInterface(IID_IHTMLElement, (void**)&pOptionElem);
if (FAILED(hr))
goto Cleanup;
// Put the text from the option into the display bar
hr = pOptionElem->get_innerHTML(&bstr);
if (FAILED(hr))
goto Cleanup;
hr = _pElemDisplay->put_innerHTML(bstr);
if (FAILED(hr))
goto Cleanup;
Cleanup:
ReleaseInterface(pOptionElem);
return hr;
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::TogglePopup()
//
// Synopsis: Toggles the popup open/closed.
//
// Arguments: None
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT
CIESelectElement::TogglePopup()
{
HRESULT hr;
VARIANT_BOOL bOpen;
Assert(_pPopup);
hr = _pPopup->get_isOpen(&bOpen);
if (FAILED(hr))
goto Cleanup;
if (_bPopupOpen == VARIANT_TRUE)
{
hr = HidePopup();
if (FAILED(hr))
goto Cleanup;
_bPopupOpen = VARIANT_FALSE;
}
else
{
hr = ShowPopup();
if (FAILED(hr))
goto Cleanup;
_bPopupOpen = VARIANT_TRUE;
}
Cleanup:
return hr;
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::HidePopup()
//
// Synopsis: Hides the popup.
//
// Arguments: None
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT
CIESelectElement::HidePopup()
{
HRESULT hr;
VARIANT_BOOL bOpen;
Assert(_pPopup && _pSelectInPopup );
hr = _pPopup->get_isOpen(&bOpen);
if (FAILED(hr))
goto Cleanup;
if (bOpen)
{
hr = _pPopup->hide();
if (FAILED(hr))
goto Cleanup;
hr = SetDisplayHighlight(TRUE);
if (FAILED(hr))
goto Cleanup;
hr = RefreshFocusRect();
if (FAILED(hr))
goto Cleanup;
}
Cleanup:
return hr;
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::ShowPopup()
//
// Synopsis: Shows the popup.
//
// Arguments: None
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT
CIESelectElement::ShowPopup()
{
HRESULT hr;
VARIANT_BOOL bOpen;
CContextAccess a(_pSite);
VARIANT var;
IUnknown *pUnk = NULL;
long lHeightSelect;
CComBSTR bstrWritingMode(_T(""));
CComBSTR bstrHTML;
long lX;
long lY;
long lWidth;
Assert(_pPopup && _pSelectInPopup);
hr = a.Open(CA_ELEM);
if (FAILED(hr))
goto Cleanup;
hr = SetDisplayHighlight(FALSE);
if (FAILED(hr))
goto Cleanup;
hr = RefreshFocusRect();
if (FAILED(hr))
goto Cleanup;
hr = _pPopup->get_isOpen(&bOpen);
if (FAILED(hr))
goto Cleanup;
hr = a.Elem()->QueryInterface(IID_IUnknown, (void **)&pUnk);
V_VT(&var) = VT_UNKNOWN;
V_UNKNOWN(&var) = pUnk;
hr = a.Elem()->get_offsetHeight(&lHeightSelect);
if (FAILED(hr))
goto Cleanup;
if (_pSelectInPopup)
{
if (_fWritingModeTBRL)
{
bstrWritingMode = _T("tb-rl");
}
hr = _pSelectInPopup->SetWritingMode(bstrWritingMode);
if (FAILED(hr))
goto Cleanup;
}
#ifdef SELECT_GETSIZE
if (!bOpen)
{
long lHeight;
long lNumOptions;
hr = GetNumOptions(&lNumOptions);
if (FAILED(hr))
goto Cleanup;
// TODO: Fix SELECT_MAXOPTIONS with correct value
lHeight = min(SELECT_MAXOPTIONS, lNumOptions);
lHeight *= _sizeOption.cy;
lHeight += 2;
if (_fWritingModeTBRL)
{
hr = a.Elem()->get_offsetWidth(&lHeightSelect);
if (FAILED(hr))
goto Cleanup;
lX = lHeightSelect- lHeight-_sizeOption.cy;
lY = 0;
lWidth = lHeight;
lHeight = _sizeSelect.cx;
}
else
{
lX = 0;
lY = lHeightSelect;
lWidth = _sizeSelect.cx;
}
hr = _pPopup->show(lX, lY, lWidth, lHeight, &var);
if (FAILED(hr))
goto Cleanup;
}
#else
if (!bOpen)
{
if (_fWritingModeTBRL)
{
lX = lHeightSelect- lHeight-_sizeOption.cy;
lY = 0;
lWidth = _lPopupSize.cy;
lHeight = _lPopupSize.cx;
}
else
{
lX = o;
lY = lHeightSelect;
lWidth = _lPopupSize.cx;
lHeight = _lPopupSize.cy;
}
hr = _pPopup->show(lX, lY, lWidth, lHeight, &var);
if (FAILED(hr))
goto Cleanup;
}
#endif
Cleanup:
ReleaseInterface(pUnk);
return hr;
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::OnChangeInPopup()
//
// Synopsis: Called when there is a change in the popup.
//
// Arguments: None
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT
CIESelectElement::OnChangeInPopup()
{
HRESULT hr;
hr = HidePopup();
if (FAILED(hr))
goto Cleanup;
hr = SynchSelWithPopup();
if (FAILED(hr))
goto Cleanup;
Cleanup:
return hr;
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::SynchSelWithPopup()
//
// Synopsis: Synchronizes the selection state between the popup
// and the select.
//
// Arguments: None
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT
CIESelectElement::SynchSelWithPopup()
{
HRESULT hr;
long lIndex;
long lPrevIndex;
Assert(_pSelectInPopup);
hr = _pSelectInPopup->GetSelectedIndex(&lIndex);
if (FAILED(hr))
goto Cleanup;
hr = get_selectedIndex(&lPrevIndex);
if (FAILED(hr))
goto Cleanup;
if (lPrevIndex != lIndex)
{
//
// Only make a selection if there's a change
//
hr = Select(lIndex);
if (FAILED(hr))
goto Cleanup;
}
Cleanup:
return hr;
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::OnButtonMouseDown()
//
// Synopsis: Called when the button in the drop control should
// go down.
//
// Arguments: CEventObjectAccess *pEvent Event information
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT
CIESelectElement::OnButtonMouseDown(CEventObjectAccess *pEvent)
{
HRESULT hr;
VARIANT_BOOL bOpen;
Assert(_pPopup);
hr = _pPopup->get_isOpen(&bOpen);
if (FAILED(hr))
goto Cleanup;
if (bOpen == VARIANT_FALSE)
{
hr = PressButton(TRUE);
if (FAILED(hr))
goto Cleanup;
}
Cleanup:
return hr;
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::PressButton()
//
// Synopsis: Makes the button appear up/down.
//
// Arguments: BOOL bDown - TRUE for down, FALSE for up
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT
CIESelectElement::PressButton(BOOL bDown)
{
HRESULT hr = S_OK;
CComBSTR bstr;
CVariant var;
if (bDown)
{
// Down border
bstr = _T("1 inset");
// Set the padding to make the arrow go down/right
V_VT(&var) = VT_I4;
V_I4(&var) = 2;
}
else
{
// Up border
bstr = _T("2 outset");
// Set the padding to make the arrow go back to center
V_VT(&var) = VT_I4;
V_I4(&var) = 0;
}
hr = _pStyleButton->put_border(bstr);
if (FAILED(hr))
goto Cleanup;
hr = _pStyleButton->put_paddingTop(var);
if (FAILED(hr))
goto Cleanup;
hr = _pStyleButton->put_paddingLeft(var);
if (FAILED(hr))
goto Cleanup;
Cleanup:
return hr;
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::TimerProc()
//
// Synopsis: Callback for the scrolling timer.
//
// Arguments: HWND hwnd - Window handle owning the timer
// UINT uMsg - Message ID
// UINT idEvent - Timer ID
// DWORD dwTime - Time that has passed
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
VOID CALLBACK
CIESelectElement::TimerProc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
{
// If the ID does not match, then don't do anything
if (idEvent != _iTimerID)
{
return;
}
if (_pTimerSelect)
{
//
// We have a select to deal with
//
_pTimerSelect->HandleDownXY(_ptSavedPoint, _bSavedCtrl);
}
else
{
//
// Someone didn't clear us, so we need to cleanup
//
if (KillTimer(NULL, _iTimerID))
{
_iTimerID = 0;
}
}
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::SetScrollTimeout()
//
// Synopsis: Sets the scrolling timer.
//
// Arguments: POINT pt - Point where the mouse was
// BOOL bCtrl - TRUE if the CTRL key was down
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT
CIESelectElement::SetScrollTimeout(POINT pt, BOOL bCtrl)
{
if (_iTimerID)
{
ClearScrollTimeout();
}
_ptSavedPoint = pt;
_bSavedCtrl = bCtrl;
_pTimerSelect = this;
_iTimerID = SetTimer(NULL, 0, SELECT_SCROLLWAIT, CIESelectElement::TimerProc);
if (!_iTimerID)
{
_pTimerSelect = NULL;
return E_FAIL;
}
return S_OK;
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::ClearScrollTimeout()
//
// Synopsis: Clears the scrolling timeout.
//
// Arguments: None
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
HRESULT
CIESelectElement::ClearScrollTimeout()
{
BOOL bSuccess = TRUE;
if (_iTimerID)
{
bSuccess = KillTimer(NULL, _iTimerID);
_iTimerID = 0;
_pTimerSelect = NULL;
}
return bSuccess ? S_OK : E_FAIL;
}
#ifdef SELECT_TIMERVL
//+------------------------------------------------------------------
//
// Member: CIESelectElement::InitTimerVLServices()
//
// Synopsis: Initializes the critical section lock for the timer.
//
// Arguments: None
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
VOID
CIESelectElement::InitTimerVLServices()
{
if (_lTimerVLRef == 0)
{
// ISSUE: May happen more than once
InitializeCriticalSection(&_lockTimerVL);
}
// Guaranteed to return 0, when _lTimerVLRef becomes 0
// Not guaranteed to return _lTimerVLRef when nonzero (in Win95)
// Guaranteed to return _lTimerVLRef in Win98/NT4
InterlockedIncrement(&_lTimerVLRef);
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::DeInitTimerVLServices()
//
// Synopsis: Frees the critical section lock.
//
// Arguments: None
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
VOID
CIESelectElement::DeInitTimerVLServices()
{
if (InterlockedDecrement(&_lTimerVLRef) == 0)
{
DeleteCriticalSection(&_lockTimerVL);
}
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::SetTimerVL()
//
// Synopsis: Sets the VL timer.
//
// Arguments: None
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
VOID
CIESelectElement::SetTimerVL()
{
_iTimerVL = SetTimer(NULL, 0, SELECT_TIMERVLWAIT, CIESelectElement::TimerVLProc);
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::ClearTimerVL()
//
// Synopsis: Kills the VL timer.
//
// Arguments: None
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
VOID
CIESelectElement::ClearTimerVL()
{
if (KillTimer(NULL, _iTimerVL))
{
_iTimerVL = 0;
}
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::AddSelectToTimerVLQueue()
//
// Synopsis: Adds a select to the queue.
//
// Arguments: None
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
VOID
CIESelectElement::AddSelectToTimerVLQueue(CIESelectElement *pSelect)
{
struct SELECT_TIMERVL_LINKELEM *pElem = new struct SELECT_TIMERVL_LINKELEM;
struct SELECT_TIMERVL_LINKELEM *pParent;
if (pElem)
{
BOOL bSetTimer;
pElem->pNext = NULL;
pElem->pSelect = pSelect;
InitTimerVLServices();
__try
{
EnterCriticalSection (&_lockTimerVL);
bSetTimer = (_queueTimerVL == NULL);
// Add to the queue
if (_queueTimerVL == NULL)
{
_queueTimerVL = pElem;
}
else
{
pElem->pNext = _queueTimerVL;
_queueTimerVL = pElem;
}
if (bSetTimer)
{
SetTimerVL();
}
}
__finally
{
// Release ownership of the critical section
LeaveCriticalSection (&_lockTimerVL);
}
}
else
{
Assert(FALSE);
}
}
//+------------------------------------------------------------------
//
// Member: CIESelectElement::TimerVLProc()
//
// Synopsis: Called by the timer.
//
// Arguments: None
//
// Returns: Nonzero on errors.
//
//-------------------------------------------------------------------
VOID CALLBACK
CIESelectElement::TimerVLProc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
{
long lNumRemoved = 0;
long lIndex;
struct SELECT_TIMERVL_LINKELEM *pRoot = NULL;
struct SELECT_TIMERVL_LINKELEM *pNode;
if (idEvent != _iTimerVL)
return;
__try
{
EnterCriticalSection (&_lockTimerVL);
ClearTimerVL();
// Empty the queue
pRoot = _queueTimerVL;
_queueTimerVL = NULL;
}
__finally
{
// Release ownership of the critical section
LeaveCriticalSection (&_lockTimerVL);
}
// Go through the nodes we retrieved
while (pRoot)
{
pNode = pRoot;
pRoot = pRoot->pNext;
// Init the viewlink
pNode->pSelect->InitViewLink();
delete pNode;
// Dealloc the reference to the timer services
DeInitTimerVLServices();
}
}
#endif
//+------------------------------------------------------------------------
//
// CIESelectElement::CEventSink
//
// IDispatch Implementation
// The event sink's IDispatch interface is what gets called when events
// are fired.
//
//-------------------------------------------------------------------------
//+------------------------------------------------------------------------
//
// Member: CIESelectElement::CEventSink::GetTypeInfoCount
// CIESelectElement::CEventSink::GetTypeInfo
// CIESelectElement::CEventSink::GetIDsOfNames
//
// Synopsis: We don't really need a nice IDispatch... this minimalist
// version does just plenty.
//
//-------------------------------------------------------------------------
STDMETHODIMP
CIESelectElement::CEventSink::GetTypeInfoCount(UINT* /*pctinfo*/)
{
return E_NOTIMPL;
}
STDMETHODIMP
CIESelectElement::CEventSink::GetTypeInfo(/* [in] */ UINT /*iTInfo*/,
/* [in] */ LCID /*lcid*/,
/* [out] */ ITypeInfo** /*ppTInfo*/)
{
return E_NOTIMPL;
}
STDMETHODIMP
CIESelectElement::CEventSink::GetIDsOfNames( REFIID riid,
OLECHAR** rgszNames,
UINT cNames,
LCID lcid,
DISPID* rgDispId)
{
return E_NOTIMPL;
}
//+------------------------------------------------------------------------
//
// Member: CIESelectElement::CEventSink::Invoke
//
// Synopsis: This gets called for all events on our object. (it was
// registered to do so in Init with attach_event.) It calls
// the appropriate parent functions to handle the events.
//
//-------------------------------------------------------------------------
STDMETHODIMP
CIESelectElement::CEventSink::Invoke(DISPID dispIdMember,
REFIID, LCID,
WORD wFlags,
DISPPARAMS* pDispParams,
VARIANT* pVarResult,
EXCEPINFO*,
UINT* puArgErr)
{
HRESULT hr = S_OK;
long lCookie;
CComBSTR bstrEvent;
BOOL bRefire = FALSE;
Assert(_pParent);
if (!pDispParams || (pDispParams->cArgs < 1))
goto Cleanup;
if (pDispParams->rgvarg[0].vt == VT_DISPATCH)
{
CEventObjectAccess eoa(pDispParams);
hr = eoa.Open(EOA_EVENTOBJ);
if (FAILED(hr))
goto Cleanup;
hr = eoa.EventObj()->get_type(&bstrEvent);
if (FAILED(hr))
goto Cleanup;
if (_dwFlags & SELECTES_POPUP)
{
if (!StrCmpICW(bstrEvent, L"change"))
{
hr = _pParent->OnChangeInPopup();
if (FAILED(hr))
goto Cleanup;
}
else if (!StrCmpICW(bstrEvent, L"keydown"))
{
bRefire = TRUE;
lCookie = _pParent->_lOnKeyDownCookie;
}
else if (!StrCmpICW(bstrEvent, L"keyup"))
{
bRefire = TRUE;
lCookie = _pParent->_lOnKeyUpCookie;
}
else if (!StrCmpICW(bstrEvent, L"keypress"))
{
bRefire = TRUE;
lCookie = _pParent->_lOnKeyPressedCookie;
}
}
else if (_dwFlags & SELECTES_VIEWLINK)
{
if (!StrCmpICW(bstrEvent, L"mousedown"))
{
hr = _pParent->OnMouseDown(&eoa);
if (FAILED(hr))
goto Cleanup;
lCookie = _pParent->_lOnMouseDownCookie;
bRefire = TRUE;
}
else if (!StrCmpICW(bstrEvent, L"selectstart"))
{
hr = _pParent->OnSelectStart(&eoa);
if (FAILED(hr))
goto Cleanup;
}
else if (!StrCmpICW(bstrEvent, L"contextmenu"))
{
hr = _pParent->OnContextMenu(&eoa);
if (FAILED(hr))
goto Cleanup;
}
else if (!StrCmpICW(bstrEvent, L"beforefocusenter"))
{
hr = _pParent->CancelEvent(&eoa);
if (FAILED(hr))
goto Cleanup;
}
else if (!StrCmpICW(bstrEvent, L"mouseup"))
{
bRefire = TRUE;
lCookie = _pParent->_lOnMouseUpCookie;
}
else if (!StrCmpICW(bstrEvent, L"click"))
{
bRefire = TRUE;
lCookie = _pParent->_lOnClickCookie;
}
}
else if (_dwFlags & SELECTES_BUTTON)
{
if (!StrCmpICW(bstrEvent, L"mousedown"))
{
hr = _pParent->OnButtonMouseDown(&eoa);
if (FAILED(hr))
goto Cleanup;
}
else if (!StrCmpICW(bstrEvent, L"mouseout") || !StrCmpICW(bstrEvent, L"mouseup"))
{
hr = _pParent->PressButton(FALSE);
if (FAILED(hr))
goto Cleanup;
}
}
else if (_dwFlags & SELECTES_INPOPUP)
{
// ISSUE: This might not be needed if popup is able to send keystrokes
if (!StrCmpICW(bstrEvent, L"keydown"))
{
hr = _pParent->OnKeyDown(&eoa);
if (FAILED(hr))
goto Cleanup;
}
}
if (bRefire)
{
// Cancel bubbling to master
hr = eoa.EventObj()->put_cancelBubble(VB_TRUE);
if (FAILED(hr))
goto Cleanup;
// refire the event on master
hr = _pParent->FireEvent(lCookie, NULL, bstrEvent);
if (FAILED(hr))
goto Cleanup;
}
}
Cleanup:
return hr;
}
//+------------------------------------------------------------------------
//
// Member: CIESelectElement::CEventSink
//
// Synopsis: This is used to allow communication between the parent class
// and the event sink class. The event sink will call the ProcOn*
// methods on the parent at the appropriate times.
//
//-------------------------------------------------------------------------
CIESelectElement::CEventSink::CEventSink(CIESelectElement *pParent, DWORD dwFlags)
{
_pParent = pParent;
_dwFlags = dwFlags;
}
// ========================================================================
// CIESelectElement::CEventSink
//
// IUnknown Implementation
// Vanilla IUnknown implementation for the event sink.
// ========================================================================
STDMETHODIMP
CIESelectElement::CEventSink::QueryInterface(REFIID riid, void ** ppUnk)
{
void * pUnk = NULL;
if (riid == IID_IDispatch)
pUnk = (IDispatch*)this;
if (riid == IID_IUnknown)
pUnk = (IUnknown*)this;
if (pUnk)
{
*ppUnk = pUnk;
AddRef();
return S_OK;
}
return E_NOINTERFACE;
}
STDMETHODIMP_(ULONG)
CIESelectElement::CEventSink::AddRef(void)
{
return ((IElementBehavior*)_pParent)->AddRef();
}
STDMETHODIMP_(ULONG)
CIESelectElement::CEventSink::Release(void)
{
return ((IElementBehavior*)_pParent)->Release();
}