blob: b5fb2ceeea8083e449ea6366f1b4fcd6bcf66cd0 [file] [log] [blame]
/*
* Listview control
*
* Copyright 1998, 1999 Eric Kohl
* Copyright 1999 Luc Tourangeau
* Copyright 2000 Jason Mawdsley
*
* NOTES
* Listview control implementation.
*
* TODO:
* 1. No horizontal scrolling when header is larger than the client area.
* 2. Drawing optimizations.
* 3. Hot item handling.
*
* Notifications:
* LISTVIEW_Notify : most notifications from children (editbox and header)
*
* Data structure:
* LISTVIEW_SetItemCount : not completed for non OWNERDATA
*
* Unicode:
* LISTVIEW_SetItemW : no unicode support
* LISTVIEW_InsertItemW : no unicode support
* LISTVIEW_InsertColumnW : no unicode support
* LISTVIEW_GetColumnW : no unicode support
* LISTVIEW_SetColumnW : no unicode support
*
* Advanced functionality:
* LISTVIEW_GetNumberOfWorkAreas : not implemented
* LISTVIEW_GetHotCursor : not implemented
* LISTVIEW_GetISearchString : not implemented
* LISTVIEW_GetBkImage : not implemented
* LISTVIEW_SetBkImage : not implemented
* LISTVIEW_GetColumnOrderArray : simple hack only
* LISTVIEW_SetColumnOrderArray : simple hack only
* LISTVIEW_Arrange : empty stub
* LISTVIEW_ApproximateViewRect : incomplete
* LISTVIEW_Scroll : not implemented
* LISTVIEW_Update : not completed
*/
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include "winbase.h"
#include "heap.h"
#include "commctrl.h"
#include "debugtools.h"
DEFAULT_DEBUG_CHANNEL(listview);
/* Some definitions for inline edit control */
typedef BOOL (*EditlblCallback)(HWND, LPSTR, DWORD);
typedef struct tagEDITLABEL_ITEM
{
WNDPROC EditWndProc;
DWORD param;
EditlblCallback EditLblCb;
} EDITLABEL_ITEM;
typedef struct tagLISTVIEW_SUBITEM
{
LPSTR pszText;
INT iImage;
INT iSubItem;
} LISTVIEW_SUBITEM;
typedef struct tagLISTVIEW_ITEM
{
UINT state;
LPSTR pszText;
INT iImage;
LPARAM lParam;
INT iIndent;
POINT ptPosition;
} LISTVIEW_ITEM;
typedef struct tagLISTVIEW_SELECTION
{
DWORD lower;
DWORD upper;
} LISTVIEW_SELECTION;
typedef struct tagLISTVIEW_INFO
{
COLORREF clrBk;
COLORREF clrText;
COLORREF clrTextBk;
HIMAGELIST himlNormal;
HIMAGELIST himlSmall;
HIMAGELIST himlState;
BOOL bLButtonDown;
BOOL bRButtonDown;
INT nFocusedItem;
HDPA hdpaSelectionRanges;
INT nItemHeight;
INT nItemWidth;
INT nSelectionMark;
INT nHotItem;
SHORT notifyFormat;
RECT rcList;
RECT rcView;
SIZE iconSize;
SIZE iconSpacing;
UINT uCallbackMask;
HWND hwndHeader;
HFONT hDefaultFont;
HFONT hFont;
BOOL bFocus;
DWORD dwExStyle; /* extended listview style */
HDPA hdpaItems;
PFNLVCOMPARE pfnCompare;
LPARAM lParamSort;
HWND hwndEdit;
INT nEditLabelItem;
EDITLABEL_ITEM *pedititem;
DWORD dwHoverTime;
INT nColumnCount; /* the number of columns in this control */
DWORD lastKeyPressTimestamp; /* Added */
WPARAM charCode; /* Added */
INT nSearchParamLength; /* Added */
CHAR szSearchParam[ MAX_PATH ]; /* Added */
} LISTVIEW_INFO;
/*
* constants
*/
/* maximum size of a label */
#define DISP_TEXT_SIZE 512
/* padding for items in list and small icon display modes */
#define WIDTH_PADDING 12
/* padding for items in list, report and small icon display modes */
#define HEIGHT_PADDING 1
/* offset of items in report display mode */
#define REPORT_MARGINX 2
/* padding for icon in large icon display mode */
#define ICON_TOP_PADDING 2
#define ICON_BOTTOM_PADDING 2
/* padding for label in large icon display mode */
#define LABEL_VERT_OFFSET 2
/* default label width for items in list and small icon display modes */
#define DEFAULT_LABEL_WIDTH 40
/* default column width for items in list display mode */
#define DEFAULT_COLUMN_WIDTH 96
/* Increment size of the horizontal scroll bar */
#define LISTVIEW_SCROLL_DIV_SIZE 10
/* Padding betwen image and label */
#define IMAGE_PADDING 2
/* Padding behind the label */
#define TRAILING_PADDING 5
/* Border for the icon caption */
#define CAPTION_BORDER 2
/*
* macros
*/
#define ListView_LVNotify(hwnd,lCtrlId,plvnm) \
(BOOL)SendMessageA((hwnd),WM_NOTIFY,(WPARAM)(INT)lCtrlId,(LPARAM)(LPNMLISTVIEW)(plvnm))
#define ListView_Notify(hwnd,lCtrlId,pnmh) \
(BOOL)SendMessageA((hwnd),WM_NOTIFY,(WPARAM)(INT)lCtrlId,(LPARAM)(LPNMHDR)(pnmh))
/* retrieve the number of items in the listview */
#define GETITEMCOUNT(infoPtr) ((infoPtr)->hdpaItems->nItemCount)
HWND CreateEditLabel(LPCSTR text, DWORD style, INT x, INT y,
INT width, INT height, HWND parent, HINSTANCE hinst,
EditlblCallback EditLblCb, DWORD param);
/*
* forward declarations
*/
static LRESULT LISTVIEW_GetItemA(HWND hwnd, LPLVITEMA lpLVItem, BOOL internal);
static INT LISTVIEW_HitTestItem(HWND, LPLVHITTESTINFO, BOOL);
static INT LISTVIEW_GetCountPerRow(HWND);
static INT LISTVIEW_GetCountPerColumn(HWND);
static VOID LISTVIEW_AlignLeft(HWND);
static VOID LISTVIEW_AlignTop(HWND);
static VOID LISTVIEW_AddGroupSelection(HWND, INT);
static VOID LISTVIEW_AddSelection(HWND, INT);
static BOOL LISTVIEW_AddSubItem(HWND, LPLVITEMA);
static INT LISTVIEW_FindInsertPosition(HDPA, INT);
static INT LISTVIEW_GetItemHeight(HWND);
static BOOL LISTVIEW_GetItemPosition(HWND, INT, LPPOINT);
static LRESULT LISTVIEW_GetItemRect(HWND, INT, LPRECT);
static INT LISTVIEW_GetItemWidth(HWND);
static INT LISTVIEW_GetLabelWidth(HWND, INT);
static LRESULT LISTVIEW_GetOrigin(HWND, LPPOINT);
static INT LISTVIEW_CalculateWidth(HWND hwnd, INT nItem);
static LISTVIEW_SUBITEM* LISTVIEW_GetSubItem(HDPA, INT);
static LRESULT LISTVIEW_GetViewRect(HWND, LPRECT);
static BOOL LISTVIEW_InitItem(HWND, LISTVIEW_ITEM *, LPLVITEMA);
static BOOL LISTVIEW_InitSubItem(HWND, LISTVIEW_SUBITEM *, LPLVITEMA);
static LRESULT LISTVIEW_MouseSelection(HWND, POINT);
static BOOL LISTVIEW_RemoveColumn(HDPA, INT);
static BOOL LISTVIEW_RemoveSubItem(HDPA, INT);
static VOID LISTVIEW_SetGroupSelection(HWND, INT);
static BOOL LISTVIEW_SetItem(HWND, LPLVITEMA);
static BOOL LISTVIEW_SetItemFocus(HWND, INT);
static BOOL LISTVIEW_SetItemPosition(HWND, INT, LONG, LONG);
static VOID LISTVIEW_UpdateScroll(HWND);
static VOID LISTVIEW_SetSelection(HWND, INT);
static VOID LISTVIEW_UpdateSize(HWND);
static BOOL LISTVIEW_SetSubItem(HWND, LPLVITEMA);
static LRESULT LISTVIEW_SetViewRect(HWND, LPRECT);
static BOOL LISTVIEW_ToggleSelection(HWND, INT);
static VOID LISTVIEW_UnsupportedStyles(LONG lStyle);
static HWND LISTVIEW_EditLabelA(HWND hwnd, INT nItem);
static BOOL LISTVIEW_EndEditLabel(HWND hwnd, LPSTR pszText, DWORD nItem);
static LRESULT LISTVIEW_Command(HWND hwnd, WPARAM wParam, LPARAM lParam);
static LRESULT LISTVIEW_SortItems(HWND hwnd, WPARAM wParam, LPARAM lParam);
static LRESULT LISTVIEW_GetStringWidthA(HWND hwnd, LPCSTR lpszText);
static INT LISTVIEW_ProcessLetterKeys( HWND hwnd, WPARAM charCode, LPARAM keyData );
static BOOL LISTVIEW_KeySelection(HWND hwnd, INT nItem);
static LRESULT LISTVIEW_GetItemState(HWND hwnd, INT nItem, UINT uMask);
static LRESULT LISTVIEW_SetItemState(HWND hwnd, INT nItem, LPLVITEMA lpLVItem);
static BOOL LISTVIEW_IsSelected(HWND hwnd, INT nItem);
static VOID LISTVIEW_RemoveSelectionRange(HWND hwnd, INT lItem, INT uItem);
static void LISTVIEW_FillBackground(HWND hwnd, HDC hdc, LPRECT rc);
/******** Defines that LISTVIEW_ProcessLetterKeys uses ****************/
#define KEY_DELAY 450
static BOOL
LISTVIEW_SendCustomDrawNotify (HWND hwnd, DWORD dwDrawStage, HDC hdc,
RECT rc)
{
LISTVIEW_INFO *infoPtr;
NMLVCUSTOMDRAW nmcdhdr;
LPNMCUSTOMDRAW nmcd;
TRACE("drawstage:%lx hdc:%x\n", dwDrawStage, hdc);
infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
nmcd= & nmcdhdr.nmcd;
nmcd->hdr.hwndFrom = hwnd;
nmcd->hdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
nmcd->hdr.code = NM_CUSTOMDRAW;
nmcd->dwDrawStage= dwDrawStage;
nmcd->hdc = hdc;
nmcd->rc.left = rc.left;
nmcd->rc.right = rc.right;
nmcd->rc.bottom = rc.bottom;
nmcd->rc.top = rc.top;
nmcd->dwItemSpec = 0;
nmcd->uItemState = 0;
nmcd->lItemlParam= 0;
nmcdhdr.clrText = infoPtr->clrText;
nmcdhdr.clrTextBk= infoPtr->clrBk;
return (BOOL)SendMessageA (GetParent (hwnd), WM_NOTIFY,
(WPARAM) GetWindowLongA( hwnd, GWL_ID), (LPARAM)&nmcdhdr);
}
static BOOL
LISTVIEW_SendCustomDrawItemNotify (HWND hwnd, HDC hdc,
UINT iItem, UINT iSubItem,
UINT uItemDrawState)
{
LISTVIEW_INFO *infoPtr;
NMLVCUSTOMDRAW nmcdhdr;
LPNMCUSTOMDRAW nmcd;
DWORD dwDrawStage,dwItemSpec;
UINT uItemState;
INT retval;
RECT itemRect;
LVITEMA item;
infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
ZeroMemory(&item,sizeof(LVITEMA));
item.iItem = iItem;
item.mask = LVIF_PARAM;
ListView_GetItemA(hwnd,&item);
dwDrawStage=CDDS_ITEM | uItemDrawState;
dwItemSpec=iItem;
uItemState=0;
if (LISTVIEW_IsSelected(hwnd,iItem)) uItemState|=CDIS_SELECTED;
if (iItem==infoPtr->nFocusedItem) uItemState|=CDIS_FOCUS;
if (iItem==infoPtr->nHotItem) uItemState|=CDIS_HOT;
itemRect.left = LVIR_BOUNDS;
LISTVIEW_GetItemRect(hwnd, iItem, &itemRect);
nmcd= & nmcdhdr.nmcd;
nmcd->hdr.hwndFrom = hwnd;
nmcd->hdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
nmcd->hdr.code = NM_CUSTOMDRAW;
nmcd->dwDrawStage= dwDrawStage;
nmcd->hdc = hdc;
nmcd->rc.left = itemRect.left;
nmcd->rc.right = itemRect.right;
nmcd->rc.bottom = itemRect.bottom;
nmcd->rc.top = itemRect.top;
nmcd->dwItemSpec = dwItemSpec;
nmcd->uItemState = uItemState;
nmcd->lItemlParam= item.lParam;
nmcdhdr.clrText = infoPtr->clrText;
nmcdhdr.clrTextBk= infoPtr->clrBk;
nmcdhdr.iSubItem =iSubItem;
TRACE("drawstage:%lx hdc:%x item:%lx, itemstate:%x, lItemlParam:%lx\n",
nmcd->dwDrawStage, nmcd->hdc, nmcd->dwItemSpec,
nmcd->uItemState, nmcd->lItemlParam);
retval=SendMessageA (GetParent (hwnd), WM_NOTIFY,
(WPARAM) GetWindowLongA( hwnd, GWL_ID), (LPARAM)&nmcdhdr);
infoPtr->clrText=nmcdhdr.clrText;
infoPtr->clrBk =nmcdhdr.clrTextBk;
return (BOOL) retval;
}
/*************************************************************************
* LISTVIEW_ProcessLetterKeys
*
* Processes keyboard messages generated by pressing the letter keys
* on the keyboard.
* What this does is perform a case insensitive search from the
* current position with the following quirks:
* - If two chars or more are pressed in quick succession we search
* for the corresponding string (e.g. 'abc').
* - If there is a delay we wipe away the current search string and
* restart with just that char.
* - If the user keeps pressing the same character, whether slowly or
* fast, so that the search string is entirely composed of this
* character ('aaaaa' for instance), then we search for first item
* that starting with that character.
* - If the user types the above character in quick succession, then
* we must also search for the corresponding string ('aaaaa'), and
* go to that string if there is a match.
*
* RETURNS
*
* Zero.
*
* BUGS
*
* - The current implementation has a list of characters it will
* accept and it ignores averything else. In particular it will
* ignore accentuated characters which seems to match what
* Windows does. But I'm not sure it makes sense to follow
* Windows there.
* - We don't sound a beep when the search fails.
*
* SEE ALSO
*
* TREEVIEW_ProcessLetterKeys
*/
static INT LISTVIEW_ProcessLetterKeys(
HWND hwnd, /* handle to the window */
WPARAM charCode, /* the character code, the actual character */
LPARAM keyData /* key data */
)
{
LISTVIEW_INFO *infoPtr;
INT nItem;
INT nSize;
INT endidx,idx;
LVITEMA item;
CHAR buffer[MAX_PATH];
DWORD timestamp,elapsed;
/* simple parameter checking */
if (!hwnd || !charCode || !keyData)
return 0;
infoPtr=(LISTVIEW_INFO*)GetWindowLongA(hwnd, 0);
if (!infoPtr)
return 0;
/* only allow the valid WM_CHARs through */
if (!isalnum(charCode) &&
charCode != '.' && charCode != '`' && charCode != '!' &&
charCode != '@' && charCode != '#' && charCode != '$' &&
charCode != '%' && charCode != '^' && charCode != '&' &&
charCode != '*' && charCode != '(' && charCode != ')' &&
charCode != '-' && charCode != '_' && charCode != '+' &&
charCode != '=' && charCode != '\\'&& charCode != ']' &&
charCode != '}' && charCode != '[' && charCode != '{' &&
charCode != '/' && charCode != '?' && charCode != '>' &&
charCode != '<' && charCode != ',' && charCode != '~')
return 0;
nSize=GETITEMCOUNT(infoPtr);
/* if there's one item or less, there is no where to go */
if (nSize <= 1)
return 0;
/* compute how much time elapsed since last keypress */
timestamp=GetTickCount();
if (timestamp > infoPtr->lastKeyPressTimestamp) {
elapsed=timestamp-infoPtr->lastKeyPressTimestamp;
} else {
elapsed=infoPtr->lastKeyPressTimestamp-timestamp;
}
/* update the search parameters */
infoPtr->lastKeyPressTimestamp=timestamp;
if (elapsed < KEY_DELAY) {
if (infoPtr->nSearchParamLength < sizeof(infoPtr->szSearchParam)) {
infoPtr->szSearchParam[infoPtr->nSearchParamLength++]=charCode;
}
if (infoPtr->charCode != charCode) {
infoPtr->charCode=charCode=0;
}
} else {
infoPtr->charCode=charCode;
infoPtr->szSearchParam[0]=charCode;
infoPtr->nSearchParamLength=1;
/* Redundant with the 1 char string */
charCode=0;
}
/* and search from the current position */
nItem=-1;
if (infoPtr->nFocusedItem >= 0) {
endidx=infoPtr->nFocusedItem;
idx=endidx;
/* if looking for single character match,
* then we must always move forward
*/
if (infoPtr->nSearchParamLength == 1)
idx++;
} else {
endidx=nSize;
idx=0;
}
do {
if (idx == nSize) {
if (endidx == nSize)
break;
idx=0;
}
/* get item */
ZeroMemory(&item, sizeof(item));
item.mask = LVIF_TEXT;
item.iItem = idx;
item.iSubItem = 0;
item.pszText = buffer;
item.cchTextMax = sizeof(buffer);
ListView_GetItemA( hwnd, &item );
/* check for a match */
if (strncasecmp(item.pszText,infoPtr->szSearchParam,infoPtr->nSearchParamLength) == 0) {
nItem=idx;
break;
} else if ( (charCode != 0) && (nItem == -1) && (nItem != infoPtr->nFocusedItem) &&
(strncasecmp(item.pszText,infoPtr->szSearchParam,1) == 0) ) {
/* This would work but we must keep looking for a longer match */
nItem=idx;
}
idx++;
} while (idx != endidx);
if (nItem != -1) {
if (LISTVIEW_KeySelection(hwnd, nItem) != FALSE) {
/* refresh client area */
InvalidateRect(hwnd, NULL, TRUE);
UpdateWindow(hwnd);
}
}
return 0;
}
/*************************************************************************
* LISTVIEW_UpdateHeaderSize [Internal]
*
* Function to resize the header control
*
* PARAMS
* hwnd [I] handle to a window
* nNewScrollPos [I] Scroll Pos to Set
*
* RETURNS
* nothing
*
* NOTES
*/
static VOID LISTVIEW_UpdateHeaderSize(HWND hwnd, INT nNewScrollPos)
{
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
RECT winRect;
POINT point[2];
GetWindowRect(infoPtr->hwndHeader, &winRect);
point[0].x = winRect.left;
point[0].y = winRect.top;
point[1].x = winRect.right;
point[1].y = winRect.bottom;
MapWindowPoints(HWND_DESKTOP, hwnd, point, 2);
point[0].x = -(nNewScrollPos * LISTVIEW_SCROLL_DIV_SIZE);
point[1].x += (nNewScrollPos * LISTVIEW_SCROLL_DIV_SIZE);
SetWindowPos(infoPtr->hwndHeader,0,
point[0].x,point[0].y,point[1].x,point[1].y,
SWP_NOZORDER | SWP_NOACTIVATE);
}
/***
* DESCRIPTION:
* Update the scrollbars. This functions should be called whenever
* the content, size or view changes.
*
* PARAMETER(S):
* [I] HWND : window handle
*
* RETURN:
* None
*/
static VOID LISTVIEW_UpdateScroll(HWND hwnd)
{
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
UINT uView = lStyle & LVS_TYPEMASK;
INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
SCROLLINFO scrollInfo;
ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
scrollInfo.cbSize = sizeof(SCROLLINFO);
if (uView == LVS_LIST)
{
/* update horizontal scrollbar */
INT nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
INT nCountPerRow = LISTVIEW_GetCountPerRow(hwnd);
INT nNumOfItems = GETITEMCOUNT(infoPtr);
scrollInfo.nMax = nNumOfItems / nCountPerColumn;
if((nNumOfItems % nCountPerColumn) == 0)
{
scrollInfo.nMax--;
}
scrollInfo.nPos = ListView_GetTopIndex(hwnd) / nCountPerColumn;
scrollInfo.nPage = nCountPerRow;
scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
ShowScrollBar(hwnd, SB_VERT, FALSE);
}
else if (uView == LVS_REPORT)
{
/* update vertical scrollbar */
scrollInfo.nMin = 0;
scrollInfo.nMax = GETITEMCOUNT(infoPtr) - 1;
scrollInfo.nPos = ListView_GetTopIndex(hwnd);
scrollInfo.nPage = LISTVIEW_GetCountPerColumn(hwnd);
scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE);
/* update horizontal scrollbar */
nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) == FALSE
|| GETITEMCOUNT(infoPtr) == 0)
{
scrollInfo.nPos = 0;
}
scrollInfo.nMin = 0;
scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE ;
scrollInfo.nPage = nListWidth / LISTVIEW_SCROLL_DIV_SIZE;
scrollInfo.nMax = max(infoPtr->nItemWidth / LISTVIEW_SCROLL_DIV_SIZE, 0)-1;
SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
/* Update the Header Control */
scrollInfo.fMask = SIF_POS;
GetScrollInfo(hwnd, SB_HORZ, &scrollInfo);
LISTVIEW_UpdateHeaderSize(hwnd, scrollInfo.nPos);
}
else
{
RECT rcView;
if (LISTVIEW_GetViewRect(hwnd, &rcView) != FALSE)
{
INT nViewWidth = rcView.right - rcView.left;
INT nViewHeight = rcView.bottom - rcView.top;
/* Update Horizontal Scrollbar */
scrollInfo.fMask = SIF_POS;
if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) == FALSE
|| GETITEMCOUNT(infoPtr) == 0)
{
scrollInfo.nPos = 0;
}
scrollInfo.nMax = max(nViewWidth / LISTVIEW_SCROLL_DIV_SIZE, 0)-1;
scrollInfo.nMin = 0;
scrollInfo.nPage = nListWidth / LISTVIEW_SCROLL_DIV_SIZE;
scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
/* Update Vertical Scrollbar */
nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
scrollInfo.fMask = SIF_POS;
if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) == FALSE
|| GETITEMCOUNT(infoPtr) == 0)
{
scrollInfo.nPos = 0;
}
scrollInfo.nMax = max(nViewHeight / LISTVIEW_SCROLL_DIV_SIZE,0)-1;
scrollInfo.nMin = 0;
scrollInfo.nPage = nListHeight / LISTVIEW_SCROLL_DIV_SIZE;
scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE);
}
}
}
/***
* DESCRIPTION:
* Prints a message for unsupported window styles.
* A kind of TODO list for window styles.
*
* PARAMETER(S):
* [I] LONG : window style
*
* RETURN:
* None
*/
static VOID LISTVIEW_UnsupportedStyles(LONG lStyle)
{
if ((LVS_TYPEMASK & lStyle) == LVS_EDITLABELS)
{
FIXME(" LVS_EDITLABELS\n");
}
if ((LVS_TYPEMASK & lStyle) == LVS_NOLABELWRAP)
{
FIXME(" LVS_NOLABELWRAP\n");
}
if ((LVS_TYPEMASK & lStyle) == LVS_NOSCROLL)
{
FIXME(" LVS_NOSCROLL\n");
}
if ((LVS_TYPEMASK & lStyle) == LVS_NOSORTHEADER)
{
FIXME(" LVS_NOSORTHEADER\n");
}
if ((LVS_TYPEMASK & lStyle) == LVS_OWNERDRAWFIXED)
{
FIXME(" LVS_OWNERDRAWFIXED\n");
}
if ((LVS_TYPEMASK & lStyle) == LVS_SHAREIMAGELISTS)
{
FIXME(" LVS_SHAREIMAGELISTS\n");
}
if ((LVS_TYPEMASK & lStyle) == LVS_SORTASCENDING)
{
FIXME(" LVS_SORTASCENDING\n");
}
if ((LVS_TYPEMASK & lStyle) == LVS_SORTDESCENDING)
{
FIXME(" LVS_SORTDESCENDING\n");
}
}
/***
* DESCRIPTION:
* Aligns the items with the top edge of the window.
*
* PARAMETER(S):
* [I] HWND : window handle
*
* RETURN:
* None
*/
static VOID LISTVIEW_AlignTop(HWND hwnd)
{
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
POINT ptItem;
RECT rcView;
INT i;
if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
{
ZeroMemory(&ptItem, sizeof(POINT));
ZeroMemory(&rcView, sizeof(RECT));
if (nListWidth > infoPtr->nItemWidth)
{
for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
{
if (ptItem.x + infoPtr->nItemWidth > nListWidth)
{
ptItem.x = 0;
ptItem.y += infoPtr->nItemHeight;
}
ListView_SetItemPosition(hwnd, i, ptItem.x, ptItem.y);
ptItem.x += infoPtr->nItemWidth;
rcView.right = max(rcView.right, ptItem.x);
}
rcView.bottom = ptItem.y + infoPtr->nItemHeight;
}
else
{
for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
{
ListView_SetItemPosition(hwnd, i, ptItem.x, ptItem.y);
ptItem.y += infoPtr->nItemHeight;
}
rcView.right = infoPtr->nItemWidth;
rcView.bottom = ptItem.y;
}
LISTVIEW_SetViewRect(hwnd, &rcView);
}
}
/***
* DESCRIPTION:
* Aligns the items with the left edge of the window.
*
* PARAMETER(S):
* [I] HWND : window handle
*
* RETURN:
* None
*/
static VOID LISTVIEW_AlignLeft(HWND hwnd)
{
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
POINT ptItem;
RECT rcView;
INT i;
if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
{
ZeroMemory(&ptItem, sizeof(POINT));
ZeroMemory(&rcView, sizeof(RECT));
if (nListHeight > infoPtr->nItemHeight)
{
for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
{
if (ptItem.y + infoPtr->nItemHeight > nListHeight)
{
ptItem.y = 0;
ptItem.x += infoPtr->nItemWidth;
}
ListView_SetItemPosition(hwnd, i, ptItem.x, ptItem.y);
ptItem.y += infoPtr->nItemHeight;
rcView.bottom = max(rcView.bottom, ptItem.y);
}
rcView.right = ptItem.x + infoPtr->nItemWidth;
}
else
{
for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
{
ListView_SetItemPosition(hwnd, i, ptItem.x, ptItem.y);
ptItem.x += infoPtr->nItemWidth;
}
rcView.bottom = infoPtr->nItemHeight;
rcView.right = ptItem.x;
}
LISTVIEW_SetViewRect(hwnd, &rcView);
}
}
/***
* DESCRIPTION:
* Set the bounding rectangle of all the items.
*
* PARAMETER(S):
* [I] HWND : window handle
* [I] LPRECT : bounding rectangle
*
* RETURN:
* SUCCESS : TRUE
* FAILURE : FALSE
*/
static LRESULT LISTVIEW_SetViewRect(HWND hwnd, LPRECT lprcView)
{
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongA(hwnd, 0);
BOOL bResult = FALSE;
TRACE("(hwnd=%x, left=%d, top=%d, right=%d, bottom=%d)\n", hwnd,
lprcView->left, lprcView->top, lprcView->right, lprcView->bottom);
if (lprcView != NULL)
{
bResult = TRUE;
infoPtr->rcView.left = lprcView->left;
infoPtr->rcView.top = lprcView->top;
infoPtr->rcView.right = lprcView->right;
infoPtr->rcView.bottom = lprcView->bottom;
}
return bResult;
}
/***
* DESCRIPTION:
* Retrieves the bounding rectangle of all the items.
*
* PARAMETER(S):
* [I] HWND : window handle
* [O] LPRECT : bounding rectangle
*
* RETURN:
* SUCCESS : TRUE
* FAILURE : FALSE
*/
static LRESULT LISTVIEW_GetViewRect(HWND hwnd, LPRECT lprcView)
{
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongA(hwnd, 0);
BOOL bResult = FALSE;
POINT ptOrigin;
TRACE("(hwnd=%x, lprcView=%p)\n", hwnd, lprcView);
if (lprcView != NULL)
{
bResult = LISTVIEW_GetOrigin(hwnd, &ptOrigin);
if (bResult != FALSE)
{
lprcView->left = infoPtr->rcView.left + ptOrigin.x;
lprcView->top = infoPtr->rcView.top + ptOrigin.y;
lprcView->right = infoPtr->rcView.right + ptOrigin.x;
lprcView->bottom = infoPtr->rcView.bottom + ptOrigin.y;
}
TRACE("(left=%d, top=%d, right=%d, bottom=%d)\n",
lprcView->left, lprcView->top, lprcView->right, lprcView->bottom);
}
return bResult;
}
/***
* DESCRIPTION:
* Retrieves the subitem pointer associated with the subitem index.
*
* PARAMETER(S):
* [I] HDPA : DPA handle for a specific item
* [I] INT : index of subitem
*
* RETURN:
* SUCCESS : subitem pointer
* FAILURE : NULL
*/
static LISTVIEW_SUBITEM* LISTVIEW_GetSubItemPtr(HDPA hdpaSubItems,
INT nSubItem)
{
LISTVIEW_SUBITEM *lpSubItem;
INT i;
for (i = 1; i < hdpaSubItems->nItemCount; i++)
{
lpSubItem = (LISTVIEW_SUBITEM *) DPA_GetPtr(hdpaSubItems, i);
if (lpSubItem != NULL)
{
if (lpSubItem->iSubItem == nSubItem)
{
return lpSubItem;
}
}
}
return NULL;
}
/***
* DESCRIPTION:
* Calculates the width of an item.
*
* PARAMETER(S):
* [I] HWND : window handle
* [I] LONG : window style
*
* RETURN:
* Returns item width.
*/
static INT LISTVIEW_GetItemWidth(HWND hwnd)
{
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
LONG style = GetWindowLongA(hwnd, GWL_STYLE);
UINT uView = style & LVS_TYPEMASK;
INT nHeaderItemCount;
RECT rcHeaderItem;
INT nItemWidth = 0;
INT nLabelWidth;
INT i;
TRACE("(hwnd=%x)\n", hwnd);
if (uView == LVS_ICON)
{
nItemWidth = infoPtr->iconSpacing.cx;
}
else if (uView == LVS_REPORT)
{
/* calculate width of header */
nHeaderItemCount = Header_GetItemCount(infoPtr->hwndHeader);
for (i = 0; i < nHeaderItemCount; i++)
{
if (Header_GetItemRect(infoPtr->hwndHeader, i, &rcHeaderItem) != 0)
{
nItemWidth += (rcHeaderItem.right - rcHeaderItem.left);
}
}
}
else
{
for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
{
nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, i);
nItemWidth = max(nItemWidth, nLabelWidth);
}
/* default label size */
if (GETITEMCOUNT(infoPtr) == 0)
{
nItemWidth = DEFAULT_COLUMN_WIDTH;
}
else
{
if (nItemWidth == 0)
{
nItemWidth = DEFAULT_LABEL_WIDTH;
}
else
{
/* add padding */
nItemWidth += WIDTH_PADDING;
if (infoPtr->himlSmall != NULL)
{
nItemWidth += infoPtr->iconSize.cx;
}
if (infoPtr->himlState != NULL)
{
nItemWidth += infoPtr->iconSize.cx;
}
}
}
}
if(nItemWidth == 0)
{
/* nItemWidth Cannot be Zero */
nItemWidth = 1;
}
return nItemWidth;
}
/***
* DESCRIPTION:
* Calculates the width of a specific item.
*
* PARAMETER(S):
* [I] HWND : window handle
* [I] LPSTR : string
*
* RETURN:
* Returns the width of an item width a specified string.
*/
static INT LISTVIEW_CalculateWidth(HWND hwnd, INT nItem)
{
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
INT nHeaderItemCount;
RECT rcHeaderItem;
INT nItemWidth = 0;
INT i;
TRACE("(hwnd=%x)\n", hwnd);
if (uView == LVS_ICON)
{
nItemWidth = infoPtr->iconSpacing.cx;
}
else if (uView == LVS_REPORT)
{
/* calculate width of header */
nHeaderItemCount = Header_GetItemCount(infoPtr->hwndHeader);
for (i = 0; i < nHeaderItemCount; i++)
{
if (Header_GetItemRect(infoPtr->hwndHeader, i, &rcHeaderItem) != 0)
{
nItemWidth += (rcHeaderItem.right - rcHeaderItem.left);
}
}
}
else
{
/* get width of string */
nItemWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
/* default label size */
if (GETITEMCOUNT(infoPtr) == 0)
{
nItemWidth = DEFAULT_COLUMN_WIDTH;
}
else
{
if (nItemWidth == 0)
{
nItemWidth = DEFAULT_LABEL_WIDTH;
}
else
{
/* add padding */
nItemWidth += WIDTH_PADDING;
if (infoPtr->himlSmall != NULL)
{
nItemWidth += infoPtr->iconSize.cx;
}
if (infoPtr->himlState != NULL)
{
nItemWidth += infoPtr->iconSize.cx;
}
}
}
}
return nItemWidth;
}
/***
* DESCRIPTION:
* Calculates the height of an item.
*
* PARAMETER(S):
* [I] HWND : window handle
* [I] LONG : window style
*
* RETURN:
* Returns item height.
*/
static INT LISTVIEW_GetItemHeight(HWND hwnd)
{
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
INT nItemHeight = 0;
if (uView == LVS_ICON)
{
nItemHeight = infoPtr->iconSpacing.cy;
}
else
{
TEXTMETRICA tm;
HDC hdc = GetDC(hwnd);
HFONT hOldFont = SelectObject(hdc, infoPtr->hFont);
GetTextMetricsA(hdc, &tm);
if(infoPtr->himlState || infoPtr->himlSmall)
nItemHeight = max(tm.tmHeight, infoPtr->iconSize.cy) + HEIGHT_PADDING;
else
nItemHeight = tm.tmHeight;
SelectObject(hdc, hOldFont);
ReleaseDC(hwnd, hdc);
}
return nItemHeight;
}
static void LISTVIEW_PrintSelectionRanges(HWND hwnd)
{
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
LISTVIEW_SELECTION *selection;
INT topSelection = infoPtr->hdpaSelectionRanges->nItemCount;
INT i;
TRACE("Selections are:\n");
for (i = 0; i < topSelection; i++)
{
selection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,i);
TRACE(" %lu - %lu\n",selection->lower,selection->upper);
}
}
/***
* DESCRIPTION:
* A compare function for selection ranges
*
*PARAMETER(S)
* [I] LPVOID : Item 1;
* [I] LPVOID : Item 2;
* [I] LPARAM : flags
*
*RETURNS:
* >0 : if Item 1 > Item 2
* <0 : if Item 2 > Item 1
* 0 : if Item 1 == Item 2
*/
static INT CALLBACK LISTVIEW_CompareSelectionRanges(LPVOID range1, LPVOID range2,
LPARAM flags)
{
int l1 = ((LISTVIEW_SELECTION*)(range1))->lower;
int l2 = ((LISTVIEW_SELECTION*)(range2))->lower;
int u1 = ((LISTVIEW_SELECTION*)(range1))->upper;
int u2 = ((LISTVIEW_SELECTION*)(range2))->upper;
int rc=0;
if (u1 < l2)
rc= -1;
if (u2 < l1)
rc= 1;
return rc;
}
/**
* DESCRIPTION:
* Adds a selection range.
*
* PARAMETER(S):
* [I] HWND : window handle
* [I] INT : lower item index
* [I] INT : upper item index
*
* RETURN:
* None
*/
static VOID LISTVIEW_AddSelectionRange(HWND hwnd, INT lItem, INT uItem)
{
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
LISTVIEW_SELECTION *selection;
INT topSelection = infoPtr->hdpaSelectionRanges->nItemCount;
BOOL lowerzero=FALSE;
selection = (LISTVIEW_SELECTION *)COMCTL32_Alloc(sizeof(LISTVIEW_SELECTION));
selection->lower = lItem;
selection->upper = uItem;
TRACE("Add range %i - %i\n",lItem,uItem);
if (topSelection)
{
LISTVIEW_SELECTION *checkselection,*checkselection2;
INT index,mergeindex;
/* find overlapping selections */
/* we want to catch adjacent ranges so expand our range by 1 */
selection->upper++;
if (selection->lower == 0)
lowerzero = TRUE;
else
selection->lower--;
index = DPA_Search(infoPtr->hdpaSelectionRanges, selection, 0,
LISTVIEW_CompareSelectionRanges,
0,0);
selection->upper --;
if (lowerzero)
lowerzero=FALSE;
else
selection->lower ++;
if (index >=0)
{
checkselection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,index);
TRACE("Merge with index %i (%lu - %lu)\n",index,checkselection->lower,
checkselection->upper);
checkselection->lower = min(selection->lower,checkselection->lower);
checkselection->upper = max(selection->upper,checkselection->upper);
TRACE("New range (%lu - %lu)\n", checkselection->lower,
checkselection->upper);
COMCTL32_Free(selection);
/* merge now common selection ranges in the lower group*/
do
{
checkselection->upper ++;
if (checkselection->lower == 0)
lowerzero = TRUE;
else
checkselection->lower --;
TRACE("search lower range (%lu - %lu)\n", checkselection->lower,
checkselection->upper);
/* not sorted yet */
mergeindex = DPA_Search(infoPtr->hdpaSelectionRanges, checkselection, 0,
LISTVIEW_CompareSelectionRanges, 0,
0);
checkselection->upper --;
if (lowerzero)
lowerzero = FALSE;
else
checkselection->lower ++;
if (mergeindex >=0 && mergeindex != index)
{
TRACE("Merge with index %i\n",mergeindex);
checkselection2 = DPA_GetPtr(infoPtr->hdpaSelectionRanges,
mergeindex);
checkselection->lower = min(checkselection->lower,
checkselection2->lower);
checkselection->upper = max(checkselection->upper,
checkselection2->upper);
COMCTL32_Free(checkselection2);
DPA_DeletePtr(infoPtr->hdpaSelectionRanges,mergeindex);
index --;
}
}
while (mergeindex > -1 && mergeindex <index);
/* merge now common selection ranges in the upper group*/
do
{
checkselection->upper ++;
if (checkselection->lower == 0)
lowerzero = TRUE;
else
checkselection->lower --;
TRACE("search upper range %i (%lu - %lu)\n",index,
checkselection->lower, checkselection->upper);
/* not sorted yet */
mergeindex = DPA_Search(infoPtr->hdpaSelectionRanges, checkselection,
index+1,
LISTVIEW_CompareSelectionRanges, 0,
0);
checkselection->upper --;
if (lowerzero)
lowerzero = FALSE;
else
checkselection->lower ++;
if (mergeindex >=0 && mergeindex !=index)
{
TRACE("Merge with index %i\n",mergeindex);
checkselection2 = DPA_GetPtr(infoPtr->hdpaSelectionRanges,
mergeindex);
checkselection->lower = min(checkselection->lower,
checkselection2->lower);
checkselection->upper = max(checkselection->upper,
checkselection2->upper);
COMCTL32_Free(checkselection2);
DPA_DeletePtr(infoPtr->hdpaSelectionRanges,mergeindex);
}
}
while (mergeindex > -1);
}
else
{
index = DPA_Search(infoPtr->hdpaSelectionRanges, selection, 0,
LISTVIEW_CompareSelectionRanges, 0,
DPAS_INSERTAFTER);
TRACE("Insert before index %i\n",index);
if (index == -1)
index = 0;
DPA_InsertPtr(infoPtr->hdpaSelectionRanges,index,selection);
}
}
else
{
DPA_InsertPtr(infoPtr->hdpaSelectionRanges,0,selection);
}
/*
* Incase of error
*/
DPA_Sort(infoPtr->hdpaSelectionRanges,LISTVIEW_CompareSelectionRanges,0);
LISTVIEW_PrintSelectionRanges(hwnd);
}
/**
* DESCRIPTION:
* check if a specified index is selected.
*
* PARAMETER(S):
* [I] HWND : window handle
* [I] INT : item index
*
* RETURN:
* None
*/
static BOOL LISTVIEW_IsSelected(HWND hwnd, INT nItem)
{
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
LISTVIEW_SELECTION selection;
INT index;
selection.upper = nItem;
selection.lower = nItem;
index = DPA_Search(infoPtr->hdpaSelectionRanges, &selection, 0,
LISTVIEW_CompareSelectionRanges,
0,DPAS_SORTED);
if (index != -1)
return TRUE;
else
return FALSE;
}
/***
* DESCRIPTION:
* Removes all selection ranges
*
* Parameters(s):
* HWND: window handle
*
* RETURNS:
* SUCCESS : TRUE
* FAILURE : TRUE
*/
static LRESULT LISTVIEW_RemoveAllSelections(HWND hwnd)
{
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
LISTVIEW_SELECTION *selection;
INT i;
LVITEMA item;
TRACE("(0x%x)\n",hwnd);
ZeroMemory(&item,sizeof(LVITEMA));
item.stateMask = LVIS_SELECTED;
do
{
selection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,0);
if (selection)
{
TRACE("Removing %lu to %lu\n",selection->lower, selection->upper);
for (i = selection->lower; i<=selection->upper; i++)
LISTVIEW_SetItemState(hwnd,i,&item);
LISTVIEW_RemoveSelectionRange(hwnd,selection->lower,selection->upper);
}
}
while (infoPtr->hdpaSelectionRanges->nItemCount>0);
TRACE("done\n");
return TRUE;
}
/**
* DESCRIPTION:
* Removes a range selections.
*
* PARAMETER(S):
* [I] HWND : window handle
* [I] INT : lower item index
* [I] INT : upper item index
*
* RETURN:
* None
*/
static VOID LISTVIEW_RemoveSelectionRange(HWND hwnd, INT lItem, INT uItem)
{
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
LISTVIEW_SELECTION removeselection,*checkselection;
INT index;
removeselection.lower = lItem;
removeselection.upper = uItem;
TRACE("Remove range %lu - %lu\n",removeselection.lower,removeselection.upper);
LISTVIEW_PrintSelectionRanges(hwnd);
index = DPA_Search(infoPtr->hdpaSelectionRanges, &removeselection, 0,
LISTVIEW_CompareSelectionRanges,
0,0);
if (index == -1)
return;
checkselection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,
index);
TRACE("Matches range index %i (%lu-%lu)\n",index,checkselection->lower,
checkselection->upper);
/* case 1: Same */
if ((checkselection->upper == removeselection.upper) &&
(checkselection->lower == removeselection.lower))
{
DPA_DeletePtr(infoPtr->hdpaSelectionRanges,index);
TRACE("Case 1\n");
}
/* case 2: engulf */
else if (((checkselection->upper < removeselection.upper) &&
(checkselection->lower > removeselection.lower))||
((checkselection->upper <= removeselection.upper) &&
(checkselection->lower > removeselection.lower)) ||
((checkselection->upper < removeselection.upper) &&
(checkselection->lower >= removeselection.lower)))
{
DPA_DeletePtr(infoPtr->hdpaSelectionRanges,index);
/* do it again because others may also get caught */
TRACE("Case 2\n");
LISTVIEW_RemoveSelectionRange(hwnd,lItem,uItem);
}
/* case 3: overlap upper */
else if ((checkselection->upper < removeselection.upper) &&
(checkselection->lower < removeselection.lower))
{
checkselection->upper = removeselection.lower - 1;
TRACE("Case 3\n");
LISTVIEW_RemoveSelectionRange(hwnd,lItem,uItem);
}
/* case 4: overlap lower */
else if ((checkselection->upper > removeselection.upper) &&
(checkselection->lower > removeselection.lower))
{
checkselection->lower = removeselection.upper + 1;
TRACE("Case 4\n");
LISTVIEW_RemoveSelectionRange(hwnd,lItem,uItem);
}
/* case 5: fully internal */
else if (checkselection->upper == removeselection.upper)
checkselection->upper = removeselection.lower - 1;
else if (checkselection->lower == removeselection.lower)
checkselection->lower = removeselection.upper + 1;
else
{
/* bisect the range */
LISTVIEW_SELECTION *newselection;
newselection = (LISTVIEW_SELECTION *)
COMCTL32_Alloc(sizeof(LISTVIEW_SELECTION));
newselection -> lower = checkselection->lower;
newselection -> upper = removeselection.lower - 1;
checkselection -> lower = removeselection.upper + 1;
DPA_InsertPtr(infoPtr->hdpaSelectionRanges,index,newselection);
TRACE("Case 5\n");
DPA_Sort(infoPtr->hdpaSelectionRanges,LISTVIEW_CompareSelectionRanges,0);
}
LISTVIEW_PrintSelectionRanges(hwnd);
}
/**
* DESCRIPTION:
* Updates the various indices after an item has been inserted or deleted.
*
* PARAMETER(S):
* [I] HWND : window handle
* [I] INT : item index
* [I] INT : Direction of shift, +1 or -1.
*
* RETURN:
* None
*/
static VOID LISTVIEW_ShiftIndices(HWND hwnd, INT nItem, INT direction)
{
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
LISTVIEW_SELECTION selection,*checkselection;
INT index;
TRACE("Shifting %iu, %i steps\n",nItem,direction);
selection.upper = nItem;
selection.lower = nItem;
index = DPA_Search(infoPtr->hdpaSelectionRanges, &selection, 0,
LISTVIEW_CompareSelectionRanges,
0,DPAS_SORTED|DPAS_INSERTAFTER);
while ((index < infoPtr->hdpaSelectionRanges->nItemCount)&&(index != -1))
{
checkselection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,index);
if ((checkselection->lower >= nItem)&&
(checkselection->lower + direction >= 0))
checkselection->lower += direction;
if ((checkselection->upper >= nItem)&&
(checkselection->upper + direction >=0))
checkselection->upper += direction;
index ++;
}
/* Note that the following will fail if direction != +1 and -1 */
if (infoPtr->nSelectionMark > nItem)
infoPtr->nSelectionMark += direction;
else if (infoPtr->nSelectionMark == nItem)
{
if (direction > 0)
infoPtr->nSelectionMark += direction;
else if (infoPtr->nSelectionMark >= GETITEMCOUNT(infoPtr))
infoPtr->nSelectionMark = GETITEMCOUNT(infoPtr) - 1;
}
if (infoPtr->nFocusedItem > nItem)
infoPtr->nFocusedItem += direction;
else if (infoPtr->nFocusedItem == nItem)
{
if (direction > 0)
infoPtr->nFocusedItem += direction;
else
{
if (infoPtr->nFocusedItem >= GETITEMCOUNT(infoPtr))
infoPtr->nFocusedItem = GETITEMCOUNT(infoPtr) - 1;
if (infoPtr->nFocusedItem >= 0)
LISTVIEW_SetItemFocus(hwnd, infoPtr->nFocusedItem);
}
}
/* But we are not supposed to modify nHotItem! */
}
/**
* DESCRIPTION:
* Adds a block of selections.
*
* PARAMETER(S):
* [I] HWND : window handle
* [I] INT : item index
*
* RETURN:
* None
*/
static VOID LISTVIEW_AddGroupSelection(HWND hwnd, INT nItem)
{
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
INT nFirst = min(infoPtr->nSelectionMark, nItem);
INT nLast = max(infoPtr->nSelectionMark, nItem);
INT i;
LVITEMA item;
if (nFirst == -1)
nFirst = nItem;
ZeroMemory(&item,sizeof(LVITEMA));
item.stateMask = LVIS_SELECTED;
item.state = LVIS_SELECTED;
for (i = nFirst; i <= nLast; i++)
{
LISTVIEW_SetItemState(hwnd,i,&item);
}
LISTVIEW_SetItemFocus(hwnd, nItem);
infoPtr->nSelectionMark = nItem;
}
/***
* DESCRIPTION:
* Adds a single selection.
*
* PARAMETER(S):
* [I] HWND : window handle
* [I] INT : item index
*
* RETURN:
* None
*/
static VOID LISTVIEW_AddSelection(HWND hwnd, INT nItem)
{
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
LVITEMA item;
ZeroMemory(&item,sizeof(LVITEMA));
item.state = LVIS_SELECTED;
item.stateMask = LVIS_SELECTED;
LISTVIEW_SetItemState(hwnd,nItem,&item);
LISTVIEW_SetItemFocus(hwnd, nItem);
infoPtr->nSelectionMark = nItem;
}
/***
* DESCRIPTION:
* Selects or unselects an item.
*
* PARAMETER(S):
* [I] HWND : window handle
* [I] INT : item index
*
* RETURN:
* SELECT: TRUE
* UNSELECT : FALSE
*/
static BOOL LISTVIEW_ToggleSelection(HWND hwnd, INT nItem)
{
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
BOOL bResult;
LVITEMA item;
ZeroMemory(&item,sizeof(LVITEMA));
item.stateMask = LVIS_SELECTED;
if (LISTVIEW_IsSelected(hwnd,nItem))
{
LISTVIEW_SetItemState(hwnd,nItem,&item);
bResult = FALSE;
}
else
{
item.state = LVIS_SELECTED;
LISTVIEW_SetItemState(hwnd,nItem,&item);
bResult = TRUE;
}
LISTVIEW_SetItemFocus(hwnd, nItem);
infoPtr->nSelectionMark = nItem;
return bResult;
}
/***
* DESCRIPTION:
* Selects items based on view coordinates.
*
* PARAMETER(S):
* [I] HWND : window handle
* [I] RECT : selection rectangle
*
* RETURN:
* None
*/
static VOID LISTVIEW_SetSelectionRect(HWND hwnd, RECT rcSelRect)
{
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
POINT ptItem;
INT i;
LVITEMA item;
ZeroMemory(&item,sizeof(LVITEMA));
item.stateMask = LVIS_SELECTED;
for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
{
LISTVIEW_GetItemPosition(hwnd, i, &ptItem);
if (PtInRect(&rcSelRect, ptItem) != FALSE)
{
item.state = LVIS_SELECTED;
LISTVIEW_SetItemState(hwnd,i,&item);
}
else
{
item.state = 0;
LISTVIEW_SetItemState(hwnd,i,&item);
}
}
}
/***
* DESCRIPTION:
* Sets a single group selection.
*
* PARAMETER(S):
* [I] HWND : window handle
* [I] INT : item index
*
* RETURN:
* None
*/
static VOID LISTVIEW_SetGroupSelection(HWND hwnd, INT nItem)
{
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
LVITEMA item;
ZeroMemory(&item,sizeof(LVITEMA));
item.stateMask = LVIS_SELECTED;
if ((uView == LVS_LIST) || (uView == LVS_REPORT))
{
INT i;
INT nFirst, nLast;
if (infoPtr->nSelectionMark == -1)
{
infoPtr->nSelectionMark = nFirst = nLast = nItem;
}
else
{
nFirst = min(infoPtr->nSelectionMark, nItem);
nLast = max(infoPtr->nSelectionMark, nItem);
}
for (i = 0; i <= GETITEMCOUNT(infoPtr); i++)
{
if ((i < nFirst) || (i > nLast))
{
item.state = 0;
LISTVIEW_SetItemState(hwnd,i,&item);
}
else
{
item.state = LVIS_SELECTED;
LISTVIEW_SetItemState(hwnd,i,&item);
}
}
}
else
{
POINT ptItem;
POINT ptSelMark;
RECT rcSel;
LISTVIEW_GetItemPosition(hwnd, nItem, &ptItem);
LISTVIEW_GetItemPosition(hwnd, infoPtr->nSelectionMark, &ptSelMark);
rcSel.left = min(ptSelMark.x, ptItem.x);
rcSel.top = min(ptSelMark.y, ptItem.y);
rcSel.right = max(ptSelMark.x, ptItem.x) + infoPtr->nItemWidth;
rcSel.bottom = max(ptSelMark.y, ptItem.y) + infoPtr->nItemHeight;
LISTVIEW_SetSelectionRect(hwnd, rcSel);
}
LISTVIEW_SetItemFocus(hwnd, nItem);
}
/***
* DESCRIPTION:
* Manages the item focus.
*
* PARAMETER(S):
* [I] HWND : window handle
* [I] INT : item index
*
* RETURN:
* TRUE : focused item changed
* FALSE : focused item has NOT changed
*/
static BOOL LISTVIEW_SetItemFocus(HWND hwnd, INT nItem)
{
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
BOOL bResult = FALSE;
LVITEMA lvItem;
if (infoPtr->nFocusedItem != nItem)
{
if (infoPtr->nFocusedItem >= 0)
{
INT oldFocus = infoPtr->nFocusedItem;
bResult = TRUE;
infoPtr->nFocusedItem = -1;
ZeroMemory(&lvItem, sizeof(LVITEMA));
lvItem.stateMask = LVIS_FOCUSED;
ListView_SetItemState(hwnd, oldFocus, &lvItem);
}
lvItem.state = LVIS_FOCUSED;
lvItem.stateMask = LVIS_FOCUSED;
ListView_SetItemState(hwnd, nItem, &lvItem);
infoPtr->nFocusedItem = nItem;
ListView_EnsureVisible(hwnd, nItem, FALSE);
}
return bResult;
}
/***
* DESCRIPTION:
* Sets a single selection.
*
* PARAMETER(S):
* [I] HWND : window handle
* [I] INT : item index
*
* RETURN:
* None
*/
static VOID LISTVIEW_SetSelection(HWND hwnd, INT nItem)
{
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
LVITEMA lvItem;
ZeroMemory(&lvItem, sizeof(LVITEMA));
lvItem.stateMask = LVIS_FOCUSED;
ListView_SetItemState(hwnd, infoPtr->nFocusedItem, &lvItem);
LISTVIEW_RemoveAllSelections(hwnd);
lvItem.state = LVIS_FOCUSED|LVIS_SELECTED;
lvItem.stateMask = LVIS_FOCUSED|LVIS_SELECTED;
ListView_SetItemState(hwnd, nItem, &lvItem);
infoPtr->nFocusedItem = nItem;
infoPtr->nSelectionMark = nItem;
}
/***
* DESCRIPTION:
* Set selection(s) with keyboard.
*
* PARAMETER(S):
* [I] HWND : window handle
* [I] INT : item index
*
* RETURN:
* SUCCESS : TRUE (needs to be repainted)
* FAILURE : FALSE (nothing has changed)
*/
static BOOL LISTVIEW_KeySelection(HWND hwnd, INT nItem)
{
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
WORD wShift = HIWORD(GetKeyState(VK_SHIFT));
WORD wCtrl = HIWORD(GetKeyState(VK_CONTROL));
BOOL bResult = FALSE;
if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
{
if (lStyle & LVS_SINGLESEL)
{
bResult = TRUE;
LISTVIEW_SetSelection(hwnd, nItem);
ListView_EnsureVisible(hwnd, nItem, FALSE);
}
else
{
if (wShift)
{
bResult = TRUE;
LISTVIEW_SetGroupSelection(hwnd, nItem);
}
else if (wCtrl)
{
bResult = LISTVIEW_SetItemFocus(hwnd, nItem);
}
else
{
bResult = TRUE;
LISTVIEW_SetSelection(hwnd, nItem);
ListView_EnsureVisible(hwnd, nItem, FALSE);
}
}
}
return bResult;
}
/***
* DESCRIPTION:
* Called when the mouse is being actively tracked and has hovered for a specified
* amount of time
*
* PARAMETER(S):
* [I] HWND : window handle
* [I] wParam : key indicator
* [I] lParam : mouse position
*
* RETURN:
* 0 if the message was processed, non-zero if there was an error
*
* INFO:
* LVS_EX_TRACKSELECT: An item is automatically selected when the cursor remains
* over the item for a certain period of time.
*
*/
static LRESULT LISTVIEW_MouseHover(HWND hwnd, WPARAM wParam, LPARAM lParam)
{
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
POINT pt;
pt.x = (INT)LOWORD(lParam);
pt.y = (INT)HIWORD(lParam);
if(infoPtr->dwExStyle & LVS_EX_TRACKSELECT) {
/* select the item under the cursor */
LISTVIEW_MouseSelection(hwnd, pt);
}
return 0;
}
/***
* DESCRIPTION:
* Called whenever WM_MOUSEMOVE is received.
*
* PARAMETER(S):
* [I] HWND : window handle
* [I] wParam : key indicators
* [I] lParam : cursor position
*
* RETURN:
* 0 if the message is processed, non-zero if there was an error
*/
static LRESULT LISTVIEW_MouseMove(HWND hwnd, WPARAM wParam, LPARAM lParam)
{
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
TRACKMOUSEEVENT trackinfo;
/* see if we are supposed to be tracking mouse hovering */
if(infoPtr->dwExStyle & LVS_EX_TRACKSELECT) {
/* fill in the trackinfo struct */
trackinfo.cbSize = sizeof(TRACKMOUSEEVENT);
trackinfo.dwFlags = TME_QUERY;
trackinfo.hwndTrack = hwnd;
trackinfo.dwHoverTime = infoPtr->dwHoverTime;
/* see if we are already tracking this hwnd */
_TrackMouseEvent(&trackinfo);
if(!(trackinfo.dwFlags & TME_HOVER)) {
trackinfo.dwFlags = TME_HOVER;
/* call TRACKMOUSEEVENT so we receive WM_MOUSEHOVER messages */
_TrackMouseEvent(&trackinfo);
}
}
return 0;
}
/***
* DESCRIPTION:
* Selects an item based on coordinates.
*
* PARAMETER(S):
* [I] HWND : window handle
* [I] POINT : mouse click ccordinates
*
* RETURN:
* SUCCESS : item index
* FAILURE : -1
*/
static LRESULT LISTVIEW_MouseSelection(HWND hwnd, POINT pt)
{
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
RECT rcItem;
INT i,topindex,bottomindex;
LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
UINT uView = lStyle & LVS_TYPEMASK;
topindex = ListView_GetTopIndex(hwnd);
if (uView == LVS_REPORT)
{
bottomindex = topindex + LISTVIEW_GetCountPerColumn(hwnd) + 1;
bottomindex = min(bottomindex,GETITEMCOUNT(infoPtr));
}
else
{
bottomindex = GETITEMCOUNT(infoPtr);
}
for (i = topindex; i < bottomindex; i++)
{
rcItem.left = LVIR_SELECTBOUNDS;
if (LISTVIEW_GetItemRect(hwnd, i, &rcItem) == TRUE)
{
if (PtInRect(&rcItem, pt) != FALSE)
{
return i;
}
}
}
return -1;
}
/***
* DESCRIPTION:
* Removes a column.
*
* PARAMETER(S):
* [IO] HDPA : dynamic pointer array handle
* [I] INT : column index (subitem index)
*
* RETURN:
* SUCCCESS : TRUE
* FAILURE : FALSE
*/
static BOOL LISTVIEW_RemoveColumn(HDPA hdpaItems, INT nSubItem)
{
BOOL bResult = TRUE;
HDPA hdpaSubItems;
INT i;
for (i = 0; i < hdpaItems->nItemCount; i++)
{
hdpaSubItems = (HDPA)DPA_GetPtr(hdpaItems, i);
if (hdpaSubItems != NULL)
{
if (LISTVIEW_RemoveSubItem(hdpaSubItems, nSubItem) == FALSE)
{
bResult = FALSE;
}
}
}
return bResult;
}
/***
* DESCRIPTION:
* Removes a subitem at a given position.
*
* PARAMETER(S):
* [IO] HDPA : dynamic pointer array handle
* [I] INT : subitem index
*
* RETURN:
* SUCCCESS : TRUE
* FAILURE : FALSE
*/
static BOOL LISTVIEW_RemoveSubItem(HDPA hdpaSubItems, INT nSubItem)
{
LISTVIEW_SUBITEM *lpSubItem;
INT i;
for (i = 1; i < hdpaSubItems->nItemCount; i++)
{
lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
if (lpSubItem != NULL)
{
if (lpSubItem->iSubItem == nSubItem)
{
/* free string */
if ((lpSubItem->pszText != NULL) &&
(lpSubItem->pszText != LPSTR_TEXTCALLBACKA))
{
COMCTL32_Free(lpSubItem->pszText);
}
/* free item */
COMCTL32_Free(lpSubItem);
/* free dpa memory */
if (DPA_DeletePtr(hdpaSubItems, i) == NULL)
{
return FALSE;
}
}
else if (lpSubItem->iSubItem > nSubItem)
{
return TRUE;
}
}
}
return TRUE;
}
/***
* DESCRIPTION:
* Compares the item information.
*
* PARAMETER(S):
* [I] LISTVIEW_ITEM *: destination item
* [I] LPLVITEM : source item
*
* RETURN:
* SUCCCESS : TRUE (EQUAL)
* FAILURE : FALSE (NOT EQUAL)
*/
static UINT LISTVIEW_GetItemChanges(LISTVIEW_ITEM *lpItem, LPLVITEMA lpLVItem)
{
UINT uChanged = 0;
if ((lpItem != NULL) && (lpLVItem != NULL))
{
if (lpLVItem->mask & LVIF_STATE)
{
if ((lpItem->state & lpLVItem->stateMask) !=
(lpLVItem->state & lpLVItem->stateMask))
{
uChanged |= LVIF_STATE;
}
}
if (lpLVItem->mask & LVIF_IMAGE)
{
if (lpItem->iImage != lpLVItem->iImage)
{
uChanged |= LVIF_IMAGE;
}
}
if (lpLVItem->mask & LVIF_PARAM)
{
if (lpItem->lParam != lpLVItem->lParam)
{
uChanged |= LVIF_PARAM;
}
}
if (lpLVItem->mask & LVIF_INDENT)
{
if (lpItem->iIndent != lpLVItem->iIndent)
{
uChanged |= LVIF_INDENT;
}
}
if (lpLVItem->mask & LVIF_TEXT)
{
if (lpLVItem->pszText == LPSTR_TEXTCALLBACKA)
{
if (lpItem->pszText != LPSTR_TEXTCALLBACKA)
{
uChanged |= LVIF_TEXT;
}
}
else
{
if (lpItem->pszText == LPSTR_TEXTCALLBACKA)
{
uChanged |= LVIF_TEXT;
}
else
{
if (lpLVItem->pszText)
{
if (lpItem->pszText)
{
if (strcmp(lpLVItem->pszText, lpItem->pszText) != 0)
{
uChanged |= LVIF_TEXT;
}
}
else
{
uChanged |= LVIF_TEXT;
}
}
else
{
if (lpItem->pszText)
{
uChanged |= LVIF_TEXT;
}
}
}
}
}
}
return uChanged;
}
/***
* DESCRIPTION:
* Initializes item attributes.
*
* PARAMETER(S):
* [I] HWND : window handle
* [O] LISTVIEW_ITEM *: destination item
* [I] LPLVITEM : source item
*
* RETURN:
* SUCCCESS : TRUE
* FAILURE : FALSE
*/
static BOOL LISTVIEW_InitItem(HWND hwnd, LISTVIEW_ITEM *lpItem,
LPLVITEMA lpLVItem)
{
LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
BOOL bResult = FALSE;
if ((lpItem != NULL) && (lpLVItem != NULL))
{
bResult = TRUE;
if (lpLVItem->mask & LVIF_STATE)
{
lpItem->state &= ~lpLVItem->stateMask;
lpItem->state |= (lpLVItem->state & lpLVItem->stateMask);
}
if (lpLVItem->mask & LVIF_IMAGE)
{
lpItem->iImage = lpLVItem->iImage;
}
if (lpLVItem->mask & LVIF_PARAM)
{
lpItem->lParam = lpLVItem->lParam;
}
if (lpLVItem->mask & LVIF_INDENT)
{
lpItem->iIndent = lpLVItem->iIndent;
}
if (lpLVItem->mask & LVIF_TEXT)
{
if (lpLVItem->pszText == LPSTR_TEXTCALLBACKA)
{
if ((lStyle & LVS_SORTASCENDING) || (lStyle & LVS_SORTDESCENDING))
{
return FALSE;
}
if ((lpItem->pszText != NULL) &&
(lpItem->pszText != LPSTR_TEXTCALLBACKA))
{
COMCTL32_Free(lpItem->pszText);
}
lpItem->pszText = LPSTR_TEXTCALLBACKA;
}
else
{
if (lpItem->pszText == LPSTR_TEXTCALLBACKA)
{
lpItem->pszText = NULL;
}
bResult = Str_SetPtrA(&lpItem->pszText, lpLVItem->pszText);
}
}
}
return bResult;
}
/***
* DESCRIPTION:
* Initializes subitem attributes.
*
* NOTE: The documentation specifies that the operation fails if the user
* tries to set the indent of a subitem.
*
* PARAMETER(S):
* [I] HWND : window handle
* [O] LISTVIEW_SUBITEM *: destination subitem
* [I] LPLVITEM : source subitem
*
* RETURN:
* SUCCCESS : TRUE
* FAILURE : FALSE
*/
static BOOL LISTVIEW_InitSubItem(HWND hwnd, LISTVIEW_SUBITEM *lpSubItem,
LPLVITEMA lpLVItem)
{
LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
BOOL bResult = FALSE;
if ((lpSubItem != NULL) && (lpLVItem != NULL))
{
if (!(lpLVItem->mask & LVIF_INDENT))
{
bResult = TRUE;
lpSubItem->iSubItem = lpLVItem->iSubItem;
if (lpLVItem->mask & LVIF_IMAGE)
{
lpSubItem->iImage = lpLVItem->iImage;
}
if (lpLVItem->mask & LVIF_TEXT)
{
if (lpLVItem->pszText == LPSTR_TEXTCALLBACKA)
{
if ((lStyle & LVS_SORTASCENDING) || (lStyle & LVS_SORTDESCENDING))
{
return FALSE;
}
if ((lpSubItem->pszText != NULL) &&
(lpSubItem->pszText != LPSTR_TEXTCALLBACKA))
{
COMCTL32_Free(lpSubItem->pszText);
}
lpSubItem->pszText = LPSTR_TEXTCALLBACKA;
}
else
{
if (lpSubItem->pszText == LPSTR_TEXTCALLBACKA)
{
lpSubItem->pszText = NULL;
}
bResult = Str_SetPtrA(&lpSubItem->pszText, lpLVItem->pszText);
}
}
}
}
return bResult;
}
/***
* DESCRIPTION:
* Adds a subitem at a given position (column index).
*
* PARAMETER(S):
* [I] HWND : window handle
* [I] LPLVITEM : new subitem atttributes
*
* RETURN:
* SUCCESS : TRUE
* FAILURE : FALSE
*/
static BOOL LISTVIEW_AddSubItem(HWND hwnd, LPLVITEMA lpLVItem)
{
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
LISTVIEW_SUBITEM *lpSubItem = NULL;
BOOL bResult = FALSE;
HDPA hdpaSubItems;
INT nPosition, nItem;
LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
if (lStyle & LVS_OWNERDATA)
return FALSE;
if (lpLVItem != NULL)
{
hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
if (hdpaSubItems != NULL)
{
lpSubItem = (LISTVIEW_SUBITEM *)COMCTL32_Alloc(sizeof(LISTVIEW_SUBITEM));
if (lpSubItem != NULL)
{
ZeroMemory(lpSubItem, sizeof(LISTVIEW_SUBITEM));
if (LISTVIEW_InitSubItem(hwnd, lpSubItem, lpLVItem) != FALSE)
{
nPosition = LISTVIEW_FindInsertPosition(hdpaSubItems,
lpSubItem->iSubItem);
nItem = DPA_InsertPtr(hdpaSubItems, nPosition, lpSubItem);
if (nItem != -1)
{
bResult = TRUE;
}
}
}
}
}
/* cleanup if unsuccessful */
if ((bResult == FALSE) && (lpSubItem != NULL))
{
COMCTL32_Free(lpSubItem);
}
return bResult;
}
/***
* DESCRIPTION:
* Finds the dpa insert position (array index).
*
* PARAMETER(S):
* [I] HWND : window handle
* [I] INT : subitem index
*
* RETURN:
* SUCCESS : TRUE
* FAILURE : FALSE
*/
static INT LISTVIEW_FindInsertPosition(HDPA hdpaSubItems, INT nSubItem)
{
LISTVIEW_SUBITEM *lpSubItem;
INT i;
for (i = 1; i < hdpaSubItems->nItemCount; i++)
{
lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
if (lpSubItem != NULL)
{
if (lpSubItem->iSubItem > nSubItem)
{
return i;
}
}
}
return hdpaSubItems->nItemCount;
}
/***
* DESCRIPTION:
* Retrieves a listview subitem at a given position (column index).
*
* PARAMETER(S):
* [I] HWND : window handle
* [I] INT : subitem index
*
* RETURN:
* SUCCESS : TRUE
* FAILURE : FALSE
*/
static LISTVIEW_SUBITEM* LISTVIEW_GetSubItem(HDPA hdpaSubItems, INT nSubItem)
{
LISTVIEW_SUBITEM *lpSubItem;
INT i;
for (i = 1; i < hdpaSubItems->nItemCount; i++)
{
lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
if (lpSubItem != NULL)
{
if (lpSubItem->iSubItem == nSubItem)
{
return lpSubItem;
}
else if (lpSubItem->iSubItem > nSubItem)
{
return NULL;
}
}
}
return NULL;
}
/***
* DESCRIPTION:
* Sets item attributes.
*
* PARAMETER(S):
* [I] HWND : window handle
* [I] LPLVITEM : new item atttributes
*
* RETURN:
* SUCCESS : TRUE
* FAILURE : FALSE
*/
static BOOL LISTVIEW_SetItem(HWND hwnd, LPLVITEMA lpLVItem)
{
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
BOOL bResult = FALSE;
HDPA hdpaSubItems;
LISTVIEW_ITEM *lpItem;
NMLISTVIEW nmlv;
LONG lCtrlId = GetWindowLongA(hwnd, GWL_ID);
LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
UINT uChanged;
UINT uView = lStyle & LVS_TYPEMASK;
INT item_width;
RECT rcItem;
if (lStyle & LVS_OWNERDATA)
{
if ((lpLVItem->iSubItem == 0)&&(lpLVItem->mask == LVIF_STATE))
{
LVITEMA itm;
ZeroMemory(&itm,sizeof(LVITEMA));
itm.mask = LVIF_STATE | LVIF_PARAM;
itm.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
itm.iItem = lpLVItem->iItem;
itm.iSubItem = 0;
ListView_GetItemA(hwnd,&itm);
ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
nmlv.hdr.hwndFrom = hwnd;
nmlv.hdr.idFrom = lCtrlId;
nmlv.hdr.code = LVN_ITEMCHANGING;
nmlv.uNewState = lpLVItem->state;
nmlv.uOldState = itm.state;
nmlv.uChanged = LVIF_STATE;
nmlv.lParam = itm.lParam;
nmlv.iItem = lpLVItem->iItem;
if ((itm.state & lpLVItem->stateMask) !=
(lpLVItem->state & lpLVItem->stateMask))
{
/* send LVN_ITEMCHANGING notification */
if (!ListView_LVNotify(GetParent(hwnd), lCtrlId, &nmlv))
{
if (lpLVItem->stateMask & LVIS_FOCUSED)
{
if (lpLVItem->state & LVIS_FOCUSED)
infoPtr->nFocusedItem = lpLVItem->iItem;
else if (infoPtr->nFocusedItem == lpLVItem->iItem)
infoPtr->nFocusedItem = -1;
}
if (lpLVItem->stateMask & LVIS_SELECTED)
{
if (lpLVItem->state & LVIS_SELECTED)
{
if (lStyle & LVS_SINGLESEL)
{
LISTVIEW_RemoveAllSelections(hwnd);
}
LISTVIEW_AddSelectionRange(hwnd,lpLVItem->iItem,lpLVItem->iItem);
}
else
LISTVIEW_RemoveSelectionRange(hwnd,lpLVItem->iItem,
lpLVItem->iItem);
}
nmlv.hdr.code = LVN_ITEMCHANGED;
ListView_LVNotify(GetParent(hwnd), lCtrlId, &nmlv);
rcItem.left = LVIR_BOUNDS;
LISTVIEW_GetItemRect(hwnd, lpLVItem->iItem, &rcItem);
InvalidateRect(hwnd, &rcItem, TRUE);
}
}
return TRUE;
}
return FALSE;
}
if (lpLVItem != NULL)
{
if (lpLVItem->iSubItem == 0)
{
hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
if (hdpaSubItems != NULL && hdpaSubItems != (HDPA)-1)
{
lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, lpLVItem->iSubItem);
if (lpItem != NULL)
{
ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
nmlv.hdr.hwndFrom = hwnd;
nmlv.hdr.idFrom = lCtrlId;
nmlv.hdr.code = LVN_ITEMCHANGING;
nmlv.lParam = lpItem->lParam;
uChanged = LISTVIEW_GetItemChanges(lpItem, lpLVItem);
if (uChanged != 0)
{
if (uChanged & LVIF_STATE)
{
nmlv.uNewState = lpLVItem->state & lpLVItem->stateMask;
nmlv.uOldState = lpItem->state & lpLVItem->stateMask;
if (nmlv.uNewState & LVIS_SELECTED)
{
/*
* This is redundant if called through SetSelection
*
* however is required if the used directly calls SetItem
* to set the selection.
*/
if (lStyle & LVS_SINGLESEL)
{
LISTVIEW_RemoveAllSelections(hwnd);
}
LISTVIEW_AddSelectionRange(hwnd,lpLVItem->iItem,
lpLVItem->iItem);
}
else if (lpLVItem->stateMask & LVIS_SELECTED)
{
LISTVIEW_RemoveSelectionRange(hwnd,lpLVItem->iItem,
lpLVItem->iItem);
}
if (nmlv.uNewState & LVIS_FOCUSED)
{
/*
* This is a fun hoop to jump to try to catch if
* the user is calling us directly to call focus or if
* this function is being called as a result of a
* SetItemFocus call.
*/
if (infoPtr->nFocusedItem >= 0)
LISTVIEW_SetItemFocus(hwnd, lpLVItem->iItem);
}
}
nmlv.uChanged = uChanged;
nmlv.iItem = lpLVItem->iItem;
nmlv.lParam = lpItem->lParam;
/* send LVN_ITEMCHANGING notification */
ListView_LVNotify(GetParent(hwnd), lCtrlId, &nmlv);
/* copy information */
bResult = LISTVIEW_InitItem(hwnd, lpItem, lpLVItem);
/* if LVS_LIST or LVS_SMALLICON, update the width of the items
based on the width of the items text */
if((uView == LVS_LIST) || (uView == LVS_SMALLICON))
{
item_width = LISTVIEW_GetStringWidthA(hwnd, lpItem->pszText);
if(item_width > infoPtr->nItemWidth)
infoPtr->nItemWidth = item_width;
}
/* send LVN_ITEMCHANGED notification */
nmlv.hdr.code = LVN_ITEMCHANGED;
ListView_LVNotify(GetParent(hwnd), lCtrlId, &nmlv);
}
else
{
bResult = TRUE;
}
if (uChanged)
{
rcItem.left = LVIR_BOUNDS;
LISTVIEW_GetItemRect(hwnd, lpLVItem->iItem, &rcItem);
InvalidateRect(hwnd, &rcItem, TRUE);
}
}
}
}
}
return bResult;
}
/***
* DESCRIPTION:
* Sets subitem attributes.
*
* PARAMETER(S):
* [I] HWND : window handle
* [I] LPLVITEM : new subitem atttributes
*
* RETURN:
* SUCCESS : TRUE
* FAILURE : FALSE
*/
static BOOL LISTVIEW_SetSubItem(HWND hwnd, LPLVITEMA lpLVItem)
{
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
BOOL bResult = FALSE;
HDPA hdpaSubItems;
LISTVIEW_SUBITEM *lpSubItem;
LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
RECT rcItem;
if (lStyle & LVS_OWNERDATA)
return FALSE;
if (lpLVItem != NULL)
{
if (lpLVItem->iSubItem > 0)
{
hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
if (hdpaSubItems != NULL)
{
/* set subitem only if column is present */
if (Header_GetItemCount(infoPtr->hwndHeader) > lpLVItem->iSubItem)
{
lpSubItem = LISTVIEW_GetSubItem(hdpaSubItems, lpLVItem->iSubItem);
if (lpSubItem != NULL)
{
bResult = LISTVIEW_InitSubItem(hwnd, lpSubItem, lpLVItem);
}
else
{
bResult = LISTVIEW_AddSubItem(hwnd, lpLVItem);
}
rcItem.left = LVIR_BOUNDS;
LISTVIEW_GetItemRect(hwnd, lpLVItem->iItem, &rcItem);
InvalidateRect(hwnd, &rcItem, FALSE);
}
}
}
}
return bResult;
}
/***
* DESCRIPTION:
* Retrieves the index of the item at coordinate (0, 0) of the client area.
*
* PARAMETER(S):
* [I] HWND : window handle
*
* RETURN:
* item index
*/
static INT LISTVIEW_GetTopIndex(HWND hwnd)
{
LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
UINT uView = lStyle & LVS_TYPEMASK;
INT nItem = 0;
SCROLLINFO scrollInfo;
ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
scrollInfo.cbSize = sizeof(SCROLLINFO);
scrollInfo.fMask = SIF_POS;
if (uView == LVS_LIST)
{
if (lStyle & WS_HSCROLL)
{
if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) != FALSE)
{
nItem = scrollInfo.nPos * LISTVIEW_GetCountPerColumn(hwnd);
}
}
}
else if (uView == LVS_REPORT)
{
if (lStyle & WS_VSCROLL)
{
if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
{
nItem = scrollInfo.nPos;
}
}
}
return nItem;
}
/***
* DESCRIPTION:
* Draws a subitem.
*
* PARAMETER(S):
* [I] HWND : window handle
* [I] HDC : device context handle
* [I] INT : item index
* [I] INT : subitem index
* [I] RECT * : clipping rectangle
*
* RETURN:
* None
*/
static VOID LISTVIEW_DrawSubItem(HWND hwnd, HDC hdc, INT nItem, INT nSubItem,
RECT rcItem, BOOL Selected)
{
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
CHAR szDispText[DISP_TEXT_SIZE];
LVITEMA lvItem;
UINT textoutOptions = ETO_CLIPPED | ETO_OPAQUE;
RECT rcTemp;
TRACE("(hwnd=%x, hdc=%x, nItem=%d, nSubItem=%d)\n", hwnd, hdc,
nItem, nSubItem);
/* get information needed for drawing the item */
ZeroMemory(&lvItem, sizeof(LVITEMA));
lvItem.mask = LVIF_TEXT;
lvItem.iItem = nItem;
lvItem.iSubItem = nSubItem;
lvItem.cchTextMax = DISP_TEXT_SIZE;
lvItem.pszText = szDispText;
LISTVIEW_GetItemA(hwnd, &lvItem, TRUE);
/* redraw the background of the item */
rcTemp = rcItem;
if(infoPtr->nColumnCount == (nSubItem + 1))
rcTemp.right = infoPtr->rcList.right;
else
rcTemp.right+=WIDTH_PADDING;
LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
/* set item colors */
if (ListView_GetItemState(hwnd,nItem,LVIS_SELECTED) && Selected)
{
if (infoPtr->bFocus)
{
SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
}
else
{
SetBkColor(hdc, GetSysColor(COLOR_3DFACE));
SetTextColor(hdc, GetSysColor(COLOR_BTNTEXT));
}
}
else
{
if ( (infoPtr->clrTextBk == CLR_DEFAULT) || (infoPtr->clrTextBk == CLR_NONE) )
{
SetBkMode(hdc, TRANSPARENT);
textoutOptions &= ~ETO_OPAQUE;
}
else
{
SetBkMode(hdc, OPAQUE);
SetBkColor(hdc, infoPtr->clrTextBk);
}
SetTextColor(hdc, infoPtr->clrText);
}
ExtTextOutA(hdc, rcItem.left, rcItem.top, textoutOptions,
&rcItem, lvItem.pszText, lstrlenA(lvItem.pszText), NULL);
if (Selected)
{
/* fill in the gap */
RECT rec;
if (nSubItem < Header_GetItemCount(infoPtr->hwndHeader)-1)
{
CopyRect(&rec,&rcItem);
rec.left = rec.right;
rec.right = rec.left+REPORT_MARGINX;
ExtTextOutA(hdc, rec.left , rec.top, textoutOptions,
&rec, NULL, 0, NULL);
}
CopyRect(&rec,&rcItem);
rec.right = rec.left;
rec.left = rec.left - REPORT_MARGINX;
ExtTextOutA(hdc, rec.left , rec.top, textoutOptions,
&rec, NULL, 0, NULL);
}
}
/***
* DESCRIPTION:
* Draws an item.
*
* PARAMETER(S):
* [I] HWND : window handle
* [I] HDC : device context handle
* [I] INT : item index
* [I] RECT * : clipping rectangle
*
* RETURN:
* None
*/
static VOID LISTVIEW_DrawItem(HWND hwnd, HDC hdc, INT nItem, RECT rcItem, BOOL FullSelect, RECT* SuggestedFocus)
{
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
CHAR szDispText[DISP_TEXT_SIZE];
INT nLabelWidth;
LVITEMA lvItem;
INT nMixMode;
DWORD dwBkColor;
DWORD dwTextColor,dwTextX;
BOOL bImage = FALSE;
INT iBkMode = -1;
UINT textoutOptions = ETO_OPAQUE | ETO_CLIPPED;
RECT rcTemp;
TRACE("(hwnd=%x, hdc=%x, nItem=%d)\n", hwnd, hdc, nItem);
/* get information needed for drawing the item */
ZeroMemory(&lvItem, sizeof(LVITEMA));
lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_STATE | LVIF_INDENT;
lvItem.stateMask = LVIS_SELECTED | LVIS_STATEIMAGEMASK;
lvItem.iItem = nItem;
lvItem.iSubItem = 0;
lvItem.cchTextMax = DISP_TEXT_SIZE;
lvItem.pszText = szDispText;
LISTVIEW_GetItemA(hwnd, &lvItem, TRUE);
/* redraw the background of the item */
rcTemp = rcItem;
if(infoPtr->nColumnCount == (nItem + 1))
rcTemp.right = infoPtr->rcList.right;
else
rcTemp.right+=WIDTH_PADDING;
LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
/* do indent */
if (lvItem.iIndent>0 && infoPtr->iconSize.cx > 0)
{
rcItem.left += infoPtr->iconSize.cx * lvItem.iIndent;
if (SuggestedFocus)
SuggestedFocus->left += infoPtr->iconSize.cx * lvItem.iIndent;
}
/* state icons */
if (infoPtr->himlState != NULL)
{
UINT uStateImage = (lvItem.state & LVIS_STATEIMAGEMASK) >> 12;
if (uStateImage > 0)
{
ImageList_Draw(infoPtr->himlState, uStateImage - 1, hdc, rcItem.left,
rcItem.top, ILD_NORMAL);
}
rcItem.left += infoPtr->iconSize.cx;
if (SuggestedFocus)
SuggestedFocus->left += infoPtr->iconSize.cx;
bImage = TRUE;
}
/* small icons */
if (infoPtr->himlSmall != NULL)
{
if ((lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus != FALSE) &&
(lvItem.iImage>=0))
{
ImageList_SetBkColor(infoPtr->himlSmall, CLR_NONE);
ImageList_Draw(infoPtr->himlSmall, lvItem.iImage, hdc, rcItem.left,
rcItem.top, ILD_SELECTED);
}
else if (lvItem.iImage>=0)
{
ImageList_SetBkColor(infoPtr->himlSmall, CLR_NONE);
ImageList_Draw(infoPtr->himlSmall, lvItem.iImage, hdc, rcItem.left,
rcItem.top, ILD_NORMAL);
}
rcItem.left += infoPtr->iconSize.cx;
if (SuggestedFocus)
SuggestedFocus->left += infoPtr->iconSize.cx;
bImage = TRUE;
}
/* Don't bother painting item being edited */
if (infoPtr->hwndEdit && lvItem.state & LVIS_FOCUSED && !FullSelect)
return;
if ((lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus != FALSE))
{
/* set item colors */
dwBkColor = SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
dwTextColor = SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
/* set raster mode */
nMixMode = SetROP2(hdc, R2_XORPEN);
}
else if ((GetWindowLongA(hwnd, GWL_STYLE) & LVS_SHOWSELALWAYS) &&
(lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus == FALSE))
{
dwBkColor = SetBkColor(hdc, GetSysColor(COLOR_3DFACE));
dwTextColor = SetTextColor(hdc, GetSysColor(COLOR_BTNTEXT));
/* set raster mode */
nMixMode = SetROP2(hdc, R2_COPYPEN);
}
else
{
/* set item colors */
if ( (infoPtr->clrTextBk == CLR_DEFAULT) || (infoPtr->clrTextBk == CLR_NONE) )
{
dwBkColor = GetBkColor(hdc);
iBkMode = SetBkMode(hdc, TRANSPARENT);
textoutOptions &= ~ETO_OPAQUE;
}
else
{
dwBkColor = SetBkColor(hdc, infoPtr->clrTextBk);
iBkMode = SetBkMode(hdc, OPAQUE);
}
dwTextColor = SetTextColor(hdc, infoPtr->clrText);
/* set raster mode */
nMixMode = SetROP2(hdc, R2_COPYPEN);
}
nLabelWidth = ListView_GetStringWidthA(hwnd, lvItem.pszText);
if (rcItem.left + nLabelWidth < rcItem.right)
{
if (!FullSelect)
rcItem.right = rcItem.left + nLabelWidth + TRAILING_PADDING;
if (bImage)
rcItem.right += IMAGE_PADDING;
}
/* draw label */
dwTextX = rcItem.left + 1;
if (bImage)
dwTextX += IMAGE_PADDING;
if (lvItem.pszText)
ExtTextOutA(hdc, dwTextX, rcItem.top, textoutOptions,
&rcItem, lvItem.pszText, lstrlenA(lvItem.pszText), NULL);
if ((FullSelect)&&(Header_GetItemCount(infoPtr->hwndHeader) > 1))
{
/* fill in the gap */
RECT rec;
CopyRect(&rec,&rcItem);
rec.left = rec.right;
rec.right = rec.left+REPORT_MARGINX;
ExtTextOutA(hdc, rec.left , rec.top, textoutOptions,
&rec, NULL, 0, NULL);
}
if (!FullSelect)
CopyRect(SuggestedFocus,&rcItem);
if (nMixMode != 0)
{
SetROP2(hdc, R2_COPYPEN);
SetBkColor(hdc, dwBkColor);
SetTextColor(hdc, dwTextColor);
if (iBkMode != -1)
SetBkMode(hdc, iBkMode);
}
}
/***
* DESCRIPTION:
* Draws an item when in large icon display mode.
*
* PARAMETER(S):
* [I] HWND : window handle
* [I] HDC : device context handle
* [I] LISTVIEW_ITEM * : item
* [I] INT : item index
* [I] RECT * : clipping rectangle
*
* RETURN:
* None
*/
static VOID LISTVIEW_DrawLargeItem(HWND hwnd, HDC hdc, INT nItem, RECT rcItem,
RECT *SuggestedFocus)
{
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
CHAR szDispText[DISP_TEXT_SIZE];
INT nDrawPosX = rcItem.left;
INT nLabelWidth, rcWidth;
TEXTMETRICA tm;
LVITEMA lvItem;
UINT textoutOptions = ETO_CLIPPED | ETO_OPAQUE;
RECT rcTemp;
TRACE("(hwnd=%x, hdc=%x, nItem=%d, left=%d, top=%d, right=%d, \
bottom=%d)\n", hwnd, hdc, nItem, rcItem.left, rcItem.top, rcItem.right,
rcItem.bottom);
/* get information needed for drawing the item */
ZeroMemory(&lvItem, sizeof(LVITEMA));
lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_STATE;
lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
lvItem.iItem = nItem;
lvItem.iSubItem = 0;
lvItem.cchTextMax = DISP_TEXT_SIZE;
lvItem.pszText = szDispText;
LISTVIEW_GetItemA(hwnd, &lvItem, TRUE);
/* redraw the background of the item */
rcTemp = rcItem;
if(infoPtr->nColumnCount == (nItem + 1))
rcTemp.right = infoPtr->rcList.right;
else
rcTemp.right+=WIDTH_PADDING;
LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
if (lvItem.state & LVIS_SELECTED)
{
/* set item colors */
SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
/* set raster mode */
SetROP2(hdc, R2_XORPEN);
}
else
{
/* set item colors */
if ( (infoPtr->clrTextBk == CLR_DEFAULT) || (infoPtr->clrTextBk == CLR_NONE) )
{
SetBkMode(hdc, TRANSPARENT);
textoutOptions &= ~ETO_OPAQUE;
}
else
{
SetBkMode(hdc, OPAQUE);
SetBkColor(hdc, infoPtr->clrTextBk);
}
SetTextColor(hdc, infoPtr->clrText);
/* set raster mode */
SetROP2(hdc, R2_COPYPEN);
}
if (infoPtr->himlNormal != NULL)
{
rcItem.top += ICON_TOP_PADDING;
nDrawPosX += (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
if ((lvItem.state & LVIS_SELECTED) && (lvItem.iImage>=0))
{
ImageList_Draw(infoPtr->himlNormal, lvItem.iImage, hdc, nDrawPosX,
rcItem.top, ILD_SELECTED);
}
else if (lvItem.iImage>=0)
{
ImageList_Draw(infoPtr->himlNormal, lvItem.iImage, hdc, nDrawPosX,
rcItem.top, ILD_NORMAL);
}
}
/* Don't bother painting item being edited */
if (infoPtr->hwndEdit && lvItem.state & LVIS_FOCUSED)
return;
InflateRect(&rcItem, -(2*CAPTION_BORDER), 0);
rcItem.top += infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
nLabelWidth = ListView_GetStringWidthA(hwnd, lvItem.pszText);
GetTextMetricsA(hdc, &tm);
/* append an ellipse ('...') if the caption won't fit in the rect */
rcWidth = max(0, rcItem.right - rcItem.left);
if (nLabelWidth > rcWidth)
{
INT i, len, eos, nCharsFit;
/* give or take a couple, how many average sized chars would fit? */
nCharsFit = tm.tmAveCharWidth > 0 ? (rcWidth/tm.tmAveCharWidth)+2 : 0;
/* place the ellipse accordingly, without overrunning the buffer */
len = strlen(szDispText);
eos = min((nCharsFit > 1 && nCharsFit < len) ? nCharsFit+3 : len+2,
sizeof(szDispText)-1);
nLabelWidth = ListView_GetStringWidthA(hwnd, szDispText);
while ((nLabelWidth > rcWidth) && (eos > 3))
{
for (i = 1; i < 4; i++)
szDispText[eos-i] = '.';
/* shift the ellipse one char to the left for each iteration */
szDispText[eos--] = '\0';
nLabelWidth = ListView_GetStringWidthA(hwnd, szDispText);
}
}
InflateRect(&rcItem, 2*CAPTION_BORDER, 0);
nDrawPosX = infoPtr->iconSpacing.cx - 2*CAPTION_BORDER - nLabelWidth;
if (nDrawPosX > 1)
{
rcItem.left += nDrawPosX / 2;
rcItem.right = rcItem.left + nLabelWidth + 2*CAPTION_BORDER;
}
else
{
rcItem.left += 1;
rcItem.right = rcItem.left + infoPtr->iconSpacing.cx - 1;
}
/* draw label */
rcItem.bottom = rcItem.top + tm.tmHeight + HEIGHT_PADDING;
ExtTextOutA(hdc, rcItem.left + CAPTION_BORDER, rcItem.top, textoutOptions,
&rcItem, lvItem.pszText, lstrlenA(lvItem.pszText), NULL);
CopyRect(SuggestedFocus,&rcItem);
}
/***
* DESCRIPTION:
* Draws listview items when in report display mode.
*
* PARAMETER(S):
* [I] HWND : window handle
* [I] HDC : device context handle
*
* RETURN:
* None
*/
static VOID LISTVIEW_RefreshReport(HWND hwnd, HDC hdc, DWORD cdmode)
{
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd,0);
SCROLLINFO scrollInfo;
INT nDrawPosY = infoPtr->rcList.top;
INT nColumnCount;
RECT rcItem, rcTemp;
INT j;
INT nItem;
INT nLast;
BOOL FullSelected;
DWORD cditemmode = CDRF_DODEFAULT;
LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
INT scrollOffset;
ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
scrollInfo.cbSize = sizeof(SCROLLINFO);
scrollInfo.fMask = SIF_POS;
nItem = ListView_GetTopIndex(hwnd);
/* add 1 for displaying a partial item at the bottom */
nLast = nItem + LISTVIEW_GetCountPerColumn(hwnd) + 1;
nLast = min(nLast, GETITEMCOUNT(infoPtr));
/* send cache hint notification */
if (GetWindowLongA(hwnd,GWL_STYLE) & LVS_OWNERDATA)
{
NMLVCACHEHINT nmlv;
nmlv.hdr.hwndFrom = hwnd;
nmlv.hdr.idFrom = GetWindowLongA(hwnd,GWL_ID);
nmlv.hdr.code = LVN_ODCACHEHINT;
nmlv.iFrom = nItem;
nmlv.iTo = nLast;
SendMessageA(GetParent(hwnd), WM_NOTIFY, (WPARAM)nmlv.hdr.idFrom,
(LPARAM)&nmlv);
}
nColumnCount = Header_GetItemCount(infoPtr->hwndHeader);
infoPtr->nColumnCount = nColumnCount; /* update nColumnCount */
FullSelected = infoPtr->dwExStyle & LVS_EX_FULLROWSELECT;
/* clear the background of any part of the control that doesn't contain items */
SubtractRect(&rcTemp, &infoPtr->rcList, &infoPtr->rcView);
LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
/* nothing to draw */
if(GETITEMCOUNT(infoPtr) == 0)
return;
/* Get scroll bar info once before loop */
GetScrollInfo(hwnd, SB_HORZ, &scrollInfo);
scrollOffset = scrollInfo.nPos * LISTVIEW_SCROLL_DIV_SIZE;
for (; nItem < nLast; nItem++)
{
RECT SuggestedFocusRect;
/* Do Owner Draw */
if (lStyle & LVS_OWNERDRAWFIXED)
{
UINT uID = GetWindowLongA( hwnd, GWL_ID);
DRAWITEMSTRUCT dis;
LVITEMA item;
RECT br;
TRACE("Owner Drawn\n");
dis.CtlType = ODT_LISTVIEW;
dis.CtlID = uID;
dis.itemID = nItem;
dis.itemAction = ODA_DRAWENTIRE;
dis.itemState = 0;
if (LISTVIEW_IsSelected(hwnd,nItem)) dis.itemState|=ODS_SELECTED;
if (nItem==infoPtr->nFocusedItem) dis.itemState|=ODS_FOCUS;
dis.hwndItem = hwnd;
dis.hDC = hdc;
Header_GetItemRect(infoPtr->hwndHeader, nColumnCount-1, &br);
dis.rcItem.left = -scrollOffset;
dis.rcItem.right = max(dis.rcItem.left, br.right - scrollOffset);
dis.rcItem.top = nDrawPosY;
dis.rcItem.bottom = dis.rcItem.top + infoPtr->nItemHeight;
ZeroMemory(&item,sizeof(LVITEMA));
item.iItem = nItem;
item.mask = LVIF_PARAM;
ListView_GetItemA(hwnd,&item);
dis.itemData = item.lParam;
if (SendMessageA(GetParent(hwnd),WM_DRAWITEM,(WPARAM)uID,(LPARAM)&dis))
{
nDrawPosY += infoPtr->nItemHeight;
continue;
}
}
if (FullSelected)
{
RECT ir,br;
Header_GetItemRect(infoPtr->hwndHeader, 0, &ir);
Header_GetItemRect(infoPtr->hwndHeader, nColumnCount-1, &br);
ir.left += REPORT_MARGINX;
ir.right = max(ir.left, br.right - REPORT_MARGINX);
ir.top = nDrawPosY;
ir.bottom = ir.top + infoPtr->nItemHeight;
CopyRect(&SuggestedFocusRect,&ir);
}
for (j = 0; j < nColumnCount; j++)
{
if (cdmode & CDRF_NOTIFYITEMDRAW)
cditemmode = LISTVIEW_SendCustomDrawItemNotify (hwnd, hdc, nItem, j,
CDDS_ITEMPREPAINT);
if (cditemmode & CDRF_SKIPDEFAULT)
continue;
Header_GetItemRect(infoPtr->hwndHeader, j, &rcItem);
rcItem.left += REPORT_MARGINX;
rcItem.right = max(rcItem.left, rcItem.right - REPORT_MARGINX);
rcItem.top = nDrawPosY;
rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
/* Offset the Scroll Bar Pos */
rcItem.left -= scrollOffset;
rcItem.right -= scrollOffset;
if (j == 0)
{
LISTVIEW_DrawItem(hwnd, hdc, nItem, rcItem, FullSelected,
&SuggestedFocusRect);
}
else
{
LISTVIEW_DrawSubItem(hwnd, hdc, nItem, j, rcItem,
FullSelected);
}
if (cditemmode & CDRF_NOTIFYPOSTPAINT)
LISTVIEW_SendCustomDrawItemNotify(hwnd, hdc, nItem, 0,
CDDS_ITEMPOSTPAINT);
}
/*
* Draw Focus Rect
*/
if (LISTVIEW_GetItemState(hwnd,nItem,LVIS_FOCUSED) && infoPtr->bFocus)
{
BOOL rop=FALSE;
if (FullSelected && LISTVIEW_GetItemState(hwnd,nItem,LVIS_SELECTED))
rop = SetROP2(hdc, R2_XORPEN);
Rectangle(hdc, SuggestedFocusRect.left, SuggestedFocusRect.top,
SuggestedFocusRect.right,SuggestedFocusRect.bottom);
if (rop)
SetROP2(hdc, R2_COPYPEN);
}
nDrawPosY += infoPtr->nItemHeight;
}
}
/***
* DESCRIPTION:
* Retrieves the number of items that can fit vertically in the client area.
*
* PARAMETER(S):
* [I] HWND : window handle
*
* RETURN:
* Number of items per row.
*/
static INT LISTVIEW_GetCountPerRow(HWND hwnd)
{
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd,0);
UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
INT nCountPerRow = 1;
if (nListWidth > 0)
{
if (uView == LVS_REPORT)
{
nCountPerRow = 1;
}
else
{
nCountPerRow = nListWidth / infoPtr->nItemWidth;
if (nCountPerRow == 0)
{
nCountPerRow = 1;
}
}
}
return nCountPerRow;
}
/***
* DESCRIPTION:
* Retrieves the number of items that can fit horizontally in the client
* area.
*
* PARAMETER(S):
* [I] HWND : window handle
*
* RETURN:
* Number of items per column.
*/
static INT LISTVIEW_GetCountPerColumn(HWND hwnd)
{
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd,0);
INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
INT nCountPerColumn = 1;
if (nListHeight > 0)
{
nCountPerColumn = nListHeight / infoPtr->nItemHeight;
if (nCountPerColumn == 0)
{
nCountPerColumn = 1;
}
}
return nCountPerColumn;
}
/***
* DESCRIPTION:
* Retrieves the number of columns needed to display all the items when in
* list display mode.
*
* PARAMETER(S):
* [I] HWND : window handle
*
* RETURN:
* Number of columns.
*/
static INT LISTVIEW_GetColumnCount(HWND hwnd)
{
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
INT nColumnCount = 0;
if ((lStyle & LVS_TYPEMASK) == LVS_LIST)
{
if (infoPtr->rcList.right % infoPtr->nItemWidth == 0)
{
nColumnCount = infoPtr->rcList.right / infoPtr->nItemWidth;
}
else
{
nColumnCount = infoPtr->rcList.right / infoPtr->nItemWidth + 1;
}
}
return nColumnCount;
}
/***
* DESCRIPTION:
* Draws listview items when in list display mode.
*
* PARAMETER(S):
* [I] HWND : window handle
* [I] HDC : device context handle
*
* RETURN:
* None
*/
static VOID LISTVIEW_RefreshList(HWND hwnd, HDC hdc, DWORD cdmode)
{
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
RECT rcItem, FocusRect, rcTemp;
INT i, j;
INT nItem;
INT nColumnCount;
INT nCountPerColumn;
INT nItemWidth = infoPtr->nItemWidth;
INT nItemHeight = infoPtr->nItemHeight;
DWORD cditemmode = CDRF_DODEFAULT;
/* get number of fully visible columns */
nColumnCount = LISTVIEW_GetColumnCount(hwnd);
infoPtr->nColumnCount = nColumnCount;
nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
nItem = ListView_GetTopIndex(hwnd);
/* paint the background of the control that doesn't contain any items */
SubtractRect(&rcTemp, &infoPtr->rcList, &infoPtr->rcView);
LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
/* nothing to draw, return here */
if(GETITEMCOUNT(infoPtr) == 0)
return;
for (i = 0; i < nColumnCount; i++)
{
for (j = 0; j < nCountPerColumn; j++, nItem++)
{
if (nItem >= GETITEMCOUNT(infoPtr))
return;
if (cdmode & CDRF_NOTIFYITEMDRAW)
cditemmode = LISTVIEW_SendCustomDrawItemNotify (hwnd, hdc, nItem, 0,
CDDS_ITEMPREPAINT);
if (cditemmode & CDRF_SKIPDEFAULT)
continue;
rcItem.top = j * nItemHeight;
rcItem.left = i * nItemWidth;
rcItem.bottom = rcItem.top + nItemHeight;
rcItem.right = rcItem.left + nItemWidth;
LISTVIEW_DrawItem(hwnd, hdc, nItem, rcItem, FALSE, &FocusRect);
/*
* Draw Focus Rect
*/
if (LISTVIEW_GetItemState(hwnd,nItem,LVIS_FOCUSED) && infoPtr->bFocus)
Rectangle(hdc, FocusRect.left, FocusRect.top,
FocusRect.right,FocusRect.bottom);
if (cditemmode & CDRF_NOTIFYPOSTPAINT)
LISTVIEW_SendCustomDrawItemNotify(hwnd, hdc, nItem, 0,
CDDS_ITEMPOSTPAINT);
}
}
}
/***
* DESCRIPTION:
* Draws listview items when in icon or small icon display mode.
*
* PARAMETER(S):
* [I] HWND : window handle
* [I] HDC : device context handle
*
* RETURN:
* None
*/
static VOID LISTVIEW_RefreshIcon(HWND hwnd, HDC hdc, BOOL bSmall, DWORD cdmode)
{
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
POINT ptPosition;
POINT ptOrigin;
RECT rcItem, SuggestedFocus, rcTemp;
INT i;
DWORD cditemmode = CDRF_DODEFAULT;
infoPtr->nColumnCount = 1; /* set this to an arbitrary value to prevent */
/* DrawItem from erasing the incorrect background area */
/* paint the background of the control that doesn't contain any items */
SubtractRect(&rcTemp, &infoPtr->rcList, &infoPtr->rcView);
LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
/* nothing to draw, return here */
if(GETITEMCOUNT(infoPtr) == 0)
return;
LISTVIEW_GetOrigin(hwnd, &ptOrigin);
for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
{
if (cdmode & CDRF_NOTIFYITEMDRAW)
cditemmode = LISTVIEW_SendCustomDrawItemNotify (hwnd, hdc, i, 0,
CDDS_ITEMPREPAINT);
if (cditemmode & CDRF_SKIPDEFAULT)
continue;
LISTVIEW_GetItemPosition(hwnd, i, &ptPosition);
ptPosition.x += ptOrigin.x;
ptPosition.y += ptOrigin.y;
if (ptPosition.y + infoPtr->nItemHeight > infoPtr->rcList.top)
{
if (ptPosition.x + infoPtr->nItemWidth > infoPtr->rcList.left)
{
if (ptPosition.y < infoPtr->rcList.bottom)
{
if (ptPosition.x < infoPtr->rcList.right)
{
rcItem.top = ptPosition.y;
rcItem.left = ptPosition.x;
rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
rcItem.right = rcItem.left + infoPtr->nItemWidth;
if (bSmall == FALSE)
{
LISTVIEW_DrawLargeItem(hwnd, hdc, i, rcItem, &SuggestedFocus);
}
else
{
LISTVIEW_DrawItem(hwnd, hdc, i, rcItem, FALSE, &SuggestedFocus);
}
/*
* Draw Focus Rect
*/
if (LISTVIEW_GetItemState(hwnd,i,LVIS_FOCUSED) &&
infoPtr->bFocus)
Rectangle(hdc, SuggestedFocus.left, SuggestedFocus.top,
SuggestedFocus.right,SuggestedFocus.bottom);
}
}
}
}
if (cditemmode & CDRF_NOTIFYPOSTPAINT)
LISTVIEW_SendCustomDrawItemNotify(hwnd, hdc, i, 0,
CDDS_ITEMPOSTPAINT);
}
}
/***
* DESCRIPTION:
* Draws listview items.
*
* PARAMETER(S):
* [I] HWND : window handle
* [I] HDC : device context handle
*
* RETURN:
* NoneX
*/
static VOID LISTVIEW_Refresh(HWND hwnd, HDC hdc)
{
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
HFONT hOldFont;
HPEN hPen, hOldPen;
DWORD cdmode;
RECT rect;
GetClientRect(hwnd, &rect);
cdmode = LISTVIEW_SendCustomDrawNotify(hwnd,CDDS_PREPAINT,hdc,rect);
if (cdmode == CDRF_SKIPDEFAULT) return;
/* select font */
hOldFont = SelectObject(hdc, infoPtr->hFont);
/* select the dotted pen (for drawing the focus box) */
hPen = CreatePen(PS_ALTERNATE, 1, 0);
hOldPen = SelectObject(hdc, hPen);
/* select transparent brush (for drawing the focus box) */
SelectObject(hdc, GetStockObject(NULL_BRUSH));
if (uView == LVS_LIST)
{
LISTVIEW_RefreshList(hwnd, hdc, cdmode);
}
else if (uView == LVS_REPORT)
{
LISTVIEW_RefreshReport(hwnd, hdc, cdmode);
}
else if (uView == LVS_SMALLICON)
{
LISTVIEW_RefreshIcon(hwnd, hdc, TRUE, cdmode);
}
else if (uView == LVS_ICON)
{
LISTVIEW_RefreshIcon(hwnd, hdc, FALSE, cdmode);
}
/* unselect objects */
SelectObject(hdc, hOldFont);
SelectObject(hdc, hOldPen);
/* delete pen */
DeleteObject(hPen);
if (cdmode & CDRF_NOTIFYPOSTPAINT)
LISTVIEW_SendCustomDrawNotify(hwnd, CDDS_POSTPAINT, hdc, rect);
}
/***
* DESCRIPTION:
* Calculates the approximate width and height of a given number of items.
*
* PARAMETER(S):
* [I] HWND : window handle
* [I] INT : number of items
* [I] INT : width
* [I] INT : height
*
* RETURN:
* Returns a DWORD. The width in the low word and the height in high word.
*/
static LRESULT LISTVIEW_ApproximateViewRect(HWND hwnd, INT nItemCount,
WORD wWidth, WORD wHeight)
{
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
INT nItemCountPerColumn = 1;
INT nColumnCount = 0;
DWORD dwViewRect = 0;
if (nItemCount == -1)
{
nItemCount = GETITEMCOUNT(infoPtr);
}
if (uView == LVS_LIST)
{
if (wHeight == 0xFFFF)
{
/* use current height */
wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
}
if (wHeight < infoPtr->nItemHeight)
{
wHeight = infoPtr->nItemHeight;
}
if (nItemCount > 0)
{
if (infoPtr->nItemHeight > 0)
{
nItemCountPerColumn = wHeight / infoPtr->nItemHeight;
if (nItemCountPerColumn == 0)
{
nItemCountPerColumn = 1;
}
if (nItemCount % nItemCountPerColumn != 0)
{
nColumnCount = nItemCount / nItemCountPerColumn;
}
else
{
nColumnCount = nItemCount / nItemCountPerColumn + 1;
}
}
}
/* Microsoft padding magic */
wHeight = nItemCountPerColumn * infoPtr->nItemHeight + 2;
wWidth = nColumnCount * infoPtr->nItemWidth + 2;
dwViewRect = MAKELONG(wWidth, wHeight);
}
else if (uView == LVS_REPORT)
{
/* TO DO */
}
else if (uView == LVS_SMALLICON)
{
/* TO DO */
}
else if (uView == LVS_ICON)
{
/* TO DO */
}
return dwViewRect;
}
/***
* DESCRIPTION:
* Arranges listview items in icon display mode.
*
* PARAMETER(S):
* [I] HWND : window handle
* [I] INT : alignment code
*
* RETURN:
* SUCCESS : TRUE
* FAILURE : FALSE
*/
static LRESULT LISTVIEW_Arrange(HWND hwnd, INT nAlignCode)
{
UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
BOOL bResult = FALSE;
if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
{
switch (nAlignCode)
{
case LVA_ALIGNLEFT:
/* TO DO */
break;
case LVA_ALIGNTOP:
/* TO DO */
break;
case LVA_DEFAULT:
/* TO DO */
break;
case LVA_SNAPTOGRID:
/* TO DO */
break;
}
}
return bResult;
}
/* << LISTVIEW_CreateDragImage >> */
/***
* DESCRIPTION:
* Removes all listview items and subitems.
*
* PARAMETER(S):
* [I] HWND : window handle
*
* RETURN:
* SUCCESS : TRUE
* FAILURE : FALSE
*/
static LRESULT LISTVIEW_DeleteAllItems(HWND hwnd)
{
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
LONG lCtrlId = GetWindowLongA(hwnd, GWL_ID);
LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
UINT uView = lStyle & LVS_TYPEMASK;
LISTVIEW_ITEM *lpItem;
LISTVIEW_SUBITEM *lpSubItem;
NMLISTVIEW nmlv;
BOOL bSuppress;
BOOL bResult = FALSE;
INT i;
INT j;
HDPA hdpaSubItems;
TRACE("(hwnd=%x,)\n", hwnd);
LISTVIEW_RemoveAllSelections(hwnd);
infoPtr->nSelectionMark=-1;
infoPtr->nFocusedItem=-1;
/* But we are supposed to leave nHotItem as is! */
if (lStyle & LVS_OWNERDATA)
{
infoPtr->hdpaItems->nItemCount = 0;
InvalidateRect(hwnd, NULL, TRUE);
return TRUE;
}
if (GETITEMCOUNT(infoPtr) > 0)
{
/* initialize memory */
ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
/* send LVN_DELETEALLITEMS notification */
nmlv.hdr.hwndFrom = hwnd;
nmlv.hdr.idFrom = lCtrlId;
nmlv.hdr.code = LVN_DELETEALLITEMS;
nmlv.iItem = -1;
/* verify if subsequent LVN_DELETEITEM notifications should be
suppressed */
bSuppress = ListView_LVNotify(GetParent(hwnd), lCtrlId, &nmlv);
for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
{
hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
if (hdpaSubItems != NULL)
{
for (j = 1; j < hdpaSubItems->nItemCount; j++)
{
lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, j);
if (lpSubItem != NULL)
{
/* free subitem string */
if ((lpSubItem->pszText != NULL) &&
(lpSubItem->pszText != LPSTR_TEXTCALLBACKA))
{
COMCTL32_Free(lpSubItem->pszText);
}
/* free subitem */
COMCTL32_Free(lpSubItem);
}
}
lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
if (lpItem != NULL)
{
if (bSuppress == FALSE)
{
/* send LVN_DELETEITEM notification */
nmlv.hdr.code = LVN_DELETEITEM;
nmlv.iItem = i;
nmlv.lParam = lpItem->lParam;
ListView_LVNotify(GetParent(hwnd), lCtrlId, &nmlv);
}
/* free item string */
if ((lpItem->pszText != NULL) &&
(lpItem->pszText != LPSTR_TEXTCALLBACKA))
{
COMCTL32_Free(lpItem->pszText);
}
/* free item */
COMCTL32_Free(lpItem);
}
DPA_Destroy(hdpaSubItems);
}
}
/* reinitialize listview memory */
bResult = DPA_DeleteAllPtrs(infoPtr->hdpaItems);
/* align items (set position of each item) */
if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
{
if (lStyle & LVS_ALIGNLEFT)
{
LISTVIEW_AlignLeft(hwnd);
}
else
{
LISTVIEW_AlignTop(hwnd);
}
}
LISTVIEW_UpdateScroll(hwnd);
/* invalidate client area (optimization needed) */
InvalidateRect(hwnd, NULL, TRUE);
}
return bResult;
}
/***
* DESCRIPTION:
* Removes a column from the listview control.
*
* PARAMETER(S):
* [I] HWND : window handle
* [I] INT : column index
*
* RETURN:
* SUCCESS : TRUE
* FAILURE : FALSE
*/
static LRESULT LISTVIEW_DeleteColumn(HWND hwnd, INT nColumn)
{
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
UINT uOwnerData = GetWindowLongA(hwnd, GWL_STYLE) & LVS_OWNERDATA;
BOOL bResult = FALSE;
if (Header_DeleteItem(infoPtr->hwndHeader, nColumn) != FALSE)
{
if (!uOwnerData)
bResult = LISTVIEW_RemoveColumn(infoPtr->hdpaItems, nColumn);
/* Need to reset the item width when deleting a column */
infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
/* reset scroll parameters */
if (uView == LVS_REPORT)
{
/* update scrollbar(s) */
LISTVIEW_UpdateScroll(hwnd);
/* refresh client area */
InvalidateRect(hwnd, NULL, FALSE);
}
}
return bResult;
}
/***
* DESCRIPTION:
* Removes an item from the listview control.
*
* PARAMETER(S):
* [I] HWND : window handle
* [I] INT : item index
*
* RETURN:
* SUCCESS : TRUE
* FAILURE : FALSE
*/
static LRESULT LISTVIEW_DeleteItem(HWND hwnd, INT nItem)
{
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
UINT uView = lStyle & LVS_TYPEMASK;
LONG lCtrlId = GetWindowLongA(hwnd, GWL_ID);
NMLISTVIEW nmlv;
BOOL bResult = FALSE;
HDPA hdpaSubItems;
LISTVIEW_ITEM *lpItem;
LISTVIEW_SUBITEM *lpSubItem;
INT i;
LVITEMA item;
TRACE("(hwnd=%x,nItem=%d)\n", hwnd, nItem);
/* First, send LVN_DELETEITEM notification. */
memset(&nmlv, 0, sizeof (NMLISTVIEW));
nmlv.hdr.hwndFrom = hwnd;
nmlv.hdr.idFrom = lCtrlId;
nmlv.hdr.code = LVN_DELETEITEM;
nmlv.iItem = nItem;
SendMessageA(GetParent(hwnd), WM_NOTIFY, (WPARAM)lCtrlId,
(LPARAM)&nmlv);
/* remove it from the selection range */
ZeroMemory(&item,sizeof(LVITEMA));
item.stateMask = LVIS_SELECTED;
LISTVIEW_SetItemState(hwnd,nItem,&item);
if (lStyle & LVS_OWNERDATA)
{
infoPtr->hdpaItems->nItemCount --;
InvalidateRect(hwnd, NULL, TRUE);
return TRUE;
}
if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
{
/* initialize memory */
ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
hdpaSubItems = (HDPA)DPA_DeletePtr(infoPtr->hdpaItems, nItem);
if (hdpaSubItems != NULL)
{
for (i = 1; i < hdpaSubItems->nItemCount; i++)
{
lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
if (lpSubItem != NULL)
{
/* free item string */
if ((lpSubItem->pszText != NULL) &&
(lpSubItem->pszText != LPSTR_TEXTCALLBACKA))
{
COMCTL32_Free(lpSubItem->pszText);
}
/* free item */
COMCTL32_Free(lpSubItem);
}
}
lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
if (lpItem != NULL)
{
/* free item string */
if ((lpItem->pszText != NULL) &&
(lpItem->pszText != LPSTR_TEXTCALLBACKA))
{
COMCTL32_Free(lpItem->pszText);
}
/* free item */
COMCTL32_Free(lpItem);
}
bResult = DPA_Destroy(hdpaSubItems);
}
LISTVIEW_ShiftIndices(hwnd,nItem,-1);
/* align items (set position of each item) */
if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
{
if (lStyle & LVS_ALIGNLEFT)
{
LISTVIEW_AlignLeft(hwnd);
}
else
{
LISTVIEW_AlignTop(hwnd);
}
}
LISTVIEW_UpdateScroll(hwnd);
/* refresh client area */
InvalidateRect(hwnd, NULL, TRUE);
}
return bResult;
}
/***
* DESCRIPTION:
* Return edit control handle of current edit label
*
* PARAMETER(S):
* [I] HWND : window handle
*
* RETURN:
* SUCCESS : HWND
* FAILURE : 0
*/
static LRESULT LISTVIEW_GetEditControl(HWND hwnd)
{
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
return infoPtr->hwndEdit;
}
/***
* DESCRIPTION:
* Callback implementation for editlabel control
*
* PARAMETER(S):
* [I] HWND : window handle
* [I] LPSTR : modified text
* [I] DWORD : item index
*
* RETURN:
* SUCCESS : TRUE
* FAILURE : FALSE
*/
static BOOL LISTVIEW_EndEditLabel(HWND hwnd, LPSTR pszText, DWORD nItem)
{
NMLVDISPINFOA dispInfo;
LISTVIEW_ITEM *lpItem;
INT nCtrlId = GetWindowLongA(hwnd, GWL_ID);
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
HDPA hdpaSubItems;
BOOL bUpdateItemText;
LISTVIEW_ITEM lvItemRef;
LVITEMA item;
ZeroMemory(&dispInfo, sizeof(NMLVDISPINFOA));
if (!(lStyle & LVS_OWNERDATA))
{
if (NULL == (hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem)))
return FALSE;
if (NULL == (lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)))
return FALSE;
}
else
{
ZeroMemory(&lvItemRef,sizeof(LISTVIEW_ITEM));
ZeroMemory(&item,sizeof(LVITEMA));
item.iItem = nItem;
item.iSubItem = 0;
item.mask = LVIF_PARAM | LVIF_STATE;
ListView_GetItemA(hwnd,&item);
lvItemRef.state = item.state;
lvItemRef.iImage = item.iImage;
lvItemRef.lParam = item.lParam;
lpItem = &lvItemRef;
}
dispInfo.hdr.hwndFrom = hwnd;
dispInfo.hdr.idFrom = nCtrlId;
dispInfo.hdr.code = LVN_ENDLABELEDITA;
dispInfo.item.mask = 0;
dispInfo.item.iItem = nItem;
dispInfo.item.state = lpItem->state;
dispInfo.item.stateMask = 0;
dispInfo.item.pszText = pszText;
dispInfo.item.cchTextMax = pszText ? strlen(pszText) : 0;
dispInfo.item.iImage = lpItem->iImage;
dispInfo.item.lParam = lpItem->lParam;
infoPtr->hwndEdit = 0;
bUpdateItemText = ListView_Notify(GetParent(hwnd), nCtrlId, &dispInfo);
/* Do we need to update the Item Text */
if(bUpdateItemText)
{
if ((lpItem->pszText != LPSTR_TEXTCALLBACKA)&&(!(lStyle & LVS_OWNERDATA)))
{
Str_SetPtrA(&lpItem->pszText, pszText);
}
}
return TRUE;
}
/***
* DESCRIPTION:
* Begin in place editing of specified list view item
*
* PARAMETER(S):
* [I] HWND : window handle
* [I] INT : item index
*
* RETURN:
* SUCCESS : TRUE
* FAILURE : FALSE
*/
static HWND LISTVIEW_EditLabelA(HWND hwnd, INT nItem)
{
NMLVDISPINFOA dispInfo;
RECT rect;
LISTVIEW_ITEM *lpItem;
HWND hedit;
HINSTANCE hinst = GetWindowLongA(hwnd, GWL_HINSTANCE);
INT nCtrlId = GetWindowLongA(hwnd, GWL_ID);
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
HDPA hdpaSubItems;
CHAR szDispText[DISP_TEXT_SIZE];
LVITEMA lvItem,item;
LISTVIEW_ITEM lvItemRef;
LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
if (~GetWindowLongA(hwnd, GWL_STYLE) & LVS_EDITLABELS)
return FALSE;
/* Is the EditBox still there, if so remove it */
if(infoPtr->hwndEdit != 0)
{
SetFocus(hwnd);
}
LISTVIEW_SetSelection(hwnd, nItem);
LISTVIEW_SetItemFocus(hwnd, nItem);
ZeroMemory(&dispInfo, sizeof(NMLVDISPINFOA));
if (!(lStyle & LVS_OWNERDATA))
{
if (NULL == (hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem)))
return 0;
if (NULL == (lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)))
return 0;
}
else
{
ZeroMemory(&lvItemRef,sizeof(LISTVIEW_ITEM));
ZeroMemory(&item,sizeof(LVITEMA));
item.iItem = nItem;
item.iSubItem = 0;
item.mask = LVIF_PARAM | LVIF_STATE;
ListView_GetItemA(hwnd,&item);
lvItemRef.iImage = item.iImage;
lvItemRef.state = item.state;
lvItemRef.lParam = item.lParam;
lpItem = &lvItemRef;
}
/* get information needed for drawing the item */
ZeroMemory(&lvItem, sizeof(LVITEMA));
lvItem.mask = LVIF_TEXT;
lvItem.iItem = nItem;
lvItem.iSubItem = 0;
lvItem.cchTextMax = DISP_TEXT_SIZE;
lvItem.pszText = szDispText;
ListView_GetItemA(hwnd, &lvItem);
dispInfo.hdr.hwndFrom = hwnd;
dispInfo.hdr.idFrom = nCtrlId;
dispInfo.hdr.code = LVN_BEGINLABELEDITA;
dispInfo.item.mask = 0;
dispInfo.item.iItem = nItem;
dispInfo.item.state = lpItem->state;
dispInfo.item.stateMask = 0;
dispInfo.item.pszText = lvItem.pszText;
dispInfo.item.cchTextMax = strlen(lvItem.pszText);
dispInfo.item.iImage = lpItem->iImage;
dispInfo.item.lParam = lpItem->lParam;
if (ListView_LVNotify(GetParent(hwnd), nCtrlId, &dispInfo))
return 0;
rect.left = LVIR_LABEL;
if (!LISTVIEW_GetItemRect(hwnd, nItem, &rect))
return 0;
if (!(hedit = CreateEditLabel(szDispText , WS_VISIBLE,
rect.left-2, rect.top-1, 0,
rect.bottom - rect.top+2,
hwnd, hinst, LISTVIEW_EndEditLabel, nItem)))
return 0;
infoPtr->hwndEdit = hedit;
SetFocus(hedit);
SendMessageA(hedit, EM_SETSEL, 0, -1);
return hedit;
}
/***
* DESCRIPTION:
* Ensures the specified item is visible, scrolling into view if necessary.
*
* PARAMETER(S):
* [I] HWND : window handle
* [I] INT : item index
* [I] BOOL : partially or entirely visible
*
* RETURN:
* SUCCESS : TRUE
* FAILURE : FALSE
*/
static BOOL LISTVIEW_EnsureVisible(HWND hwnd, INT nItem, BOOL bPartial)
{
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
INT nScrollPosHeight = 0;
INT nScrollPosWidth = 0;
SCROLLINFO scrollInfo;
RECT rcItem;
BOOL bRedraw = FALSE;
ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
scrollInfo.cbSize = sizeof(SCROLLINFO);
scrollInfo.fMask = SIF_POS;
/* ALWAYS bPartial == FALSE, FOR NOW! */
rcItem.left = LVIR_BOUNDS;
if (LISTVIEW_GetItemRect(hwnd, nItem, &rcItem) != FALSE)
{
if (rcItem.left < infoPtr->rcList.left)
{
if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) != FALSE)
{
/* scroll left */
bRedraw = TRUE;
if (uView == LVS_LIST)
{
nScrollPosWidth = infoPtr->nItemWidth;
rcItem.left += infoPtr->rcList.left;
}
else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
{
nScrollPosWidth = LISTVIEW_SCROLL_DIV_SIZE;
rcItem.left += infoPtr->rcList.left;
}
/* When in LVS_REPORT view, the scroll position should
not be updated. */
if (nScrollPosWidth != 0)
{
if (rcItem.left % nScrollPosWidth == 0)
{
scrollInfo.nPos += rcItem.left / nScrollPosWidth;
}
else
{
scrollInfo.nPos += rcItem.left / nScrollPosWidth - 1;
}
SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
}
}
}
else if (rcItem.right > infoPtr->rcList.right)
{
if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) != FALSE)
{
/* scroll right */
bRedraw = TRUE;
if (uView == LVS_LIST)
{
rcItem.right -= infoPtr->rcList.right;
nScrollPosWidth = infoPtr->nItemWidth;
}
else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
{
rcItem.right -= infoPtr->rcList.right;
nScrollPosWidth = LISTVIEW_SCROLL_DIV_SIZE;
}
/* When in LVS_REPORT view, the scroll position should
not be updated. */
if (nScrollPosWidth != 0)
{
if (rcItem.right % nScrollPosWidth == 0)
{
scrollInfo.nPos += rcItem.right / nScrollPosWidth;
}
else
{
scrollInfo.nPos += rcItem.right / nScrollPosWidth + 1;
}
SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
}
}
}
if (rcItem.top < infoPtr->rcList.top)
{
/* scroll up */
bRedraw = TRUE;
if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
{
if (uView == LVS_REPORT)
{
rcItem.top -= infoPtr->rcList.top;
nScrollPosHeight = infoPtr->nItemHeight;
}
else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
{
nScrollPosHeight = LISTVIEW_SCROLL_DIV_SIZE;
rcItem.top += infoPtr->rcList.top;
}
if (rcItem.top % nScrollPosHeight == 0)
{
scrollInfo.nPos += rcItem.top / nScrollPosHeight;
}
else
{
scrollInfo.nPos += rcItem.top / nScrollPosHeight - 1;
}
SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE);
}
}
else if (rcItem.bottom > infoPtr->rcList.bottom)
{
/* scroll down */
bRedraw = TRUE;
if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
{
if (uView == LVS_REPORT)
{
rcItem.bottom -= infoPtr->rcList.bottom;
nScrollPosHeight = infoPtr->nItemHeight;
}
else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
{
nScrollPosHeight = LISTVIEW_SCROLL_DIV_SIZE;
rcItem.bottom -= infoPtr->rcList.bottom;
}
if (rcItem.bottom % nScrollPosHeight == 0)
{
scrollInfo.nPos += rcItem.bottom / nScrollPosHeight;
}
else
{
scrollInfo.nPos += rcItem.bottom / nScrollPosHeight + 1;
}
SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE);
}
}
}
if(bRedraw)
InvalidateRect(hwnd,NULL,TRUE);
return TRUE;
}
/***
* DESCRIPTION:
* Retrieves the nearest item, given a position and a direction.
*
* PARAMETER(S):
* [I] HWND : window handle
* [I] POINT : start position
* [I] UINT : direction
*
* RETURN:
* Item index if successdful, -1 otherwise.
*/
static INT LISTVIEW_GetNearestItem(HWND hwnd, POINT pt, UINT vkDirection)
{
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
LVHITTESTINFO lvHitTestInfo;
INT nItem = -1;
RECT rcView;
if (LISTVIEW_GetViewRect(hwnd, &rcView) != FALSE)
{
ZeroMemory(&lvHitTestInfo, sizeof(LVHITTESTINFO));
LISTVIEW_GetOrigin(hwnd, &lvHitTestInfo.pt);
lvHitTestInfo.pt.x += pt.x;
lvHitTestInfo.pt.y += pt.y;
do
{
if (vkDirection == VK_DOWN)
{
lvHitTestInfo.pt.y += infoPtr->nItemHeight;
}
else if (vkDirection == VK_UP)
{
lvHitTestInfo.pt.y -= infoPtr->nItemHeight;
}
else if (vkDirection == VK_LEFT)
{
lvHitTestInfo.pt.x -= infoPtr->nItemWidth;
}
else if (vkDirection == VK_RIGHT)
{
lvHitTestInfo.pt.x += infoPtr->nItemWidth;
}
if (PtInRect(&rcView, lvHitTestInfo.pt) == FALSE)
{
return -1;
}
else
{
nItem = LISTVIEW_HitTestItem(hwnd, &lvHitTestInfo, TRUE);
}
}
while (nItem == -1);
}
return nItem;
}
/***
* DESCRIPTION:
* Searches for an item with specific characteristics.
*
* PARAMETER(S):
* [I] HWND : window handle
* [I] INT : base item index
* [I] LPLVFINDINFO : item information to look for
*
* RETURN:
* SUCCESS : index of item
* FAILURE : -1
*/
static LRESULT LISTVIEW_FindItem(HWND hwnd, INT nStart,
LPLVFINDINFO lpFindInfo)
{
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
POINT ptItem;
CHAR szDispText[DISP_TEXT_SIZE];
LVITEMA lvItem;
BOOL bWrap = FALSE;
INT nItem = nStart;
INT nLast = GETITEMCOUNT(infoPtr);
if ((nItem >= -1) && (lpFindInfo != NULL))
{
ZeroMemory(&lvItem, sizeof(LVITEMA));
if (lpFindInfo->flags & LVFI_PARAM)
{
lvItem.mask |= LVIF_PARAM;
}
if (lpFindInfo->flags & LVFI_STRING)
{
lvItem.mask |= LVIF_TEXT;
lvItem.pszText = szDispText;
lvItem.cchTextMax = DISP_TEXT_SIZE;
}
if (lpFindInfo->flags & LVFI_PARTIAL)
{
lvItem.mask |= LVIF_TEXT;
lvItem.pszText = szDispText;
lvItem.cchTextMax = DISP_TEXT_SIZE;
}
if (lpFindInfo->flags & LVFI_WRAP)
{
bWrap = TRUE;
}
if (lpFindInfo->flags & LVFI_NEARESTXY)
{
ptItem.x = lpFindInfo->pt.x;
ptItem.y = lpFindInfo->pt.y;
}
while (1)
{
while (nItem < nLast)
{
if (lpFindInfo->flags & LVFI_NEARESTXY)
{
nItem = LISTVIEW_GetNearestItem(hwnd, ptItem,
lpFindInfo->vkDirection);
if (nItem != -1)
{
/* get position of the new item index */
if (ListView_GetItemPosition(hwnd, nItem, &ptItem) == FALSE)
return -1;
}
else
return -1;
}
else
{
nItem++;
}
lvItem.iItem = nItem;
lvItem.iSubItem = 0;
if (LISTVIEW_GetItemA(hwnd, &lvItem, TRUE) != FALSE)
{
if (lvItem.mask & LVIF_TEXT)
{
if (lpFindInfo->flags & LVFI_PARTIAL)
{
if (strstr(lvItem.pszText, lpFindInfo->psz) == NULL)
continue;
}
else
{
if (strcmp(lvItem.pszText, lpFindInfo->psz) != 0)
continue;
}
}
if (lvItem.mask & LVIF_PARAM)
{
if (lpFindInfo->lParam != lvItem.lParam)
continue;
}
return nItem;
}
}
if (bWrap != FALSE)
{
nItem = -1;
nLast = nStart + 1;
bWrap = FALSE;
}
else
{
return -1;
}
}
}
return -1;
}
/***
* DESCRIPTION:
* Retrieves the background color of the listview control.
*
* PARAMETER(S):
* [I] HWND : window handle
*
* RETURN:
* COLORREF associated with the background.
*/
static LRESULT LISTVIEW_GetBkColor(HWND hwnd)
{
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
return infoPtr->clrBk;
}
/***
* DESCRIPTION:
* Retrieves the background image of the listview control.
*
* PARAMETER(S):
* [I] HWND : window handle
* [O] LPLVMKBIMAGE : background image attributes
*
* RETURN:
* SUCCESS : TRUE
* FAILURE : FALSE
*/
/* static LRESULT LISTVIEW_GetBkImage(HWND hwnd, LPLVBKIMAGE lpBkImage) */
/* { */
/* FIXME (listview, "empty stub!\n"); */
/* return FALSE; */
/* } */
/***
* DESCRIPTION:
* Retrieves the callback mask.
*
* PARAMETER(S):
* [I] HWND : window handle
*
* RETURN:
* Value of mask
*/
static UINT LISTVIEW_GetCallbackMask(HWND hwnd)
{
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
return infoPtr->uCallbackMask;
}
/***
* DESCRIPTION:
* Retrieves column attributes.
*
* PARAMETER(S):
* [I] HWND : window handle
* [I] INT : column index
* [IO] LPLVCOLUMNA : column information
*
* RETURN:
* SUCCESS : TRUE
* FAILURE : FALSE
*/
static LRESULT LISTVIEW_GetColumnA(HWND hwnd, INT nItem, LPLVCOLUMNA lpColumn)
{
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
HDITEMA hdi;
BOOL bResult = FALSE;
if (lpColumn != NULL)
{
/* initialize memory */
ZeroMemory(&hdi, sizeof(HDITEMA));
if (lpColumn->mask & LVCF_FMT)
{
hdi.mask |= HDI_FORMAT;
}
if (lpColumn->mask & LVCF_WIDTH)
{
hdi.mask |= HDI_WIDTH;
}
if (lpColumn->mask & LVCF_TEXT)
{
hdi.mask |= HDI_TEXT;
hdi.cchTextMax = lpColumn->cchTextMax;
hdi.pszText = lpColumn->pszText;
}
if (lpColumn->mask & LVCF_IMAGE)
{
hdi.mask |= HDI_IMAGE;
}
if (lpColumn->mask & LVCF_ORDER)
{
hdi.mask |= HDI_ORDER;
}
bResult = Header_GetItemA(infoPtr->hwndHeader, nItem, &hdi);
if (bResult != FALSE)
{
if (lpColumn->mask & LVCF_FMT)
{
lpColumn->fmt = 0;
if (hdi.fmt & HDF_LEFT)
{
lpColumn->fmt |= LVCFMT_LEFT;
}
else if (hdi.fmt & HDF_RIGHT)
{
lpColumn->fmt |= LVCFMT_RIGHT;
}
else if (hdi.fmt & HDF_CENTER)
{
lpColumn->fmt |= LVCFMT_CENTER;
}
if (hdi.fmt & HDF_IMAGE)
{
lpColumn->fmt |= LVCFMT_COL_HAS_IMAGES;
}
if (hdi.fmt & HDF_BITMAP_ON_RIGHT)
{
lpColumn->fmt |= LVCFMT_BITMAP_ON_RIGHT;
}
}
if (lpColumn->mask & LVCF_WIDTH)
{
lpColumn->cx = hdi.cxy;
}
if (lpColumn->mask & LVCF_IMAGE)
{
lpColumn->iImage = hdi.iImage;
}
if (lpColumn->mask & LVCF_ORDER)
{
lpColumn->iOrder = hdi.iOrder;
}
}
}
return bResult;
}
/* LISTVIEW_GetColumnW */
static LRESULT LISTVIEW_GetColumnOrderArray(HWND hwnd, INT iCount, LPINT lpiArray)
{
/* LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0); */
INT i;
if (!lpiArray)
return FALSE;
/* little hack */
for (i = 0; i < iCount; i++)
lpiArray[i] = i;
return TRUE;
}
/***
* DESCRIPTION:
* Retrieves the column width.
*
* PARAMETER(S):
* [I] HWND : window handle
* [I] int : column index
*
* RETURN:
* SUCCESS : column width
* FAILURE : zero
*/
static LRESULT LISTVIEW_GetColumnWidth(HWND hwnd, INT nColumn)
{
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
INT nColumnWidth = 0;
HDITEMA hdi;
if (uView == LVS_LIST)
{
nColumnWidth = infoPtr->nItemWidth;
}
else if (uView == LVS_REPORT)
{
/* get column width from header */
ZeroMemory(&hdi, sizeof(HDITEMA));
hdi.mask = HDI_WIDTH;
if (Header_GetItemA(infoPtr->hwndHeader, nColumn, &hdi) != FALSE)
{
nColumnWidth = hdi.cxy;
}
}
return nColumnWidth;
}
/***
* DESCRIPTION:
* In list or report display mode, retrieves the number of items that can fit
* vertically in the visible area. In icon or small icon display mode,
* retrieves the total number of visible items.
*
* PARAMETER(S):
* [I] HWND : window handle
*
* RETURN:
* Number of fully visible items.
*/
static LRESULT LISTVIEW_GetCountPerPage(HWND hwnd)
{
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
INT nItemCount = 0;
if (uView == LVS_LIST)
{
if (infoPtr->rcList.right > infoPtr->nItemWidth)
{
nItemCount = LISTVIEW_GetCountPerRow(hwnd) *
LISTVIEW_GetCountPerColumn(hwnd);
}
}
else if (uView == LVS_REPORT)
{
nItemCount = LISTVIEW_GetCountPerColumn(hwnd);
}
else
{
nItemCount = GETITEMCOUNT(infoPtr);
}
return nItemCount;
}
/* LISTVIEW_GetEditControl */
/***
* DESCRIPTION:
* Retrieves the extended listview style.
*
* PARAMETERS:
* [I] HWND : window handle
*
* RETURN:
* SUCCESS : previous style
* FAILURE : 0
*/
static LRESULT LISTVIEW_GetExtendedListViewStyle(HWND hwnd)
{
LISTVIEW_INFO *infoPtr;
/* make sure we can get the listview info */
if (!(infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0)))
return (0);
return (infoPtr->dwExStyle);
}
/***
* DESCRIPTION:
* Retrieves the handle to the header control.
*
* PARAMETER(S):
* [I] HWND : window handle
*
* RETURN:
* Header handle.
*/
static LRESULT LISTVIEW_GetHeader(HWND hwnd)
{
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
return infoPtr->hwndHeader;
}
/* LISTVIEW_GetHotCursor */
/***
* DESCRIPTION:
* Returns the time that the mouse cursor must hover over an item
* before it is selected.
*
* PARAMETER(S):
* [I] HWND : window handle
*
* RETURN:
* Returns the previously set hover time or (DWORD)-1 to indicate that the
* hover time is set to the default hover time.
*/
static LRESULT LISTVIEW_GetHoverTime(HWND hwnd)
{
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
return infoPtr->dwHoverTime;
}
/***
* DESCRIPTION:
* Retrieves an image list handle.
*
* PARAMETER(S):
* [I] HWND : window handle
* [I] INT : image list identifier
*
* RETURN:
* SUCCESS : image list handle
* FAILURE : NULL
*/
static LRESULT LISTVIEW_GetImageList(HWND hwnd, INT nImageList)
{
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
HIMAGELIST himl = NULL;
switch (nImageList)
{
case LVSIL_NORMAL:
himl = infoPtr->himlNormal;
break;
case LVSIL_SMALL:
himl = infoPtr->himlSmall;
break;
case LVSIL_STATE:
himl = infoPtr->himlState;
break;
}
return (LRESULT)himl;
}
/* LISTVIEW_GetISearchString */
/***
* DESCRIPTION:
* Retrieves item attributes.
*
* PARAMETER(S):
* [I] HWND : window handle
* [IO] LPLVITEMA : item info
* [I] internal : if true then we will use tricks that avoid copies
* but are not compatible with the regular interface
*
* RETURN:
* SUCCESS : TRUE
* FAILURE : FALSE
*/
static LRESULT LISTVIEW_GetItemA(HWND hwnd, LPLVITEMA lpLVItem, BOOL internal)
{
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
LONG lCtrlId = GetWindowLongA(hwnd, GWL_ID);
NMLVDISPINFOA dispInfo;
LISTVIEW_SUBITEM *lpSubItem;
LISTVIEW_ITEM *lpItem;
INT* piImage;
LPSTR* ppszText;
HDPA hdpaSubItems;
LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
/* In the following:
* lpLVItem describes the information requested by the user
* lpItem/lpSubItem is what we have
* dispInfo is a structure we use to request the missing
* information from the application
*/
TRACE("(hwnd=%x, lpLVItem=%p)\n", hwnd, lpLVItem);
if ((lpLVItem == NULL) ||
(lpLVItem->iItem < 0) ||
(lpLVItem->iItem >= GETITEMCOUNT(infoPtr))
)
return FALSE;
if (lStyle & LVS_OWNERDATA)
{
if (lpLVItem->mask & ~LVIF_STATE)
{
dispInfo.hdr.hwndFrom = hwnd;
dispInfo.hdr.idFrom = lCtrlId;
dispInfo.hdr.code = LVN_GETDISPINFOA;
memcpy(&dispInfo.item,lpLVItem,sizeof(LVITEMA));
ListView_Notify(GetParent(hwnd), lCtrlId, &dispInfo);
memcpy(lpLVItem,&dispInfo.item,sizeof(LVITEMA));
}
if ((lpLVItem->mask & LVIF_STATE)&&(lpLVItem->iSubItem == 0))
{
lpLVItem->state = 0;
if (infoPtr->nFocusedItem == lpLVItem->iItem)
lpLVItem->state |= LVIS_FOCUSED;
if (LISTVIEW_IsSelected(hwnd,lpLVItem->iItem))
lpLVItem->state |= LVIS_SELECTED;
}
return TRUE;
}
hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
if (hdpaSubItems == NULL)
return FALSE;
lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
if (lpItem == NULL)
return FALSE;
ZeroMemory(&dispInfo, sizeof(NMLVDISPINFOA));
if (lpLVItem->iSubItem == 0)
{
piImage=&lpItem->iImage;
ppszText=&lpItem->pszText;
if ((infoPtr->uCallbackMask != 0) && (lpLVItem->mask & LVIF_STATE))
{
dispInfo.item.mask |= LVIF_STATE;
dispInfo.item.stateMask = infoPtr->uCallbackMask;
}
}
else
{
lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
if (lpSubItem != NULL)
{
piImage=&lpSubItem->iImage;
ppszText=&lpSubItem->pszText;
}
else
{
piImage=NULL;
ppszText=NULL;
}
}
if ((lpLVItem->mask & LVIF_IMAGE) &&
((piImage==NULL) || (*piImage == I_IMAGECALLBACK)))
{
dispInfo.item.mask |= LVIF_IMAGE;
}
if ((lpLVItem->mask & LVIF_TEXT) &&
((ppszText==NULL) || (*ppszText == LPSTR_TEXTCALLBACKA)))
{
dispInfo.item.mask |= LVIF_TEXT;
dispInfo.item.pszText = lpLVItem->pszText;
dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
}
if (dispInfo.item.mask != 0)
{
/* We don't have all the requested info, query the application */
dispInfo.hdr.hwndFrom = hwnd;
dispInfo.hdr.idFrom = lCtrlId;
dispInfo.hdr.code = LVN_GETDISPINFOA;
dispInfo.item.iItem = lpLVItem->iItem;
dispInfo.item.iSubItem = lpLVItem->iSubItem;
dispInfo.item.lParam = lpItem->lParam;
ListView_Notify(GetParent(hwnd), lCtrlId, &dispInfo);
}
if (dispInfo.item.mask & LVIF_IMAGE)
{
lpLVItem->iImage = dispInfo.item.iImage;
}
else if (lpLVItem->mask & LVIF_IMAGE)
{
lpLVItem->iImage = *piImage;
}
if (dispInfo.item.mask & LVIF_PARAM)
{
lpLVItem->lParam = dispInfo.item.lParam;
}
else if (lpLVItem->mask & LVIF_PARAM)
{
lpLVItem->lParam = lpItem->lParam;
}
if (dispInfo.item.mask & LVIF_TEXT)
{
if ((dispInfo.item.mask & LVIF_DI_SETITEM) && (ppszText != NULL))
{
Str_SetPtrA(ppszText, dispInfo.item.pszText);
}
/* If lpLVItem->pszText==dispInfo.item.pszText a copy is unnecessary, but */
/* some apps give a new pointer in ListView_Notify so we can't be sure. */
if (lpLVItem->pszText!=dispInfo.item.pszText) {
lstrcpynA(lpLVItem->pszText, dispInfo.item.pszText, lpLVItem->cchTextMax);
}
}
else if (lpLVItem->mask & LVIF_TEXT)
{
if (internal==TRUE)
{
lpLVItem->pszText=*ppszText;
} else {
lstrcpynA(lpLVItem->pszText, *ppszText, lpLVItem->cchTextMax);
}
}
if (lpLVItem->iSubItem == 0)
{
if (dispInfo.item.mask & LVIF_STATE)
{
lpLVItem->state = lpItem->state;
lpLVItem->state &= ~dispInfo.item.stateMask;
lpLVItem->state |= (dispInfo.item.state & dispInfo.item.stateMask);
lpLVItem->state &= ~LVIS_SELECTED;
if ((dispInfo.item.stateMask & LVIS_SELECTED) &&
(LISTVIEW_IsSelected(hwnd,dispInfo.item.iItem)))
lpLVItem->state |= LVIS_SELECTED;
}
else if (lpLVItem->mask & LVIF_STATE)
{
lpLVItem->state = lpItem->state & lpLVItem->stateMask;
lpLVItem->state &= ~LVIS_SELECTED;
if ((lpLVItem->stateMask & LVIS_SELECTED) &&
(LISTVIEW_IsSelected(hwnd,lpLVItem->iItem)))
lpLVItem->state |= LVIS_SELECTED;
}
if (lpLVItem->mask & LVIF_PARAM)
{
lpLVItem->lParam = lpItem->lParam;
}
if (lpLVItem->mask & LVIF_INDENT)
{
lpLVItem->iIndent = lpItem->iIndent;
}
}
return TRUE;
}
/* LISTVIEW_GetItemW */
/* LISTVIEW_GetHotCursor */
/***
* DESCRIPTION:
* Retrieves the index of the hot item.
*
* PARAMETERS:
* [I] HWND : window handle
*
* RETURN:
* SUCCESS : hot item index
* FAILURE : -1 (no hot item)
*/
static LRESULT LISTVIEW_GetHotItem(HWND hwnd)
{
LISTVIEW_INFO *infoPtr;
/* make sure we can get the listview info */
if (!(infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0)))
return (-1);
return (infoPtr->nHotItem);
}
/* LISTVIEW_GetHoverTime */
/***
* DESCRIPTION:
* Retrieves the number of items in the listview control.
*
* PARAMETER(S):
* [I] HWND : window handle
*
* RETURN:
* Number of items.
*/
static LRESULT LISTVIEW_GetItemCount(HWND hwnd)
{
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
return GETITEMCOUNT(infoPtr);
}
/***
* DESCRIPTION:
* Retrieves the position (upper-left) of the listview control item.
*
* PARAMETER(S):
* [I] HWND : window handle
* [I] INT : item index
* [O] LPPOINT : coordinate information
*
* RETURN:
* SUCCESS : TRUE
* FAILURE : FALSE
*/
static BOOL LISTVIEW_GetItemPosition(HWND hwnd, INT nItem,
LPPOINT lpptPosition)
{
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
BOOL bResult = FALSE;
HDPA hdpaSubItems;
LISTVIEW_ITEM *lpItem;
INT nCountPerColumn;
INT nRow;
TRACE("(hwnd=%x,nItem=%d,lpptPosition=%p)\n", hwnd, nItem,
lpptPosition);
if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)) &&
(lpptPosition != NULL))
{
if (uView == LVS_LIST)
{
bResult = TRUE;
nItem = nItem - ListView_GetTopIndex(hwnd);
nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
if (nItem < 0)
{
nRow = nItem % nCountPerColumn;
if (nRow == 0)
{
lpptPosition->x = nItem / nCountPerColumn * infoPtr->nItemWidth;
lpptPosition->y = 0;
}
else
{
lpptPosition->x = (nItem / nCountPerColumn -1) * infoPtr->nItemWidth;
lpptPosition->y = (nRow + nCountPerColumn) * infoPtr->nItemHeight;
}
}
else
{
lpptPosition->x = nItem / nCountPerColumn * infoPtr->nItemWidth;
lpptPosition->y = nItem % nCountPerColumn * infoPtr->nItemHeight;
}
}
else if (uView == LVS_REPORT)
{
SCROLLINFO scrollInfo;
bResult = TRUE;
lpptPosition->x = REPORT_MARGINX;
lpptPosition->y = ((nItem - ListView_GetTopIndex(hwnd)) *
infoPtr->nItemHeight) + infoPtr->rcList.top;
/* Adjust position by scrollbar offset */
ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
scrollInfo.cbSize = sizeof(SCROLLINFO);
scrollInfo.fMask = SIF_POS;
GetScrollInfo(hwnd, SB_HORZ, &scrollInfo);
lpptPosition->x -= scrollInfo.nPos * LISTVIEW_SCROLL_DIV_SIZE;
}
else
{
hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
if (hdpaSubItems != NULL)
{
lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
if (lpItem != NULL)
{
bResult = TRUE;
lpptPosition->x = lpItem->ptPosition.x;
lpptPosition->y = lpItem->ptPosition.y;
}
}
}
}
return bResult;
}
/***
* DESCRIPTION:
* Retrieves the bounding rectangle for a listview control item.
*
* PARAMETER(S):
* [I] HWND : window handle
* [I] INT : item index
* [IO] LPRECT : bounding rectangle coordinates
* lprc->left specifies the portion of the item for which the bounding
* rectangle will be retrieved.
*
* LVIR_BOUNDS Returns the bounding rectangle of the entire item,
* including the icon and label.
* LVIR_ICON Returns the bounding rectangle of the icon or small icon.
* LVIR_LABEL Returns the bounding rectangle of the item text.
* LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
* rectangles, but excludes columns in report view.
*
* RETURN:
* SUCCESS : TRUE
* FAILURE : FALSE
*/
static LRESULT LISTVIEW_GetItemRect(HWND hwnd, INT nItem, LPRECT lprc)
{
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
BOOL bResult = FALSE;
POINT ptOrigin;
POINT ptItem;
HDC hdc;
HFONT hOldFont;
INT nLeftPos;
INT nLabelWidth;
INT nIndent;
TEXTMETRICA tm;
LVITEMA lvItem;
TRACE("(hwnd=%x, nItem=%d, lprc=%p)\n", hwnd, nItem, lprc);
if (uView & LVS_REPORT)
{
ZeroMemory(&lvItem, sizeof(LVITEMA));
lvItem.mask = LVIF_INDENT;
lvItem.iItem = nItem;
lvItem.iSubItem = 0;
LISTVIEW_GetItemA(hwnd, &lvItem, TRUE);
/* do indent */
if (lvItem.iIndent>0 && infoPtr->iconSize.cx > 0)
{
nIndent = infoPtr->iconSize.cx * lvItem.iIndent;
}
else
nIndent = 0;
}
else
nIndent = 0;
if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)) && (lprc != NULL))
{
if (ListView_GetItemPosition(hwnd, nItem, &ptItem) != FALSE)
{
switch(lprc->left)
{
case LVIR_ICON:
if (uView == LVS_ICON)
{
if (infoPtr->himlNormal != NULL)
{
if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
{
bResult = TRUE;
lprc->left = ptItem.x + ptOrigin.x;
lprc->top = ptItem.y + ptOrigin.y;
lprc->right = lprc->left + infoPtr->iconSize.cx;
lprc->bottom = (lprc->top + infoPtr->iconSize.cy +
ICON_BOTTOM_PADDING + ICON_TOP_PADDING);
}
}
}
else if (uView == LVS_SMALLICON)
{
if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
{
bResult = TRUE;
lprc->left = ptItem.x + ptOrigin.x;
lprc->top = ptItem.y + ptOrigin.y;
lprc->bottom = lprc->top + infoPtr->nItemHeight;
if (infoPtr->himlState != NULL)
lprc->left += infoPtr->iconSize.cx;
if (infoPtr->himlSmall != NULL)
lprc->right = lprc->left + infoPtr->iconSize.cx;
else
lprc->right = lprc->left;
}
}
else
{
bResult = TRUE;
lprc->left = ptItem.x;
if (uView & LVS_REPORT)
lprc->left += nIndent;
lprc->top = ptItem.y;
lprc->bottom = lprc->top + infoPtr->nItemHeight;
if (infoPtr->himlState != NULL)
{
lprc->left += infoPtr->iconSize.cx;
}
if (infoPtr->himlSmall != NULL)
{
lprc->right = lprc->left + infoPtr->iconSize.cx;
}
else
{
lprc->right = lprc->left;
}
}
break;
case LVIR_LABEL:
if (uView == LVS_ICON)
{
if (infoPtr->himlNormal != NULL)
{
if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
{
bResult = TRUE;
lprc->left = ptItem.x + ptOrigin.x;
lprc->top = (ptItem.y + ptOrigin.y + infoPtr->iconSize.cy +
ICON_BOTTOM_PADDING + ICON_TOP_PADDING);
nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
if (infoPtr->iconSpacing.cx - nLabelWidth > 1)
{
lprc->left += (infoPtr->iconSpacing.cx - nLabelWidth) / 2;
lprc->right = lprc->left + nLabelWidth;
}
else
{
lprc->left += 1;
lprc->right = lprc->left + infoPtr->iconSpacing.cx - 1;
}
hdc = GetDC(hwnd);
hOldFont = SelectObject(hdc, infoPtr->hFont);
GetTextMetricsA(hdc, &tm);
lprc->bottom = lprc->top + tm.tmHeight + HEIGHT_PADDING;
SelectObject(hdc, hOldFont);
ReleaseDC(hwnd, hdc);
}
}
}
else if (uView == LVS_SMALLICON)
{
if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
{
bResult = TRUE;
nLeftPos = lprc->left = ptItem.x + ptOrigin.x;
lprc->top = ptItem.y + ptOrigin.y;
lprc->bottom = lprc->top + infoPtr->nItemHeight;
if (infoPtr->himlState != NULL)
{
lprc->left += infoPtr->iconSize.cx;
}
if (infoPtr->himlSmall != NULL)
{
lprc->left += infoPtr->iconSize.cx;
}
nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
nLabelWidth += TRAILING_PADDING;
if (lprc->left + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
{
lprc->right = lprc->left + nLabelWidth;
}
else
{
lprc->right = nLeftPos + infoPtr->nItemWidth;
}
}
}
else
{
bResult = TRUE;
if (uView & LVS_REPORT)
nLeftPos = lprc->left = ptItem.x + nIndent;
else
nLeftPos = lprc->left = ptItem.x;
lprc->top = ptItem.y;
lprc->bottom = lprc->top + infoPtr->nItemHeight;
if (infoPtr->himlState != NULL)
{
lprc->left += infoPtr->iconSize.cx;
}
if (infoPtr->himlSmall != NULL)
{
lprc->left += infoPtr->iconSize.cx;
}
nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
nLabelWidth += TRAILING_PADDING;
if (infoPtr->himlSmall)
nLabelWidth += IMAGE_PADDING;
if (lprc->left + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
{
lprc->right = lprc->left + nLabelWidth;
}
else
{
lprc->right = nLeftPos + infoPtr->nItemWidth;
}
}
break;
case LVIR_BOUNDS:
if (uView == LVS_ICON)
{
if (infoPtr->himlNormal != NULL)
{
if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
{
bResult = TRUE;
lprc->left = ptItem.x + ptOrigin.x;
lprc->top = ptItem.y + ptOrigin.y;
lprc->right = lprc->left + infoPtr->iconSpacing.cx;
lprc->bottom = lprc->top + infoPtr->iconSpacing.cy;
}
}
}
else if (uView == LVS_SMALLICON)
{
if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
{
bResult = TRUE;
lprc->left = ptItem.x + ptOrigin.x;
lprc->right = lprc->left;
lprc->top = ptItem.y + ptOrigin.y;
lprc->bottom = lprc->top + infoPtr->nItemHeight;
if (infoPtr->himlState != NULL)
lprc->right += infoPtr->iconSize.cx;
if (infoPtr->himlSmall != NULL)
lprc->right += infoPtr->iconSize.cx;
nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
nLabelWidth += TRAILING_PADDING;
if (infoPtr->himlSmall)
nLabelWidth += IMAGE_PADDING;
if (lprc->right + nLabelWidth < lprc->left + infoPtr->nItemWidth)
{
lprc->right += nLabelWidth;
}
else
{
lprc->right = lprc->left + infoPtr->nItemWidth;
}
}
}
else
{
bResult = TRUE;
lprc->left = ptItem.x;
if (!(infoPtr->dwExStyle&LVS_EX_FULLROWSELECT) && uView&LVS_REPORT)
lprc->left += nIndent;
lprc->right = lprc->left;
lprc->top = ptItem.y;
lprc->bottom = lprc->top + infoPtr->nItemHeight;
if (infoPtr->dwExStyle & LVS_EX_FULLROWSELECT)
{
RECT br;
int nColumnCount = Header_GetItemCount(infoPtr->hwndHeader);
Header_GetItemRect(infoPtr->hwndHeader, nColumnCount-1, &br);
lprc->right = max(lprc->left, br.right - REPORT_MARGINX);
}
else
{
if (infoPtr->himlState != NULL)
{
lprc->right += infoPtr->iconSize.cx;
}
if (infoPtr->himlSmall != NULL)
{
lprc->right += infoPtr->iconSize.cx;
}
nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
nLabelWidth += TRAILING_PADDING;
if (lprc->right + nLabelWidth < lprc->left + infoPtr->nItemWidth)
{
lprc->right += nLabelWidth;
}
else
{
lprc->right = lprc->left + infoPtr->nItemWidth;
}
}
}
break;
case LVIR_SELECTBOUNDS:
if (uView == LVS_ICON)
{
if (infoPtr->himlNormal != NULL)
{
if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
{
bResult = TRUE;
lprc->left = ptItem.x + ptOrigin.x;
lprc->top = ptItem.y + ptOrigin.y;
lprc->right = lprc->left + infoPtr->iconSpacing.cx;
lprc->bottom = lprc->top + infoPtr->iconSpacing.cy;
}
}
}
else if (uView == LVS_SMALLICON)
{
if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
{
bResult = TRUE;
nLeftPos= lprc->left = ptItem.x + ptOrigin.x;
lprc->top = ptItem.y + ptOrigin.y;
lprc->bottom = lprc->top + infoPtr->nItemHeight;
if (infoPtr->himlState != NULL)
{
lprc->left += infoPtr->iconSize.cx;
}
lprc->right = lprc->left;
if (infoPtr->himlSmall != NULL)
{
lprc->right += infoPtr->iconSize.cx;
}
nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
nLabelWidth += TRAILING_PADDING;
if (lprc->right + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
{
lprc->right += nLabelWidth;
}
else
{
lprc->right = nLeftPos + infoPtr->nItemWidth;
}
}
}
else
{
bResult = TRUE;
if (!(infoPtr->dwExStyle&LVS_EX_FULLROWSELECT) && (uView&LVS_REPORT))
nLeftPos = lprc->left = ptItem.x + nIndent;
else
nLeftPos = lprc->left = ptItem.x;
lprc->top = ptItem.y;
lprc->bottom = lprc->top + infoPtr->nItemHeight;
if (infoPtr->himlState != NULL)
{
lprc->left += infoPtr->iconSize.cx;
}
lprc->right = lprc->left;
if (infoPtr->dwExStyle & LVS_EX_FULLROWSELECT)
{
RECT br;
int nColumnCount = Header_GetItemCount(infoPtr->hwndHeader);
Header_GetItemRect(infoPtr->hwndHeader, nColumnCount-1, &br);
lprc->right = max(lprc->left, br.right - REPORT_MARGINX);
}
else
{
if (infoPtr->himlSmall != NULL)
{
lprc->right += infoPtr->iconSize.cx;
}
nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
nLabelWidth += TRAILING_PADDING;
if (infoPtr->himlSmall)
nLabelWidth += IMAGE_PADDING;
if (lprc->right + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
{
lprc->right += nLabelWidth;
}
else
{
lprc->right = nLeftPos + infoPtr->nItemWidth;
}
}
}
break;
}
}
}
return bResult;
}
/***
* DESCRIPTION:
* Retrieves the width of a label.
*
* PARAMETER(S):
* [I] HWND : window handle
*
* RETURN:
* SUCCESS : string width (in pixels)
* FAILURE : zero
*/
static INT LISTVIEW_GetLabelWidth(HWND hwnd, INT nItem)
{
CHAR szDispText[DISP_TEXT_SIZE];
INT nLabelWidth = 0;
LVITEMA lvItem;
TRACE("(hwnd=%x, nItem=%d)\n", hwnd, nItem);
ZeroMemory(&lvItem, sizeof(LVITEMA));
lvItem.mask = LVIF_TEXT;
lvItem.iItem = nItem;
lvItem.cchTextMax = DISP_TEXT_SIZE;
lvItem.pszText = szDispText;
if (LISTVIEW_GetItemA(hwnd, &lvItem, TRUE) != FALSE)
{
nLabelWidth = ListView_GetStringWidthA(hwnd, lvItem.pszText);
}
return nLabelWidth;
}
/***
* DESCRIPTION:
* Retrieves the spacing between listview control items.
*
* PARAMETER(S):
* [I] HWND : window handle
* [I] BOOL : flag for small or large icon
*
* RETURN:
* Horizontal + vertical spacing
*/
static LRESULT LISTVIEW_GetItemSpacing(HWND hwnd, BOOL bSmall)
{
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
LONG lResult;
if (bSmall == FALSE)
{
lResult = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
}
else
{
/* TODO: need to store width of smallicon item */
lResult = MAKELONG(0, infoPtr->nItemHeight);
}
return lResult;
}
/***
* DESCRIPTION:
* Retrieves the state of a listview control item.
*
* PARAMETER(S):
* [I] HWND : window handle
* [I] INT : item index
* [I] UINT : state mask
*
* RETURN:
* State specified by the mask.
*/
static LRESULT LISTVIEW_GetItemState(HWND hwnd, INT nItem, UINT uMask)
{
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
LVITEMA lvItem;
UINT uState = 0;
if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
{
ZeroMemory(&lvItem, sizeof(LVITEMA));
lvItem.iItem = nItem;
lvItem.stateMask = uMask;
lvItem.mask = LVIF_STATE;
if (LISTVIEW_GetItemA(hwnd, &lvItem, TRUE) != FALSE)
{
uState = lvItem.state;
}
}
return uState;
}
/***
* DESCRIPTION:
* Retrieves the text of a listview control item or subitem.
*
* PARAMETER(S):
* [I] HWND : window handle
* [I] INT : item index
* [IO] LPLVITEMA : item information
*
* RETURN:
* SUCCESS : string length
* FAILURE : 0
*/
static LRESULT LISTVIEW_GetItemTextA(HWND hwnd, INT nItem, LPLVITEMA lpLVItem)
{
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
INT nLength = 0;
if (lpLVItem != NULL)
{
if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
{
lpLVItem->mask = LVIF_TEXT;
lpLVItem->iItem = nItem;
if (LISTVIEW_GetItemA(hwnd, lpLVItem, FALSE) != FALSE)
{
nLength = lstrlenA(lpLVItem->pszText);
}
}
}
return nLength;
}
/***
* DESCRIPTION:
* Searches for an item based on properties + relationships.
*
* PARAMETER(S):
* [I] HWND : window handle
* [I] INT : item index
* [I] INT : relationship flag
*
* RETURN:
* SUCCESS : item index
* FAILURE : -1
*/
static LRESULT LISTVIEW_GetNextItem(HWND hwnd, INT nItem, UINT uFlags)
{
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
UINT uMask = 0;
LVFINDINFO lvFindInfo;
INT nCountPerColumn;
INT i;
if ((nItem >= -1) && (nItem < GETITEMCOUNT(infoPtr)))
{
ZeroMemory(&lvFindInfo, sizeof(LVFINDINFO));
if (uFlags & LVNI_CUT)
uMask |= LVIS_CUT;
if (uFlags & LVNI_DROPHILITED)
uMask |= LVIS_DROPHILITED;
if (uFlags & LVNI_FOCUSED)
uMask |= LVIS_FOCUSED;
if (uFlags & LVNI_SELECTED)
uMask |= LVIS_SELECTED;
if (uFlags & LVNI_ABOVE)
{
if ((uView == LVS_LIST) || (uView == LVS_REPORT))
{
while (nItem >= 0)
{
nItem--;
if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
return nItem;
}
}
else
{
lvFindInfo.flags = LVFI_NEARESTXY;
lvFindInfo.vkDirection = VK_UP;
ListView_GetItemPosition(hwnd, nItem, &lvFindInfo.pt);
while ((nItem = ListView_FindItem(hwnd, nItem, &lvFindInfo)) != -1)
{
if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
return nItem;
}
}
}
else if (uFlags & LVNI_BELOW)
{
if ((uView == LVS_LIST) || (uView == LVS_REPORT))
{
while (nItem < GETITEMCOUNT(infoPtr))
{
nItem++;
if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
return nItem;
}
}
else
{
lvFindInfo.flags = LVFI_NEARESTXY;
lvFindInfo.vkDirection = VK_DOWN;
ListView_GetItemPosition(hwnd, nItem, &lvFindInfo.pt);
while ((nItem = ListView_FindItem(hwnd, nItem, &lvFindInfo)) != -1)
{
if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
return nItem;
}
}
}
else if (uFlags & LVNI_TOLEFT)
{
if (uView == LVS_LIST)
{
nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
while (nItem - nCountPerColumn >= 0)
{
nItem -= nCountPerColumn;
if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
return nItem;
}
}
else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
{
lvFindInfo.flags = LVFI_NEARESTXY;
lvFindInfo.vkDirection = VK_LEFT;
ListView_GetItemPosition(hwnd, nItem, &lvFindInfo.pt);
while ((nItem = ListView_FindItem(hwnd, nItem, &lvFindInfo)) != -1)
{
if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
return nItem;
}
}
}
else if (uFlags & LVNI_TORIGHT)
{
if (uView == LVS_LIST)
{
nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
while (nItem + nCountPerColumn < GETITEMCOUNT(infoPtr))
{
nItem += nCountPerColumn;
if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
return nItem;
}
}
else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
{
lvFindInfo.flags = LVFI_NEARESTXY;
lvFindInfo.vkDirection = VK_RIGHT;
ListView_GetItemPosition(hwnd, nItem, &lvFindInfo.pt);
while ((nItem = ListView_FindItem(hwnd, nItem, &lvFindInfo)) != -1)
{
if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
return nItem;
}
}
}
else
{
nItem++;
/* search by index */
for (i = nItem; i < GETITEMCOUNT(infoPtr); i++)
{
if ((ListView_GetItemState(hwnd, i, uMask) & uMask) == uMask)
return i;
}
}
}
return -1;
}
/* LISTVIEW_GetNumberOfWorkAreas */
/***
* DESCRIPTION:
* Retrieves the origin coordinates when in icon or small icon display mode.
*
* PARAMETER(S):
* [I] HWND : window handle
* [O] LPPOINT : coordinate information
*
* RETURN:
* SUCCESS : TRUE
* FAILURE : FALSE
*/
static LRESULT LISTVIEW_GetOrigin(HWND hwnd, LPPOINT lpptOrigin)
{
LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
UINT uView = lStyle & LVS_TYPEMASK;
BOOL bResult = FALSE;
TRACE("(hwnd=%x, lpptOrigin=%p)\n", hwnd, lpptOrigin);
if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
{
SCROLLINFO scrollInfo;
ZeroMemory(lpptOrigin, sizeof(POINT));
ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
scrollInfo.cbSize = sizeof(SCROLLINFO);
if (lStyle & WS_HSCROLL)
{
scrollInfo.fMask = SIF_POS;
if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) != FALSE)
{
lpptOrigin->x = -scrollInfo.nPos * LISTVIEW_SCROLL_DIV_SIZE;
}
}
if (lStyle & WS_VSCROLL)
{
scrollInfo.fMask = SIF_POS;
if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
{
lpptOrigin->y = -scrollInfo.nPos * LISTVIEW_SCROLL_DIV_SIZE;
}
}
bResult = TRUE;
}
return bResult;
}
/***
* DESCRIPTION:
* Retrieves the number of items that are marked as selected.
*
* PARAMETER(S):
* [I] HWND : window handle
*
* RETURN:
* Number of items selected.
*/
static LRESULT LISTVIEW_GetSelectedCount(HWND hwnd)
{
/* REDO THIS */
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
INT nSelectedCount = 0;
INT i;
for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
{
if (ListView_GetItemState(hwnd, i, LVIS_SELECTED) & LVIS_SELECTED)
{
nSelectedCount++;
}
}
return nSelectedCount;
}
/***
* DESCRIPTION:
* Retrieves item index that marks the start of a multiple selection.
*
* PARAMETER(S):
* [I] HWND : window handle
*
* RETURN:
* Index number or -1 if there is no selection mark.
*/
static LRESULT LISTVIEW_GetSelectionMark(HWND hwnd)
{
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
return infoPtr->nSelectionMark;
}
/***
* DESCRIPTION:
* Retrieves the width of a string.
*
* PARAMETER(S):
* [I] HWND : window handle
*
* RETURN:
* SUCCESS : string width (in pixels)
* FAILURE : zero
*/
static LRESULT LISTVIEW_GetStringWidthA(HWND hwnd, LPCSTR lpszText)
{
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
HFONT hFont, hOldFont;
SIZE stringSize;
HDC hdc;
ZeroMemory(&stringSize, sizeof(SIZE));
if (lpszText != NULL && lpszText != LPSTR_TEXTCALLBACKA)
{
hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
hdc = GetDC(hwnd);
hOldFont = SelectObject(hdc, hFont);
GetTextExtentPointA(hdc, lpszText, lstrlenA(lpszText), &stringSize);
SelectObject(hdc, hOldFont);
ReleaseDC(hwnd, hdc);
}
return stringSize.cx;
}
/***
* DESCRIPTION:
* Retrieves the text backgound color.
*
* PARAMETER(S):
* [I] HWND : window handle
*
* RETURN:
* COLORREF associated with the the background.
*/
static LRESULT LISTVIEW_GetTextBkColor(HWND hwnd)
{
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongA(hwnd, 0);
return infoPtr->clrTextBk;
}
/***
* DESCRIPTION:
* Retrieves the text color.
*
* PARAMETER(S):
* [I] HWND : window handle
*
* RETURN:
* COLORREF associated with the text.
*/
static LRESULT LISTVIEW_GetTextColor(HWND hwnd)
{
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongA(hwnd, 0);
return infoPtr->clrText;
}
/***
* DESCRIPTION:
* Determines which section of the item was selected (if any).
*
* PARAMETER(S):
* [I] HWND : window handle
* [IO] LPLVHITTESTINFO : hit test information
* [I] subitem : fill out iSubItem.
*
* RETURN:
* SUCCESS : item index
* FAILURE : -1
*/
static INT LISTVIEW_HitTestItem(
HWND hwnd, LPLVHITTESTINFO lpHitTestInfo, BOOL subitem
) {
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
RECT rcItem;
INT i,topindex,bottomindex;
LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
UINT uView = lStyle & LVS_TYPEMASK;
TRACE("(hwnd=%x, x=%ld, y=%ld)\n", hwnd, lpHitTestInfo->pt.x,
lpHitTestInfo->pt.y);
topindex = ListView_GetTopIndex(hwnd);
if (uView == LVS_REPORT)
{
bottomindex = topindex + LISTVIEW_GetCountPerColumn(hwnd) + 1;
bottomindex = min(bottomindex,GETITEMCOUNT(infoPtr));
}
else
{
bottomindex = GETITEMCOUNT(infoPtr);
}
for (i = topindex; i < bottomindex; i++)
{
rcItem.left = LVIR_BOUNDS;
if (LISTVIEW_GetItemRect(hwnd, i, &rcItem) != FALSE)
{
if (PtInRect(&rcItem, lpHitTestInfo->pt) != FALSE)
{
rcItem.left = LVIR_ICON;
if (LISTVIEW_GetItemRect(hwnd, i, &rcItem) != FALSE)
{
if (PtInRect(&rcItem, lpHitTestInfo->pt) != FALSE)
{
lpHitTestInfo->flags = LVHT_ONITEMICON;
lpHitTestInfo->iItem = i;
if (subitem) lpHitTestInfo->iSubItem = 0;
return i;
}
}
rcItem.left = LVIR_LABEL;
if (LISTVIEW_GetItemRect(hwnd, i, &rcItem) != FALSE)
{
if (PtInRect(&rcItem, lpHitTestInfo->pt) != FALSE)
{
lpHitTestInfo->flags = LVHT_ONITEMLABEL;
lpHitTestInfo->iItem = i;
if (subitem) lpHitTestInfo->iSubItem = 0;
return i;
}
}
lpHitTestInfo->flags = LVHT_ONITEMSTATEICON;
lpHitTestInfo->iItem = i;
if (subitem) lpHitTestInfo->iSubItem = 0;
return i;
}
}
}
lpHitTestInfo->flags = LVHT_NOWHERE;
return -1;
}
/***
* DESCRIPTION:
* Determines which listview item is located at the specified position.
*
* PARAMETER(S):
* [I] HWND : window handle
* [IO} LPLVHITTESTINFO : hit test information
*
* RETURN:
* SUCCESS : item index
* FAILURE : -1
*/
static LRESULT LISTVIEW_HitTest(HWND hwnd, LPLVHITTESTINFO lpHitTestInfo)
{
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
INT nItem = -1;
lpHitTestInfo->flags = 0;
if (infoPtr->rcList.left > lpHitTestInfo->pt.x)
{
lpHitTestInfo->flags = LVHT_TOLEFT;
}
else if (infoPtr->rcList.right < lpHitTestInfo->pt.x)
{
lpHitTestInfo->flags = LVHT_TORIGHT;
}
if (infoPtr->rcList.top > lpHitTestInfo->pt.y)
{
lpHitTestInfo->flags |= LVHT_ABOVE;
}
else if (infoPtr->rcList.bottom < lpHitTestInfo->pt.y)
{
lpHitTestInfo->flags |= LVHT_BELOW;
}
if (lpHitTestInfo->flags == 0)
{
/* NOTE (mm 20001022): We must not allow iSubItem to be touched, for
* an app might pass only a structure with space up to iItem!
* (MS Office 97 does that for instance in the file open dialog)
*/
nItem = LISTVIEW_HitTestItem(hwnd, lpHitTestInfo, FALSE);
}
return nItem;
}
/***
* DESCRIPTION:
* Inserts a new column.
*
* PARAMETER(S):
* [I] HWND : window handle
* [I] INT : column index
* [I] LPLVCOLUMNA : column information
*
* RETURN:
* SUCCESS : new column index
* FAILURE : -1
*/
static LRESULT LISTVIEW_InsertColumnA(HWND hwnd, INT nColumn,
LPLVCOLUMNA lpColumn)
{
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
HDITEMA hdi;
INT nNewColumn = -1;
TRACE("(hwnd=%x, nColumn=%d, lpColumn=%p)\n",hwnd, nColumn,
lpColumn);
if (lpColumn != NULL)
{
/* initialize memory */
ZeroMemory(&hdi, sizeof(HDITEMA));
if (lpColumn->mask & LVCF_FMT)
{
/* format member is valid */
hdi.mask |= HDI_FORMAT;
/* set text alignment (leftmost column must be left-aligned) */
if (nColumn == 0)
{
hdi.fmt |= HDF_LEFT;
}
else
{
if (lpColumn->fmt & LVCFMT_LEFT)
{
hdi.fmt |= HDF_LEFT;
}
else if (lpColumn->fmt & LVCFMT_RIGHT)
{
hdi.fmt |= HDF_RIGHT;
}
else if (lpColumn->fmt & LVCFMT_CENTER)
{
hdi.fmt |= HDF_CENTER;
}
}
if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
{
hdi.fmt |= HDF_BITMAP_ON_RIGHT;
/* ??? */
}
if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
{
/* ??? */
}
if (lpColumn->fmt & LVCFMT_IMAGE)
{
hdi.fmt |= HDF_IMAGE;
hdi.iImage = I_IMAGECALLBACK;
}
}
if (lpColumn->mask & LVCF_WIDTH)
{
hdi.mask |= HDI_WIDTH;
hdi.cxy = lpColumn->cx;
}
if (lpColumn->mask & LVCF_TEXT)
{
hdi.mask |= HDI_TEXT | HDI_FORMAT;
hdi.pszText = lpColumn->pszText;
hdi.cchTextMax = ((lpColumn->pszText!=NULL) && (lpColumn->pszText!=LPSTR_TEXTCALLBACKA) ? strlen(lpColumn->pszText) : 0);
hdi.fmt |= HDF_STRING;
}
if (lpColumn->mask & LVCF_IMAGE)
{
hdi.mask |= HDI_IMAGE;
hdi.iImage = lpColumn->iImage;
}
if (lpColumn->mask & LVCF_ORDER)
{
hdi.mask |= HDI_ORDER;
hdi.iOrder = lpColumn->iOrder;
}
/* insert item in header control */
nNewColumn = SendMessageA(infoPtr->hwndHeader, HDM_INSERTITEMA,
(WPARAM)nColumn, (LPARAM)&hdi);
/* Need to reset the item width when inserting a new column */
infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
LISTVIEW_UpdateScroll(hwnd);
InvalidateRect(hwnd, NULL, FALSE);
}
return nNewColumn;
}
static LRESULT LISTVIEW_InsertColumnW(HWND hwnd, INT nColumn,
LPLVCOLUMNW lpColumn)
{
LVCOLUMNA lvca;
LRESULT lres;
memcpy(&lvca,lpColumn,sizeof(lvca));
if (lpColumn->mask & LVCF_TEXT) {
if (lpColumn->pszText == LPSTR_TEXTCALLBACKW)
lvca.pszText = LPSTR_TEXTCALLBACKA;
else
lvca.pszText = HEAP_strdupWtoA(GetProcessHeap(),0,lpColumn->pszText);
}
lres = LISTVIEW_InsertColumnA(hwnd,nColumn,&lvca);
if (lpColumn->mask & LVCF_TEXT) {
if (lpColumn->pszText != LPSTR_TEXTCALLBACKW)
HeapFree(GetProcessHeap(),0,lvca.pszText);
}
return lres;
}
/* LISTVIEW_InsertCompare: callback routine for comparing pszText members of the LV_ITEMS
in a LISTVIEW on insert. Passed to DPA_Sort in LISTVIEW_InsertItem.
This function should only be used for inserting items into a sorted list (LVM_INSERTITEM)
and not during the processing of a LVM_SORTITEMS message. Applications should provide
their own sort proc. when sending LVM_SORTITEMS.
*/
/* Platform SDK:
(remarks on LVITEM: LVM_INSERTITEM will insert the new item in the proper sort postion...
if:
LVS_SORTXXX must be specified,
LVS_OWNERDRAW is not set,
<item>.pszText is not LPSTR_TEXTCALLBACK.
(LVS_SORT* flags): "For the LVS_SORTASCENDING... styles, item indices
are sorted based on item text..."
*/
static INT WINAPI LISTVIEW_InsertCompare( LPVOID first, LPVOID second, LPARAM lParam)
{
HDPA hdpa_first = (HDPA) first;
HDPA hdpa_second = (HDPA) second;
LISTVIEW_ITEM* lv_first = (LISTVIEW_ITEM*) DPA_GetPtr( hdpa_first, 0 );
LISTVIEW_ITEM* lv_second = (LISTVIEW_ITEM*) DPA_GetPtr( hdpa_second, 0 );
LONG lStyle = GetWindowLongA((HWND) lParam, GWL_STYLE);
INT cmpv = lstrcmpA( lv_first->pszText, lv_second->pszText );
/* if we're sorting descending, negate the return value */
return (lStyle & LVS_SORTDESCENDING) ? -cmpv : cmpv;
}
/***
* nESCRIPTION:
* Inserts a new item in the listview control.
*
* PARAMETER(S):
* [I] HWND : window handle
* [I] LPLVITEMA : item information
*
* RETURN:
* SUCCESS : new item index
* FAILURE : -1
*/
static LRESULT LISTVIEW_InsertItemA(HWND hwnd, LPLVITEMA lpLVItem)
{
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
UINT uView = lStyle & LVS_TYPEMASK;
LONG lCtrlId = GetWindowLongA(hwnd, GWL_ID);
NMLISTVIEW nmlv;
INT nItem = -1;
HDPA hdpaSubItems;
INT nItemWidth = 0;
LISTVIEW_ITEM *lpItem = NULL;
TRACE("(hwnd=%x,lpLVItem=%p)\n", hwnd, lpLVItem);
if (lStyle & LVS_OWNERDATA)
{
nItem = infoPtr->hdpaItems->nItemCount;
infoPtr->hdpaItems->nItemCount ++;
return nItem;
}
if (lpLVItem != NULL)
{
/* make sure it's not a subitem; cannot insert a subitem */
if (lpLVItem->iSubItem == 0)
{
lpItem = (LISTVIEW_ITEM *)COMCTL32_Alloc(sizeof(LISTVIEW_ITEM));
if (lpItem != NULL)
{
ZeroMemory(lpItem, sizeof(LISTVIEW_ITEM));
if (LISTVIEW_InitItem(hwnd, lpItem, lpLVItem) != FALSE)
{
/* insert item in listview control data structure */
hdpaSubItems = DPA_Create(8);
if (hdpaSubItems != NULL)
{
nItem = DPA_InsertPtr(hdpaSubItems, 0, lpItem);
if (nItem != -1)
{
if ( ((lStyle & LVS_SORTASCENDING) || (lStyle & LVS_SORTDESCENDING))
&& !(lStyle & LVS_OWNERDRAWFIXED)
&& (LPSTR_TEXTCALLBACKA != lpLVItem->pszText) )
{
/* Insert the item in the proper sort order based on the pszText
member. See comments for LISTVIEW_InsertCompare() for greater detail */
nItem = DPA_InsertPtr( infoPtr->hdpaItems,
GETITEMCOUNT( infoPtr ) + 1, hdpaSubItems );
DPA_Sort( infoPtr->hdpaItems, LISTVIEW_InsertCompare, hwnd );
nItem = DPA_GetPtrIndex( infoPtr->hdpaItems, hdpaSubItems );
}
else
{
nItem = DPA_InsertPtr(infoPtr->hdpaItems, lpLVItem->iItem,
hdpaSubItems);
}
if (nItem != -1)
{
LISTVIEW_ShiftIndices(hwnd,nItem,1);
/* manage item focus */
if (lpLVItem->mask & LVIF_STATE)
{
lpItem->state &= ~(LVIS_FOCUSED|LVIS_SELECTED);
if (lpLVItem->stateMask & LVIS_SELECTED)
{
LISTVIEW_SetSelection(hwnd, nItem);
}
else if (lpLVItem->stateMask & LVIS_FOCUSED)
{
LISTVIEW_SetItemFocus(hwnd, nItem);
}
}
/* send LVN_INSERTITEM notification */
ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
nmlv.hdr.hwndFrom = hwnd;
nmlv.hdr.idFrom = lCtrlId;
nmlv.hdr.code = LVN_INSERTITEM;
nmlv.iItem = nItem;
nmlv.lParam = lpItem->lParam;;
ListView_LVNotify(GetParent(hwnd), lCtrlId, &nmlv);
if ((uView == LVS_SMALLICON) || (uView == LVS_LIST))
{
nItemWidth = LISTVIEW_CalculateWidth(hwnd, lpLVItem->iItem);
if (nItemWidth > infoPtr->nItemWidth)
{
infoPtr->nItemWidth = nItemWidth;
}
}
/* align items (set position of each item) */
if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
{
if (lStyle & LVS_ALIGNLEFT)
{
LISTVIEW_AlignLeft(hwnd);
}
else
{
LISTVIEW_AlignTop(hwnd);
}
}
LISTVIEW_UpdateScroll(hwnd);
/* refresh client area */
InvalidateRect(hwnd, NULL, FALSE);
}
}
}
}
}
}
}
/* free memory if unsuccessful */
if ((nItem == -1) && (lpItem != NULL))
{
COMCTL32_Free(lpItem);
}
return nItem;
}
static LRESULT LISTVIEW_InsertItemW(HWND hwnd, LPLVITEMW lpLVItem) {
LVITEMA lvia;
LRESULT lres;
memcpy(&lvia,lpLVItem,sizeof(LVITEMA));
if (lvia.mask & LVIF_TEXT) {
if (lpLVItem->pszText == LPSTR_TEXTCALLBACKW)
lvia.pszText = LPSTR_TEXTCALLBACKA;
else
lvia.pszText = HEAP_strdupWtoA(GetProcessHeap(),0,lpLVItem->pszText);
}
lres = LISTVIEW_InsertItemA(hwnd, &lvia);
if (lvia.mask & LVIF_TEXT) {
if (lpLVItem->pszText != LPSTR_TEXTCALLBACKW)
HeapFree(GetProcessHeap(),0,lvia.pszText);
}
return lres;
}
/* LISTVIEW_InsertItemW */
/***
* DESCRIPTION:
* Redraws a range of items.
*
* PARAMETER(S):
* [I] HWND : window handle
* [I] INT : first item
* [I] INT : last item
*
* RETURN:
* SUCCESS : TRUE
* FAILURE : FALSE
*/
static LRESULT LISTVIEW_RedrawItems(HWND hwnd, INT nFirst, INT nLast)
{
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
BOOL bResult = FALSE;
RECT rcItem;
if (nFirst <= nLast)
{
if ((nFirst >= 0) && (nFirst < GETITEMCOUNT(infoPtr)))
{
if ((nLast >= 0) && (nLast < GETITEMCOUNT(infoPtr)))
{
INT i;
for (i = nFirst; i <= nLast; i++)
{
rcItem.left = LVIR_BOUNDS;
LISTVIEW_GetItemRect(hwnd, i, &rcItem);
InvalidateRect(hwnd, &rcItem, TRUE);
}
}
}
}
return bResult;
}
/* LISTVIEW_Scroll */
/***
* DESCRIPTION:
* Sets the background color.
*
* PARAMETER(S):
* [I] HWND : window handle
* [I] COLORREF : background color
*
* RETURN:
* SUCCESS : TRUE
* FAILURE : FALSE
*/
static LRESULT LISTVIEW_SetBkColor(HWND hwnd, COLORREF clrBk)
{
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
infoPtr->clrBk = clrBk;
InvalidateRect(hwnd, NULL, TRUE);
return TRUE;
}
/* LISTVIEW_SetBkImage */
/***
* DESCRIPTION:
* Sets the callback mask. This mask will be used when the parent
* window stores state information (some or all).
*
* PARAMETER(S):
* [I] HWND : window handle
* [I] UINT : state mask
*
* RETURN:
* SUCCESS : TRUE
* FAILURE : FALSE
*/
static BOOL LISTVIEW_SetCallbackMask(HWND hwnd, UINT uMask)
{
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
infoPtr->uCallbackMask = uMask;
return TRUE;
}
/***
* DESCRIPTION:
* Sets the attributes of a header item.
*
* PARAMETER(S):
* [I] HWND : window handle
* [I] INT : column index
* [I] LPLVCOLUMNA : column attributes
*
* RETURN:
* SUCCESS : TRUE
* FAILURE : FALSE
*/
static LRESULT LISTVIEW_SetColumnA(HWND hwnd, INT nColumn,
LPLVCOLUMNA lpColumn)
{
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
BOOL bResult = FALSE;
HDITEMA hdi, hdiget;
if ((lpColumn != NULL) && (nColumn >= 0) &&
(nColumn < Header_GetItemCount(infoPtr->hwndHeader)))
{
/* initialize memory */
ZeroMemory(&hdi, sizeof(HDITEMA));
if (lpColumn->mask & LVCF_FMT)
{
/* format member is valid */
hdi.mask |= HDI_FORMAT;
/* get current format first */
hdiget.mask = HDI_FORMAT;
if (Header_GetItemA(infoPtr->hwndHeader, nColumn, &hdiget))
/* preserve HDF_STRING if present */
hdi.fmt = hdiget.fmt & HDF_STRING;
/* set text alignment (leftmost column must be left-aligned) */
if (nColumn == 0)
{
hdi.fmt |= HDF_LEFT;
}
else
{
if (lpColumn->fmt & LVCFMT_LEFT)
{
hdi.fmt |= HDF_LEFT;
}
else if (lpColumn->fmt & LVCFMT_RIGHT)
{
hdi.fmt |= HDF_RIGHT;
}
else if (lpColumn->fmt & LVCFMT_CENTER)
{
hdi.fmt |= HDF_CENTER;
}
}
if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
{
hdi.fmt |= HDF_BITMAP_ON_RIGHT;
}
if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
{
hdi.fmt |= HDF_IMAGE;
}
if (lpColumn->fmt & LVCFMT_IMAGE)
{
hdi.fmt |= HDF_IMAGE;
hdi.iImage = I_IMAGECALLBACK;
}
}
if (lpColumn->mask & LVCF_WIDTH)
{
hdi.mask |= HDI_WIDTH;
hdi.cxy = lpColumn->cx;
}
if (lpColumn->mask & LVCF_TEXT)
{
hdi.mask |= HDI_TEXT | HDI_FORMAT;
hdi.pszText = lpColumn->pszText;
hdi.cchTextMax = ((lpColumn->pszText!=NULL) && (lpColumn->pszText!=LPSTR_TEXTCALLBACKA) ? strlen(lpColumn->pszText) : 0);
hdi.fmt |= HDF_STRING;
}
if (lpColumn->mask & LVCF_IMAGE)
{
hdi.mask |= HDI_IMAGE;
hdi.iImage = lpColumn->iImage;
}
if (lpColumn->mask & LVCF_ORDER)
{
hdi.mask |= HDI_ORDER;
hdi.iOrder = lpColumn->iOrder;
}
/* set header item attributes */
bResult = Header_SetItemA(infoPtr->hwndHeader, nColumn, &hdi);
}
return bResult;
}
/* LISTVIEW_SetColumnW */
/***
* DESCRIPTION:
* Sets the column order array
*
* PARAMETERS:
* [I] HWND : window handle
* [I] INT : number of elements in column order array
* [I] INT : pointer to column order array
*
* RETURN:
* SUCCESS : TRUE
* FAILURE : FALSE
*/
static LRESULT LISTVIEW_SetColumnOrderArray(HWND hwnd, INT iCount, LPINT lpiArray)
{
/* LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0); */
FIXME("iCount %d lpiArray %p\n", iCount, lpiArray);
if (!lpiArray)
return FALSE;
return TRUE;
}
/***
* DESCRIPTION:
* Sets the width of a column
*
* PARAMETERS:
* [I] HWND : window handle
* [I] INT : column index
* [I] INT : column width
*
* RETURN:
* SUCCESS : TRUE
* FAILURE : FALSE
*/
static LRESULT LISTVIEW_SetColumnWidth(HWND hwnd, INT iCol, INT cx)
{
LISTVIEW_INFO *infoPtr;
HDITEMA hdi;
LRESULT lret;
LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
UINT uView = lStyle & LVS_TYPEMASK;
HDC hdc;
HFONT header_font;
HFONT old_font;
SIZE size;
CHAR text_buffer[DISP_TEXT_SIZE];
INT header_item_count;
INT item_index;
RECT rcHeader;
/* make sure we can get the listview info */
if (!(infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0)))
return (FALSE);
if (!infoPtr->hwndHeader) /* make sure we have a header */
return (FALSE);
/* set column width only if in report or list mode */
if ((uView != LVS_REPORT) && (uView != LVS_LIST))
return (FALSE);
/* take care of invalid cx values */
if((uView == LVS_REPORT) && (cx < -2))
cx = LVSCW_AUTOSIZE;
else if (uView == LVS_LIST && (cx < 1))
return FALSE;
/* resize all columns if in LVS_LIST mode */
if(uView == LVS_LIST) {
infoPtr->nItemWidth = cx;
InvalidateRect(hwnd, NULL, TRUE); /* force redraw of the listview */
return TRUE;
}
/* autosize based on listview items width */
if(cx == LVSCW_AUTOSIZE)
{
/* set the width of the header to the width of the widest item */
for(item_index = 0; item_index < GETITEMCOUNT(infoPtr); item_index++)
{
if(cx < LISTVIEW_GetLabelWidth(hwnd, item_index))
cx = LISTVIEW_GetLabelWidth(hwnd, item_index);
}
} /* autosize based on listview header width */
else if(cx == LVSCW_AUTOSIZE_USEHEADER)
{
header_item_count = Header_GetItemCount(infoPtr->hwndHeader);
/* if iCol is the last column make it fill the remainder of the controls width */
if(iCol == (header_item_count - 1)) {
/* get the width of every item except the current one */
hdi.mask = HDI_WIDTH;
cx = 0;
for(item_index = 0; item_index < (header_item_count - 1); item_index++) {
Header_GetItemA(infoPtr->hwndHeader, item_index, (LPARAM)(&hdi));
cx+=hdi.cxy;
}
/* retrieve the layout of the header */
GetWindowRect(infoPtr->hwndHeader, &rcHeader);
cx = (rcHeader.right - rcHeader.left) - cx;
}
else
{
/* retrieve header font */
header_font = SendMessageA(infoPtr->hwndHeader, WM_GETFONT, 0L, 0L);
/* retrieve header text */
hdi.mask = HDI_TEXT;
hdi.cchTextMax = sizeof(text_buffer);
hdi.pszText = text_buffer;
Header_GetItemA(infoPtr->hwndHeader, iCol, (LPARAM)(&hdi));
/* determine the width of the text in the header */
hdc = GetDC(hwnd);
old_font = SelectObject(hdc, header_font); /* select the font into hdc */
GetTextExtentPoint32A(hdc, text_buffer, strlen(text_buffer), &size);
SelectObject(hdc, old_font); /* restore the old font */
ReleaseDC(hwnd, hdc);
/* set the width of this column to the width of the text */
cx = size.cx;
}
}
/* call header to update the column change */
hdi.mask = HDI_WIDTH;
hdi.cxy = cx;
lret = Header_SetItemA(infoPtr->hwndHeader, (WPARAM)iCol, (LPARAM)&hdi);
InvalidateRect(hwnd, NULL, TRUE); /* force redraw of the listview */
return lret;
}
/***
* DESCRIPTION:
* Sets the extended listview style.
*
* PARAMETERS:
* [I] HWND : window handle
* [I] DWORD : mask
* [I] DWORD : style
*
* RETURN:
* SUCCESS : previous style
* FAILURE : 0
*/
static LRESULT LISTVIEW_SetExtendedListViewStyle(HWND hwnd, DWORD dwMask, DWORD dwStyle)
{
LISTVIEW_INFO *infoPtr;
DWORD dwOldStyle;
/* make sure we can get the listview info */
if (!(infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0)))
return (0);
/* store previous style */
dwOldStyle = infoPtr->dwExStyle;
/* set new style */
if (dwMask)
infoPtr->dwExStyle = (dwOldStyle & ~dwMask) | (dwStyle & dwMask);
else
infoPtr->dwExStyle = dwStyle;
return (dwOldStyle);
}
/* LISTVIEW_SetHotCursor */
/***
* DESCRIPTION:
* Sets the hot item index.
*
* PARAMETERS:
* [I] HWND : window handle
* [I] INT : index
*
* RETURN:
* SUCCESS : previous hot item index
* FAILURE : -1 (no hot item)
*/
static LRESULT LISTVIEW_SetHotItem(HWND hwnd, INT iIndex)
{
LISTVIEW_INFO *infoPtr;
INT iOldIndex;
/* make sure we can get the listview info */
if (!(infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0)))
return (-1);
/* store previous index */
iOldIndex = infoPtr->nHotItem;
/* set new style */
infoPtr->nHotItem = iIndex;
return (iOldIndex);
}
/***
* DESCRIPTION:
* Sets the amount of time the cursor must hover over an item before it is selected.
*
* PARAMETER(S):
* [I] HWND : window handle
* [I] DWORD : dwHoverTime, if -1 the hover time is set to the default
*
* RETURN:
* Returns the previous hover time
*/
static LRESULT LISTVIEW_SetHoverTime(HWND hwnd, DWORD dwHoverTime)
{
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
DWORD oldHoverTime = infoPtr->dwHoverTime;
infoPtr->dwHoverTime = dwHoverTime;
return oldHoverTime;
}
/* LISTVIEW_SetIconSpacing */
/***
* DESCRIPTION:
* Sets image lists.
*
* PARAMETER(S):
* [I] HWND : window handle
* [I] INT : image list type
* [I] HIMAGELIST : image list handle
*
* RETURN:
* SUCCESS : old image list
* FAILURE : NULL
*/
static LRESULT LISTVIEW_SetImageList(HWND hwnd, INT nType, HIMAGELIST himl)
{
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
HIMAGELIST himlOld = 0;
INT oldHeight;
switch (nType)
{
case LVSIL_NORMAL:
himlOld = infoPtr->himlNormal;
infoPtr->himlNormal = himl;
break;
case LVSIL_SMALL:
himlOld = infoPtr->himlSmall;
infoPtr->himlSmall = himl;
break;
case LVSIL_STATE:
himlOld = infoPtr->himlState;
infoPtr->himlState = himl;
ImageList_SetBkColor(infoPtr->himlState, CLR_NONE);
break;
}
oldHeight = infoPtr->nItemHeight;
infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
if (infoPtr->nItemHeight != oldHeight)
LISTVIEW_UpdateScroll(hwnd);
return (LRESULT)himlOld;
}
/***
* DESCRIPTION:
* Sets the attributes of an item.
*
* PARAMETER(S):
* [I] HWND : window handle
* [I] LPLVITEM : item information
*
* RETURN:
* SUCCESS : TRUE
* FAILURE : FALSE
*/
static LRESULT LISTVIEW_SetItemA(HWND hwnd, LPLVITEMA lpLVItem)
{
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
BOOL bResult = FALSE;
if (lpLVItem != NULL)
{
if ((lpLVItem->iItem >= 0) && (lpLVItem->iItem < GETITEMCOUNT(infoPtr)))
{
if (lpLVItem->iSubItem == 0)
{
bResult = LISTVIEW_SetItem(hwnd, lpLVItem);
}
else
{
bResult = LISTVIEW_SetSubItem(hwnd, lpLVItem);
}
}
}
return bResult;
}
/* LISTVIEW_SetItemW */
/***
* DESCRIPTION:
* Preallocates memory (does *not* set the actual count of items !)
*
* PARAMETER(S):
* [I] HWND : window handle
* [I] INT : item count (projected number of items to allocate)
* [I] DWORD : update flags
*
* RETURN:
* SUCCESS : TRUE
* FAILURE : FALSE
*/
static BOOL LISTVIEW_SetItemCount(HWND hwnd, INT nItems, DWORD dwFlags)
{
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongA(hwnd, 0);
if (GetWindowLongA(hwnd, GWL_STYLE) & LVS_OWNERDATA)
{
int precount,topvisible;
TRACE("LVS_OWNERDATA is set!\n");
/*
* Internally remove all the selections.
*/
do
{
LISTVIEW_SELECTION *selection;
selection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,0);
if (selection)
LISTVIEW_RemoveSelectionRange(hwnd,selection->lower,
selection->upper);
}
while (infoPtr->hdpaSelectionRanges->nItemCount>0);
precount = infoPtr->hdpaItems->nItemCount;
topvisible = ListView_GetTopIndex(hwnd) +
LISTVIEW_GetCountPerColumn(hwnd) + 1;
infoPtr->hdpaItems->nItemCount = nItems;
LISTVIEW_UpdateSize(hwnd);
LISTVIEW_UpdateScroll(hwnd);
if (min(precount,infoPtr->hdpaItems->nItemCount)<topvisible)
InvalidateRect(hwnd, NULL, TRUE);
}
else
{
FIXME("setitemcount not done for non-ownerdata\n");
}
return TRUE;
}
/***
* DESCRIPTION:
* Sets the position of an item.
*
* PARAMETER(S):
* [I] HWND : window handle
* [I] INT : item index
* [I] LONG : x coordinate
* [I] LONG : y coordinate
*
* RETURN:
* SUCCESS : TRUE
* FAILURE : FALSE
*/
static BOOL LISTVIEW_SetItemPosition(HWND hwnd, INT nItem,
LONG nPosX, LONG nPosY)
{
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongA(hwnd, 0);
UINT lStyle = GetWindowLongA(hwnd, GWL_STYLE);
UINT uView = lStyle & LVS_TYPEMASK;
LISTVIEW_ITEM *lpItem;
HDPA hdpaSubItems;
BOOL bResult = FALSE;
TRACE("(hwnd=%x,nItem=%d,X=%ld,Y=%ld)\n", hwnd, nItem, nPosX, nPosY);
if (lStyle & LVS_OWNERDATA)
return FALSE;
if ((nItem >= 0) || (nItem < GETITEMCOUNT(infoPtr)))
{
if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
{
hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
if (hdpaSubItems != NULL)
{
lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
if (lpItem != NULL)
{
bResult = TRUE;
lpItem->ptPosition.x = nPosX;
lpItem->ptPosition.y = nPosY;
}
}
}
}
return bResult;
}
/***
* DESCRIPTION:
* Sets the state of one or many items.
*
* PARAMETER(S):
* [I] HWND : window handle
* [I]INT : item index
* [I] LPLVITEM : item or subitem info
*
* RETURN:
* SUCCESS : TRUE
* FAILURE : FALSE
*/
static LRESULT LISTVIEW_SetItemState(HWND hwnd, INT nItem, LPLVITEMA lpLVItem)
{
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
BOOL bResult = FALSE;
LVITEMA lvItem;
INT i;
if (nItem == -1)
{
bResult = TRUE;
ZeroMemory(&lvItem, sizeof(LVITEMA));
lvItem.mask = LVIF_STATE;
lvItem.state = lpLVItem->state;
lvItem.stateMask = lpLVItem->stateMask ;
/* apply to all items */
for (i = 0; i< GETITEMCOUNT(infoPtr); i++)
{
lvItem.iItem = i;
if (ListView_SetItemA(hwnd, &lvItem) == FALSE)
{
bResult = FALSE;
}
}
}
else
{
ZeroMemory(&lvItem, sizeof(LVITEMA));
lvItem.mask = LVIF_STATE;
lvItem.state = lpLVItem->state;
lvItem.stateMask = lpLVItem->stateMask;
lvItem.iItem = nItem;
bResult = ListView_SetItemA(hwnd, &lvItem);
}
return bResult;
}
/***
* DESCRIPTION:
* Sets the text of an item or subitem.
*
* PARAMETER(S):
* [I] HWND : window handle
* [I] INT : item index
* [I] LPLVITEMA : item or subitem info
*
* RETURN:
* SUCCESS : TRUE
* FAILURE : FALSE
*/
static BOOL LISTVIEW_SetItemTextA(HWND hwnd, INT nItem, LPLVITEMA lpLVItem)
{
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
BOOL bResult = FALSE;
LVITEMA lvItem;
if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
{
ZeroMemory(&lvItem, sizeof(LVITEMA));
lvItem.mask = LVIF_TEXT;
lvItem.pszText = lpLVItem->pszText;
lvItem.iItem = nItem;
lvItem.iSubItem = lpLVItem->iSubItem;
bResult = ListView_SetItemA(hwnd, &lvItem);
}
return bResult;
}
/* LISTVIEW_SetItemTextW */
/***
* DESCRIPTION:
* Set item index that marks the start of a multiple selection.
*
* PARAMETER(S):
* [I] HWND : window handle
* [I] INT : index
*
* RETURN:
* Index number or -1 if there is no selection mark.
*/
static LRESULT LISTVIEW_SetSelectionMark(HWND hwnd, INT nIndex)
{
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
INT nOldIndex = infoPtr->nSelectionMark;
infoPtr->nSelectionMark = nIndex;
return nOldIndex;
}
/***
* DESCRIPTION:
* Sets the text background color.
*
* PARAMETER(S):
* [I] HWND : window handle
* [I] COLORREF : text background color
*
* RETURN:
* SUCCESS : TRUE
* FAILURE : FALSE
*/
static LRESULT LISTVIEW_SetTextBkColor(HWND hwnd, COLORREF clrTextBk)
{
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
infoPtr->clrTextBk = clrTextBk;
InvalidateRect(hwnd, NULL, TRUE);
return TRUE;
}
/***
* DESCRIPTION:
* Sets the text foreground color.
*
* PARAMETER(S):
* [I] HWND : window handle
* [I] COLORREF : text color
*
* RETURN:
* SUCCESS : TRUE
* FAILURE : FALSE
*/
static LRESULT LISTVIEW_SetTextColor (HWND hwnd, COLORREF clrText)
{
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
infoPtr->clrText = clrText;
InvalidateRect(hwnd, NULL, TRUE);
return TRUE;
}
/* LISTVIEW_SetToolTips */
/* LISTVIEW_SetUnicodeFormat */
/* LISTVIEW_SetWorkAreas */
/***
* DESCRIPTION:
* Callback internally used by LISTVIEW_SortItems()
*
* PARAMETER(S):
* [I] LPVOID : first LISTVIEW_ITEM to compare
* [I] LPVOID : second LISTVIEW_ITEM to compare
* [I] LPARAM : HWND of control
*
* RETURN:
* if first comes before second : negative
* if first comes after second : positive
* if first and second are equivalent : zero
*/
static INT WINAPI LISTVIEW_CallBackCompare(
LPVOID first,
LPVOID second,
LPARAM lParam)
{
/* Forward the call to the client defined callback */
INT rv;
HWND hwnd = (HWND)lParam;
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
HDPA hdpa_first = (HDPA) first;
HDPA hdpa_second = (HDPA) second;
LISTVIEW_ITEM* lv_first = (LISTVIEW_ITEM*) DPA_GetPtr( hdpa_first, 0 );
LISTVIEW_ITEM* lv_second = (LISTVIEW_ITEM*) DPA_GetPtr( hdpa_second, 0 );
rv = (infoPtr->pfnCompare)( lv_first->lParam , lv_second->lParam, infoPtr->lParamSort );
return rv;
}
/***
* DESCRIPTION:
* Sorts the listview items.
*
* PARAMETER(S):
* [I] HWND : window handle
* [I] WPARAM : application-defined value
* [I] LPARAM : pointer to comparision callback
*
* RETURN:
* SUCCESS : TRUE
* FAILURE : FALSE
*/
static LRESULT LISTVIEW_SortItems(HWND hwnd, WPARAM wParam, LPARAM lParam)
{
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
int nCount, i;
UINT lStyle = GetWindowLongA(hwnd, GWL_STYLE);
HDPA hdpaSubItems=NULL;
LISTVIEW_ITEM *pLVItem=NULL;
LPVOID selectionMarkItem;
if (lStyle & LVS_OWNERDATA)
return FALSE;
if (!infoPtr || !infoPtr->hdpaItems)
return FALSE;
nCount = GETITEMCOUNT(infoPtr);
/* if there are 0 or 1 items, there is no need to sort */
if (nCount < 2)
return TRUE;
infoPtr->pfnCompare = (PFNLVCOMPARE)lParam;
infoPtr->lParamSort = (LPARAM)wParam;
DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompare, hwnd);
/* Adjust selections and indices so that they are the way they should
* be after the sort (otherwise, the list items move around, but
* whatever is at the item's previous original position will be
* selected instead)
*/
selectionMarkItem=(infoPtr->nSelectionMark>=0)?DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nSelectionMark):NULL;
for (i=0; i < nCount; i++)
{
hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
pLVItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
if (pLVItem->state & LVIS_SELECTED)
LISTVIEW_AddSelectionRange(hwnd, i, i);
else
LISTVIEW_RemoveSelectionRange(hwnd, i, i);
if (pLVItem->state & LVIS_FOCUSED)
infoPtr->nFocusedItem=i;
}
if (selectionMarkItem != NULL)
infoPtr->nSelectionMark = DPA_GetPtrIndex(infoPtr->hdpaItems, selectionMarkItem);
/* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
/* align the items */
LISTVIEW_AlignTop(hwnd);
/* refresh the display */
InvalidateRect(hwnd, NULL, TRUE);
return TRUE;
}
/* LISTVIEW_SubItemHitTest */
/***
* DESCRIPTION:
* Updates an items or rearranges the listview control.
*
* PARAMETER(S):
* [I] HWND : window handle
* [I] INT : item index
*
* RETURN:
* SUCCESS : TRUE
* FAILURE : FALSE
*/
static LRESULT LISTVIEW_Update(HWND hwnd, INT nItem)
{
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
BOOL bResult = FALSE;
RECT rc;
if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
{
bResult = TRUE;
/* rearrange with default alignment style */
if ((lStyle & LVS_AUTOARRANGE) && (((lStyle & LVS_TYPEMASK) == LVS_ICON) ||
((lStyle & LVS_TYPEMASK) == LVS_SMALLICON)))
{
ListView_Arrange(hwnd, 0);
}
else
{
/* get item bounding rectangle */
ListView_GetItemRect(hwnd, nItem, &rc, LVIR_BOUNDS);
InvalidateRect(hwnd, &rc, TRUE);
}
}
return bResult;
}
/***
* DESCRIPTION:
* Creates the listview control.
*
* PARAMETER(S):
* [I] HWND : window handle
*
* RETURN:
* Zero
*/
static LRESULT LISTVIEW_Create(HWND hwnd, WPARAM wParam, LPARAM lParam)
{
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
LPCREATESTRUCTA lpcs = (LPCREATESTRUCTA)lParam;
UINT uView = lpcs->style & LVS_TYPEMASK;
LOGFONTA logFont;
/* initialize info pointer */
ZeroMemory(infoPtr, sizeof(LISTVIEW_INFO));
/* determine the type of structures to use */
infoPtr->notifyFormat = SendMessageA(GetParent(hwnd), WM_NOTIFYFORMAT,
(WPARAM)hwnd, (LPARAM)NF_QUERY);
if (infoPtr->notifyFormat != NFR_ANSI)
{
FIXME("ANSI notify format is NOT used\n");
}
/* initialize color information */
infoPtr->clrBk = GetSysColor(COLOR_WINDOW);
infoPtr->clrText = GetSysColor(COLOR_WINDOWTEXT);
infoPtr->clrTextBk = CLR_DEFAULT;
/* set default values */
infoPtr->uCallbackMask = 0;
infoPtr->nFocusedItem = -1;
infoPtr->nSelectionMark = -1;
infoPtr->nHotItem = -1;
infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING);
infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING);
ZeroMemory(&infoPtr->rcList, sizeof(RECT));
infoPtr->hwndEdit = 0;
infoPtr->pedititem = NULL;
infoPtr->nEditLabelItem = -1;
/* get default font (icon title) */
SystemParametersInfoA(SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
infoPtr->hDefaultFont = CreateFontIndirectA(&logFont);
infoPtr->hFont = infoPtr->hDefaultFont;
/* create header */
infoPtr->hwndHeader = CreateWindowA(WC_HEADERA, (LPCSTR)NULL,
WS_CHILD | HDS_HORZ | HDS_BUTTONS,
0, 0, 0, 0, hwnd, (HMENU)0,
lpcs->hInstance, NULL);
/* set header font */
SendMessageA(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)infoPtr->hFont,
(LPARAM)TRUE);
if (uView == LVS_ICON)
{
infoPtr->iconSize.cx = GetSystemMetrics(SM_CXICON);
infoPtr->iconSize.cy = GetSystemMetrics(SM_CYICON);
}
else if (uView == LVS_REPORT)
{
if (!(LVS_NOCOLUMNHEADER & lpcs->style))
{
ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
}
else
{
/* set HDS_HIDDEN flag to hide the header bar */
SetWindowLongA(infoPtr->hwndHeader, GWL_STYLE,
GetWindowLongA(infoPtr->hwndHeader, GWL_STYLE) | HDS_HIDDEN);
}
infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
}
else
{
infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
}
/* display unsupported listview window styles */
LISTVIEW_UnsupportedStyles(lpcs->style);
/* allocate memory for the data structure */
infoPtr->hdpaItems = DPA_Create(10);
/* allocate memory for the selection ranges */
infoPtr->hdpaSelectionRanges = DPA_Create(10);
/* initialize size of items */
infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
/* initialize the hover time to -1(indicating the default system hover time) */
infoPtr->dwHoverTime = -1;
return 0;
}
/***
* DESCRIPTION:
* Erases the background of the listview control.
*
* PARAMETER(S):
* [I] HWND : window handle
* [I] WPARAM : device context handle
* [I] LPARAM : not used
*
* RETURN:
* SUCCESS : TRUE
* FAILURE : FALSE
*/
static LRESULT LISTVIEW_EraseBackground(HWND hwnd, WPARAM wParam,
LPARAM lParam)
{
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
BOOL bResult;
if (infoPtr->clrBk == CLR_NONE)
{
bResult = SendMessageA(GetParent(hwnd), WM_ERASEBKGND, wParam, lParam);
}
else
{
RECT rc;
HBRUSH hBrush = CreateSolidBrush(infoPtr->clrBk);
GetClientRect(hwnd, &rc);
FillRect((HDC)wParam, &rc, hBrush);
DeleteObject(hBrush);
bResult = TRUE;
}
return bResult;
}
static void LISTVIEW_FillBackground(HWND hwnd, HDC hdc, LPRECT rc)
{
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
if (infoPtr->clrBk != CLR_NONE)
{
HBRUSH hBrush = CreateSolidBrush(infoPtr->clrBk);
FillRect(hdc, rc, hBrush);
DeleteObject(hBrush);
}
}
/***
* DESCRIPTION:
* Retrieves the listview control font.
*
* PARAMETER(S):
* [I] HWND : window handle
*
* RETURN:
* Font handle.
*/
static LRESULT LISTVIEW_GetFont(HWND hwnd)
{
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
return infoPtr->hFont;
}
/***
* DESCRIPTION:
* Performs vertical scrolling.
*
* PARAMETER(S):
* [I] HWND : window handle
* [I] INT : scroll code
* [I] SHORT : current scroll position if scroll code is SB_THIMBPOSITION
* or SB_THUMBTRACK.
* [I] HWND : scrollbar control window handle
*
* RETURN:
* Zero
*/
static LRESULT LISTVIEW_VScroll(HWND hwnd, INT nScrollCode, SHORT nCurrentPos,
HWND hScrollWnd)
{
SCROLLINFO scrollInfo;
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
SendMessageA(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
scrollInfo.cbSize = sizeof(SCROLLINFO);
scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE;
if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
{
INT nOldScrollPos = scrollInfo.nPos;
switch (nScrollCode)
{
case SB_LINEUP:
if (scrollInfo.nPos > scrollInfo.nMin)
{
scrollInfo.nPos--;
}
break;
case SB_LINEDOWN:
if (scrollInfo.nPos < scrollInfo.nMax)
{
scrollInfo.nPos++;
}
break;
case SB_PAGEUP:
if (scrollInfo.nPos > scrollInfo.nMin)
{
if (scrollInfo.nPos >= scrollInfo.nPage)
{
scrollInfo.nPos -= scrollInfo.nPage;
}
else
{
scrollInfo.nPos = scrollInfo.nMin;
}
}
break;
case SB_PAGEDOWN:
if (scrollInfo.nPos < scrollInfo.nMax)
{
if (scrollInfo.nPos <= scrollInfo.nMax - scrollInfo.nPage)
{
scrollInfo.nPos += scrollInfo.nPage;
}
else
{
scrollInfo.nPos = scrollInfo.nMax;
}
}
break;
case SB_THUMBTRACK:
scrollInfo.nPos = nCurrentPos;
if (scrollInfo.nPos > scrollInfo.nMax)
scrollInfo.nPos=scrollInfo.nMax;
if (scrollInfo.nPos < scrollInfo.nMin)
scrollInfo.nPos=scrollInfo.nMin;
break;
}
if (nOldScrollPos != scrollInfo.nPos)
{
scrollInfo.fMask = SIF_POS;
SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE);
InvalidateRect(hwnd, NULL, TRUE);
}
}
return 0;
}
/***
* DESCRIPTION:
* Performs horizontal scrolling.
*
* PARAMETER(S):
* [I] HWND : window handle
* [I] INT : scroll code
* [I] SHORT : current scroll position if scroll code is SB_THUMBPOSITION
* or SB_THUMBTRACK.
* [I] HWND : scrollbar control window handle
*
* RETURN:
* Zero
*/
static LRESULT LISTVIEW_HScroll(HWND hwnd, INT nScrollCode, SHORT nCurrentPos,
HWND hScrollWnd)
{
SCROLLINFO scrollInfo;
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
SendMessageA(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
scrollInfo.cbSize = sizeof(SCROLLINFO);
scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE;
if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) != FALSE)
{
INT nOldScrollPos = scrollInfo.nPos;
switch (nScrollCode)
{
case SB_LINELEFT:
if (scrollInfo.nPos > scrollInfo.nMin)
{
scrollInfo.nPos--;
}
break;
case SB_LINERIGHT:
if (scrollInfo.nPos < scrollInfo.nMax)
{
scrollInfo.nPos++;
}
break;
case SB_PAGELEFT:
if (scrollInfo.nPos > scrollInfo.nMin)
{
if (scrollInfo.nPos >= scrollInfo.nPage)
{
scrollInfo.nPos -= scrollInfo.nPage;
}
else
{
scrollInfo.nPos = scrollInfo.nMin;
}
}
break;
case SB_PAGERIGHT:
if (scrollInfo.nPos < scrollInfo.nMax)
{
if (scrollInfo.nPos <= scrollInfo.nMax - scrollInfo.nPage)
{
scrollInfo.nPos += scrollInfo.nPage;
}
else
{
scrollInfo.nPos = scrollInfo.nMax;
}
}
break;
case SB_THUMBTRACK:
scrollInfo.nPos = nCurrentPos;
if (scrollInfo.nPos > scrollInfo.nMax)
scrollInfo.nPos=scrollInfo.nMax;
if (scrollInfo.nPos < scrollInfo.nMin)
scrollInfo.nPos=scrollInfo.nMin;
break;
}
if (nOldScrollPos != scrollInfo.nPos)
{
UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
scrollInfo.fMask = SIF_POS;
SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
if(uView == LVS_REPORT)
{
scrollInfo.fMask = SIF_POS;
GetScrollInfo(hwnd, SB_HORZ, &scrollInfo);
LISTVIEW_UpdateHeaderSize(hwnd, scrollInfo.nPos);
}
InvalidateRect(hwnd, NULL, TRUE);
}
}
return 0;
}
static LRESULT LISTVIEW_MouseWheel(HWND hwnd, INT wheelDelta)
{
INT gcWheelDelta = 0;
UINT pulScrollLines = 3;
SCROLLINFO scrollInfo;
UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
gcWheelDelta -= wheelDelta;
ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
scrollInfo.cbSize = sizeof(SCROLLINFO);
scrollInfo.fMask = SIF_POS | SIF_RANGE;
switch(uView)
{
case LVS_ICON:
case LVS_SMALLICON:
/*
* listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
* should be fixed in the future.
*/
if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
LISTVIEW_VScroll(hwnd, SB_THUMBPOSITION, scrollInfo.nPos + (gcWheelDelta < 0) ? 37 : -37, 0);
break;
case LVS_REPORT:
if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
{
if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
{
int cLineScroll = min(LISTVIEW_GetCountPerColumn(hwnd), pulScrollLines);
cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
LISTVIEW_VScroll(hwnd, SB_THUMBPOSITION, scrollInfo.nPos + cLineScroll, 0);
}
}
break;
case LVS_LIST:
LISTVIEW_HScroll(hwnd, (gcWheelDelta < 0) ? SB_LINELEFT : SB_LINERIGHT, 0, 0);
break;
}
return 0;
}
/***
* DESCRIPTION:
* ???
*
* PARAMETER(S):
* [I] HWND : window handle
* [I] INT : virtual key
* [I] LONG : key data
*
* RETURN:
* Zero
*/
static LRESULT LISTVIEW_KeyDown(HWND hwnd, INT nVirtualKey, LONG lKeyData)
{
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
INT nCtrlId = GetWindowLongA(hwnd, GWL_ID);
HWND hwndParent = GetParent(hwnd);
NMLVKEYDOWN nmKeyDown;
NMHDR nmh;
INT nItem = -1;
BOOL bRedraw = FALSE;
LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
UINT uView = lStyle & LVS_TYPEMASK;
/* send LVN_KEYDOWN notification */
ZeroMemory(&nmKeyDown, sizeof(NMLVKEYDOWN));
nmKeyDown.hdr.hwndFrom = hwnd;
nmKeyDown.hdr.idFrom = nCtrlId;
nmKeyDown.hdr.code = LVN_KEYDOWN;
nmKeyDown.wVKey = nVirtualKey;
nmKeyDown.flags = 0;
SendMessageA(hwndParent, WM_NOTIFY, (WPARAM)nCtrlId, (LPARAM)&nmKeyDown);
/* initialize */
nmh.hwndFrom = hwnd;
nmh.idFrom = nCtrlId;
switch (nVirtualKey)
{
case VK_RETURN:
if ((GETITEMCOUNT(infoPtr) > 0) && (infoPtr->nFocusedItem != -1))
{
/* send NM_RETURN notification */
nmh.code = NM_RETURN;
ListView_Notify(hwndParent, nCtrlId, &nmh);
/* send LVN_ITEMACTIVATE notification */
nmh.code = LVN_ITEMACTIVATE;
ListView_Notify(hwndParent, nCtrlId, &nmh);
}
break;
case VK_HOME:
if (GETITEMCOUNT(infoPtr) > 0)
{
nItem = 0;
}
break;
case VK_END:
if (GETITEMCOUNT(infoPtr) > 0)
{
nItem = GETITEMCOUNT(infoPtr) - 1;
}
break;
case VK_LEFT:
nItem = ListView_GetNextItem(hwnd, infoPtr->nFocusedItem, LVNI_TOLEFT);
break;
case VK_UP:
nItem = ListView_GetNextItem(hwnd, infoPtr->nFocusedItem, LVNI_ABOVE);
break;
case VK_RIGHT:
nItem = ListView_GetNextItem(hwnd, infoPtr->nFocusedItem, LVNI_TORIGHT);
break;
case VK_DOWN:
nItem = ListView_GetNextItem(hwnd, infoPtr->nFocusedItem, LVNI_BELOW);
break;
case VK_PRIOR:
if (uView == LVS_REPORT)
{
nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(hwnd);
}
else
{
nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(hwnd)
* LISTVIEW_GetCountPerRow(hwnd);
}
if(nItem < 0) nItem = 0;
break;
case VK_NEXT:
if (uView == LVS_REPORT)
{
nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(hwnd);
}
else
{
nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(hwnd)
* LISTVIEW_GetCountPerRow(hwnd);
}
if(nItem >= GETITEMCOUNT(infoPtr)) nItem = GETITEMCOUNT(infoPtr) - 1;
break;
}
if ((nItem != -1) && (nItem != infoPtr->nFocusedItem))
{
bRedraw = LISTVIEW_KeySelection(hwnd, nItem);
if (bRedraw != FALSE)
{
/* refresh client area */
UpdateWindow(hwnd);
}
}
return 0;
}
/***
* DESCRIPTION:
* Kills the focus.
*
* PARAMETER(S):
* [I] HWND : window handle
*
* RETURN:
* Zero
*/
static LRESULT LISTVIEW_KillFocus(HWND hwnd)
{
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongA(hwnd, 0);
INT nCtrlId = GetWindowLongA(hwnd, GWL_ID);
NMHDR nmh;
INT i,nTop,nBottom;
RECT rcItem;
LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
UINT uView = lStyle & LVS_TYPEMASK;
TRACE("(hwnd=%x)\n", hwnd);
/* send NM_KILLFOCUS notification */
nmh.hwndFrom = hwnd;
nmh.idFrom = nCtrlId;
nmh.code = NM_KILLFOCUS;
ListView_Notify(GetParent(hwnd), nCtrlId, &nmh);
/* set window focus flag */
infoPtr->bFocus = FALSE;
/* NEED drawing optimization ; redraw the selected items */
if (uView & LVS_REPORT)
{
nTop = LISTVIEW_GetTopIndex(hwnd);
nBottom = nTop +
LISTVIEW_GetCountPerColumn(hwnd) + 1;
}
else
{
nTop = 0;
nBottom = GETITEMCOUNT(infoPtr);
}
for (i = nTop; i<nBottom; i++)
{
if (LISTVIEW_IsSelected(hwnd,i))
{
rcItem.left = LVIR_BOUNDS;
LISTVIEW_GetItemRect(hwnd, i, &rcItem);
InvalidateRect(hwnd, &rcItem, FALSE);
}
}
return 0;
}
/***
* DESCRIPTION:
* Processes double click messages (left mouse button).
*
* PARAMETER(S):
* [I] HWND : window handle
* [I] WORD : key flag
* [I] WORD : x coordinate
* [I] WORD : y coordinate
*
* RETURN:
* Zero
*/
static LRESULT LISTVIEW_LButtonDblClk(HWND hwnd, WORD wKey, WORD wPosX,
WORD wPosY)
{
LONG nCtrlId = GetWindowLongA(hwnd, GWL_ID);
LVHITTESTINFO htInfo;
NMHDR nmh;
NMLISTVIEW nmlv;
INT ret;
TRACE("(hwnd=%x,key=%hu,X=%hu,Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
htInfo.pt.x = wPosX;
htInfo.pt.y = wPosY;
/* send NM_DBLCLK notification */
ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
nmlv.hdr.hwndFrom = hwnd;
nmlv.hdr.idFrom = nCtrlId;
nmlv.hdr.code = NM_DBLCLK;
ret = LISTVIEW_HitTestItem(hwnd, &htInfo, TRUE);
if (ret != -1)
{
nmlv.iItem = htInfo.iItem;
nmlv.iSubItem = htInfo.iSubItem;
}
else
{
nmlv.iItem = -1;
nmlv.iSubItem = 0;
}
nmlv.ptAction.x = wPosX;
nmlv.ptAction.y = wPosY;
ListView_LVNotify(GetParent(hwnd), nCtrlId, &nmlv);
/* To send the LVN_ITEMACTIVATE, it must be on an Item */
if(ret != -1)
{
/* send LVN_ITEMACTIVATE notification */
nmh.hwndFrom = hwnd;
nmh.idFrom = nCtrlId;
nmh.code = LVN_ITEMACTIVATE;
ListView_Notify(GetParent(hwnd), nCtrlId, &nmh);
}
return 0;
}
/***
* DESCRIPTION:
* Processes mouse down messages (left mouse button).
*
* PARAMETER(S):
* [I] HWND : window handle
* [I] WORD : key flag
* [I] WORD : x coordinate
* [I] WORD : y coordinate
*
* RETURN:
* Zero
*/
static LRESULT LISTVIEW_LButtonDown(HWND hwnd, WORD wKey, WORD wPosX,
WORD wPosY)
{
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
INT nCtrlId = GetWindowLongA(hwnd, GWL_ID);
static BOOL bGroupSelect = TRUE;
POINT ptPosition;
NMHDR nmh;
INT nItem;
TRACE("(hwnd=%x, key=%hu, X=%hu, Y=%hu)\n", hwnd, wKey, wPosX,
wPosY);
/* send NM_RELEASEDCAPTURE notification */
nmh.hwndFrom = hwnd;
nmh.idFrom = nCtrlId;
nmh.code = NM_RELEASEDCAPTURE;
ListView_Notify(GetParent(hwnd), nCtrlId, &nmh);
if (infoPtr->bFocus == FALSE)
{
SetFocus(hwnd);
}
/* set left button down flag */
infoPtr->bLButtonDown = TRUE;
ptPosition.x = wPosX;
ptPosition.y = wPosY;
nItem = LISTVIEW_MouseSelection(hwnd, ptPosition);
if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
{
if (lStyle & LVS_SINGLESEL)
{
if ((ListView_GetItemState(hwnd, nItem, LVIS_SELECTED) & LVIS_SELECTED)
&& infoPtr->nEditLabelItem == -1)
{
infoPtr->nEditLabelItem = nItem;
}
else
{
LISTVIEW_SetSelection(hwnd, nItem);
}
}
else
{
if ((wKey & MK_CONTROL) && (wKey & MK_SHIFT))
{
if (bGroupSelect != FALSE)
{
LISTVIEW_AddGroupSelection(hwnd, nItem);
}
else
{
LISTVIEW_AddSelection(hwnd, nItem);
}
}
else if (wKey & MK_CONTROL)
{
bGroupSelect = LISTVIEW_ToggleSelection(hwnd, nItem);
}
else if (wKey & MK_SHIFT)
{
LISTVIEW_SetGroupSelection(hwnd, nItem);
}
else
{
BOOL was_selected =
(ListView_GetItemState(hwnd, nItem, LVIS_SELECTED) & LVIS_SELECTED);
/* set selection (clears other pre-existing selections) */
LISTVIEW_SetSelection(hwnd, nItem);
if (was_selected && infoPtr->nEditLabelItem == -1)
{
infoPtr->nEditLabelItem = nItem;
}
}
}
}
else
{
/* remove all selections */
LISTVIEW_RemoveAllSelections(hwnd);
}
/* redraw if we could have possibly selected something */
if(!GETITEMCOUNT(infoPtr)) InvalidateRect(hwnd, NULL, TRUE);
return 0;
}
/***
* DESCRIPTION:
* Processes mouse up messages (left mouse button).
*
* PARAMETER(S):
* [I] HWND : window handle
* [I] WORD : key flag
* [I] WORD : x coordinate
* [I] WORD : y coordinate
*
* RETURN:
* Zero
*/
static LRESULT LISTVIEW_LButtonUp(HWND hwnd, WORD wKey, WORD wPosX,
WORD wPosY)
{
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
TRACE("(hwnd=%x,key=%hu,X=%hu,Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
if (infoPtr->bLButtonDown != FALSE)
{
INT nCtrlId = GetWindowLongA(hwnd, GWL_ID);
NMLISTVIEW nmlv;
LVHITTESTINFO lvHitTestInfo;
INT ret;
lvHitTestInfo.pt.x = wPosX;
lvHitTestInfo.pt.y = wPosY;
/* send NM_CLICK notification */
ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
nmlv.hdr.hwndFrom = hwnd;
nmlv.hdr.idFrom = nCtrlId;
nmlv.hdr.code = NM_CLICK;
ret = LISTVIEW_HitTestItem(hwnd, &lvHitTestInfo, TRUE);
if (ret != -1)
{
nmlv.iItem = lvHitTestInfo.iItem;
nmlv.iSubItem = lvHitTestInfo.iSubItem;
}
else
{
nmlv.iItem = -1;
nmlv.iSubItem = 0;
}
nmlv.ptAction.x = wPosX;
nmlv.ptAction.y = wPosY;
ListView_LVNotify(GetParent(hwnd), nCtrlId, &nmlv);
/* set left button flag */
infoPtr->bLButtonDown = FALSE;
if(infoPtr->nEditLabelItem != -1)
{
if(lvHitTestInfo.iItem == infoPtr->nEditLabelItem)
{
LISTVIEW_EditLabelA(hwnd, lvHitTestInfo.iItem);
}
infoPtr->nEditLabelItem = -1;
}
}
return 0;
}
/***
* DESCRIPTION:
* Creates the listview control (called before WM_CREATE).
*
* PARAMETER(S):
* [I] HWND : window handle
* [I] WPARAM : unhandled
* [I] LPARAM : widow creation info
*
* RETURN:
* Zero
*/
static LRESULT LISTVIEW_NCCreate(HWND hwnd, WPARAM wParam, LPARAM lParam)
{
LISTVIEW_INFO *infoPtr;
TRACE("(hwnd=%x,wParam=%x,lParam=%lx)\n", hwnd, wParam, lParam);
/* allocate memory for info structure */
infoPtr = (LISTVIEW_INFO *)COMCTL32_Alloc(sizeof(LISTVIEW_INFO));
SetWindowLongA(hwnd, 0, (LONG)infoPtr);
if (infoPtr == NULL)
{
ERR("could not allocate info memory!\n");
return 0;
}
if ((LISTVIEW_INFO *)GetWindowLongA(hwnd, 0) != infoPtr)
{
ERR("pointer assignment error!\n");
return 0;
}
return DefWindowProcA(hwnd, WM_NCCREATE, wParam, lParam);
}
/***
* DESCRIPTION:
* Destroys the listview control (called after WM_DESTROY).
*
* PARAMETER(S):
* [I] HWND : window handle
*
* RETURN:
* Zero
*/
static LRESULT LISTVIEW_NCDestroy(HWND hwnd)
{
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
TRACE("(hwnd=%x)\n", hwnd);
/* delete all items */
LISTVIEW_DeleteAllItems(hwnd);
/* destroy data structure */
DPA_Destroy(infoPtr->hdpaItems);
DPA_Destroy(infoPtr->hdpaSelectionRanges);
/* destroy font */
infoPtr->hFont = (HFONT)0;
if (infoPtr->hDefaultFont)
{
DeleteObject(infoPtr->hDefaultFont);
}
/* free listview info pointer*/
COMCTL32_Free(infoPtr);
SetWindowLongA(hwnd, 0, 0);
return 0;
}
/***
* DESCRIPTION:
* Handles notifications from children.
*
* PARAMETER(S):
* [I] HWND : window handle
* [I] INT : control identifier
* [I] LPNMHDR : notification information
*
* RETURN:
* Zero
*/
static LRESULT LISTVIEW_Notify(HWND hwnd, INT nCtrlId, LPNMHDR lpnmh)
{
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
if (lpnmh->hwndFrom == infoPtr->hwndHeader)
{
/* handle notification from header control */
if (lpnmh->code == HDN_ENDTRACKA)
{
infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
InvalidateRect(hwnd, NULL, TRUE);
}
else if(lpnmh->code == HDN_ITEMCLICKA)
{
/* Handle sorting by Header Column */
NMLISTVIEW nmlv;
LPNMHEADERA pnmHeader = (LPNMHEADERA) lpnmh;
LONG lCtrlId = GetWindowLongA(hwnd, GWL_ID);
ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
nmlv.hdr.hwndFrom = hwnd;
nmlv.hdr.idFrom = lCtrlId;
nmlv.hdr.code = LVN_COLUMNCLICK;
nmlv.iItem = -1;
nmlv.iSubItem = pnmHeader->iItem;
ListView_LVNotify(GetParent(hwnd),lCtrlId, &nmlv);
}
else if(lpnmh->code == NM_RELEASEDCAPTURE)
{
/* Idealy this should be done in HDN_ENDTRACKA
* but since SetItemBounds in Header.c is called after
* the notification is sent, it is neccessary to handle the
* update of the scroll bar here (Header.c works fine as it is,
* no need to disturb it)
*/
infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
LISTVIEW_UpdateScroll(hwnd);
InvalidateRect(hwnd, NULL, TRUE);
}
}
return 0;
}
/***
* DESCRIPTION:
* Determines the type of structure to use.
*
* PARAMETER(S):
* [I] HWND : window handle of the sender
* [I] HWND : listview window handle
* [I] INT : command specifying the nature of the WM_NOTIFYFORMAT
*
* RETURN:
* Zero
*/
static LRESULT LISTVIEW_NotifyFormat(HWND hwndFrom, HWND hwnd, INT nCommand)
{
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
if (nCommand == NF_REQUERY)
{
/* determine the type of structure to use */
infoPtr->notifyFormat = SendMessageA(hwndFrom, WM_NOTIFYFORMAT,
(WPARAM)hwnd, (LPARAM)NF_QUERY);
if (infoPtr->notifyFormat == NFR_UNICODE)
{
FIXME("NO support for unicode structures");
}
}
return 0;
}
/***
* DESCRIPTION:
* Paints/Repaints the listview control.
*
* PARAMETER(S):
* [I] HWND : window handle
* [I] HDC : device context handle
*
* RETURN:
* Zero
*/
static LRESULT LISTVIEW_Paint(HWND hwnd, HDC hdc)
{
PAINTSTRUCT ps;
TRACE("(hwnd=%x,hdc=%x)\n", hwnd, hdc);
if (hdc == 0)
{
hdc = BeginPaint(hwnd, &ps);
LISTVIEW_Refresh(hwnd, hdc);
EndPaint(hwnd, &ps);
}
else
{
LISTVIEW_Refresh(hwnd, hdc);
}
return 0;
}
/***
* DESCRIPTION:
* Processes double click messages (right mouse button).
*
* PARAMETER(S):
* [I] HWND : window handle
* [I] WORD : key flag
* [I] WORD : x coordinate
* [I] WORD : y coordinate
*
* RETURN:
* Zero
*/
static LRESULT LISTVIEW_RButtonDblClk(HWND hwnd, WORD wKey, WORD wPosX,
WORD wPosY)
{
INT nCtrlId = GetWindowLongA(hwnd, GWL_ID);
NMHDR nmh;
TRACE("(hwnd=%x,key=%hu,X=%hu,Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
/* send NM_RELEASEDCAPTURE notification */
nmh.hwndFrom = hwnd;
nmh.idFrom = nCtrlId;
nmh.code = NM_RELEASEDCAPTURE;
ListView_Notify(GetParent(hwnd), nCtrlId, &nmh);
/* send NM_RDBLCLK notification */
nmh.code = NM_RDBLCLK;
ListView_Notify(GetParent(hwnd), nCtrlId, &nmh);
return 0;
}
/***
* DESCRIPTION:
* Processes mouse down messages (right mouse button).
*
* PARAMETER(S):
* [I] HWND : window handle
* [I] WORD : key flag
* [I] WORD : x coordinate
* [I] WORD : y coordinate
*
* RETURN:
* Zero
*/
static LRESULT LISTVIEW_RButtonDown(HWND hwnd, WORD wKey, WORD wPosX,
WORD wPosY)
{
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
INT nCtrlId = GetWindowLongA(hwnd, GWL_ID);
POINT ptPosition;
NMHDR nmh;
INT nItem;
TRACE("(hwnd=%x,key=%hu,X=%hu,Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
/* send NM_RELEASEDCAPTURE notification */
nmh.hwndFrom = hwnd;
nmh.idFrom = nCtrlId;
nmh.code = NM_RELEASEDCAPTURE;
ListView_Notify(GetParent(hwnd), nCtrlId, &nmh);
/* make sure the listview control window has the focus */
if (infoPtr->bFocus == FALSE)
{
SetFocus(hwnd);
}
/* set right button down flag */
infoPtr->bRButtonDown = TRUE;
/* determine the index of the selected item */
ptPosition.x = wPosX;
ptPosition.y = wPosY;
nItem = LISTVIEW_MouseSelection(hwnd, ptPosition);
if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
{
LISTVIEW_SetItemFocus(hwnd,nItem);
if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)) &&
!LISTVIEW_IsSelected(hwnd,nItem))
{
LISTVIEW_SetSelection(hwnd, nItem);
}
}
else
{
LISTVIEW_RemoveAllSelections(hwnd);
}
return 0;
}
/***
* DESCRIPTION:
* Processes mouse up messages (right mouse button).
*
* PARAMETER(S):
* [I] HWND : window handle
* [I] WORD : key flag
* [I] WORD : x coordinate
* [I] WORD : y coordinate
*
* RETURN:
* Zero
*/
static LRESULT LISTVIEW_RButtonUp(HWND hwnd, WORD wKey, WORD wPosX,
WORD wPosY)
{
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
INT nCtrlId = GetWindowLongA(hwnd, GWL_ID);
TRACE("(hwnd=%x,key=%hu,X=%hu,Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
if (infoPtr->bRButtonDown != FALSE)
{
NMLISTVIEW nmlv;
LVHITTESTINFO lvHitTestInfo;
POINT pt;
INT ret;
lvHitTestInfo.pt.x = wPosX;
lvHitTestInfo.pt.y = wPosY;
/* Send NM_RClICK notification */
ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
nmlv.hdr.hwndFrom = hwnd;
nmlv.hdr.idFrom = nCtrlId;
nmlv.hdr.code = NM_RCLICK;
ret = LISTVIEW_HitTestItem(hwnd, &lvHitTestInfo, TRUE);
if (ret != -1)
{
nmlv.iItem = lvHitTestInfo.iItem;
nmlv.iSubItem = lvHitTestInfo.iSubItem;
}
else
{
nmlv.iItem = -1;
nmlv.iSubItem = 0;
}
nmlv.ptAction.x = wPosX;
nmlv.ptAction.y = wPosY;
ListView_LVNotify(GetParent(hwnd), nCtrlId, &nmlv);
pt.x = wPosX;
pt.y = wPosY;
/* set button flag */
infoPtr->bRButtonDown = FALSE;
/* Change to screen coordinate for WM_CONTEXTMENU */
ClientToScreen(hwnd, &pt);
/* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
SendMessageA( hwnd, WM_CONTEXTMENU, (WPARAM) hwnd, MAKELPARAM(pt.x, pt.y));
}
return 0;
}
/***
* DESCRIPTION:
* Sets the focus.
*
* PARAMETER(S):
* [I] HWND : window handle
* [I] HWND : window handle of previously focused window
*
* RETURN:
* Zero
*/
static LRESULT LISTVIEW_SetFocus(HWND hwnd, HWND hwndLoseFocus)
{
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
INT nCtrlId = GetWindowLongA(hwnd, GWL_ID);
NMHDR nmh;
TRACE("(hwnd=%x, hwndLoseFocus=%x)\n", hwnd, hwndLoseFocus);
/* send NM_SETFOCUS notification */
nmh.hwndFrom = hwnd;
nmh.idFrom = nCtrlId;
nmh.code = NM_SETFOCUS;
ListView_Notify(GetParent(hwnd), nCtrlId, &nmh);
/* set window focus flag */
infoPtr->bFocus = TRUE;
UpdateWindow(hwnd);
return 0;
}
/***
* DESCRIPTION:
* Sets the font.
*
* PARAMETER(S):
* [I] HWND : window handle
* [I] HFONT : font handle
* [I] WORD : redraw flag
*
* RETURN:
* Zero
*/
static LRESULT LISTVIEW_SetFont(HWND hwnd, HFONT hFont, WORD fRedraw)
{
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
TRACE("(hwnd=%x,hfont=%x,redraw=%hu)\n", hwnd, hFont, fRedraw);
if (hFont == 0)
{
infoPtr->hFont = infoPtr->hDefaultFont;
}
else
{
infoPtr->hFont = hFont;
}
if (uView == LVS_REPORT)
{
/* set header font */
SendMessageA(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont,
MAKELPARAM(fRedraw, 0));
}
/* invalidate listview control client area */
InvalidateRect(hwnd, NULL, TRUE);
if (fRedraw != FALSE)
{
UpdateWindow(hwnd);
}
return 0;
}
/***
* DESCRIPTION:
* Message handling for WM_SETREDRAW.
* For the Listview, it invalidates the entire window (the doc specifies otherwise)
*
* PARAMETER(S):
* [I] HWND : window handle
* [I] bRedraw: state of redraw flag
*
* RETURN:
* DefWinProc return value
*/
static LRESULT LISTVIEW_SetRedraw(HWND hwnd, BOOL bRedraw)
{
LRESULT lResult;
lResult = DefWindowProcA(hwnd, WM_SETREDRAW, bRedraw, 0);
if(bRedraw)
{
RedrawWindow(hwnd, NULL, 0,
RDW_INVALIDATE | RDW_FRAME | RDW_ERASE | RDW_ALLCHILDREN | RDW_ERASENOW);
}
return lResult;
}
/***
* DESCRIPTION:
* Resizes the listview control. This function processes WM_SIZE
* messages. At this time, the width and height are not used.
*
* PARAMETER(S):
* [I] HWND : window handle
* [I] WORD : new width
* [I] WORD : new height
*
* RETURN:
* Zero
*/
static LRESULT LISTVIEW_Size(HWND hwnd, int Width, int Height)
{
LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
UINT uView = lStyle & LVS_TYPEMASK;
TRACE("(hwnd=%x, width=%d, height=%d)\n",hwnd, Width, Height);
LISTVIEW_UpdateSize(hwnd);
if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
{
if (lStyle & LVS_ALIGNLEFT)
{
LISTVIEW_AlignLeft(hwnd);
}
else
{
LISTVIEW_AlignTop(hwnd);
}
}
LISTVIEW_UpdateScroll(hwnd);
/* invalidate client area + erase background */
InvalidateRect(hwnd, NULL, TRUE);
return 0;
}
/***
* DESCRIPTION:
* Sets the size information.
*
* PARAMETER(S):
* [I] HWND : window handle
*
* RETURN:
* Zero
*/
static VOID LISTVIEW_UpdateSize(HWND hwnd)
{
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
UINT uView = lStyle & LVS_TYPEMASK;
RECT rcList;
GetClientRect(hwnd, &rcList);
infoPtr->rcList.left = 0;
infoPtr->rcList.right = max(rcList.right - rcList.left, 1);
infoPtr->rcList.top = 0;
infoPtr->rcList.bottom = max(rcList.bottom - rcList.top, 1);
if (uView == LVS_LIST)
{
if (lStyle & WS_HSCROLL)
{
INT nHScrollHeight = GetSystemMetrics(SM_CYHSCROLL);
if (infoPtr->rcList.bottom > nHScrollHeight)
{
infoPtr->rcList.bottom -= nHScrollHeight;
}
}
}
else if (uView == LVS_REPORT)
{
HDLAYOUT hl;
WINDOWPOS wp;
hl.prc = &rcList;
hl.pwpos = &wp;
Header_Layout(infoPtr->hwndHeader, &hl);
SetWindowPos(wp.hwnd, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
if (!(LVS_NOCOLUMNHEADER & lStyle))
{
infoPtr->rcList.top = max(wp.cy, 0);
}
}
}
/***
* DESCRIPTION:
* Processes WM_STYLECHANGED messages.
*
* PARAMETER(S):
* [I] HWND : window handle
* [I] WPARAM : window style type (normal or extended)
* [I] LPSTYLESTRUCT : window style information
*
* RETURN:
* Zero
*/
static INT LISTVIEW_StyleChanged(HWND hwnd, WPARAM wStyleType,
LPSTYLESTRUCT lpss)
{
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
UINT uNewView = lpss->styleNew & LVS_TYPEMASK;
UINT uOldView = lpss->styleOld & LVS_TYPEMASK;
RECT rcList = infoPtr->rcList;
TRACE("(hwnd=%x, styletype=%x, stylestruct=%p)\n",
hwnd, wStyleType, lpss);
if (wStyleType == GWL_STYLE)
{
if (uOldView == LVS_REPORT)
{
ShowWindow(infoPtr->hwndHeader, SW_HIDE);
}
if ((lpss->styleOld & WS_HSCROLL) != 0)
{
ShowScrollBar(hwnd, SB_HORZ, FALSE);
}
if ((lpss->styleOld & WS_VSCROLL) != 0)
{
ShowScrollBar(hwnd, SB_VERT, FALSE);
}
if (uNewView == LVS_ICON)
{
infoPtr->iconSize.cx = GetSystemMetrics(SM_CXICON);
infoPtr->iconSize.cy = GetSystemMetrics(SM_CYICON);
infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
if (lpss->styleNew & LVS_ALIGNLEFT)
{
LISTVIEW_AlignLeft(hwnd);
}
else
{
LISTVIEW_AlignTop(hwnd);
}
}
else if (uNewView == LVS_REPORT)
{
HDLAYOUT hl;
WINDOWPOS wp;
hl.prc = &rcList;
hl.pwpos = &wp;
Header_Layout(infoPtr->hwndHeader, &hl);
SetWindowPos(infoPtr->hwndHeader, hwnd, wp.x, wp.y, wp.cx, wp.cy,
wp.flags);
if (!(LVS_NOCOLUMNHEADER & lpss->styleNew))
ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
}
else if (uNewView == LVS_LIST)
{
infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
}
else
{
infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
if (lpss->styleNew & LVS_ALIGNLEFT)
{
LISTVIEW_AlignLeft(hwnd);
}
else
{
LISTVIEW_AlignTop(hwnd);
}
}
/* update the size of the client area */
LISTVIEW_UpdateSize(hwnd);
/* add scrollbars if needed */
LISTVIEW_UpdateScroll(hwnd);
/* invalidate client area + erase background */
InvalidateRect(hwnd, NULL, TRUE);
/* print the list of unsupported window styles */
LISTVIEW_UnsupportedStyles(lpss->styleNew);
}
/* If they change the view and we have an active edit control
we will need to kill the control since the redraw will
misplace the edit control.
*/
if (infoPtr->hwndEdit &&
((uNewView & (LVS_ICON|LVS_LIST|LVS_SMALLICON)) !=
((LVS_ICON|LVS_LIST|LVS_SMALLICON) & uOldView)))
{
SendMessageA(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
}
return 0;
}
/***
* DESCRIPTION:
* Window procedure of the listview control.
*
*/
static LRESULT WINAPI LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam,
LPARAM lParam)
{
TRACE("hwnd=%x uMsg=%x wParam=%x lParam=%lx\n", hwnd, uMsg, wParam, lParam);
if (!GetWindowLongA(hwnd, 0) && (uMsg != WM_NCCREATE))
return DefWindowProcA( hwnd, uMsg, wParam, lParam );
switch (uMsg)
{
case LVM_APPROXIMATEVIEWRECT:
return LISTVIEW_ApproximateViewRect(hwnd, (INT)wParam,
LOWORD(lParam), HIWORD(lParam));
case LVM_ARRANGE:
return LISTVIEW_Arrange(hwnd, (INT)wParam);
/* case LVM_CREATEDRAGIMAGE: */
case LVM_DELETEALLITEMS:
return LISTVIEW_DeleteAllItems(hwnd);
case LVM_DELETECOLUMN:
return LISTVIEW_DeleteColumn(hwnd, (INT)wParam);
case LVM_DELETEITEM:
return LISTVIEW_DeleteItem(hwnd, (INT)wParam);
case LVM_EDITLABELW:
case LVM_EDITLABELA:
return LISTVIEW_EditLabelA(hwnd, (INT)wParam);
case LVM_ENSUREVISIBLE:
return LISTVIEW_EnsureVisible(hwnd, (INT)wParam, (BOOL)lParam);
case LVM_FINDITEMA:
return LISTVIEW_FindItem(hwnd, (INT)wParam, (LPLVFINDINFO)lParam);
case LVM_GETBKCOLOR:
return LISTVIEW_GetBkColor(hwnd);
/* case LVM_GETBKIMAGE: */
case LVM_GETCALLBACKMASK:
return LISTVIEW_GetCallbackMask(hwnd);
case LVM_GETCOLUMNA:
return LISTVIEW_GetColumnA(hwnd, (INT)wParam, (LPLVCOLUMNA)lParam);
/* case LVM_GETCOLUMNW: */
case LVM_GETCOLUMNORDERARRAY:
return LISTVIEW_GetColumnOrderArray(hwnd, (INT)wParam, (LPINT)lParam);
case LVM_GETCOLUMNWIDTH:
return LISTVIEW_GetColumnWidth(hwnd, (INT)wParam);
case LVM_GETCOUNTPERPAGE:
return LISTVIEW_GetCountPerPage(hwnd);
case LVM_GETEDITCONTROL:
return LISTVIEW_GetEditControl(hwnd);
case LVM_GETEXTENDEDLISTVIEWSTYLE:
return LISTVIEW_GetExtendedListViewStyle(hwnd);
case LVM_GETHEADER:
return LISTVIEW_GetHeader(hwnd);
/* case LVM_GETHOTCURSOR: */
case LVM_GETHOTITEM:
return LISTVIEW_GetHotItem(hwnd);
case LVM_GETHOVERTIME:
return LISTVIEW_GetHoverTime(hwnd);
case LVM_GETIMAGELIST:
return LISTVIEW_GetImageList(hwnd, (INT)wParam);
/* case LVM_GETISEARCHSTRING: */
case LVM_GETITEMA:
return LISTVIEW_GetItemA(hwnd, (LPLVITEMA)lParam, FALSE);
/* case LVM_GETITEMW: */
case LVM_GETITEMCOUNT:
return LISTVIEW_GetItemCount(hwnd);
case LVM_GETITEMPOSITION:
return LISTVIEW_GetItemPosition(hwnd, (INT)wParam, (LPPOINT)lParam);
case LVM_GETITEMRECT:
return LISTVIEW_GetItemRect(hwnd, (INT)wParam, (LPRECT)lParam);
case LVM_GETITEMSPACING:
return LISTVIEW_GetItemSpacing(hwnd, (BOOL)wParam);
case LVM_GETITEMSTATE:
return LISTVIEW_GetItemState(hwnd, (INT)wParam, (UINT)lParam);
case LVM_GETITEMTEXTA:
LISTVIEW_GetItemTextA(hwnd, (INT)wParam, (LPLVITEMA)lParam);
break;
/* case LVM_GETITEMTEXTW: */
case LVM_GETNEXTITEM:
return LISTVIEW_GetNextItem(hwnd, (INT)wParam, LOWORD(lParam));
/* case LVM_GETNUMBEROFWORKAREAS: */
case LVM_GETORIGIN:
return LISTVIEW_GetOrigin(hwnd, (LPPOINT)lParam);
case LVM_GETSELECTEDCOUNT:
return LISTVIEW_GetSelectedCount(hwnd);
case LVM_GETSELECTIONMARK:
return LISTVIEW_GetSelectionMark(hwnd);
case LVM_GETSTRINGWIDTHA:
return LISTVIEW_GetStringWidthA (hwnd, (LPCSTR)lParam);
/* case LVM_GETSTRINGWIDTHW: */
/* case LVM_GETSUBITEMRECT: */
case LVM_GETTEXTBKCOLOR:
return LISTVIEW_GetTextBkColor(hwnd);
case LVM_GETTEXTCOLOR:
return LISTVIEW_GetTextColor(hwnd);
/* case LVM_GETTOOLTIPS: */
case LVM_GETTOPINDEX:
return LISTVIEW_GetTopIndex(hwnd);
/* case LVM_GETUNICODEFORMAT: */
case LVM_GETVIEWRECT:
return LISTVIEW_GetViewRect(hwnd, (LPRECT)lParam);
/* case LVM_GETWORKAREAS: */
case LVM_HITTEST:
return LISTVIEW_HitTest(hwnd, (LPLVHITTESTINFO)lParam);
case LVM_INSERTCOLUMNA:
return LISTVIEW_InsertColumnA(hwnd, (INT)wParam, (LPLVCOLUMNA)lParam);
case LVM_INSERTCOLUMNW:
return LISTVIEW_InsertColumnW(hwnd, (INT)wParam, (LPLVCOLUMNW)lParam);
case LVM_INSERTITEMA:
return LISTVIEW_InsertItemA(hwnd, (LPLVITEMA)lParam);
case LVM_INSERTITEMW:
return LISTVIEW_InsertItemW(hwnd, (LPLVITEMW)lParam);
case LVM_REDRAWITEMS:
return LISTVIEW_RedrawItems(hwnd, (INT)wParam, (INT)lParam);
/* case LVM_SCROLL: */
/* return LISTVIEW_Scroll(hwnd, (INT)wParam, (INT)lParam); */
case LVM_SETBKCOLOR:
return LISTVIEW_SetBkColor(hwnd, (COLORREF)lParam);
/* case LVM_SETBKIMAGE: */
case LVM_SETCALLBACKMASK:
return LISTVIEW_SetCallbackMask(hwnd, (UINT)wParam);
case LVM_SETCOLUMNA:
return LISTVIEW_SetColumnA(hwnd, (INT)wParam, (LPLVCOLUMNA)lParam);
case LVM_SETCOLUMNW:
FIXME("Unimplemented msg LVM_SETCOLUMNW\n");
return 0;
case LVM_SETCOLUMNORDERARRAY:
return LISTVIEW_SetColumnOrderArray(hwnd, (INT)wParam, (LPINT)lParam);
case LVM_SETCOLUMNWIDTH:
return LISTVIEW_SetColumnWidth(hwnd, (INT)wParam, SLOWORD(lParam));
case LVM_SETEXTENDEDLISTVIEWSTYLE:
return LISTVIEW_SetExtendedListViewStyle(hwnd, (DWORD)wParam, (DWORD)lParam);
/* case LVM_SETHOTCURSOR: */
case LVM_SETHOTITEM:
return LISTVIEW_SetHotItem(hwnd, (INT)wParam);
case LVM_SETHOVERTIME:
return LISTVIEW_SetHoverTime(hwnd, (DWORD)wParam);
/* case LVM_SETICONSPACING: */
case LVM_SETIMAGELIST:
return LISTVIEW_SetImageList(hwnd, (INT)wParam, (HIMAGELIST)lParam);
case LVM_SETITEMA:
return LISTVIEW_SetItemA(hwnd, (LPLVITEMA)lParam);
/* case LVM_SETITEMW: */
case LVM_SETITEMCOUNT:
return LISTVIEW_SetItemCount(hwnd, (INT)wParam, (DWORD)lParam);
case LVM_SETITEMPOSITION:
return LISTVIEW_SetItemPosition(hwnd, (INT)wParam, (INT)LOWORD(lParam),
(INT)HIWORD(lParam));
case LVM_SETITEMPOSITION32:
return LISTVIEW_SetItemPosition(hwnd, (INT)wParam, ((POINT*)lParam)->x,
((POINT*)lParam)->y);
case LVM_SETITEMSTATE:
return LISTVIEW_SetItemState(hwnd, (INT)wParam, (LPLVITEMA)lParam);
case LVM_SETITEMTEXTA:
return LISTVIEW_SetItemTextA(hwnd, (INT)wParam, (LPLVITEMA)lParam);
/* case LVM_SETITEMTEXTW: */
case LVM_SETSELECTIONMARK:
return LISTVIEW_SetSelectionMark(hwnd, (INT)lParam);
case LVM_SETTEXTBKCOLOR:
return LISTVIEW_SetTextBkColor(hwnd, (COLORREF)lParam);
case LVM_SETTEXTCOLOR:
return LISTVIEW_SetTextColor(hwnd, (COLORREF)lParam);
/* case LVM_SETTOOLTIPS: */
/* case LVM_SETUNICODEFORMAT: */
/* case LVM_SETWORKAREAS: */
case LVM_SORTITEMS:
return LISTVIEW_SortItems(hwnd, wParam, lParam);
/* case LVM_SUBITEMHITTEST: */
case LVM_UPDATE:
return LISTVIEW_Update(hwnd, (INT)wParam);
case WM_CHAR:
return LISTVIEW_ProcessLetterKeys( hwnd, wParam, lParam );
case WM_COMMAND:
return LISTVIEW_Command(hwnd, wParam, lParam);
case WM_CREATE:
return LISTVIEW_Create(hwnd, wParam, lParam);
case WM_ERASEBKGND:
return LISTVIEW_EraseBackground(hwnd, wParam, lParam);
case WM_GETDLGCODE:
return DLGC_WANTCHARS | DLGC_WANTARROWS;
case WM_GETFONT:
return LISTVIEW_GetFont(hwnd);
case WM_HSCROLL:
return LISTVIEW_HScroll(hwnd, (INT)LOWORD(wParam),
(INT)HIWORD(wParam), (HWND)lParam);
case WM_KEYDOWN:
return LISTVIEW_KeyDown(hwnd, (INT)wParam, (LONG)lParam);
case WM_KILLFOCUS:
return LISTVIEW_KillFocus(hwnd);
case WM_LBUTTONDBLCLK:
return LISTVIEW_LButtonDblClk(hwnd, (WORD)wParam, LOWORD(lParam),
HIWORD(lParam));
case WM_LBUTTONDOWN:
return LISTVIEW_LButtonDown(hwnd, (WORD)wParam, LOWORD(lParam),
HIWORD(lParam));
case WM_LBUTTONUP:
return LISTVIEW_LButtonUp(hwnd, (WORD)wParam, LOWORD(lParam),
HIWORD(lParam));
case WM_MOUSEMOVE:
return LISTVIEW_MouseMove (hwnd, wParam, lParam);
case WM_MOUSEHOVER:
return LISTVIEW_MouseHover(hwnd, wParam, lParam);
case WM_NCCREATE:
return LISTVIEW_NCCreate(hwnd, wParam, lParam);
case WM_NCDESTROY:
return LISTVIEW_NCDestroy(hwnd);
case WM_NOTIFY:
return LISTVIEW_Notify(hwnd, (INT)wParam, (LPNMHDR)lParam);
case WM_NOTIFYFORMAT:
return LISTVIEW_NotifyFormat(hwnd, (HWND)wParam, (INT)lParam);
case WM_PAINT:
return LISTVIEW_Paint(hwnd, (HDC)wParam);
case WM_RBUTTONDBLCLK:
return LISTVIEW_RButtonDblClk(hwnd, (WORD)wParam, LOWORD(lParam),
HIWORD(lParam));
case WM_RBUTTONDOWN:
return LISTVIEW_RButtonDown(hwnd, (WORD)wParam, LOWORD(lParam),
HIWORD(lParam));
case WM_RBUTTONUP:
return LISTVIEW_RButtonUp(hwnd, (WORD)wParam, LOWORD(lParam),
HIWORD(lParam));
case WM_SETFOCUS:
return LISTVIEW_SetFocus(hwnd, (HWND)wParam);
case WM_SETFONT:
return LISTVIEW_SetFont(hwnd, (HFONT)wParam, (WORD)lParam);
case WM_SETREDRAW:
return LISTVIEW_SetRedraw(hwnd, (BOOL)wParam);
case WM_SIZE:
return LISTVIEW_Size(hwnd, (int)SLOWORD(lParam), (int)SHIWORD(lParam));
case WM_STYLECHANGED:
return LISTVIEW_StyleChanged(hwnd, wParam, (LPSTYLESTRUCT)lParam);
/* case WM_TIMER: */
case WM_VSCROLL:
return LISTVIEW_VScroll(hwnd, (INT)LOWORD(wParam),
(INT)HIWORD(wParam), (HWND)lParam);
case WM_MOUSEWHEEL:
if (wParam & (MK_SHIFT | MK_CONTROL))
return DefWindowProcA( hwnd, uMsg, wParam, lParam );
return LISTVIEW_MouseWheel(hwnd, (short int)HIWORD(wParam));/* case WM_WINDOWPOSCHANGED: */
/* case WM_WININICHANGE: */
default:
if (uMsg >= WM_USER)
{
ERR("unknown msg %04x wp=%08x lp=%08lx\n", uMsg, wParam,
lParam);
}
/* call default window procedure */
return DefWindowProcA(hwnd, uMsg, wParam, lParam);
}
return 0;
}
/***
* DESCRIPTION:
* Registers the window class.
*
* PARAMETER(S):
* None
*
* RETURN:
* None
*/
VOID LISTVIEW_Register(void)
{
WNDCLASSA wndClass;
ZeroMemory(&wndClass, sizeof(WNDCLASSA));
wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
wndClass.lpfnWndProc = (WNDPROC)LISTVIEW_WindowProc;
wndClass.cbClsExtra = 0;
wndClass.cbWndExtra = sizeof(LISTVIEW_INFO *);
wndClass.hCursor = LoadCursorA(0, IDC_ARROWA);
wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wndClass.lpszClassName = WC_LISTVIEWA;
RegisterClassA(&wndClass);
}
/***
* DESCRIPTION:
* Unregisters the window class.
*
* PARAMETER(S):
* None
*
* RETURN:
* None
*/
VOID LISTVIEW_Unregister(void)
{
UnregisterClassA(WC_LISTVIEWA, (HINSTANCE)NULL);
}
/***
* DESCRIPTION:
* Handle any WM_COMMAND messages
*
* PARAMETER(S):
*
* RETURN:
*/
static LRESULT LISTVIEW_Command(HWND hwnd, WPARAM wParam, LPARAM lParam)
{
switch (HIWORD(wParam))
{
case EN_UPDATE:
{
/*
* Adjust the edit window size
*/
char buffer[1024];
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
HDC hdc = GetDC(infoPtr->hwndEdit);
HFONT hFont, hOldFont = 0;
RECT rect;
SIZE sz;
int len;
len = GetWindowTextA(infoPtr->hwndEdit, buffer, 1023);
GetWindowRect(infoPtr->hwndEdit, &rect);
/* Select font to get the right dimension of the string */
hFont = SendMessageA(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
if(hFont != 0)
{
hOldFont = SelectObject(hdc, hFont);
}
if (GetTextExtentPoint32A(hdc, buffer, strlen(buffer), &sz))
{
TEXTMETRICA textMetric;
/* Add Extra spacing for the next character */
GetTextMetricsA(hdc, &textMetric);
sz.cx += (textMetric.tmMaxCharWidth * 2);
SetWindowPos (
infoPtr->hwndEdit,
HWND_TOP,
0,
0,
sz.cx,
rect.bottom - rect.top,
SWP_DRAWFRAME|SWP_NOMOVE);
}
if(hFont != 0)
{
SelectObject(hdc, hOldFont);
}
ReleaseDC(hwnd, hdc);
break;
}
default:
return SendMessageA (GetParent (hwnd), WM_COMMAND, wParam, lParam);
}
return 0;
}
/***
* DESCRIPTION:
* Subclassed edit control windproc function
*
* PARAMETER(S):
*
* RETURN:
*/
LRESULT CALLBACK EditLblWndProc(HWND hwnd, UINT uMsg,
WPARAM wParam, LPARAM lParam)
{
BOOL cancel = FALSE;
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(GetParent(hwnd), 0);
EDITLABEL_ITEM *einfo = infoPtr->pedititem;
static BOOL bIgnoreKillFocus = FALSE;
switch (uMsg)
{
case WM_GETDLGCODE:
return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
case WM_KILLFOCUS:
if(bIgnoreKillFocus)
{
return TRUE;
}
break;
case WM_DESTROY:
{
WNDPROC editProc = einfo->EditWndProc;
SetWindowLongA(hwnd, GWL_WNDPROC, (LONG)editProc);
COMCTL32_Free(einfo);
infoPtr->pedititem = NULL;
return CallWindowProcA(editProc, hwnd, uMsg, wParam, lParam);
}
case WM_KEYDOWN:
if (VK_ESCAPE == (INT)wParam)
{
cancel = TRUE;
break;
}
else if (VK_RETURN == (INT)wParam)
break;
default:
return CallWindowProcA(einfo->EditWndProc, hwnd,
uMsg, wParam, lParam);
}
if (einfo->EditLblCb)
{
char *buffer = NULL;
if (!cancel)
{
int len = 1 + GetWindowTextLengthA(hwnd);
if (len > 1)
{
if (NULL != (buffer = (char *)COMCTL32_Alloc(len*sizeof(char))))
{
GetWindowTextA(hwnd, buffer, len);
}
}
}
/* Processing LVN_ENDLABELEDIT message could kill the focus */
/* eg. Using a messagebox */
bIgnoreKillFocus = TRUE;
einfo->EditLblCb(GetParent(hwnd), buffer, einfo->param);
if (buffer)
COMCTL32_Free(buffer);
einfo->EditLblCb = NULL;
bIgnoreKillFocus = FALSE;
}
SendMessageA(hwnd, WM_CLOSE, 0, 0);
return TRUE;
}
/***
* DESCRIPTION:
* Creates a subclassed edit cotrol
*
* PARAMETER(S):
*
* RETURN:
*/
HWND CreateEditLabel(LPCSTR text, DWORD style, INT x, INT y,
INT width, INT height, HWND parent, HINSTANCE hinst,
EditlblCallback EditLblCb, DWORD param)
{
HWND hedit;
SIZE sz;
HDC hdc;
HDC hOldFont=0;
TEXTMETRICA textMetric;
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(parent, 0);
if (NULL == (infoPtr->pedititem = COMCTL32_Alloc(sizeof(EDITLABEL_ITEM))))
return 0;
style |= WS_CHILDWINDOW|WS_CLIPSIBLINGS|ES_LEFT|WS_BORDER;
hdc = GetDC(parent);
/* Select the font to get appropriate metric dimensions */
if(infoPtr->hFont != 0)
{
hOldFont = SelectObject(hdc, infoPtr->hFont);
}
/*Get String Lenght in pixels */
GetTextExtentPoint32A(hdc, text, strlen(text), &sz);
/*Add Extra spacing for the next character */
GetTextMetricsA(hdc, &textMetric);
sz.cx += (textMetric.tmMaxCharWidth * 2);
if(infoPtr->hFont != 0)
{
SelectObject(hdc, hOldFont);
}
ReleaseDC(parent, hdc);
if (!(hedit = CreateWindowA("Edit", text, style, x, y, sz.cx, height,
parent, 0, hinst, 0)))
{
COMCTL32_Free(infoPtr->pedititem);
return 0;
}
infoPtr->pedititem->param = param;
infoPtr->pedititem->EditLblCb = EditLblCb;
infoPtr->pedititem->EditWndProc = (WNDPROC)SetWindowLongA(hedit,
GWL_WNDPROC, (LONG) EditLblWndProc);
SendMessageA(hedit, WM_SETFONT, infoPtr->hFont, FALSE);
return hedit;
}