Iterator updates:
Add the notion of a special item which sits at the start of the list. Add the ability to iterate in both directions through the list. Document the iterator interface. Remove iterator_visibleitems. Rename iterator_clippeditems to iterator_visibleitems for consistency. Use the new bidirectionality to paint the items in decreasing order.
This commit is contained in:
parent
9444dd2541
commit
50b4688c7e
|
@ -51,9 +51,6 @@
|
||||||
* these differences cause problems):
|
* these differences cause problems):
|
||||||
* LVM_INSERTITEM issues LVM_SETITEMSTATE and LVM_SETITEM in certain cases.
|
* LVM_INSERTITEM issues LVM_SETITEMSTATE and LVM_SETITEM in certain cases.
|
||||||
* LVM_SETITEM does not always issue LVN_ITEMCHANGING/LVN_ITEMCHANGED.
|
* LVM_SETITEM does not always issue LVN_ITEMCHANGING/LVN_ITEMCHANGED.
|
||||||
* WM_PAINT does LVN_GETDISPINFO in item order 0->n, native does n->0.
|
|
||||||
* WM_SETREDRAW(True) native does LVN_GETDISPINFO for all items and
|
|
||||||
* does *not* invoke DefWindowProc
|
|
||||||
* WM_CREATE does not issue WM_QUERYUISTATE and associated registry
|
* WM_CREATE does not issue WM_QUERYUISTATE and associated registry
|
||||||
* processing for "USEDOUBLECLICKTIME".
|
* processing for "USEDOUBLECLICKTIME".
|
||||||
*/
|
*/
|
||||||
|
@ -112,6 +109,7 @@ typedef struct tagRANGE
|
||||||
typedef struct tagITERATOR
|
typedef struct tagITERATOR
|
||||||
{
|
{
|
||||||
INT nItem;
|
INT nItem;
|
||||||
|
INT nSpecial;
|
||||||
RANGE range;
|
RANGE range;
|
||||||
HDPA ranges;
|
HDPA ranges;
|
||||||
INT index;
|
INT index;
|
||||||
|
@ -712,27 +710,143 @@ static BOOL ranges_add(HDPA ranges, RANGE range);
|
||||||
static BOOL ranges_del(HDPA ranges, RANGE range);
|
static BOOL ranges_del(HDPA ranges, RANGE range);
|
||||||
static void ranges_dump(HDPA ranges);
|
static void ranges_dump(HDPA ranges);
|
||||||
|
|
||||||
|
/***
|
||||||
|
* ITERATOR DOCUMENTATION
|
||||||
|
*
|
||||||
|
* The iterator functions allow for easy, and convenient iteration
|
||||||
|
* over items of iterest in the list. Typically, you create a
|
||||||
|
* iterator, use it, and destroy it, as such:
|
||||||
|
* ITERATOR i;
|
||||||
|
*
|
||||||
|
* iterator_xxxitems(&i, ...);
|
||||||
|
* while (iterator_{prev,next}(&i)
|
||||||
|
* {
|
||||||
|
* //code which uses i.nItem
|
||||||
|
* }
|
||||||
|
* iterator_destroy(&i);
|
||||||
|
*
|
||||||
|
* where xxx is either: framed, or visible.
|
||||||
|
* Note that it is important that the code destroys the iterator
|
||||||
|
* after it's done with it, as the creation of the iterator may
|
||||||
|
* allocate memory, which thus needs to be freed.
|
||||||
|
*
|
||||||
|
* You can iterate both forwards, and backwards through the list,
|
||||||
|
* by using iterator_next or iterator_prev respectively.
|
||||||
|
*
|
||||||
|
* Lower numbered items are draw on top of higher number items in
|
||||||
|
* LVS_ICON, and LVS_SMALLICON (which are the only modes where
|
||||||
|
* items may overlap). So, to test items, you should use
|
||||||
|
* iterator_next
|
||||||
|
* which lists the items top to bottom (in Z-order).
|
||||||
|
* For drawing items, you should use
|
||||||
|
* iterator_prev
|
||||||
|
* which lists the items bottom to top (in Z-order).
|
||||||
|
* If you keep iterating over the items after the end-of-items
|
||||||
|
* marker (-1) is returned, the iterator will start from the
|
||||||
|
* beginning. Typically, you don't need to test for -1,
|
||||||
|
* because iterator_{next,prev} will return TRUE if more items
|
||||||
|
* are to be iterated over, or FALSE otherwise.
|
||||||
|
*
|
||||||
|
* Note: the iterator is defined to be bidirectional. That is,
|
||||||
|
* any number of prev followed by any number of next, or
|
||||||
|
* five versa, should leave the iterator at the same item:
|
||||||
|
* prev * n, next * n = next * n, prev * n
|
||||||
|
*
|
||||||
|
* The iterator has a notion of a out-of-order, special item,
|
||||||
|
* which sits at the start of the list. This is used in
|
||||||
|
* LVS_ICON, and LVS_SMALLICON mode to handle the focused item,
|
||||||
|
* which needs to be first, as it may overlap other items.
|
||||||
|
*
|
||||||
|
* The code is a bit messy because we have:
|
||||||
|
* - a special item to deal with
|
||||||
|
* - simple range, or composite range
|
||||||
|
* - empty range.
|
||||||
|
* If find bugs, or want to add features, please make sure you
|
||||||
|
* always check/modify *both* iterator_prev, and iterator_next.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/****
|
||||||
|
* This function iterates through the items in increasing order,
|
||||||
|
* but prefixed by the special item, then -1. That is:
|
||||||
|
* special, 1, 2, 3, ..., n, -1.
|
||||||
|
* Each item is listed only once.
|
||||||
|
*/
|
||||||
static inline BOOL iterator_next(ITERATOR* i)
|
static inline BOOL iterator_next(ITERATOR* i)
|
||||||
{
|
{
|
||||||
if (i->nItem == -1)
|
if (i->nItem == -1)
|
||||||
{
|
{
|
||||||
if (i->ranges) goto pickarange;
|
i->nItem = i->nSpecial;
|
||||||
return (i->nItem = i->range.lower) != -1;
|
if (i->nItem != -1) return TRUE;
|
||||||
}
|
}
|
||||||
|
if (i->nItem == i->nSpecial)
|
||||||
|
{
|
||||||
|
if (i->ranges) i->index = 0;
|
||||||
|
goto pickarange;
|
||||||
|
}
|
||||||
|
|
||||||
i->nItem++;
|
i->nItem++;
|
||||||
if (i->nItem <= i->range.upper)
|
testitem:
|
||||||
return TRUE;
|
if (i->nItem == i->nSpecial) i->nItem++;
|
||||||
|
if (i->nItem <= i->range.upper) return TRUE;
|
||||||
|
|
||||||
pickarange:
|
pickarange:
|
||||||
if (i->ranges && i->index < i->ranges->nItemCount)
|
if (i->ranges)
|
||||||
{
|
{
|
||||||
i->range = *(RANGE*)DPA_GetPtr(i->ranges, i->index++);
|
if (i->index < i->ranges->nItemCount)
|
||||||
return (i->nItem = i->range.lower) != -1;
|
i->range = *(RANGE*)DPA_GetPtr(i->ranges, i->index++);
|
||||||
|
else goto end;
|
||||||
}
|
}
|
||||||
i->nItem = i->range.lower = i->range.upper = -1;
|
else if (i->nItem > i->range.upper) goto end;
|
||||||
|
|
||||||
|
i->nItem = i->range.lower;
|
||||||
|
if (i->nItem >= 0) goto testitem;
|
||||||
|
end:
|
||||||
|
i->nItem = -1;
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/****
|
||||||
|
* This function iterates through the items in decreasing order,
|
||||||
|
* followed by the special item, then -1. That is:
|
||||||
|
* n, n-1, ..., 3, 2, 1, special, -1.
|
||||||
|
* Each item is listed only once.
|
||||||
|
*/
|
||||||
|
static inline BOOL iterator_prev(ITERATOR* i)
|
||||||
|
{
|
||||||
|
BOOL start = FALSE;
|
||||||
|
|
||||||
|
if (i->nItem == -1)
|
||||||
|
{
|
||||||
|
start = TRUE;
|
||||||
|
if (i->ranges) i->index = i->ranges->nItemCount;
|
||||||
|
goto pickarange;
|
||||||
|
}
|
||||||
|
if (i->nItem == i->nSpecial)
|
||||||
|
{
|
||||||
|
i->nItem = -1;
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
i->nItem--;
|
||||||
|
testitem:
|
||||||
|
if (i->nItem == i->nSpecial) i->nItem--;
|
||||||
|
if (i->nItem >= i->range.lower) return TRUE;
|
||||||
|
|
||||||
|
pickarange:
|
||||||
|
if (i->ranges)
|
||||||
|
{
|
||||||
|
if (i->index > 0)
|
||||||
|
i->range = *(RANGE*)DPA_GetPtr(i->ranges, --i->index);
|
||||||
|
else goto end;
|
||||||
|
}
|
||||||
|
else if (!start && i->nItem < i->range.lower) goto end;
|
||||||
|
|
||||||
|
i->nItem = i->range.upper;
|
||||||
|
if (i->nItem >= 0) goto testitem;
|
||||||
|
end:
|
||||||
|
return (i->nItem = i->nSpecial) != -1;
|
||||||
|
}
|
||||||
|
|
||||||
static RANGE iterator_range(ITERATOR* i)
|
static RANGE iterator_range(ITERATOR* i)
|
||||||
{
|
{
|
||||||
RANGE range;
|
RANGE range;
|
||||||
|
@ -744,18 +858,27 @@ static RANGE iterator_range(ITERATOR* i)
|
||||||
return range;
|
return range;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Releases resources associated with this ierator.
|
||||||
|
*/
|
||||||
static inline void iterator_destroy(ITERATOR* i)
|
static inline void iterator_destroy(ITERATOR* i)
|
||||||
{
|
{
|
||||||
if (i->ranges) DPA_Destroy(i->ranges);
|
if (i->ranges) DPA_Destroy(i->ranges);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Create an empty iterator.
|
||||||
|
*/
|
||||||
static inline BOOL iterator_empty(ITERATOR* i)
|
static inline BOOL iterator_empty(ITERATOR* i)
|
||||||
{
|
{
|
||||||
ZeroMemory(i, sizeof(*i));
|
ZeroMemory(i, sizeof(*i));
|
||||||
i->nItem = i->range.lower = i->range.upper = -1;
|
i->nItem = i->nSpecial = i->range.lower = i->range.upper = -1;
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Creates an iterator over the items which intersect lprc.
|
||||||
|
*/
|
||||||
static BOOL iterator_frameditems(ITERATOR* i, LISTVIEW_INFO* infoPtr, const RECT* lprc)
|
static BOOL iterator_frameditems(ITERATOR* i, LISTVIEW_INFO* infoPtr, const RECT* lprc)
|
||||||
{
|
{
|
||||||
UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
|
UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
|
||||||
|
@ -803,7 +926,10 @@ static BOOL iterator_frameditems(ITERATOR* i, LISTVIEW_INFO* infoPtr, const RECT
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static BOOL iterator_clippeditems(ITERATOR* i, LISTVIEW_INFO *infoPtr, HDC hdc)
|
/***
|
||||||
|
* Creates an iterator over the items which intersect the visible region of hdc.
|
||||||
|
*/
|
||||||
|
static BOOL iterator_visibleitems(ITERATOR* i, LISTVIEW_INFO *infoPtr, HDC hdc)
|
||||||
{
|
{
|
||||||
POINT Origin, Position;
|
POINT Origin, Position;
|
||||||
RECT rcItem, rcClip;
|
RECT rcItem, rcClip;
|
||||||
|
@ -814,7 +940,7 @@ static BOOL iterator_clippeditems(ITERATOR* i, LISTVIEW_INFO *infoPtr, HDC hdc)
|
||||||
if (!iterator_frameditems(i, infoPtr, &rcClip)) return FALSE;
|
if (!iterator_frameditems(i, infoPtr, &rcClip)) return FALSE;
|
||||||
if (rgntype == SIMPLEREGION) return TRUE;
|
if (rgntype == SIMPLEREGION) return TRUE;
|
||||||
|
|
||||||
/* if we can't deal with the region, we'll just go with the simple range */
|
/* if we can't deal with the region, we'll just go with the simple range */
|
||||||
if (!LISTVIEW_GetOrigin(infoPtr, &Origin)) return TRUE;
|
if (!LISTVIEW_GetOrigin(infoPtr, &Origin)) return TRUE;
|
||||||
if (!(i->ranges = DPA_Create(10))) return TRUE;
|
if (!(i->ranges = DPA_Create(10))) return TRUE;
|
||||||
if (!ranges_add(i->ranges, i->range))
|
if (!ranges_add(i->ranges, i->range))
|
||||||
|
@ -824,7 +950,7 @@ static BOOL iterator_clippeditems(ITERATOR* i, LISTVIEW_INFO *infoPtr, HDC hdc)
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* no delete the invisible items from the list */
|
/* now delete the invisible items from the list */
|
||||||
for (nItem = i->range.lower; nItem <= i->range.upper; nItem++)
|
for (nItem = i->range.lower; nItem <= i->range.upper; nItem++)
|
||||||
{
|
{
|
||||||
if (!LISTVIEW_GetItemListOrigin(infoPtr, nItem, &Position)) continue;
|
if (!LISTVIEW_GetItemListOrigin(infoPtr, nItem, &Position)) continue;
|
||||||
|
@ -842,11 +968,6 @@ static BOOL iterator_clippeditems(ITERATOR* i, LISTVIEW_INFO *infoPtr, HDC hdc)
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline BOOL iterator_visibleitems(ITERATOR* i, LISTVIEW_INFO *infoPtr)
|
|
||||||
{
|
|
||||||
return iterator_frameditems(i, infoPtr, &infoPtr->rcList);
|
|
||||||
}
|
|
||||||
|
|
||||||
/******** Misc helper functions ************************************/
|
/******** Misc helper functions ************************************/
|
||||||
|
|
||||||
static inline LRESULT CallWindowProcT(WNDPROC proc, HWND hwnd, UINT uMsg,
|
static inline LRESULT CallWindowProcT(WNDPROC proc, HWND hwnd, UINT uMsg,
|
||||||
|
@ -1281,7 +1402,7 @@ static void LISTVIEW_InvalidateSelectedItems(LISTVIEW_INFO *infoPtr)
|
||||||
{
|
{
|
||||||
ITERATOR i;
|
ITERATOR i;
|
||||||
|
|
||||||
iterator_visibleitems(&i, infoPtr);
|
iterator_frameditems(&i, infoPtr, &infoPtr->rcList);
|
||||||
while(iterator_next(&i))
|
while(iterator_next(&i))
|
||||||
{
|
{
|
||||||
if (LISTVIEW_GetItemState(infoPtr, i.nItem, LVIS_SELECTED))
|
if (LISTVIEW_GetItemState(infoPtr, i.nItem, LVIS_SELECTED))
|
||||||
|
@ -3238,7 +3359,7 @@ static void LISTVIEW_RefreshOwnerDraw(LISTVIEW_INFO *infoPtr, HDC hdc)
|
||||||
if (!LISTVIEW_GetOrigin(infoPtr, &Origin)) return;
|
if (!LISTVIEW_GetOrigin(infoPtr, &Origin)) return;
|
||||||
|
|
||||||
/* figure out what we need to draw */
|
/* figure out what we need to draw */
|
||||||
iterator_clippeditems(&i, infoPtr, hdc);
|
iterator_visibleitems(&i, infoPtr, hdc);
|
||||||
|
|
||||||
/* send cache hint notification */
|
/* send cache hint notification */
|
||||||
if (infoPtr->dwStyle & LVS_OWNERDATA)
|
if (infoPtr->dwStyle & LVS_OWNERDATA)
|
||||||
|
@ -3252,7 +3373,7 @@ static void LISTVIEW_RefreshOwnerDraw(LISTVIEW_INFO *infoPtr, HDC hdc)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* iterate through the invalidated rows */
|
/* iterate through the invalidated rows */
|
||||||
while(iterator_next(&i))
|
while(iterator_prev(&i))
|
||||||
{
|
{
|
||||||
item.iItem = i.nItem;
|
item.iItem = i.nItem;
|
||||||
item.iSubItem = 0;
|
item.iSubItem = 0;
|
||||||
|
@ -3344,13 +3465,13 @@ static void LISTVIEW_RefreshReport(LISTVIEW_INFO *infoPtr, HDC hdc, DWORD cdmode
|
||||||
}
|
}
|
||||||
|
|
||||||
/* figure out what we need to draw */
|
/* figure out what we need to draw */
|
||||||
iterator_clippeditems(&i, infoPtr, hdc);
|
iterator_visibleitems(&i, infoPtr, hdc);
|
||||||
|
|
||||||
/* a last few bits before we start drawing */
|
/* a last few bits before we start drawing */
|
||||||
TRACE("Colums=(%di - %d)\n", nFirstCol, nLastCol);
|
TRACE("Colums=(%di - %d)\n", nFirstCol, nLastCol);
|
||||||
|
|
||||||
/* iterate through the invalidated rows */
|
/* iterate through the invalidated rows */
|
||||||
while(iterator_next(&i))
|
while(iterator_prev(&i))
|
||||||
{
|
{
|
||||||
nDrawPosY = i.nItem * infoPtr->nItemHeight;
|
nDrawPosY = i.nItem * infoPtr->nItemHeight;
|
||||||
|
|
||||||
|
@ -3404,9 +3525,9 @@ static void LISTVIEW_RefreshList(LISTVIEW_INFO *infoPtr, HDC hdc, DWORD cdmode)
|
||||||
if (!LISTVIEW_GetOrigin(infoPtr, &Origin)) return;
|
if (!LISTVIEW_GetOrigin(infoPtr, &Origin)) return;
|
||||||
|
|
||||||
/* figure out what we need to draw */
|
/* figure out what we need to draw */
|
||||||
iterator_clippeditems(&i, infoPtr, hdc);
|
iterator_visibleitems(&i, infoPtr, hdc);
|
||||||
|
|
||||||
while(iterator_next(&i))
|
while(iterator_prev(&i))
|
||||||
{
|
{
|
||||||
if (!LISTVIEW_GetItemListOrigin(infoPtr, i.nItem, &Position)) continue;
|
if (!LISTVIEW_GetItemListOrigin(infoPtr, i.nItem, &Position)) continue;
|
||||||
Position.x += Origin.x;
|
Position.x += Origin.x;
|
||||||
|
@ -3439,9 +3560,9 @@ static void LISTVIEW_RefreshIcon(LISTVIEW_INFO *infoPtr, HDC hdc, DWORD cdmode)
|
||||||
if (!LISTVIEW_GetOrigin(infoPtr, &Origin)) return;
|
if (!LISTVIEW_GetOrigin(infoPtr, &Origin)) return;
|
||||||
|
|
||||||
/* figure out what we need to draw */
|
/* figure out what we need to draw */
|
||||||
iterator_clippeditems(&i, infoPtr, hdc);
|
iterator_visibleitems(&i, infoPtr, hdc);
|
||||||
|
|
||||||
while(iterator_next(&i))
|
while(iterator_prev(&i))
|
||||||
{
|
{
|
||||||
if (LISTVIEW_GetItemState(infoPtr, i.nItem, LVIS_FOCUSED))
|
if (LISTVIEW_GetItemState(infoPtr, i.nItem, LVIS_FOCUSED))
|
||||||
continue;
|
continue;
|
||||||
|
|
Loading…
Reference in New Issue