//------------------------------------------------------------------- // // File: SelRange.cpp // // Contents: // This file contians Selection Range handling code. // //------------------------------------------------------------------- #include "ctlspriv.h" #include "selrange.h" #include "stdio.h" #include #define MINCOUNT 6 // number of sel ranges to start with amd maintain #define GROWSIZE 150 // percent to grow when needed #define COUNT_SELRANGES_NONE 2 // When count of selranges really means none typedef struct tag_SELRANGEITEM { LONG iBegin; LONG iEnd; } SELRANGEITEM, *PSELRANGEITEM; class CLVRange : public ILVRange { public: // *** IUnknown methods *** STDMETHODIMP QueryInterface(REFIID riid, LPVOID * ppv); STDMETHODIMP_(ULONG) AddRef(void); STDMETHODIMP_(ULONG) Release(void); // *** ILVRange methods *** STDMETHODIMP IncludeRange(LONG iBegin, LONG iEnd); STDMETHODIMP ExcludeRange(LONG iBegin, LONG iEnd); STDMETHODIMP InvertRange(LONG iBegin, LONG iEnd); STDMETHODIMP InsertItem(LONG iItem); STDMETHODIMP RemoveItem(LONG iItem); STDMETHODIMP Clear(); STDMETHODIMP IsSelected(LONG iItem); STDMETHODIMP IsEmpty(); STDMETHODIMP NextSelected(LONG iItem, LONG *piItem); STDMETHODIMP NextUnSelected(LONG iItem, LONG *piItem); STDMETHODIMP CountIncluded(LONG *pcIncluded); protected: // Helper Functions. friend ILVRange *LVRange_Create(); CLVRange(); ~CLVRange(); BOOL _Enlarge(); BOOL _Shrink(); BOOL _InsertRange(LONG iAfterItem, LONG iBegin, LONG iEnd); HRESULT _RemoveRanges(LONG iStartItem, LONG iStopItem, LONG *p); BOOL _FindValue(LONG Value, LONG* piItem); void _InitNew(); int _cRef; PSELRANGEITEM _VSelRanges; // Vector of sel ranges LONG _cSize; // size of above vector in sel ranges LONG _cSelRanges; // count of sel ranges used LONG _cIncluded; // Count of Included items... }; //------------------------------------------------------------------- // // Function: _Enlarge // // Summary: // This will enlarge the number of items the Sel Range can have. // // Arguments: // PSELRANGE [in] - SelRange to Enlarge // // Return: FALSE if failed. // // Notes: Though this function may fail, pselrange structure is still valid // // History: // 17-Oct-94 MikeMi Created // //------------------------------------------------------------------- BOOL CLVRange::_Enlarge() { LONG cNewSize; PSELRANGEITEM pTempSelRange; BOOL frt = FALSE; cNewSize = _cSize * GROWSIZE / 100; pTempSelRange = (PSELRANGEITEM) GlobalReAlloc( (HGLOBAL)_VSelRanges, cNewSize * sizeof( SELRANGEITEM ), GMEM_ZEROINIT | GMEM_MOVEABLE ); if (NULL != pTempSelRange) { _VSelRanges = pTempSelRange; _cSize = cNewSize; frt = TRUE; } return( frt ); } //------------------------------------------------------------------- // // Function: _Shrink // // Summary: // This will reduce the number of items the Sel Range can have. // // Arguments: // // Return: FALSE if failed // // Notes: Shrink only happens when a significant size below the next size // is obtained and the new size is at least the minimum size. // Though this function may fail, pselrange structure is still valid // // History: // 17-Oct-94 MikeMi Created // //------------------------------------------------------------------- BOOL CLVRange::_Shrink() { LONG cNewSize; LONG cTriggerSize; PSELRANGEITEM pTempSelRange; BOOL frt = TRUE; // check if we are below last grow area by a small percent cTriggerSize = _cSize * 90 / GROWSIZE; cNewSize = _cSize * 100 / GROWSIZE; if ((_cSelRanges < cTriggerSize) && (cNewSize >= MINCOUNT)) { pTempSelRange = (PSELRANGEITEM) GlobalReAlloc( (HGLOBAL)_VSelRanges, cNewSize * sizeof( SELRANGEITEM ), GMEM_ZEROINIT | GMEM_MOVEABLE ); if (NULL != pTempSelRange) { _VSelRanges = pTempSelRange; _cSize = cNewSize; } else { frt = FALSE; } } return( frt ); } //------------------------------------------------------------------- // // Function: _InsertRange // // Summary: // inserts a single range item into the range vector // // Arguments: // iAfterItem [in] - Index to insert range after, -1 means insert as first item // iBegin [in] - begin of range // iEnd [in] - end of the range // // Return: // TRUE if succesful, otherwise FALSE // // Notes: // // History: // 17-Oct-94 MikeMi Created // //------------------------------------------------------------------- BOOL CLVRange::_InsertRange(LONG iAfterItem, LONG iBegin, LONG iEnd ) { LONG iItem; BOOL frt = TRUE; ASSERT( iAfterItem >= -1 ); ASSERT( iBegin >= SELRANGE_MINVALUE ); ASSERT( iEnd >= iBegin ); ASSERT( iEnd <= SELRANGE_MAXVALUE ); ASSERT( _cSelRanges < _cSize ); // shift all over one for (iItem = _cSelRanges; iItem > iAfterItem + 1; iItem--) { _VSelRanges[iItem] = _VSelRanges[iItem-1]; } _cSelRanges++; // make the insertion _VSelRanges[iAfterItem+1].iBegin = iBegin; _VSelRanges[iAfterItem+1].iEnd = iEnd; // make sure we have room next time if (_cSelRanges == _cSize) { frt = _Enlarge(); } return( frt ); } //------------------------------------------------------------------- // // Function: _RemoveRanges // // Summary: // Removes all ranged between and including the speicifed indexes // // Arguments: // iStartItem [in] - Index to start removal // iStopItem [in] - Index to stop removal // // Return: // SELRANGE_ERROR on memory allocation error // The number of items that are unselected by this removal // // Notes: // // History: // 17-Oct-94 MikeMi Created // //------------------------------------------------------------------- HRESULT CLVRange::_RemoveRanges(LONG iStartItem, LONG iStopItem, LONG *pc ) { LONG iItem; LONG diff; LONG cUnSelected = 0; HRESULT hres = S_OK; ASSERT( iStartItem > 0 ); ASSERT( iStopItem >= iStartItem ); ASSERT( iStartItem < _cSelRanges - 1 ); ASSERT( iStopItem < _cSelRanges - 1 ); diff = iStopItem - iStartItem + 1; for (iItem = iStartItem; iItem <= iStopItem; iItem++) cUnSelected += _VSelRanges[iItem].iEnd - _VSelRanges[iItem].iBegin + 1; // shift all over the difference for (iItem = iStopItem+1; iItem < _cSelRanges; iItem++, iStartItem++) _VSelRanges[iStartItem] = _VSelRanges[iItem]; _cSelRanges -= diff; if (!_Shrink()) { hres = E_FAIL; } else if (pc) *pc = cUnSelected; return( hres ); } //------------------------------------------------------------------- // // Function: SelRange_FindValue // // Summary: // This function will search the ranges for the value, returning true // if the value was found within a range. The piItem will contain the // the index at which it was found or the index before where it should be // The piItem may be set to -1, meaning that there are no ranges in the list // This functions uses a non-recursive binary search algorithm. // // Arguments: // piItem [out] - Return of found range index, or one before // Value [in] - Value to find within a range // // Return: True if found, False if not found // // Notes: The piItem will return one before if return is false. // // History: // 14-Oct-94 MikeMi Created // //------------------------------------------------------------------- BOOL CLVRange::_FindValue(LONG Value, LONG* piItem ) { LONG First; LONG Last; LONG Item; BOOL fFound = FALSE; ASSERT( piItem ); ASSERT( _cSize >= COUNT_SELRANGES_NONE ); ASSERT( Value >= SELRANGE_MINVALUE ); ASSERT( Value <= SELRANGE_MAXVALUE ); First = 0; Last = _cSelRanges - 1; Item = Last / 2; do { if (_VSelRanges[Item].iBegin > Value) { // Value before this Item Last = Item; Item = (Last - First) / 2 + First; if (Item == Last) { Item = First; break; } } else if (_VSelRanges[Item].iEnd < Value) { // Value after this Item First = Item; Item = (Last - First) / 2 + First; if (Item == First) { break; } } else { // Value at this Item fFound = TRUE; } } while (!fFound); *piItem = Item; return( fFound ); } //------------------------------------------------------------------- // // Function: _InitNew // // Summary: // This function will initialize a SelRange object. // // Arguments: // // Return: // // Notes: // // History: // 18-Oct-94 MikeMi Created // //------------------------------------------------------------------- void CLVRange::_InitNew() { _cSize = MINCOUNT; _cSelRanges = COUNT_SELRANGES_NONE; _VSelRanges[0].iBegin = LONG_MIN; // -2 and +2 below are to stop consecutive joining of end markers _VSelRanges[0].iEnd = SELRANGE_MINVALUE - 2; _VSelRanges[1].iBegin = SELRANGE_MAXVALUE + 2; _VSelRanges[1].iEnd = SELRANGE_MAXVALUE + 2; _cIncluded = 0; } //------------------------------------------------------------------- // // Function: SelRange_Create // // Summary: // This function will create and initialize a SelRange object. // // Arguments: // // Return: HSELRANGE that is created or NULL if it failed. // // Notes: // // History: // 14-Oct-94 MikeMi Created // //------------------------------------------------------------------- ILVRange *LVRange_Create( ) { CLVRange *pselrange = new CLVRange; if (NULL != pselrange) { pselrange->_VSelRanges = (PSELRANGEITEM) GlobalAlloc( GPTR, sizeof( SELRANGEITEM ) * MINCOUNT ); if (NULL != pselrange->_VSelRanges) { pselrange->_InitNew(); } else { delete pselrange; pselrange = NULL; } } return( pselrange? SAFECAST(pselrange, ILVRange*) : NULL); } //------------------------------------------------------------------- // // Function: Constructor // //------------------------------------------------------------------- CLVRange::CLVRange() { _cRef = 1; } //------------------------------------------------------------------- // // Function: Destructor // //------------------------------------------------------------------- CLVRange::~CLVRange() { GlobalFree( _VSelRanges ); } //------------------------------------------------------------------- // // Function: QueryInterface // //------------------------------------------------------------------- HRESULT CLVRange::QueryInterface(REFIID iid, void **ppv) { if (IsEqualIID(iid, IID_ILVRange) || IsEqualIID(iid, IID_IUnknown)) { *ppv = SAFECAST(this, ILVRange *); } else { *ppv = NULL; return E_NOINTERFACE; } _cRef++; return NOERROR; } //------------------------------------------------------------------- // // Function: AddRef // //------------------------------------------------------------------- ULONG CLVRange::AddRef() { return ++_cRef; } //------------------------------------------------------------------- // // Function: Release // //------------------------------------------------------------------- ULONG CLVRange::Release() { if (--_cRef) return _cRef; delete this; return 0; } //------------------------------------------------------------------- // // Function: IncludeRange // // Summary: // This function will include the range defined into the current // ranges, compacting as needed. // // Arguments: // hselrange [in] - Handle to the SelRange // iBegin [in] - Begin of new range // iEnd [in] - End of new range // // Notes: // // History: // 14-Oct-94 MikeMi Created // //------------------------------------------------------------------- HRESULT CLVRange::IncludeRange(LONG iBegin, LONG iEnd ) { LONG iFirst; // index before or contains iBegin value LONG iLast; // index before or contains iEnd value BOOL fExtendFirst; // do we extend the iFirst or create one after it LONG iRemoveStart; // start of ranges that need to be removed LONG iRemoveFinish; // end of ranges that need to be removed LONG iNewEnd; // calculate new end value as we go BOOL fEndFound; // was the iEnd found in a range already BOOL fBeginFound; // was the iEnd found in a range already LONG cSelected = 0; HRESULT hres = S_OK; ASSERT( iEnd >= iBegin ); ASSERT( iBegin >= SELRANGE_MINVALUE ); ASSERT( iEnd <= SELRANGE_MAXVALUE ); // find approximate locations fBeginFound = _FindValue( iBegin, &iFirst ); fEndFound = _FindValue( iEnd, &iLast ); // // Find First values // // check for consecutive End-First values if ((_VSelRanges[iFirst].iEnd == iBegin - 1) || (fBeginFound)) { // extend iFirst fExtendFirst = TRUE; iRemoveStart = iFirst + 1; } else { // create one after the iFirst fExtendFirst = FALSE; iRemoveStart = iFirst + 2; } // // Find Last values // if (fEndFound) { // Use [iLast].iEnd value iRemoveFinish = iLast; iNewEnd = _VSelRanges[iLast].iEnd; } else { // check for consecutive First-End values if (_VSelRanges[iLast + 1].iBegin == iEnd + 1) { // Use [iLast + 1].iEnd value iNewEnd = _VSelRanges[iLast+1].iEnd; iRemoveFinish = iLast + 1; } else { // Use iEnd value iRemoveFinish = iLast; iNewEnd = iEnd; } } // // remove condenced items if needed // if (iRemoveStart <= iRemoveFinish) { LONG cChange; hres = _RemoveRanges(iRemoveStart, iRemoveFinish, &cChange ); if (FAILED(hres)) return hres; else { cSelected -= cChange; } } // // insert item and reset values as needed // if (fExtendFirst) { cSelected += iNewEnd - _VSelRanges[iFirst].iEnd; _VSelRanges[iFirst].iEnd = iNewEnd; } else { if (iRemoveStart > iRemoveFinish + 1) { cSelected += iEnd - iBegin + 1; // create one if (!_InsertRange(iFirst, iBegin, iNewEnd )) { hres = E_FAIL; } } else { cSelected += iNewEnd - _VSelRanges[iFirst+1].iEnd; cSelected += _VSelRanges[iFirst+1].iBegin - iBegin; // no need to create one since the Removal would have left us one _VSelRanges[iFirst+1].iEnd = iNewEnd; _VSelRanges[iFirst+1].iBegin = iBegin; } } _cIncluded += cSelected; return( hres ); } //------------------------------------------------------------------- // // Function: SelRange_ExcludeRange // // Summary: // This function will exclude the range defined from the current // ranges, compacting and enlarging as needed. // // Arguments: // hselrange [in] - Handle to the SelRange // iBegin [in] - Begin of range to remove // iEnd [in] - End of range to remove // // Return: // SELRANGE_ERROR if memory allocation error // the number actual items that changed state // // Notes: // // History: // 17-Oct-94 MikeMi Created // //------------------------------------------------------------------- HRESULT CLVRange::ExcludeRange( LONG iBegin, LONG iEnd ) { LONG iFirst; // index before or contains iBegin value LONG iLast; // index before or contains iEnd value LONG iRemoveStart; // start of ranges that need to be removed LONG iRemoveFinish; // end of ranges that need to be removed LONG iFirstNewEnd; // calculate new end value as we go BOOL fBeginFound; // was the iBegin found in a range already BOOL fEndFound; // was the iEnd found in a range already LONG cUnSelected = 0; HRESULT hres = S_OK; ASSERT( iEnd >= iBegin ); ASSERT( iBegin >= SELRANGE_MINVALUE ); ASSERT( iEnd <= SELRANGE_MAXVALUE ); // find approximate locations fBeginFound = _FindValue( iBegin, &iFirst ); fEndFound = _FindValue( iEnd, &iLast ); // // Find First values // // start removal after first iRemoveStart = iFirst + 1; // save FirstEnd as we may need to modify it iFirstNewEnd = _VSelRanges[iFirst].iEnd; if (fBeginFound) { // check for complete removal of first // (first is a single selection or match?) if (_VSelRanges[iFirst].iBegin == iBegin) { iRemoveStart = iFirst; } else { // otherwise truncate iFirst iFirstNewEnd = iBegin - 1; } } // // Find Last values // // end removal on last iRemoveFinish = iLast; if (fEndFound) { // check for complete removal of last // (first/last is a single selection or match?) if (_VSelRanges[iLast].iEnd != iEnd) { if (iFirst == iLast) { // split if (!_InsertRange(iFirst, iEnd + 1, _VSelRanges[iFirst].iEnd )) { return( E_FAIL ); } cUnSelected -= _VSelRanges[iFirst].iEnd - iEnd; } else { // truncate Last iRemoveFinish = iLast - 1; cUnSelected += (iEnd + 1) - _VSelRanges[iLast].iBegin; _VSelRanges[iLast].iBegin = iEnd + 1; } } } // Now set the new end, since Last code may have needed the original values cUnSelected -= iFirstNewEnd - _VSelRanges[iFirst].iEnd; _VSelRanges[iFirst].iEnd = iFirstNewEnd; // // remove items if needed // if (iRemoveStart <= iRemoveFinish) { LONG cChange; if (SUCCEEDED(hres = _RemoveRanges(iRemoveStart, iRemoveFinish, &cChange ))) cUnSelected += cChange; } _cIncluded -= cUnSelected; return( hres ); } //------------------------------------------------------------------- // // Function: SelRange_Clear // // Summary: // This function will remove all ranges within the SelRange object. // // Arguments: // hselrange [in] - the hselrange object to clear // // Return: FALSE if failed. // // Notes: // This function may return FALSE on memory allocation problems, but // will leave the SelRange object in the last state before this call. // // History: // 14-Oct-94 MikeMi Created // //------------------------------------------------------------------- HRESULT CLVRange::Clear() { PSELRANGEITEM pNewItems; HRESULT hres = S_OK; pNewItems = (PSELRANGEITEM) GlobalAlloc( GPTR, sizeof( SELRANGEITEM ) * MINCOUNT ); if (NULL != pNewItems) { GlobalFree( _VSelRanges ); _VSelRanges = pNewItems; _InitNew(); } else { hres = E_FAIL; } return( hres ); } //------------------------------------------------------------------- // // Function: SelRange_IsSelected // // Summary: // This function will return if the value iItem is within a // selected range. // // Arguments: // hselrange [in] - the hselrange object to use // iItem [in] - value to check for // // Return: TRUE if selected, FALSE if not. // // Notes: // // History: // 17-Oct-94 MikeMi Created // //------------------------------------------------------------------- HRESULT CLVRange::IsSelected( LONG iItem ) { LONG iFirst; ASSERT( iItem >= 0 ); ASSERT( iItem <= SELRANGE_MAXVALUE ); return( _FindValue( iItem, &iFirst ) ? S_OK : S_FALSE); } //------------------------------------------------------------------- // // Function: SelRange_IsEmpty // // Summary: // This function will return TRUE if the range is empty // // Arguments: // hselrange [in] - the hselrange object to use // // Return: TRUE if empty // // Notes: // // History: // //------------------------------------------------------------------- HRESULT CLVRange::IsEmpty() { return (_cSelRanges == COUNT_SELRANGES_NONE)? S_OK : S_FALSE; } HRESULT CLVRange::CountIncluded(LONG *pcIncluded) { *pcIncluded = _cIncluded; return S_OK; } //------------------------------------------------------------------- // // Function: SelRange_InsertItem // // Summary: // This function will insert a unselected item at the location, // which will push all selections up one index. // // Arguments: // hselrange [in] - the hselrange object to use // iItem [in] - value to check for // // Return: // False on memory allocation error // otherwise TRUE // // Notes: // // History: // 20-Dec-94 MikeMi Created // //------------------------------------------------------------------- HRESULT CLVRange::InsertItem( LONG iItem ) { LONG iFirst; LONG i; LONG iBegin; LONG iEnd; ASSERT( iItem >= 0 ); ASSERT( iItem <= SELRANGE_MAXVALUE ); if (_FindValue( iItem, &iFirst ) ) { // split it if ( _VSelRanges[iFirst].iBegin == iItem ) { // but don't split if starts with value iFirst--; } else { if (!_InsertRange(iFirst, iItem, _VSelRanges[iFirst].iEnd )) { return( E_FAIL ); } _VSelRanges[iFirst].iEnd = iItem - 1; } } // now walk all ranges past iFirst, incrementing all values by one for (i = _cSelRanges-2; i > iFirst; i--) { iBegin = _VSelRanges[i].iBegin; iEnd = _VSelRanges[i].iEnd; iBegin = min( SELRANGE_MAXVALUE, iBegin + 1 ); iEnd = min( SELRANGE_MAXVALUE, iEnd + 1 ); _VSelRanges[i].iBegin = iBegin; _VSelRanges[i].iEnd = iEnd; } return( S_OK ); } //------------------------------------------------------------------- // // Function: SelRange_RemoveItem // // Summary: // This function will remove an item at the location, // which will pull all selections down one index. // // Arguments: // hselrange [in] - the hselrange object to use // iItem [in] - value to check for // pfWasSelected [out] - was the removed item selected before the removal // // Return: // TRUE if the item was removed // FALSE if the an error happend // // Notes: // // History: // 20-Dec-94 MikeMi Created // //------------------------------------------------------------------- HRESULT CLVRange::RemoveItem(LONG iItem ) { LONG iFirst; LONG i; LONG iBegin; LONG iEnd; HRESULT hres = S_OK; ASSERT( iItem >= SELRANGE_MINVALUE ); ASSERT( iItem <= SELRANGE_MAXVALUE ); if (_FindValue( iItem, &iFirst ) ) { // item within, change the end value iEnd = _VSelRanges[iFirst].iEnd; iEnd = min( SELRANGE_MAXVALUE, iEnd - 1 ); _VSelRanges[iFirst].iEnd = iEnd; _cIncluded--; } else { // check for merge situation if ((iFirst < _cSelRanges - 1) && (_VSelRanges[iFirst].iEnd == iItem - 1) && (_VSelRanges[iFirst+1].iBegin == iItem + 1)) { _VSelRanges[iFirst].iEnd = _VSelRanges[iFirst + 1].iEnd - 1; if (FAILED(hres = _RemoveRanges(iFirst + 1, iFirst + 1, NULL ))) return( hres ); } } // now walk all ranges past iFirst, decrementing all values by one for (i = _cSelRanges-2; i > iFirst; i--) { iBegin = _VSelRanges[i].iBegin; iEnd = _VSelRanges[i].iEnd; iBegin = min( SELRANGE_MAXVALUE, iBegin - 1 ); iEnd = min( SELRANGE_MAXVALUE, iEnd - 1 ); _VSelRanges[i].iBegin = iBegin; _VSelRanges[i].iEnd = iEnd; } return( hres ); } //------------------------------------------------------------------- // // Function: NextSelected // // Summary: // This function will start with given item and find the next // item that is selected. If the given item is selected, that // item number will be returned. // // Arguments: // hselrange [in] - the hselrange object to use // iItem [in] - value to start check at // // Return: // -1 if none found, otherwise the item // // Notes: // // History: // 04-Jan-95 MikeMi Created // //------------------------------------------------------------------- HRESULT CLVRange::NextSelected( LONG iItem, LONG *piItem ) { LONG i; ASSERT( iItem >= SELRANGE_MINVALUE ); ASSERT( iItem <= SELRANGE_MAXVALUE ); if (!_FindValue( iItem, &i ) ) { i++; if (i < _cSelRanges-1) { iItem = _VSelRanges[i].iBegin; } else { iItem = -1; } } ASSERT( iItem >= -1 ); ASSERT( iItem <= SELRANGE_MAXVALUE ); *piItem = iItem; return S_OK; } //------------------------------------------------------------------- // // Function: NextUnSelected // // Summary: // This function will start with given item and find the next // item that is not selected. If the given item is not selected, that // item number will be returned. // // Arguments: // hselrange [in] - the hselrange object to use // iItem [in] - value to start check at // // Return: // -1 if none found, otherwise the item // // Notes: // // History: // 04-Jan-95 MikeMi Created // //------------------------------------------------------------------- HRESULT CLVRange::NextUnSelected( LONG iItem, LONG *piItem ) { LONG i; ASSERT( iItem >= SELRANGE_MINVALUE ); ASSERT( iItem <= SELRANGE_MAXVALUE ); if (_FindValue( iItem, &i ) ) { if (i < _cSelRanges-1) { iItem = _VSelRanges[i].iEnd + 1; if (iItem > SELRANGE_MAXVALUE) { iItem = -1; } } else { iItem = -1; } } ASSERT( iItem >= -1 ); ASSERT( iItem <= SELRANGE_MAXVALUE ); *piItem = iItem; return S_OK; } //------------------------------------------------------------------- // // Function: InvertRange // // Summary: // This function will invert the range defined from the current // ranges, compacting and enlarging as needed. // // Arguments: // iBegin [in] - Begin of range to invert // iEnd [in] - End of range to invert // // Return: // SELRANGE_ERROR on memory error // The difference in items selected from previous to current. // negative values means less items are selected in that range now. // // Notes: // // History: // 13-Dec-95 MikeMi Created // //------------------------------------------------------------------- LONG CLVRange::InvertRange( LONG iBegin, LONG iEnd ) { LONG iFirst; // index before or contains iBegin value BOOL fSelect; // are we selecting or unselecting LONG iTempE; LONG iTempB; HRESULT hres = S_OK; ASSERT( iEnd >= iBegin ); ASSERT( iBegin >= SELRANGE_MINVALUE ); ASSERT( iEnd <= SELRANGE_MAXVALUE ); // find if first is selected or not fSelect = !_FindValue( iBegin, &iFirst ); iTempE = iBegin - 1; do { iTempB = iTempE + 1; if (fSelect) NextSelected( iTempB, &iTempE ); else NextUnSelected( iTempB, &iTempE ); if (-1 == iTempE) { iTempE = SELRANGE_MAXVALUE; } else { iTempE--; } iTempE = min( iTempE, iEnd ); if (fSelect) { if (FAILED(hres = IncludeRange( iTempB, iTempE ))) { return( hres ); } } else { if (FAILED(hres = ExcludeRange( iTempB, iTempE ))) { return( hres ); } } fSelect = !fSelect; } while (iTempE < iEnd ); return( hres ); }