windows-nt/Source/XPSP1/NT/shell/comctl32/v6/selrange.cpp
2020-09-26 16:20:57 +08:00

1166 lines
28 KiB
C++

//-------------------------------------------------------------------
//
// File: SelRange.cpp
//
// Contents:
// This file contians Selection Range handling code.
//
//-------------------------------------------------------------------
#include "ctlspriv.h"
#include "selrange.h"
#include "stdio.h"
#include <shguidp.h>
#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 );
}