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:
Dimitrie O. Paun 2002-10-15 21:01:57 +00:00 committed by Alexandre Julliard
parent 9444dd2541
commit 50b4688c7e
1 changed files with 150 additions and 29 deletions

View File

@ -51,9 +51,6 @@
* these differences cause problems):
* LVM_INSERTITEM issues LVM_SETITEMSTATE and LVM_SETITEM in certain cases.
* 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
* processing for "USEDOUBLECLICKTIME".
*/
@ -112,6 +109,7 @@ typedef struct tagRANGE
typedef struct tagITERATOR
{
INT nItem;
INT nSpecial;
RANGE range;
HDPA ranges;
INT index;
@ -712,27 +710,143 @@ static BOOL ranges_add(HDPA ranges, RANGE range);
static BOOL ranges_del(HDPA ranges, RANGE range);
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)
{
if (i->nItem == -1)
{
if (i->ranges) goto pickarange;
return (i->nItem = i->range.lower) != -1;
i->nItem = i->nSpecial;
if (i->nItem != -1) return TRUE;
}
if (i->nItem == i->nSpecial)
{
if (i->ranges) i->index = 0;
goto pickarange;
}
i->nItem++;
if (i->nItem <= i->range.upper)
return TRUE;
testitem:
if (i->nItem == i->nSpecial) i->nItem++;
if (i->nItem <= i->range.upper) return TRUE;
pickarange:
if (i->ranges && i->index < i->ranges->nItemCount)
if (i->ranges)
{
i->range = *(RANGE*)DPA_GetPtr(i->ranges, i->index++);
return (i->nItem = i->range.lower) != -1;
if (i->index < i->ranges->nItemCount)
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;
}
/****
* 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)
{
RANGE range;
@ -744,18 +858,27 @@ static RANGE iterator_range(ITERATOR* i)
return range;
}
/***
* Releases resources associated with this ierator.
*/
static inline void iterator_destroy(ITERATOR* i)
{
if (i->ranges) DPA_Destroy(i->ranges);
}
/***
* Create an empty iterator.
*/
static inline BOOL iterator_empty(ITERATOR* 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;
}
/***
* Creates an iterator over the items which intersect lprc.
*/
static BOOL iterator_frameditems(ITERATOR* i, LISTVIEW_INFO* infoPtr, const RECT* lprc)
{
UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
@ -803,7 +926,10 @@ static BOOL iterator_frameditems(ITERATOR* i, LISTVIEW_INFO* infoPtr, const RECT
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;
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 (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 (!(i->ranges = DPA_Create(10))) return TRUE;
if (!ranges_add(i->ranges, i->range))
@ -824,7 +950,7 @@ static BOOL iterator_clippeditems(ITERATOR* i, LISTVIEW_INFO *infoPtr, HDC hdc)
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++)
{
if (!LISTVIEW_GetItemListOrigin(infoPtr, nItem, &Position)) continue;
@ -842,11 +968,6 @@ static BOOL iterator_clippeditems(ITERATOR* i, LISTVIEW_INFO *infoPtr, HDC hdc)
return TRUE;
}
static inline BOOL iterator_visibleitems(ITERATOR* i, LISTVIEW_INFO *infoPtr)
{
return iterator_frameditems(i, infoPtr, &infoPtr->rcList);
}
/******** Misc helper functions ************************************/
static inline LRESULT CallWindowProcT(WNDPROC proc, HWND hwnd, UINT uMsg,
@ -1281,7 +1402,7 @@ static void LISTVIEW_InvalidateSelectedItems(LISTVIEW_INFO *infoPtr)
{
ITERATOR i;
iterator_visibleitems(&i, infoPtr);
iterator_frameditems(&i, infoPtr, &infoPtr->rcList);
while(iterator_next(&i))
{
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;
/* figure out what we need to draw */
iterator_clippeditems(&i, infoPtr, hdc);
iterator_visibleitems(&i, infoPtr, hdc);
/* send cache hint notification */
if (infoPtr->dwStyle & LVS_OWNERDATA)
@ -3252,7 +3373,7 @@ static void LISTVIEW_RefreshOwnerDraw(LISTVIEW_INFO *infoPtr, HDC hdc)
}
/* iterate through the invalidated rows */
while(iterator_next(&i))
while(iterator_prev(&i))
{
item.iItem = i.nItem;
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 */
iterator_clippeditems(&i, infoPtr, hdc);
iterator_visibleitems(&i, infoPtr, hdc);
/* a last few bits before we start drawing */
TRACE("Colums=(%di - %d)\n", nFirstCol, nLastCol);
/* iterate through the invalidated rows */
while(iterator_next(&i))
while(iterator_prev(&i))
{
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;
/* 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;
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;
/* 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))
continue;