|
|
// list view (small icons, multiple columns)
#include "ctlspriv.h"
#include "listview.h"
#define COLUMN_VIEW
BOOL ListView_LDrawItem(PLVDRAWITEM plvdi) { RECT rcIcon; RECT rcLabel; RECT rcBounds; RECT rcT; LV_ITEM item; TCHAR ach[CCHLABELMAX]; LV* plv = plvdi->plv; int i = (int) plvdi->nmcd.nmcd.dwItemSpec;
// moved here to reduce call backs in OWNERDATA case
//
item.iItem = i; item.iSubItem = 0; item.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_STATE; item.stateMask = LVIS_ALL; item.pszText = ach; item.cchTextMax = ARRAYSIZE(ach);
ListView_OnGetItem(plv, &item);
ListView_LGetRects(plv, i, &rcIcon, &rcLabel, &rcBounds, NULL);
if (!plvdi->prcClip || IntersectRect(&rcT, &rcBounds, plvdi->prcClip)) { UINT fText;
if (plvdi->lpptOrg) { OffsetRect(&rcIcon, plvdi->lpptOrg->x - rcBounds.left, plvdi->lpptOrg->y - rcBounds.top); OffsetRect(&rcLabel, plvdi->lpptOrg->x - rcBounds.left, plvdi->lpptOrg->y - rcBounds.top); }
fText = ListView_DrawImage(plv, &item, plvdi->nmcd.nmcd.hdc, rcIcon.left, rcIcon.top, plvdi->flags) | SHDT_ELLIPSES;
// Don't draw the label if it is being edited.
if (plv->iEdit != i) { int ItemCxSingleLabel; UINT ItemState;
if (ListView_IsOwnerData( plv )) { LISTITEM listitem;
// calculate lable sizes from iItem
listitem.pszText = ach; ListView_RecomputeLabelSize( plv, &listitem, i, plvdi->nmcd.nmcd.hdc, TRUE );
ItemCxSingleLabel = listitem.cxSingleLabel; ItemState = item.state; } else { ItemCxSingleLabel = plvdi->pitem->cxSingleLabel; ItemState = plvdi->pitem->state; }
if (plvdi->flags & LVDI_TRANSTEXT) fText |= SHDT_TRANSPARENT;
if (ItemCxSingleLabel == SRECOMPUTE) { ListView_RecomputeLabelSize(plv, plvdi->pitem, i, plvdi->nmcd.nmcd.hdc, FALSE); ItemCxSingleLabel = plvdi->pitem->cxSingleLabel; }
if (ItemCxSingleLabel < rcLabel.right - rcLabel.left) rcLabel.right = rcLabel.left + ItemCxSingleLabel;
if ((fText & SHDT_SELECTED) && (plvdi->flags & LVDI_HOTSELECTED)) fText |= SHDT_HOTSELECTED;
if( plv->dwExStyle & WS_EX_RTLREADING) { fText |= SHDT_RTLREADING; }
SHDrawText(plvdi->nmcd.nmcd.hdc, item.pszText, &rcLabel, LVCFMT_LEFT, fText, plv->cyLabelChar, plv->cxEllipses, plvdi->nmcd.clrText, plvdi->nmcd.clrTextBk);
if ((plvdi->flags & LVDI_FOCUS) && (ItemState & LVIS_FOCUSED) && !(CCGetUIState(&(plvdi->plv->ci)) & UISF_HIDEFOCUS) ) DrawFocusRect(plvdi->nmcd.nmcd.hdc, &rcLabel); } } return TRUE; }
DWORD ListView_LApproximateViewRect(LV* plv, int iCount, int iWidth, int iHeight) { int cxItem = plv->cxItem; int cyItem = plv->cyItem; int cCols; int cRows;
cRows = iHeight / cyItem; cRows = min(cRows, iCount);
if (cRows == 0) cRows = 1; cCols = (iCount + cRows - 1) / cRows;
iWidth = cCols * cxItem; iHeight = cRows * cyItem;
return MAKELONG(iWidth + g_cxEdge, iHeight + g_cyEdge); }
int NEAR ListView_LItemHitTest(LV* plv, int x, int y, UINT FAR* pflags, int *piSubItem) { int iHit; int i; int iCol; int xItem; //where is the x in relation to the item
UINT flags; LISTITEM FAR* pitem;
if (piSubItem) *piSubItem = 0;
flags = LVHT_NOWHERE; iHit = -1;
#ifdef COLUMN_VIEW
i = y / plv->cyItem; if (i >= 0 && i < plv->cItemCol) { iCol = (x + plv->xOrigin) / plv->cxItem; i += iCol * plv->cItemCol; if (i >= 0 && i < ListView_Count(plv)) { iHit = i;
xItem = x + plv->xOrigin - iCol * plv->cxItem; if (xItem < plv->cxState) { flags = LVHT_ONITEMSTATEICON; } else if (xItem < (plv->cxState + plv->cxSmIcon)) { flags = LVHT_ONITEMICON; } else { int ItemCxSingleLabel;
if (ListView_IsOwnerData( plv )) { LISTITEM item;
// calculate lable sizes from iItem
ListView_RecomputeLabelSize( plv, &item, i, NULL, FALSE ); ItemCxSingleLabel = item.cxSingleLabel; } else { pitem = ListView_FastGetItemPtr(plv, i); if (pitem->cxSingleLabel == SRECOMPUTE) { ListView_RecomputeLabelSize(plv, pitem, i, NULL, FALSE); } ItemCxSingleLabel = pitem->cxSingleLabel; }
if (xItem < (plv->cxSmIcon + plv->cxState + ItemCxSingleLabel)) flags = LVHT_ONITEMLABEL; } } } #else
i = x / plv->cxItem; if (i < plv->cItemCol) { i += ((y + plv->xOrigin) / plv->cyItem) * plv->cItemCol; if (i < ListView_Count(plv)) { iHit = i; flags = LVHT_ONITEMICON; } } #endif
*pflags = flags; return iHit; }
void NEAR ListView_LGetRects(LV* plv, int i, RECT FAR* prcIcon, RECT FAR* prcLabel, RECT FAR *prcBounds, RECT FAR* prcSelectBounds) { RECT rcIcon; RECT rcLabel; int x, y; int cItemCol = plv->cItemCol;
if (cItemCol == 0) { // Called before other data has been initialized so call
// update scrollbars which should make sure that that
// we have valid data...
ListView_UpdateScrollBars(plv);
// but it's possible that updatescrollbars did nothing because of
// LVS_NOSCROLL or redraw
// BUGBUG raymondc v6.0: Get it right even if no redraw. Fix for v6.
if (plv->cItemCol == 0) cItemCol = 1; else cItemCol = plv->cItemCol; }
#ifdef COLUMN_VIEW
x = (i / cItemCol) * plv->cxItem; y = (i % cItemCol) * plv->cyItem; rcIcon.left = x - plv->xOrigin + plv->cxState; rcIcon.top = y; #else
x = (i % cItemCol) * plv->cxItem; y = (i / cItemCol) * plv->cyItem; rcIcon.left = x; rcIcon.top = y - plv->xOrigin; #endif
rcIcon.right = rcIcon.left + plv->cxSmIcon; rcIcon.bottom = rcIcon.top + plv->cyItem;
if (prcIcon) *prcIcon = rcIcon;
rcLabel.left = rcIcon.right; rcLabel.right = rcIcon.left + plv->cxItem - plv->cxState; rcLabel.top = rcIcon.top; rcLabel.bottom = rcIcon.bottom; if (prcLabel) *prcLabel = rcLabel;
if (prcBounds) { *prcBounds = rcLabel; prcBounds->left = rcIcon.left - plv->cxState; }
if (prcSelectBounds) { *prcSelectBounds = rcLabel; prcSelectBounds->left = rcIcon.left; } }
void NEAR ListView_LUpdateScrollBars(LV* plv) { RECT rcClient; int cItemCol; int cCol; int cColVis; SCROLLINFO si;
ASSERT(plv);
ListView_GetClientRect(plv, &rcClient, FALSE, NULL);
#ifdef COLUMN_VIEW
cColVis = (rcClient.right - rcClient.left) / plv->cxItem; cItemCol = max(1, (rcClient.bottom - rcClient.top) / plv->cyItem); #else
cColVis = (rcClient.bottom - rcClient.top) / plv->cyItem; cItemCol = max(1, (rcClient.right - rcClient.left) / plv->cxItem); #endif
cCol = (ListView_Count(plv) + cItemCol - 1) / cItemCol;
// Make the client area smaller as appropriate, and
// recompute cCol to reflect scroll bar.
//
si.cbSize = sizeof(SCROLLINFO); si.fMask = SIF_PAGE | SIF_RANGE | SIF_POS; si.nPage = cColVis; si.nMin = 0;
#ifdef COLUMN_VIEW
rcClient.bottom -= ListView_GetCyScrollbar(plv);
cItemCol = max(1, (rcClient.bottom - rcClient.top) / plv->cyItem); cCol = (ListView_Count(plv) + cItemCol - 1) / cItemCol;
si.nPos = plv->xOrigin / plv->cxItem; si.nMax = cCol - 1;
ListView_SetScrollInfo(plv, SB_HORZ, &si, TRUE); #else
rcClient.right -= ListView_GetCxScrollbar(plv);
cItemCol = max(1, (rcClient.right - rcClient.left) / plv->cxItem); cCol = (ListView_Count(plv) + cItemCol - 1) / cItemCol;
si.nPos = plv->xOrigin / plv->cyItem; si.nMax = cCol - 1;
ListView_SetScrollInfo(plv, SB_VERT, &si, TRUE); #endif
// Update number of visible lines...
//
if (plv->cItemCol != cItemCol) { plv->cItemCol = cItemCol; InvalidateRect(plv->ci.hwnd, NULL, TRUE); }
// make sure our position and page doesn't hang over max
if ((si.nPos + (LONG)si.nPage - 1 > si.nMax) && si.nPos > 0) { int iNewPos, iDelta; iNewPos = (int)si.nMax - (int)si.nPage + 1; if (iNewPos < 0) iNewPos = 0; if (iNewPos != si.nPos) { iDelta = iNewPos - (int)si.nPos; #ifdef COLUMN_VIEW
ListView_LScroll2(plv, iDelta, 0, 0); #else
ListView_LScroll2(plv, 0, iDelta, 0); #endif
ListView_LUpdateScrollBars(plv); } }
// never have the other scrollbar
#ifdef COLUMN_VIEW
ListView_SetScrollRange(plv, SB_VERT, 0, 0, TRUE); #else
ListView_SetScrollRange(plv, SB_HORZ, 0, 0, TRUE); #endif
}
//
// We need a smoothscroll callback so our background image draws
// at the correct origin. If we don't have a background image,
// then this work is superfluous but not harmful either.
//
int CALLBACK ListView_LScroll2_SmoothScroll( HWND hwnd, int dx, int dy, CONST RECT *prcScroll, CONST RECT *prcClip, HRGN hrgnUpdate, LPRECT prcUpdate, UINT flags) { LV* plv = ListView_GetPtr(hwnd); if (plv) { #ifdef COLUMN_VIEW
plv->xOrigin -= dx; #else
plv->xOrigin -= dy; #endif
}
// Now do what SmoothScrollWindow would've done if we weren't
// a callback
return ScrollWindowEx(hwnd, dx, dy, prcScroll, prcClip, hrgnUpdate, prcUpdate, flags); }
void FAR PASCAL ListView_LScroll2(LV* plv, int dx, int dy, UINT uSmooth) { #ifdef COLUMN_VIEW
if (dx) { dx *= plv->cxItem;
{ SMOOTHSCROLLINFO si; si.cbSize = sizeof(si); si.fMask = SSIF_SCROLLPROC; si.hwnd =plv->ci.hwnd ; si.dx =-dx ; si.dy = 0; si.lprcSrc = NULL; si.lprcClip = NULL; si.hrgnUpdate = NULL; si.lprcUpdate = NULL; si.fuScroll = SW_INVALIDATE | SW_ERASE; si.pfnScrollProc = ListView_LScroll2_SmoothScroll; SmoothScrollWindow(&si); }
UpdateWindow(plv->ci.hwnd); } #else
if (dy) {
dy *= plv->cyItem;
{ SMOOTHSCROLLINFO si; si.cbSize = sizeof(si); si.fMask = SSIF_SCROLLPROC; si.hwnd = plv->ci.hwnd; si.dx = 0; si.dy = -dy; si.lprcSrc = NULL; si.lprcClip = NULL; si.hrgnUpdate = NULL; si.lprcUpdate = NULL; si.fuScroll = SW_INVALIDATE | SW_ERASE; si.pfnScrollProc = ListView_LScroll2_SmoothScroll;
SmoothScrollWindow(&si); } UpdateWindow(plv->ci.hwnd); } #endif
}
void NEAR ListView_LOnScroll(LV* plv, UINT code, int posNew, UINT sb) { RECT rcClient; int cPage;
if (plv->hwndEdit) ListView_DismissEdit(plv, FALSE);
ListView_GetClientRect(plv, &rcClient, TRUE, NULL);
#ifdef COLUMN_VIEW
cPage = (rcClient.right - rcClient.left) / plv->cxItem; ListView_ComOnScroll(plv, code, posNew, SB_HORZ, 1, cPage ? cPage : 1); #else
cPage = (rcClient.bottom - rcClient.top) / plv->cyItem; ListView_ComOnScroll(plv, code, posNew, SB_VERT, 1, cPage ? cPage : 1); #endif
}
int NEAR ListView_LGetScrollUnitsPerLine(LV* plv, UINT sb) { return 1; }
//------------------------------------------------------------------------------
//
// Function: ListView_LCalcViewItem
//
// Summary: This function will calculate which item slot is at the x, y location
//
// Arguments:
// plv [in] - The list View to work with
// x [in] - The x location
// y [in] - The y location
//
// Returns: the valid slot the point was within.
//
// Notes:
//
// History:
// Nov-3-94 MikeMi Created
//
//------------------------------------------------------------------------------
int ListView_LCalcViewItem( LV* plv, int x, int y ) { int iItem; int iRow = 0; int iCol = 0;
ASSERT( plv );
#ifdef COLUMN_VIEW
iRow = y / plv->cyItem; iRow = max( iRow, 0 ); iRow = min( iRow, plv->cItemCol - 1 ); iCol = (x + plv->xOrigin) / plv->cxItem; iItem = iRow + iCol * plv->cItemCol;
#else
iCol = x / plv->cxItem; iCol = max( iCol, 0 ); iCol = min( iCol, plv->cItemCol - 1 ); iRow = (y + plv->xOrigin) / plv->cyItem; iItem = iCol + iRow * plv->cItemCol;
#endif
iItem = max( iItem, 0 ); iItem = min( iItem, ListView_Count(plv) - 1);
return( iItem ); }
int LV_GetNewColWidth(LV* plv, int iFirst, int iLast) { int cxMaxLabel = 0;
// Don't do anything if there are no items to measure
if (iFirst <= iLast) { LVFAKEDRAW lvfd; LV_ITEM lvitem; LISTITEM item;
if (ListView_IsOwnerData( plv )) { int iViewFirst; int iViewLast;
iViewFirst = ListView_LCalcViewItem( plv, 1, 1 ); iViewLast = ListView_LCalcViewItem( plv, plv->sizeClient.cx - 1, plv->sizeClient.cy - 1 ); if ((iLast - iFirst) > (iViewLast - iViewFirst)) { iFirst = max( iFirst, iViewFirst ); iLast = min( iLast, iViewLast ); }
iLast = min( ListView_Count( plv ), iLast ); iFirst = max( 0, iFirst ); iLast = max( iLast, iFirst );
ListView_NotifyCacheHint( plv, iFirst, iLast ); }
ListView_BeginFakeCustomDraw(plv, &lvfd, &lvitem); lvitem.iSubItem = 0; lvitem.mask = LVIF_PARAM; item.lParam = 0;
while (iFirst <= iLast) { LISTITEM FAR* pitem;
if (ListView_IsOwnerData( plv )) { pitem = &item; pitem->cxSingleLabel = SRECOMPUTE; } else { pitem = ListView_FastGetItemPtr(plv, iFirst); }
if (pitem->cxSingleLabel == SRECOMPUTE) { lvitem.iItem = iFirst; lvitem.lParam = pitem->lParam; ListView_BeginFakeItemDraw(&lvfd); ListView_RecomputeLabelSize(plv, pitem, iFirst, lvfd.nmcd.nmcd.hdc, FALSE); ListView_EndFakeItemDraw(&lvfd); }
if (pitem->cxSingleLabel > cxMaxLabel) cxMaxLabel = pitem->cxSingleLabel;
iFirst++; }
ListView_EndFakeCustomDraw(&lvfd); }
// We have the max label width, see if this plus the rest of the slop will
// cause us to want to resize.
//
cxMaxLabel += plv->cxSmIcon + g_cxIconMargin + plv->cxState; if (cxMaxLabel > g_cxScreen) cxMaxLabel = g_cxScreen;
return cxMaxLabel; }
//------------------------------------------------------------------------------
// This function will see if the size of column should be changed for the listview
// It will check to see if the items between first and last exceed the current width
// and if so will see if the columns are currently big enough. This wont happen
// if we are not currently in listview or if the caller has set an explicit size.
//
// OWNERDATA CHANGE
// This function is normally called with the complete list range,
// This will has been changed to be called only with currently visible
// to the user when in OWNERDATA mode. This will be much more effiencent.
//
BOOL FAR PASCAL ListView_MaybeResizeListColumns(LV* plv, int iFirst, int iLast) { HDC hdc = NULL; int cxMaxLabel;
if (!ListView_IsListView(plv) || (plv->flags & LVF_COLSIZESET)) return(FALSE);
cxMaxLabel = LV_GetNewColWidth(plv, iFirst, iLast);
// Now see if we should resize the columns...
if (cxMaxLabel > plv->cxItem) { int iScroll = plv->xOrigin / plv->cxItem; TraceMsg(TF_LISTVIEW, "LV Resize Columns: %d", cxMaxLabel); ListView_ISetColumnWidth(plv, 0, cxMaxLabel, FALSE); plv->xOrigin = iScroll * plv->cxItem; return(TRUE); }
return(FALSE); }
|