1166 lines
28 KiB
C++
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 );
|
||
|
}
|