2020-09-30 17:12:32 +02:00

1088 lines
27 KiB
C

#include "ctlspriv.h"
#include "treeview.h"
BOOL NEAR TV_EnsureVisible(PTREE pTree, TREEITEM FAR * hItem);
// ----------------------------------------------------------------------------
// Updates the iShownIndex for every item below (in list order) a given item
// ----------------------------------------------------------------------------
int NEAR TV_UpdateShownIndexes(PTREE pTree, HTREEITEM hWalk)
{
WORD iShownIndex;
if (hWalk == pTree->hRoot) {
hWalk = pTree->hRoot->hKids;
if (hWalk) {
hWalk->iShownIndex = 0;
} else {
return -1;
}
}
iShownIndex = hWalk->iShownIndex + 1;
if (iShownIndex <= 0)
{
// BUGBUG: We should #define the special TVITEM_HIDDEN value and check
// for it explicitly
// This can happen if TV_SortCB passes in a hidden item
return(-1);
}
while ((hWalk = TV_GetNextVisItem(hWalk)) != NULL)
hWalk->iShownIndex = iShownIndex++;
//#ifdef DEBUG
// DebugMsg(DM_TRACE, "tv: updated show indexes (now %d items)", (int)iShownIndex);
//#endif
return (int)iShownIndex;
}
// in:
// hItem expanded node to count decendants of
// returns:
// total number of expanded descendants below the given item.
UINT NEAR TV_CountVisibleDescendants(HTREEITEM hItem)
{
UINT cnt;
for (cnt = 0, hItem = hItem->hKids; hItem; hItem = hItem->hNext)
{
cnt++;
if (hItem->hKids && (hItem->state & TVIS_EXPANDED))
cnt += TV_CountVisibleDescendants(hItem);
}
return cnt;
}
UINT NEAR TV_ScrollBelow(PTREE pTree, HTREEITEM hItem, BOOL fRedrawParent, BOOL fDown)
{
RECT rc;
int iTop;
UINT cnt;
// Do nothing if the item is not visible
if (!ITEM_VISIBLE(hItem))
return 0;
iTop = hItem->iShownIndex - pTree->hTop->iShownIndex;
rc.left = 0;
rc.top = iTop * pTree->cyItem;
rc.right = pTree->cxWnd;
rc.bottom = pTree->cyWnd;
if (pTree->fRedraw)
InvalidateRect(pTree->hwnd, &rc, TRUE);
if (fRedrawParent)
{
cnt = TV_CountVisibleDescendants(hItem);
}
else
{
cnt = 1;
}
return(cnt);
}
// ----------------------------------------------------------------------------
// Returns the width of the widest shown item in the tree
// ----------------------------------------------------------------------------
UINT NEAR TV_RecomputeMaxWidth(PTREE pTree)
{
HTREEITEM hItem;
WORD wMax = 0;
// REVIEW: this might not be the most efficient traversal of the tree
for (hItem = pTree->hRoot->hKids; hItem; hItem = TV_GetNextVisItem(hItem))
{
if (wMax < FULL_WIDTH(pTree, hItem))
wMax = FULL_WIDTH(pTree, hItem);
}
return((UINT)wMax);
}
// ----------------------------------------------------------------------------
// Returns the horizontal text extent of the given item's text
// ----------------------------------------------------------------------------
WORD NEAR TV_GetItemTextWidth(HDC hdc, PTREE pTree, HTREEITEM hItem)
{
TV_ITEM sItem;
char szTemp[MAX_PATH];
SIZE size = {0,0};
sItem.pszText = szTemp;
sItem.cchTextMax = sizeof(szTemp);
TV_GetItem(pTree, hItem, TVIF_TEXT, &sItem);
GetTextExtentPoint(hdc, sItem.pszText, lstrlen(sItem.pszText), &size);
return size.cx + (g_cxLabelMargin * 2);
}
// ----------------------------------------------------------------------------
// Compute the text extent and the full width (indent, image, and text) of
// the given item.
// ----------------------------------------------------------------------------
void NEAR TV_ComputeItemWidth(PTREE pTree, HTREEITEM hItem, HDC hdc)
{
HDC hdcToUse;
HFONT hOldFont;
TV_ITEM sItem;
if (hdc == NULL) {
HFONT hFont;
// compute text width and full width
hdcToUse = GetDC(pTree->hwnd);
sItem.stateMask = TVIS_BOLD;
TV_GetItem(pTree, hItem, TVIF_STATE, &sItem);
hFont = sItem.state & TVIS_BOLD
? pTree->hFontBold : pTree->hFont;
hOldFont = (hFont) ? SelectObject(hdcToUse, hFont) : NULL;
}
else
{
hdcToUse = hdc;
}
hItem->iWidth = TV_GetItemTextWidth(hdcToUse, pTree, hItem);
if (hdc == NULL)
{
if (hOldFont)
SelectObject(hdcToUse, hOldFont);
ReleaseDC(pTree->hwnd, hdcToUse);
}
}
// ----------------------------------------------------------------------------
// Returns TRUE if the item is expanded, FALSE otherwise
// ----------------------------------------------------------------------------
BOOL NEAR TV_IsShowing(HTREEITEM hItem)
{
for (hItem = hItem->hParent; hItem; hItem = hItem->hParent)
if (!(hItem->state & TVIS_EXPANDED))
return FALSE;
return TRUE;
}
// ----------------------------------------------------------------------------
// If the added item is showing, update the shown (expanded) count, the max
// item width -- then recompute the scroll bars.
// sets cxMax, cShowing
// ----------------------------------------------------------------------------
BOOL NEAR TV_ScrollBarsAfterAdd(PTREE pTree, HTREEITEM hItem)
{
HTREEITEM hPrev;
if (!TV_IsShowing(hItem))
{
// item isn't visible -- set index to NOTVISIBLE and return
hItem->iShownIndex = (WORD)-1;
return FALSE;
}
hPrev = TV_GetPrevVisItem(hItem);
// increment every shown index after newly added item
hItem->iShownIndex = (hPrev) ? hPrev->iShownIndex + 1 : 0;
TV_UpdateShownIndexes(pTree, hItem);
pTree->cShowing++;
TV_ComputeItemWidth(pTree, hItem, NULL);
if (pTree->cxMax < FULL_WIDTH(pTree, hItem))
pTree->cxMax = FULL_WIDTH(pTree, hItem);
TV_CalcScrollBars(pTree);
return(TRUE);
}
// ----------------------------------------------------------------------------
// If the removed item was showing, update the shown (expanded) count, the max
// item width -- then recompute the scroll bars.
// sets cxMax, cShowing
// ----------------------------------------------------------------------------
BOOL NEAR TV_ScrollBarsAfterRemove(PTREE pTree, HTREEITEM hItem)
{
HTREEITEM hWalk;
if (!ITEM_VISIBLE(hItem))
return FALSE;
// decrement every shown index after removed item
hItem->iShownIndex = (WORD)-1;
hWalk = TV_GetNextVisItem(hItem);
if (hWalk) {
--hWalk->iShownIndex;
TV_UpdateShownIndexes(pTree, hWalk);
// If we delete the top item, the tree scrolls to the end, so ...
if (pTree->hTop == hItem) {
TV_SetTopItem(pTree, hWalk->iShownIndex);
Assert(pTree->hTop != hItem);
}
}
pTree->cShowing--;
if (pTree->fRedraw) {
if (!hItem->iWidth)
TV_ComputeItemWidth(pTree, hItem, NULL);
if (pTree->cxMax == FULL_WIDTH(pTree, hItem))
pTree->cxMax = TV_RecomputeMaxWidth(pTree);
TV_CalcScrollBars(pTree);
}
return TRUE;
}
// ----------------------------------------------------------------------------
// If the expanded items are showing, update the shown (expanded) count,
// the max item width -- then recompute the scroll bars.
// sets cxMax, cShowing
// ----------------------------------------------------------------------------
BOOL NEAR TV_ScrollBarsAfterExpand(PTREE pTree, HTREEITEM hParent)
{
WORD cxMax = 0;
HTREEITEM hWalk;
if (!ITEM_VISIBLE(hParent))
return FALSE;
for (hWalk = hParent->hKids;
hWalk && (hWalk->iLevel > hParent->iLevel);
hWalk = TV_GetNextVisItem(hWalk))
{
if (!hWalk->iWidth)
TV_ComputeItemWidth(pTree, hWalk, NULL);
if (cxMax < FULL_WIDTH(pTree, hWalk))
cxMax = FULL_WIDTH(pTree, hWalk);
}
// update every shown index after expanded parent
pTree->cShowing = TV_UpdateShownIndexes(pTree, hParent);
if (cxMax > pTree->cxMax)
pTree->cxMax = cxMax;
TV_CalcScrollBars(pTree);
return(TRUE);
}
// ----------------------------------------------------------------------------
// If the collapsed items were showing, update the shown (expanded) count,
// the max item width -- then recompute the scroll bars.
// sets cxMax, cShowing
// ----------------------------------------------------------------------------
BOOL NEAR TV_ScrollBarsAfterCollapse(PTREE pTree, HTREEITEM hParent)
{
WORD cxMax = 0;
HTREEITEM hWalk;
if (!ITEM_VISIBLE(hParent))
return FALSE;
for (hWalk = hParent->hKids;
hWalk && (hWalk->iLevel > hParent->iLevel);
hWalk = TV_GetNextVisItem(hWalk))
{
hWalk->iShownIndex = (WORD)-1;
if (!hWalk->iWidth)
TV_ComputeItemWidth(pTree, hWalk, NULL);
if (cxMax < FULL_WIDTH(pTree, hWalk))
cxMax = FULL_WIDTH(pTree, hWalk);
}
// update every shown index after expanded parent
pTree->cShowing = TV_UpdateShownIndexes(pTree, hParent);
if (cxMax == pTree->cxMax)
pTree->cxMax = TV_RecomputeMaxWidth(pTree);
TV_CalcScrollBars(pTree);
return(TRUE);
}
// ----------------------------------------------------------------------------
// Returns the item just below the given item in the tree.
// ----------------------------------------------------------------------------
TREEITEM FAR * NEAR TV_GetNext(TREEITEM FAR * hItem)
{
ValidateTreeItem(hItem, FALSE);
if (hItem->hKids)
return hItem->hKids;
checkNext:
if (hItem->hNext)
return hItem->hNext;
hItem = hItem->hParent;
if (hItem)
goto checkNext;
return NULL;
}
// ----------------------------------------------------------------------------
// Go through all the items in the tree, recomputing each item's text extent
// and full width (indent, image, and text).
// ----------------------------------------------------------------------------
void NEAR TV_RecomputeItemWidths(PTREE pTree)
{
HDC hdc = GetDC(pTree->hwnd);
HFONT hOldFont = NULL;
HTREEITEM hItem = pTree->hRoot->hKids;
if (pTree->hFont)
hOldFont = SelectObject(hdc, pTree->hFont);
while (hItem)
{
//BUGBUG: Optimize TV_ComputeItemWidth so it
// can be called every time.
if (hItem->state & TVIS_BOLD)
TV_ComputeItemWidth(pTree, hItem, NULL);
else
hItem->iWidth = TV_GetItemTextWidth(hdc, pTree, hItem);
hItem = TV_GetNext(hItem);
}
if (hOldFont)
SelectObject(hdc, hOldFont);
ReleaseDC(pTree->hwnd, hdc);
}
// ----------------------------------------------------------------------------
// If a single item's width changed, alter the max width if needed.
// If all widths changed, recompute widths and max width.
// Then recompute the scroll bars.
// sets cxMax
// ----------------------------------------------------------------------------
BOOL NEAR TV_ScrollBarsAfterSetWidth(PTREE pTree, HTREEITEM hItem)
{
if (hItem)
{
UINT iOldWidth = FULL_WIDTH(pTree, hItem);
TV_ComputeItemWidth(pTree, hItem, NULL);
if (pTree->cxMax < FULL_WIDTH(pTree, hItem))
pTree->cxMax = FULL_WIDTH(pTree, hItem);
else if (pTree->cxMax == iOldWidth)
pTree->cxMax = TV_RecomputeMaxWidth(pTree);
else
return(FALSE);
}
else
{
TV_RecomputeItemWidths(pTree);
pTree->cxMax = TV_RecomputeMaxWidth(pTree);
}
TV_CalcScrollBars(pTree);
return(TRUE);
}
// ----------------------------------------------------------------------------
// Scroll window vertically as needed to make given item fully visible
// vertically
// ----------------------------------------------------------------------------
BOOL NEAR TV_ScrollVertIntoView(PTREE pTree, HTREEITEM hItem)
{
// Do nothing if this item is not visible
if (!ITEM_VISIBLE(hItem))
return FALSE;
if (hItem->iShownIndex < pTree->hTop->iShownIndex)
return(TV_SetTopItem(pTree, hItem->iShownIndex));
if (hItem->iShownIndex >= (pTree->hTop->iShownIndex + pTree->cFullVisible))
return(TV_SetTopItem(pTree, hItem->iShownIndex + 1 - pTree->cFullVisible));
return FALSE;
}
// ----------------------------------------------------------------------------
// Scroll window vertically and horizontally as needed to make given item
// fully visible vertically and horizontally
// ----------------------------------------------------------------------------
BOOL NEAR TV_ScrollIntoView(PTREE pTree, HTREEITEM hItem)
{
UINT iWidth, iOffset;
BOOL fChange;
fChange = TV_ScrollVertIntoView(pTree, hItem);
// ensure that item's text is fully visible horizontally
iWidth = pTree->cxImage + pTree->cxState + hItem->iWidth;
if (iWidth > (UINT)pTree->cxWnd)
iWidth = pTree->cxWnd; //hItem->iWidth;
iOffset = ITEM_OFFSET(pTree, hItem);
if ((int) (iOffset) < pTree->xPos)
fChange |= TV_SetLeft(pTree, iOffset);
else if ((iOffset + iWidth) > (UINT)(pTree->xPos + pTree->cxWnd))
fChange |= TV_SetLeft(pTree, iOffset + iWidth - pTree->cxWnd);
return fChange;
}
// ----------------------------------------------------------------------------
// Sets position of horizontal scroll bar and scrolls window to match that
// position
// sets xPos
// ----------------------------------------------------------------------------
BOOL NEAR TV_SetLeft(PTREE pTree, int x)
{
if (!pTree->fHorz)
return(FALSE);
if (x > (int) (pTree->cxMax - pTree->cxWnd))
x = (pTree->cxMax - pTree->cxWnd);
if (x < 0)
x = 0;
if (x == pTree->xPos)
return(FALSE);
if (pTree->fRedraw) {
ScrollWindowEx(pTree->hwnd, pTree->xPos - x, 0, NULL, NULL, NULL, NULL, SW_INVALIDATE | SW_ERASE);
}
pTree->xPos = x;
SetScrollPos(pTree->hwnd, SB_HORZ, x, TRUE);
return(TRUE);
}
// ----------------------------------------------------------------------------
// Returns the tree's item that has the given shown index, NULL if no item
// found with that index.
// ----------------------------------------------------------------------------
HTREEITEM NEAR TV_GetShownIndexItem(HTREEITEM hItem, UINT wShownIndex)
{
HTREEITEM hWalk;
if (hItem == NULL)
return NULL;
Assert((int)wShownIndex >= 0);
for (hWalk = hItem->hNext;
hWalk && (hWalk->iShownIndex <= wShownIndex);
hWalk = hWalk->hNext)
hItem = hWalk;
if (hItem->iShownIndex == wShownIndex)
return hItem;
// Assert(hItem->hKids == NULL);
return TV_GetShownIndexItem(hItem->hKids, wShownIndex);
}
// ----------------------------------------------------------------------------
// Sets position of vertical scroll bar and scrolls window to match that
// position
// sets hTop
// ----------------------------------------------------------------------------
BOOL NEAR TV_SetTopItem(PTREE pTree, UINT wNewTop)
{
HTREEITEM hItem = pTree->hRoot->hKids;
UINT wOldTop;
if (!hItem)
return FALSE;
if ((wNewTop == (UINT)-1) || (pTree->cShowing <= pTree->cFullVisible)) {
// we've wrapped around (treat as a negative index) -- use min pos
// or there aren't enough items to scroll
wNewTop = 0;
} else if (wNewTop > (UINT)(pTree->cShowing - pTree->cFullVisible)) {
// we've gone too far down -- use max pos
wNewTop = (pTree->cShowing - pTree->cFullVisible);
}
// if there's no room for anything to show. peg at the end
if (wNewTop > 0 && wNewTop >= pTree->cShowing) {
wNewTop = pTree->cShowing - 1;
}
hItem = TV_GetShownIndexItem(hItem, wNewTop);
Assert(hItem);
if (pTree->hTop == hItem)
return FALSE;
wOldTop = pTree->hTop->iShownIndex;
pTree->hTop = hItem;
if (pTree->fRedraw) {
ScrollWindowEx(pTree->hwnd, 0, (int) (wOldTop - wNewTop) * (int) pTree->cyItem,
NULL, NULL, NULL, NULL, SW_INVALIDATE | SW_ERASE);
}
SetScrollPos(pTree->hwnd, SB_VERT, wNewTop, TRUE);
return(TRUE);
}
// ----------------------------------------------------------------------------
// Computes the horizontal and vertical scroll bar ranges, pages, and
// positions, adding or removing the scroll bars as needed.
// sets fHorz, fVert
// ----------------------------------------------------------------------------
BOOL NEAR TV_CalcScrollBars(PTREE pTree)
{
// UINT wMaxPos;
BOOL fChange = FALSE;
SCROLLINFO si;
si.cbSize = sizeof(SCROLLINFO);
if ((SHORT)pTree->cxMax > (SHORT)pTree->cxWnd)
{
if (!pTree->fHorz)
{
fChange = TRUE;
pTree->fHorz = TRUE;
}
si.fMask = SIF_PAGE | SIF_RANGE;
si.nMin = 0;
si.nMax = pTree->cxMax - 1;
si.nPage = pTree->cxWnd;
#ifdef IEWIN31_25
SetScrollRange(pTree->hwnd, SB_HORZ, (int)si.nMin, (int)(si.nMax-si.nPage+1), TRUE);
TV_SetLeft(pTree, GetScrollPos( pTree->hwnd, SB_HORZ));
#else
TV_SetLeft(pTree, (UINT)SetScrollInfo(pTree->hwnd, SB_HORZ, &si, TRUE));
#endif
}
else if (pTree->fHorz)
{
TV_SetLeft(pTree, 0);
SetScrollRange(pTree->hwnd, SB_HORZ, 0, 0, TRUE);
pTree->fHorz = FALSE;
fChange = TRUE;
}
if (pTree->cShowing > pTree->cFullVisible)
{
if (!pTree->fVert)
{
pTree->fVert = TRUE;
fChange = TRUE;
}
si.fMask = SIF_PAGE | SIF_RANGE;
si.nMin = 0;
si.nMax = pTree->cShowing - 1;
si.nPage = pTree->cFullVisible;
#ifdef IEWIN31_25
SetScrollRange(pTree->hwnd, SB_VERT, (int)si.nMin, (int)(si.nMax-si.nPage+1), TRUE);
TV_SetTopItem(pTree, GetScrollPos( pTree->hwnd, SB_VERT));
#else
TV_SetTopItem(pTree, (UINT)SetScrollInfo(pTree->hwnd, SB_VERT, &si, TRUE));
#endif
}
else if (pTree->fVert)
{
TV_SetTopItem(pTree, 0);
SetScrollRange(pTree->hwnd, SB_VERT, 0, 0, TRUE);
pTree->fVert = FALSE;
fChange = TRUE;
}
if (fChange)
TV_SizeWnd(pTree, 0, 0);
return(TRUE);
}
#define MAGIC_HORZLINE 5
// ----------------------------------------------------------------------------
// Handles horizontal scrolling.
// ----------------------------------------------------------------------------
BOOL NEAR TV_HorzScroll(PTREE pTree, UINT wCode, UINT wNewPos)
{
BOOL fChanged;
TV_DismissEdit(pTree, FALSE);
switch (wCode)
{
case SB_BOTTOM:
wNewPos = pTree->cxMax - pTree->cxWnd;
case SB_ENDSCROLL:
wNewPos = pTree->xPos;
break;
case SB_LINEDOWN:
wNewPos = pTree->xPos + MAGIC_HORZLINE;
break;
case SB_LINEUP:
wNewPos = pTree->xPos - MAGIC_HORZLINE;
break;
case SB_PAGEDOWN:
wNewPos = pTree->xPos + (pTree->cxWnd - MAGIC_HORZLINE);
break;
case SB_PAGEUP:
wNewPos = pTree->xPos - (pTree->cxWnd - MAGIC_HORZLINE);
break;
case SB_THUMBPOSITION:
case SB_THUMBTRACK:
break;
case SB_TOP:
wNewPos = 0;
break;
}
if (fChanged = TV_SetLeft(pTree, wNewPos))
UpdateWindow(pTree->hwnd);
return(fChanged);
}
// ----------------------------------------------------------------------------
// Handles vertical scrolling.
// ----------------------------------------------------------------------------
BOOL NEAR TV_VertScroll(PTREE pTree, UINT wCode, UINT wPos)
{
UINT wNewPos;
UINT wOldPos = pTree->hTop->iShownIndex;
BOOL fChanged;
TV_DismissEdit(pTree, FALSE);
switch (wCode)
{
case SB_BOTTOM:
wNewPos = pTree->cShowing - pTree->cFullVisible;
break;
case SB_ENDSCROLL:
wNewPos = wOldPos;
break;
case SB_LINEDOWN:
wNewPos = wOldPos + 1;
break;
case SB_LINEUP:
wNewPos = wOldPos - 1;
if (wNewPos > wOldPos)
wNewPos = 0;
break;
case SB_PAGEDOWN:
wNewPos = wOldPos + (pTree->cFullVisible - 1);
break;
case SB_PAGEUP:
wNewPos = wOldPos - (pTree->cFullVisible - 1);
if (wNewPos > wOldPos)
wNewPos = 0;
break;
case SB_THUMBPOSITION:
case SB_THUMBTRACK:
wNewPos = wPos;
break;
case SB_TOP:
wNewPos = 0;
break;
}
if (fChanged = TV_SetTopItem(pTree, wNewPos))
UpdateWindow(pTree->hwnd);
return(fChanged);
}
#ifdef DEBUG
static int nCompares;
#endif
typedef struct {
LPSTR lpstr;
BOOL bCallBack;
HTREEITEM hItem;
} TVCOMPARE, FAR *LPTVCOMPARE;
// Pointer comparision function for Sort and Search functions.
// lParam is lParam passed to sort/search functions. Returns
// -1 if p1 < p2, 0 if p1 == p2, and 1 if p1 > p2.
int CALLBACK TV_DefCompare(LPTVCOMPARE sCmp1, LPTVCOMPARE sCmp2, LPARAM lParam)
{
#ifdef DEBUG
++nCompares;
#endif
return lstrcmpi(sCmp1->lpstr, sCmp2->lpstr);
}
int CALLBACK TV_CompareItems(LPTVCOMPARE sCmp1, LPTVCOMPARE sCmp2, LPARAM lParam)
{
TV_SORTCB FAR *pSortCB = (TV_SORTCB FAR *)lParam;
#ifdef DEBUG
++nCompares;
#endif
return(pSortCB->lpfnCompare(sCmp1->hItem->lParam, sCmp2->hItem->lParam,
pSortCB->lParam));
}
UINT NEAR TV_CountKids(HTREEITEM hItem)
{
int cnt;
for (cnt = 0, hItem = hItem->hKids; hItem; hItem = hItem->hNext)
cnt++;
return cnt;
}
// BUGBUG: bRecurse not implemented
BOOL PASCAL TV_SortCB(PTREE pTree, TV_SORTCB FAR *pSortCB, BOOL bRecurse,
PFNDPACOMPARE lpfnDPACompare)
{
HDPA dpaSort;
HDSA dsaCmp;
HTREEITEM hItem, hNext, hFirstMoved;
LPTVCOMPARE psCompare, FAR *ppsCompare;
int i, cKids;
HTREEITEM hParent = pSortCB->hParent;
#ifdef DEBUG
DWORD dwTime = GetTickCount();
nCompares = 0;
#endif
if (!hParent || hParent == TVI_ROOT)
hParent = pTree->hRoot;
// Code below assumes at least one kid
cKids = TV_CountKids(hParent);
if (!cKids)
return FALSE;
// Create a DSA for all the extra info we'll need
dsaCmp = DSA_Create(sizeof(TVCOMPARE), cKids);
if (!dsaCmp)
goto Error1;
// Create a DPA containing all the tree items
dpaSort = DPA_Create(cKids);
if (!dpaSort)
goto Error2;
for (hItem = hParent->hKids; hItem; hItem = hItem->hNext)
{
TVCOMPARE sCompare;
int nItem;
// If I can't sort all of them, I don't want to sort any of them
// We want to cache the text callback for default processing
if (!lpfnDPACompare && hItem->lpstr==LPSTR_TEXTCALLBACK)
{
TV_ITEM sItem;
char szTemp[MAX_PATH];
sItem.pszText = szTemp;
sItem.cchTextMax = sizeof(szTemp);
TV_GetItem(pTree, hItem, TVIF_TEXT, &sItem);
sCompare.lpstr = NULL;
sCompare.bCallBack = TRUE;
Str_Set(&sCompare.lpstr, sItem.pszText);
if (!sCompare.lpstr)
{
goto Error3;
}
}
else
{
sCompare.lpstr = hItem->lpstr;
sCompare.bCallBack = FALSE;
}
// Create the pointer for this guy and add it to the DPA list
sCompare.hItem = hItem;
nItem = DSA_InsertItem(dsaCmp, 0x7fff, &sCompare);
if (nItem < 0)
{
if (sCompare.bCallBack)
{
Str_Set(&sCompare.lpstr, NULL);
}
goto Error3;
}
if (DPA_InsertPtr(dpaSort, 0x7fff, DSA_GetItemPtr(dsaCmp, nItem)) < 0)
{
goto Error3;
}
}
// Sort the DPA, then stick them back under the parent in the new order
DPA_Sort(dpaSort, lpfnDPACompare ? lpfnDPACompare : TV_DefCompare,
(LPARAM)pSortCB);
// Look for the first moved item, so we can invalidate a smaller area
ppsCompare = (LPTVCOMPARE FAR *)DPA_GetPtrPtr(dpaSort);
if (hParent->hKids != (*ppsCompare)->hItem)
{
hParent->hKids = (*ppsCompare)->hItem;
hFirstMoved = hParent->hKids;
}
else
{
hFirstMoved = NULL;
}
// We do n-1 iterations here
for (i = DPA_GetPtrCount(dpaSort) - 1; i > 0; --i, ++ppsCompare)
{
hNext = (*(ppsCompare+1))->hItem;
if ((*ppsCompare)->hItem->hNext != hNext && !hFirstMoved)
{
hFirstMoved = hNext;
}
(*ppsCompare)->hItem->hNext = hNext;
}
(*ppsCompare)->hItem->hNext = NULL;
TV_UpdateShownIndexes(pTree, hParent);
if ((pSortCB->hParent == TVI_ROOT) || !hParent) {
if (pTree->cShowing < pTree->cFullVisible) {
pTree->hTop = pTree->hRoot->hKids;
}
}
if (hFirstMoved && (hParent->state & TVIS_EXPANDED))
{
RECT rcUpdate;
TV_GetItemRect(pTree, hFirstMoved, &rcUpdate, FALSE);
if (hParent->hNext)
{
RECT rcTemp;
TV_GetItemRect(pTree, hParent->hNext, &rcTemp, FALSE);
rcUpdate.bottom = rcTemp.bottom;
}
else
{
RECT rcClient;
GetClientRect(pTree->hwnd, &rcClient);
// Set to maximal positive number, so the whole rest of
// the treeview gets invalidated
rcUpdate.bottom = rcClient.bottom;
}
if (pTree->fRedraw)
InvalidateRect(pTree->hwnd, &rcUpdate, TRUE);
}
Error3:
DPA_Destroy(dpaSort);
Error2:
for (i = DSA_GetItemCount(dsaCmp) - 1; i >= 0; --i)
{
psCompare = DSA_GetItemPtr(dsaCmp, i);
if (psCompare->bCallBack)
{
Str_Set(&(psCompare->lpstr), NULL);
}
}
DSA_Destroy(dsaCmp);
Error1:
#ifdef DEBUG
DebugMsg(DM_TRACE, "tv.sort: %ld ms; %d cmps", GetTickCount()-dwTime, nCompares);
#endif
{
int wNewPos;
// restore the scroll position
if (GetWindowStyle(pTree->hwnd) & WS_VSCROLL) {
#ifdef IEWIN31_25
wNewPos = GetScrollPos( pTree->hwnd, SB_VERT );
#else
SCROLLINFO si;
si.cbSize = sizeof(SCROLLINFO);
si.fMask = SIF_POS;
wNewPos = 0;
if (GetScrollInfo(pTree->hwnd, SB_VERT, &si)) {
wNewPos = si.nPos;
}
#endif //IEWIN31_25
} else {
wNewPos = 0;
}
if (TV_SetTopItem(pTree, wNewPos))
UpdateWindow(pTree->hwnd);
}
// if the caret is the child of the thing that was sorted, make sure it's
// visible (but if we're sorting something completely unrelated, don't bother
if (pTree->hCaret) {
hItem = pTree->hCaret;
do {
// do this first. if hParent is hCaret, we don't want to ensure visible...
// only if it's an eventual child
hItem = hItem->hParent;
if (hParent == hItem) {
TV_EnsureVisible(pTree, pTree->hCaret);
}
} while(hItem && hItem != pTree->hRoot);
}
return TRUE;
}
BOOL NEAR TV_SortChildrenCB(PTREE pTree, LPTV_SORTCB pSortCB, BOOL bRecurse)
{
return(TV_SortCB(pTree, pSortCB, bRecurse, TV_CompareItems));
}
BOOL NEAR TV_SortChildren(PTREE pTree, HTREEITEM hParent, BOOL bRecurse)
{
TV_SORTCB sSortCB;
sSortCB.hParent = hParent;
return(TV_SortCB(pTree, &sSortCB, bRecurse, NULL));
}