Sweden-Number/dlls/comctl32/listview.c

9595 lines
237 KiB
C

/*
* 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:
* shifts all selection indexs starting with the indesx specified
* in the direction specified.
*
* PARAMETER(S):
* [I] HWND : window handle
* [I] INT : item index
* [I] INT : amount and direction of shift
*
* RETURN:
* None
*/
static VOID LISTVIEW_ShiftSelections(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 ++;
}
}
/**
* 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;
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 = min(infoPtr->nSelectionMark, nItem);
INT 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 recieved.
*
* 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 recieve 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);
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);
LISTVIEW_ShiftSelections(hwnd,nItem,-1);
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);
}
/* 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);
}
}
/* If this item had focus change focus to next or previous item */
if (GETITEMCOUNT(infoPtr) > 0)
{
int sItem = nItem < GETITEMCOUNT(infoPtr) ? nItem : nItem - 1;
if (infoPtr->nFocusedItem == nItem)
LISTVIEW_SetItemFocus(hwnd, sItem);
}
else
infoPtr->nFocusedItem = -1;
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_ShiftSelections(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;
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 > 1)
{
infoPtr->pfnCompare = (PFNLVCOMPARE)lParam;
infoPtr->lParamSort = (LPARAM)wParam;
DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompare, hwnd);
}
/* Adjust selections 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) */
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);
}
/* 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;
}