10045 lines
268 KiB
C
10045 lines
268 KiB
C
/*
|
|
* Listview control
|
|
*
|
|
* Copyright 1998, 1999 Eric Kohl
|
|
* Copyright 1999 Luc Tourangeau
|
|
* Copyright 2000 Jason Mawdsley
|
|
* Copyright 2001 Codeweavers Inc.
|
|
* Copyright 2002 Dimitrie O. Paun
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*
|
|
* 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
|
|
*
|
|
* 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
|
|
*
|
|
* Known differences in message stream from native control (not known if
|
|
* these differences cause problems):
|
|
* LVM_INSERTITEM issues LVM_SETITEMSTATE and LVM_SETITEM in certain cases.
|
|
* LVM_SETITEM does not always issue LVN_ITEMCHANGING/LVN_ITEMCHANGED.
|
|
* WM_PAINT does LVN_GETDISPINFO in item order 0->n, native does n->0.
|
|
* WM_SETREDRAW(True) native does LVN_GETDISPINFO for all items and
|
|
* does *not* invoke DefWindowProc
|
|
* WM_CREATE does not issue WM_QUERYUISTATE and associated registry
|
|
* processing for "USEDOUBLECLICKTIME".
|
|
*/
|
|
|
|
|
|
#include "config.h"
|
|
#include "wine/port.h"
|
|
|
|
#include <ctype.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
|
|
#include "winbase.h"
|
|
#include "winnt.h"
|
|
#include "heap.h"
|
|
#include "commctrl.h"
|
|
#include "wine/debug.h"
|
|
|
|
WINE_DEFAULT_DEBUG_CHANNEL(listview);
|
|
|
|
/* Some definitions for inline edit control */
|
|
typedef BOOL (*EditlblCallbackW)(HWND, LPWSTR, DWORD);
|
|
typedef BOOL (*EditlblCallbackA)(HWND, LPWSTR, DWORD);
|
|
|
|
typedef struct tagLV_INTHIT
|
|
{
|
|
LVHITTESTINFO ht;
|
|
DWORD distance; /* distance to closest item */
|
|
INT iDistItem; /* item number that is closest */
|
|
} LV_INTHIT, *LPLV_INTHIT;
|
|
|
|
|
|
typedef struct tagEDITLABEL_ITEM
|
|
{
|
|
WNDPROC EditWndProc;
|
|
DWORD param;
|
|
EditlblCallbackW EditLblCb;
|
|
} EDITLABEL_ITEM;
|
|
|
|
typedef struct tagLISTVIEW_SUBITEM
|
|
{
|
|
LPWSTR pszText;
|
|
INT iImage;
|
|
INT iSubItem;
|
|
} LISTVIEW_SUBITEM;
|
|
|
|
typedef struct tagLISTVIEW_ITEM
|
|
{
|
|
UINT state;
|
|
LPWSTR 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
|
|
{
|
|
HWND hwndSelf;
|
|
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;
|
|
INT ntmHeight; /* from GetTextMetrics from above font */
|
|
INT ntmAveCharWidth; /* from GetTextMetrics from above font */
|
|
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 */
|
|
WCHAR szSearchParam[ MAX_PATH ]; /* Added */
|
|
BOOL bIsDrawing;
|
|
} 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
|
|
* ICON_TOP_PADDING_NOTHITABLE - space between top of box and area
|
|
* that HITTEST will see.
|
|
* ICON_TOP_PADDING_HITABLE - spacing between above and icon.
|
|
* ICON_TOP_PADDING - sum of the two above.
|
|
* ICON_BOTTOM_PADDING - between bottom of icon and top of text
|
|
* LABEL_VERT_OFFSET - between bottom of text and end of box
|
|
*/
|
|
#define ICON_TOP_PADDING_NOTHITABLE 2
|
|
#define ICON_TOP_PADDING_HITABLE 2
|
|
#define ICON_TOP_PADDING ICON_TOP_PADDING_NOTHITABLE + ICON_TOP_PADDING_HITABLE
|
|
#define ICON_BOTTOM_PADDING 4
|
|
#define LABEL_VERT_OFFSET 10
|
|
|
|
/* 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
|
|
*/
|
|
/* retrieve the number of items in the listview */
|
|
#define GETITEMCOUNT(infoPtr) ((infoPtr)->hdpaItems->nItemCount)
|
|
#define HDM_INSERTITEMT(isW) ( (isW) ? HDM_INSERTITEMW : HDM_INSERTITEMA )
|
|
|
|
HWND CreateEditLabelT(LPCWSTR text, DWORD style, INT x, INT y,
|
|
INT width, INT height, HWND parent, HINSTANCE hinst,
|
|
EditlblCallbackW EditLblCb, DWORD param, BOOL isW);
|
|
|
|
/*
|
|
* forward declarations
|
|
*/
|
|
static LRESULT LISTVIEW_GetItemT(HWND hwnd, LPLVITEMW lpLVItem, BOOL internal, BOOL isW);
|
|
static INT LISTVIEW_SuperHitTestItem(HWND, LPLV_INTHIT, BOOL);
|
|
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_AddSubItemT(HWND, LPLVITEMW, BOOL);
|
|
static INT LISTVIEW_FindInsertPosition(HDPA, INT);
|
|
static INT LISTVIEW_GetItemHeight(HWND);
|
|
static BOOL LISTVIEW_GetItemBoundBox(HWND, INT, LPRECT);
|
|
static BOOL LISTVIEW_GetItemPosition(HWND, INT, LPPOINT);
|
|
static LRESULT LISTVIEW_GetItemRect(HWND, INT, LPRECT);
|
|
static LRESULT LISTVIEW_GetSubItemRect(HWND, INT, INT, 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_InitItemT(HWND, LISTVIEW_ITEM *, LPLVITEMW, BOOL);
|
|
static BOOL LISTVIEW_InitSubItemT(HWND, LISTVIEW_SUBITEM *, LPLVITEMW, BOOL);
|
|
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_SetItemT(HWND, LPLVITEMW, BOOL);
|
|
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 BOOL LISTVIEW_UpdateSize(HWND);
|
|
static BOOL LISTVIEW_SetSubItemT(HWND, LPLVITEMW, BOOL);
|
|
static LRESULT LISTVIEW_SetViewRect(HWND, LPRECT);
|
|
static BOOL LISTVIEW_ToggleSelection(HWND, INT);
|
|
static VOID LISTVIEW_UnsupportedStyles(LONG lStyle);
|
|
static HWND LISTVIEW_EditLabelT(HWND hwnd, INT nItem, BOOL isW);
|
|
static BOOL LISTVIEW_EndEditLabelW(HWND hwnd, LPWSTR pszText, DWORD nItem);
|
|
static BOOL LISTVIEW_EndEditLabelA(HWND hwnd, LPSTR pszText, DWORD nItem);
|
|
static LRESULT LISTVIEW_Command(HWND hwnd, WPARAM wParam, LPARAM lParam);
|
|
static LRESULT LISTVIEW_SortItems(HWND hwnd, PFNLVCOMPARE pfnCompare, LPARAM lParamSort);
|
|
static LRESULT LISTVIEW_GetStringWidthT(HWND hwnd, LPCWSTR lpszText, BOOL isW);
|
|
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, LPLVITEMW 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);
|
|
static void ListView_UpdateLargeItemLabelRect (HWND hwnd, const LISTVIEW_INFO* infoPtr, int nItem, RECT *rect);
|
|
static LRESULT LISTVIEW_GetColumnT(HWND, INT, LPLVCOLUMNW, BOOL);
|
|
|
|
/******** Defines that LISTVIEW_ProcessLetterKeys uses ****************/
|
|
#define KEY_DELAY 450
|
|
|
|
#define COUNTOF(array) (sizeof(array)/sizeof(array[0]))
|
|
|
|
static inline BOOL is_textW(LPCWSTR text)
|
|
{
|
|
return text != NULL && text != LPSTR_TEXTCALLBACKW;
|
|
}
|
|
|
|
static inline BOOL is_textT(LPCWSTR text, BOOL isW)
|
|
{
|
|
/* we can ignore isW since LPSTR_TEXTCALLBACKW == LPSTR_TEXTCALLBACKA */
|
|
return is_textW(text);
|
|
}
|
|
|
|
static inline int textlenT(LPCWSTR text, BOOL isW)
|
|
{
|
|
return !is_textT(text, isW) ? 0 :
|
|
isW ? lstrlenW(text) : lstrlenA((LPCSTR)text);
|
|
}
|
|
|
|
static inline void textcpynT(LPWSTR dest, BOOL isDestW, LPCWSTR src, BOOL isSrcW, INT max)
|
|
{
|
|
if (isDestW)
|
|
if (isSrcW) lstrcpynW(dest, src, max);
|
|
else MultiByteToWideChar(CP_ACP, 0, (LPCSTR)src, -1, dest, max);
|
|
else
|
|
if (isSrcW) WideCharToMultiByte(CP_ACP, 0, src, -1, (LPSTR)dest, max, NULL, NULL);
|
|
else lstrcpynA((LPSTR)dest, (LPCSTR)src, max);
|
|
}
|
|
|
|
static inline LPCSTR debugstr_t(LPCWSTR text, BOOL isW)
|
|
{
|
|
return isW ? debugstr_w(text) : debugstr_a((LPCSTR)text);
|
|
}
|
|
|
|
static inline LPCSTR debugstr_tn(LPCWSTR text, BOOL isW, INT n)
|
|
{
|
|
return isW ? debugstr_wn(text, n) : debugstr_an((LPCSTR)text, n);
|
|
}
|
|
|
|
static inline LPWSTR textdupTtoW(LPCWSTR text, BOOL isW)
|
|
{
|
|
LPWSTR wstr = (LPWSTR)text;
|
|
|
|
TRACE("(text=%s, isW=%d)\n", debugstr_t(text, isW), isW);
|
|
if (!isW && text)
|
|
{
|
|
INT len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, NULL, 0);
|
|
wstr = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
|
|
if (wstr) MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, wstr, len);
|
|
}
|
|
TRACE(" wstr=%s\n", debugstr_w(wstr));
|
|
return wstr;
|
|
}
|
|
|
|
static inline void textfreeT(LPWSTR wstr, BOOL isW)
|
|
{
|
|
if (!isW && wstr) HeapFree(GetProcessHeap(), 0, wstr);
|
|
}
|
|
|
|
/*
|
|
* dest is a pointer to a Unicode string
|
|
* src is a pointer to a string (Unicode if isW, ANSI if !isW)
|
|
*/
|
|
static inline BOOL textsetptrT(LPWSTR *dest, LPWSTR src, BOOL isW)
|
|
{
|
|
LPWSTR pszText = textdupTtoW(src, isW);
|
|
BOOL bResult = TRUE;
|
|
if (*dest == LPSTR_TEXTCALLBACKW) *dest = NULL;
|
|
bResult = Str_SetPtrW(dest, pszText);
|
|
textfreeT(pszText, isW);
|
|
return bResult;
|
|
}
|
|
|
|
static inline LRESULT CallWindowProcT(WNDPROC proc, HWND hwnd, UINT uMsg,
|
|
WPARAM wParam, LPARAM lParam, BOOL isW)
|
|
{
|
|
if (isW)
|
|
return CallWindowProcW(proc, hwnd, uMsg, wParam, lParam);
|
|
else
|
|
return CallWindowProcA(proc, hwnd, uMsg, wParam, lParam);
|
|
}
|
|
|
|
static inline BOOL notify(HWND self, INT code, LPNMHDR pnmh)
|
|
{
|
|
pnmh->hwndFrom = self;
|
|
pnmh->idFrom = GetWindowLongW(self, GWL_ID);
|
|
pnmh->code = code;
|
|
return (BOOL)SendMessageW(GetParent(self), WM_NOTIFY,
|
|
(WPARAM)pnmh->idFrom, (LPARAM)pnmh);
|
|
}
|
|
|
|
static inline BOOL hdr_notify(HWND self, INT code)
|
|
{
|
|
NMHDR nmh;
|
|
return notify(self, code, &nmh);
|
|
}
|
|
|
|
static inline BOOL listview_notify(HWND self, INT code, LPNMLISTVIEW plvnm)
|
|
{
|
|
return notify(self, code, (LPNMHDR)plvnm);
|
|
}
|
|
|
|
static int tabNotification[] = {
|
|
LVN_BEGINLABELEDITW, LVN_BEGINLABELEDITA,
|
|
LVN_ENDLABELEDITW, LVN_ENDLABELEDITA,
|
|
LVN_GETDISPINFOW, LVN_GETDISPINFOA,
|
|
LVN_SETDISPINFOW, LVN_SETDISPINFOA,
|
|
LVN_ODFINDITEMW, LVN_ODFINDITEMA,
|
|
LVN_GETINFOTIPW, LVN_GETINFOTIPA,
|
|
0
|
|
};
|
|
|
|
static int get_ansi_notification(INT unicodeNotificationCode)
|
|
{
|
|
int *pTabNotif = tabNotification;
|
|
while (*pTabNotif && (unicodeNotificationCode != *pTabNotif++));
|
|
if (*pTabNotif) return *pTabNotif;
|
|
ERR("unknown notification %x\n", unicodeNotificationCode);
|
|
return unicodeNotificationCode;
|
|
}
|
|
|
|
/*
|
|
Send notification. depends on dispinfoW having same
|
|
structure as dispinfoA.
|
|
self : listview handle
|
|
notificationCode : *Unicode* notification code
|
|
pdi : dispinfo structure (can be unicode or ansi)
|
|
isW : TRUE if dispinfo is Unicode
|
|
*/
|
|
static BOOL dispinfo_notifyT(HWND self, INT notificationCode, LPNMLVDISPINFOW pdi, BOOL isW)
|
|
{
|
|
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(self, 0);
|
|
BOOL bResult = FALSE;
|
|
BOOL convertToAnsi = FALSE, convertToUnicode = FALSE;
|
|
INT realNotifCode;
|
|
INT cchTempBufMax = 0, savCchTextMax = 0;
|
|
LPWSTR pszTempBuf = NULL, savPszText = NULL;
|
|
|
|
TRACE("(self=%x, code=%x, pdi=%p, isW=%d)\n", self, notificationCode, pdi, isW);
|
|
TRACE(" notifyFormat=%s\n",
|
|
infoPtr->notifyFormat == NFR_UNICODE ? "NFR_UNICODE" :
|
|
infoPtr->notifyFormat == NFR_ANSI ? "NFR_ANSI" : "(not set)");
|
|
if (infoPtr->notifyFormat == NFR_ANSI)
|
|
realNotifCode = get_ansi_notification(notificationCode);
|
|
else
|
|
realNotifCode = notificationCode;
|
|
|
|
if (is_textT(pdi->item.pszText, isW))
|
|
{
|
|
if (isW && infoPtr->notifyFormat == NFR_ANSI)
|
|
convertToAnsi = TRUE;
|
|
if (!isW && infoPtr->notifyFormat == NFR_UNICODE)
|
|
convertToUnicode = TRUE;
|
|
}
|
|
|
|
if (convertToAnsi || convertToUnicode)
|
|
{
|
|
TRACE(" we have to convert the text to the correct format\n");
|
|
if (notificationCode != LVN_GETDISPINFOW)
|
|
{ /* length of existing text */
|
|
cchTempBufMax = convertToUnicode ?
|
|
MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1, NULL, 0):
|
|
WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, NULL, 0, NULL, NULL);
|
|
}
|
|
else
|
|
cchTempBufMax = pdi->item.cchTextMax;
|
|
|
|
pszTempBuf = HeapAlloc(GetProcessHeap(), 0,
|
|
(convertToUnicode ? sizeof(WCHAR) : sizeof(CHAR)) * cchTempBufMax);
|
|
if (!pszTempBuf) return FALSE;
|
|
if (convertToUnicode)
|
|
MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1,
|
|
pszTempBuf, cchTempBufMax);
|
|
else
|
|
WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) pszTempBuf,
|
|
cchTempBufMax, NULL, NULL);
|
|
TRACE(" text=%s\n", debugstr_t(pszTempBuf, convertToUnicode));
|
|
savCchTextMax = pdi->item.cchTextMax;
|
|
savPszText = pdi->item.pszText;
|
|
pdi->item.pszText = pszTempBuf;
|
|
pdi->item.cchTextMax = cchTempBufMax;
|
|
}
|
|
|
|
bResult = notify(self, realNotifCode, (LPNMHDR)pdi);
|
|
|
|
if (convertToUnicode || convertToAnsi)
|
|
{ /* convert back result */
|
|
TRACE(" returned text=%s\n", debugstr_t(pdi->item.pszText, convertToUnicode));
|
|
if (convertToUnicode) /* note : pointer can be changed by app ! */
|
|
WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) savPszText,
|
|
savCchTextMax, NULL, NULL);
|
|
else
|
|
MultiByteToWideChar(CP_ACP, 0, (LPSTR) pdi->item.pszText, -1,
|
|
savPszText, savCchTextMax);
|
|
pdi->item.pszText = savPszText; /* restores our buffer */
|
|
pdi->item.cchTextMax = savCchTextMax;
|
|
HeapFree(GetProcessHeap(), 0, pszTempBuf);
|
|
}
|
|
return bResult;
|
|
}
|
|
|
|
static inline LRESULT LISTVIEW_GetItemW(HWND hwnd, LPLVITEMW lpLVItem, BOOL internal)
|
|
{
|
|
return LISTVIEW_GetItemT(hwnd, lpLVItem, internal, TRUE);
|
|
}
|
|
|
|
static inline int lstrncmpiW(LPCWSTR s1, LPCWSTR s2, int n)
|
|
{
|
|
int res;
|
|
|
|
n = min(min(n, strlenW(s1)), strlenW(s2));
|
|
res = CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, s1, n, s2, n);
|
|
return res ? res - 2 : res;
|
|
}
|
|
|
|
static char* debuglvitem_t(LPLVITEMW lpLVItem, BOOL isW)
|
|
{
|
|
static int index = 0;
|
|
static char buffers[20][256];
|
|
char* buf = buffers[index++ % 20];
|
|
if (lpLVItem == NULL) return "(null)";
|
|
snprintf(buf, 256, "{mask=%x, iItem=%d, iSubItem=%d, state=%x, stateMask=%x,"
|
|
" pszText=%s, cchTextMax=%d, iImage=%d, lParam=%lx, iIndent=%d}",
|
|
lpLVItem->mask, lpLVItem->iItem, lpLVItem->iSubItem,
|
|
lpLVItem->state, lpLVItem->stateMask,
|
|
lpLVItem->pszText == LPSTR_TEXTCALLBACKW ? "(callback)" :
|
|
debugstr_tn(lpLVItem->pszText, isW, 80),
|
|
lpLVItem->cchTextMax, lpLVItem->iImage, lpLVItem->lParam,
|
|
lpLVItem->iIndent);
|
|
return buf;
|
|
}
|
|
|
|
static char* debuglvcolumn_t(LPLVCOLUMNW lpColumn, BOOL isW)
|
|
{
|
|
static int index = 0;
|
|
static char buffers[20][256];
|
|
char* buf = buffers[index++ % 20];
|
|
if (lpColumn == NULL) return "(null)";
|
|
snprintf(buf, 256, "{mask=%x, fmt=%x, cx=%d,"
|
|
" pszText=%s, cchTextMax=%d, iSubItem=%d}",
|
|
lpColumn->mask, lpColumn->fmt, lpColumn->cx,
|
|
lpColumn->mask & LVCF_TEXT ? lpColumn->pszText == LPSTR_TEXTCALLBACKW ? "(callback)" :
|
|
debugstr_tn(lpColumn->pszText, isW, 80): "",
|
|
lpColumn->mask & LVCF_TEXT ? lpColumn->cchTextMax: 0, lpColumn->iSubItem);
|
|
return buf;
|
|
}
|
|
|
|
static void LISTVIEW_DumpListview(LISTVIEW_INFO *iP, INT line)
|
|
{
|
|
DWORD dwStyle = GetWindowLongW (iP->hwndSelf, GWL_STYLE);
|
|
TRACE("listview %08x at line %d, clrBk=0x%06lx, clrText=0x%06lx, clrTextBk=0x%06lx, ItemHeight=%d, ItemWidth=%d, Style=0x%08lx\n",
|
|
iP->hwndSelf, line, iP->clrBk, iP->clrText, iP->clrTextBk,
|
|
iP->nItemHeight, iP->nItemWidth, dwStyle);
|
|
TRACE("listview %08x at line %d, himlNor=%p, himlSml=%p, himlState=%p, Focused=%d, Hot=%d, exStyle=0x%08lx\n",
|
|
iP->hwndSelf, line, iP->himlNormal, iP->himlSmall, iP->himlState,
|
|
iP->nFocusedItem, iP->nHotItem, iP->dwExStyle);
|
|
}
|
|
|
|
static BOOL
|
|
LISTVIEW_SendCustomDrawNotify (HWND hwnd, DWORD dwDrawStage, HDC hdc,
|
|
RECT rc)
|
|
{
|
|
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
|
|
NMLVCUSTOMDRAW nmcdhdr;
|
|
LPNMCUSTOMDRAW nmcd;
|
|
|
|
TRACE("(hwnd=%x, dwDrawStage=%lx, hdc=%x, rc=?)\n", hwnd, dwDrawStage, hdc);
|
|
|
|
nmcd= & nmcdhdr.nmcd;
|
|
nmcd->hdr.hwndFrom = hwnd;
|
|
nmcd->hdr.idFrom = GetWindowLongW( 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)SendMessageW (GetParent (hwnd), WM_NOTIFY,
|
|
(WPARAM) GetWindowLongW( 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;
|
|
LVITEMW item;
|
|
|
|
infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
|
|
|
|
ZeroMemory(&item,sizeof(item));
|
|
item.iItem = iItem;
|
|
item.mask = LVIF_PARAM;
|
|
ListView_GetItemW(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 = GetWindowLongW( 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=SendMessageW (GetParent (hwnd), WM_NOTIFY,
|
|
(WPARAM) GetWindowLongW( 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;
|
|
LVITEMW item;
|
|
WCHAR buffer[MAX_PATH];
|
|
DWORD timestamp,elapsed;
|
|
|
|
/* simple parameter checking */
|
|
if (!hwnd || !charCode || !keyData)
|
|
return 0;
|
|
|
|
infoPtr=(LISTVIEW_INFO*)GetWindowLongW(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 < COUNTOF(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 = COUNTOF(buffer);
|
|
ListView_GetItemW( hwnd, &item );
|
|
|
|
/* check for a match */
|
|
if (lstrncmpiW(item.pszText,infoPtr->szSearchParam,infoPtr->nSearchParamLength) == 0) {
|
|
nItem=idx;
|
|
break;
|
|
} else if ( (charCode != 0) && (nItem == -1) && (nItem != infoPtr->nFocusedItem) &&
|
|
(lstrncmpiW(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 *)GetWindowLongW(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 *)GetWindowLongW(hwnd, 0);
|
|
LONG lStyle = GetWindowLongW(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;
|
|
|
|
if (lStyle & LVS_NOSCROLL) return;
|
|
|
|
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--;
|
|
}
|
|
else
|
|
{
|
|
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_TYPESTYLEMASK & lStyle) == LVS_NOSCROLL)
|
|
FIXME(" LVS_NOSCROLL\n");
|
|
|
|
if (lStyle & LVS_EDITLABELS)
|
|
FIXME(" LVS_EDITLABELS\n");
|
|
|
|
if (lStyle & LVS_NOLABELWRAP)
|
|
FIXME(" LVS_NOLABELWRAP\n");
|
|
|
|
if (lStyle & LVS_SHAREIMAGELISTS)
|
|
FIXME(" LVS_SHAREIMAGELISTS\n");
|
|
|
|
if (lStyle & LVS_SORTASCENDING)
|
|
FIXME(" LVS_SORTASCENDING\n");
|
|
|
|
if (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 *)GetWindowLongW(hwnd, 0);
|
|
UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
|
|
INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
|
|
POINT ptItem;
|
|
RECT rcView;
|
|
INT i, off_x=0, off_y=0;
|
|
|
|
if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
|
|
{
|
|
/* Since SetItemPosition uses upper-left of icon, and for
|
|
style=LVS_ICON the icon is not left adjusted, get the offset */
|
|
if (uView == LVS_ICON)
|
|
{
|
|
off_y = ICON_TOP_PADDING;
|
|
off_x = (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
|
|
}
|
|
ptItem.x = off_x;
|
|
ptItem.y = off_y;
|
|
ZeroMemory(&rcView, sizeof(RECT));
|
|
|
|
if (nListWidth > infoPtr->nItemWidth)
|
|
{
|
|
for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
|
|
{
|
|
if (ptItem.x + infoPtr->nItemWidth > nListWidth)
|
|
{
|
|
ptItem.x = off_x;
|
|
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 *)GetWindowLongW(hwnd, 0);
|
|
UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
|
|
INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
|
|
POINT ptItem;
|
|
RECT rcView;
|
|
INT i, off_x=0, off_y=0;
|
|
|
|
if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
|
|
{
|
|
/* Since SetItemPosition uses upper-left of icon, and for
|
|
style=LVS_ICON the icon is not left adjusted, get the offset */
|
|
if (uView == LVS_ICON)
|
|
{
|
|
off_y = ICON_TOP_PADDING;
|
|
off_x = (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
|
|
}
|
|
ptItem.x = off_x;
|
|
ptItem.y = off_y;
|
|
ZeroMemory(&rcView, sizeof(RECT));
|
|
|
|
if (nListHeight > infoPtr->nItemHeight)
|
|
{
|
|
for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
|
|
{
|
|
if (ptItem.y + infoPtr->nItemHeight > nListHeight)
|
|
{
|
|
ptItem.y = off_y;
|
|
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*)GetWindowLongW(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*)GetWindowLongW(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 *)GetWindowLongW(hwnd, 0);
|
|
LONG style = GetWindowLongW(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 *)GetWindowLongW(hwnd, 0);
|
|
UINT uView = GetWindowLongW(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:
|
|
* Retrieves and saves important text metrics info for the current
|
|
* Listview font.
|
|
*
|
|
* PARAMETER(S):
|
|
* [I] HWND : window handle
|
|
*
|
|
*/
|
|
static VOID LISTVIEW_SaveTextMetrics(HWND hwnd)
|
|
{
|
|
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
|
|
TEXTMETRICW tm;
|
|
HDC hdc = GetDC(hwnd);
|
|
HFONT hOldFont = SelectObject(hdc, infoPtr->hFont);
|
|
INT oldHeight, oldACW;
|
|
|
|
GetTextMetricsW(hdc, &tm);
|
|
|
|
oldHeight = infoPtr->ntmHeight;
|
|
oldACW = infoPtr->ntmAveCharWidth;
|
|
infoPtr->ntmHeight = tm.tmHeight;
|
|
infoPtr->ntmAveCharWidth = tm.tmAveCharWidth;
|
|
|
|
SelectObject(hdc, hOldFont);
|
|
ReleaseDC(hwnd, hdc);
|
|
TRACE("tmHeight old=%d,new=%d; tmAveCharWidth old=%d,new=%d\n",
|
|
oldHeight, infoPtr->ntmHeight, oldACW, infoPtr->ntmAveCharWidth);
|
|
}
|
|
|
|
|
|
/***
|
|
* DESCRIPTION:
|
|
* Calculates the height of an item.
|
|
*
|
|
* PARAMETER(S):
|
|
* [I] HWND : window handle
|
|
*
|
|
* RETURN:
|
|
* Returns item height.
|
|
*/
|
|
static INT LISTVIEW_GetItemHeight(HWND hwnd)
|
|
{
|
|
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
|
|
UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
|
|
INT nItemHeight = 0;
|
|
|
|
if (uView == LVS_ICON)
|
|
{
|
|
nItemHeight = infoPtr->iconSpacing.cy;
|
|
}
|
|
else
|
|
{
|
|
if(infoPtr->himlState || infoPtr->himlSmall)
|
|
nItemHeight = max(infoPtr->ntmHeight, infoPtr->iconSize.cy) + HEIGHT_PADDING;
|
|
else
|
|
nItemHeight = infoPtr->ntmHeight;
|
|
}
|
|
|
|
return nItemHeight;
|
|
}
|
|
|
|
|
|
static void LISTVIEW_PrintSelectionRanges(HWND hwnd)
|
|
{
|
|
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(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 *)GetWindowLongW(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 *)GetWindowLongW(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 *)GetWindowLongW(hwnd, 0);
|
|
LISTVIEW_SELECTION *selection;
|
|
INT i;
|
|
LVITEMW item;
|
|
|
|
TRACE("(0x%x)\n",hwnd);
|
|
|
|
ZeroMemory(&item,sizeof(item));
|
|
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 *)GetWindowLongW(hwnd, 0);
|
|
LISTVIEW_SELECTION removeselection,*checkselection;
|
|
INT index;
|
|
|
|
removeselection.lower = lItem;
|
|
removeselection.upper = uItem;
|
|
|
|
TRACE("Remove range %lu - %lu\n",removeselection.lower,removeselection.upper);
|
|
LISTVIEW_PrintSelectionRanges(hwnd);
|
|
|
|
index = DPA_Search(infoPtr->hdpaSelectionRanges, &removeselection, 0,
|
|
LISTVIEW_CompareSelectionRanges,
|
|
0,0);
|
|
|
|
if (index == -1)
|
|
return;
|
|
|
|
|
|
checkselection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,
|
|
index);
|
|
|
|
TRACE("Matches range index %i (%lu-%lu)\n",index,checkselection->lower,
|
|
checkselection->upper);
|
|
|
|
/* case 1: Same */
|
|
if ((checkselection->upper == removeselection.upper) &&
|
|
(checkselection->lower == removeselection.lower))
|
|
{
|
|
DPA_DeletePtr(infoPtr->hdpaSelectionRanges,index);
|
|
TRACE("Case 1\n");
|
|
}
|
|
/* case 2: engulf */
|
|
else if (((checkselection->upper < removeselection.upper) &&
|
|
(checkselection->lower > removeselection.lower))||
|
|
((checkselection->upper <= removeselection.upper) &&
|
|
(checkselection->lower > removeselection.lower)) ||
|
|
((checkselection->upper < removeselection.upper) &&
|
|
(checkselection->lower >= removeselection.lower)))
|
|
|
|
{
|
|
DPA_DeletePtr(infoPtr->hdpaSelectionRanges,index);
|
|
/* do it again because others may also get caught */
|
|
TRACE("Case 2\n");
|
|
LISTVIEW_RemoveSelectionRange(hwnd,lItem,uItem);
|
|
}
|
|
/* case 3: overlap upper */
|
|
else if ((checkselection->upper < removeselection.upper) &&
|
|
(checkselection->lower < removeselection.lower))
|
|
{
|
|
checkselection->upper = removeselection.lower - 1;
|
|
TRACE("Case 3\n");
|
|
LISTVIEW_RemoveSelectionRange(hwnd,lItem,uItem);
|
|
}
|
|
/* case 4: overlap lower */
|
|
else if ((checkselection->upper > removeselection.upper) &&
|
|
(checkselection->lower > removeselection.lower))
|
|
{
|
|
checkselection->lower = removeselection.upper + 1;
|
|
TRACE("Case 4\n");
|
|
LISTVIEW_RemoveSelectionRange(hwnd,lItem,uItem);
|
|
}
|
|
/* case 5: fully internal */
|
|
else if (checkselection->upper == removeselection.upper)
|
|
checkselection->upper = removeselection.lower - 1;
|
|
else if (checkselection->lower == removeselection.lower)
|
|
checkselection->lower = removeselection.upper + 1;
|
|
else
|
|
{
|
|
/* bisect the range */
|
|
LISTVIEW_SELECTION *newselection;
|
|
|
|
newselection = (LISTVIEW_SELECTION *)
|
|
COMCTL32_Alloc(sizeof(LISTVIEW_SELECTION));
|
|
newselection -> lower = checkselection->lower;
|
|
newselection -> upper = removeselection.lower - 1;
|
|
checkselection -> lower = removeselection.upper + 1;
|
|
DPA_InsertPtr(infoPtr->hdpaSelectionRanges,index,newselection);
|
|
TRACE("Case 5\n");
|
|
DPA_Sort(infoPtr->hdpaSelectionRanges,LISTVIEW_CompareSelectionRanges,0);
|
|
}
|
|
LISTVIEW_PrintSelectionRanges(hwnd);
|
|
}
|
|
|
|
/**
|
|
* DESCRIPTION:
|
|
* Updates the various indices after an item has been inserted or deleted.
|
|
*
|
|
* PARAMETER(S):
|
|
* [I] HWND : window handle
|
|
* [I] INT : item index
|
|
* [I] INT : Direction of shift, +1 or -1.
|
|
*
|
|
* RETURN:
|
|
* None
|
|
*/
|
|
static VOID LISTVIEW_ShiftIndices(HWND hwnd, INT nItem, INT direction)
|
|
{
|
|
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
|
|
LISTVIEW_SELECTION selection,*checkselection;
|
|
INT index;
|
|
|
|
TRACE("Shifting %iu, %i steps\n",nItem,direction);
|
|
|
|
selection.upper = nItem;
|
|
selection.lower = nItem;
|
|
|
|
index = DPA_Search(infoPtr->hdpaSelectionRanges, &selection, 0,
|
|
LISTVIEW_CompareSelectionRanges,
|
|
0,DPAS_SORTED|DPAS_INSERTAFTER);
|
|
|
|
while ((index < infoPtr->hdpaSelectionRanges->nItemCount)&&(index != -1))
|
|
{
|
|
checkselection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,index);
|
|
if ((checkselection->lower >= nItem)&&
|
|
(checkselection->lower + direction >= 0))
|
|
checkselection->lower += direction;
|
|
if ((checkselection->upper >= nItem)&&
|
|
(checkselection->upper + direction >=0))
|
|
checkselection->upper += direction;
|
|
index ++;
|
|
}
|
|
|
|
/* Note that the following will fail if direction != +1 and -1 */
|
|
if (infoPtr->nSelectionMark > nItem)
|
|
infoPtr->nSelectionMark += direction;
|
|
else if (infoPtr->nSelectionMark == nItem)
|
|
{
|
|
if (direction > 0)
|
|
infoPtr->nSelectionMark += direction;
|
|
else if (infoPtr->nSelectionMark >= GETITEMCOUNT(infoPtr))
|
|
infoPtr->nSelectionMark = GETITEMCOUNT(infoPtr) - 1;
|
|
}
|
|
|
|
if (infoPtr->nFocusedItem > nItem)
|
|
infoPtr->nFocusedItem += direction;
|
|
else if (infoPtr->nFocusedItem == nItem)
|
|
{
|
|
if (direction > 0)
|
|
infoPtr->nFocusedItem += direction;
|
|
else
|
|
{
|
|
if (infoPtr->nFocusedItem >= GETITEMCOUNT(infoPtr))
|
|
infoPtr->nFocusedItem = GETITEMCOUNT(infoPtr) - 1;
|
|
if (infoPtr->nFocusedItem >= 0)
|
|
LISTVIEW_SetItemFocus(hwnd, infoPtr->nFocusedItem);
|
|
}
|
|
}
|
|
/* But we are not supposed to modify nHotItem! */
|
|
}
|
|
|
|
|
|
/**
|
|
* DESCRIPTION:
|
|
* Adds a block of selections.
|
|
*
|
|
* PARAMETER(S):
|
|
* [I] HWND : window handle
|
|
* [I] INT : item index
|
|
*
|
|
* RETURN:
|
|
* None
|
|
*/
|
|
static VOID LISTVIEW_AddGroupSelection(HWND hwnd, INT nItem)
|
|
{
|
|
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
|
|
INT nFirst = min(infoPtr->nSelectionMark, nItem);
|
|
INT nLast = max(infoPtr->nSelectionMark, nItem);
|
|
INT i;
|
|
LVITEMW item;
|
|
|
|
if (nFirst == -1)
|
|
nFirst = nItem;
|
|
|
|
ZeroMemory(&item,sizeof(item));
|
|
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 *)GetWindowLongW(hwnd, 0);
|
|
LVITEMW item;
|
|
|
|
ZeroMemory(&item,sizeof(item));
|
|
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 *)GetWindowLongW(hwnd, 0);
|
|
BOOL bResult;
|
|
LVITEMW item;
|
|
|
|
ZeroMemory(&item,sizeof(item));
|
|
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 *)GetWindowLongW(hwnd, 0);
|
|
POINT ptItem;
|
|
INT i;
|
|
LVITEMW item;
|
|
|
|
ZeroMemory(&item,sizeof(item));
|
|
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;
|
|
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 *)GetWindowLongW(hwnd, 0);
|
|
UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
|
|
LVITEMW item;
|
|
|
|
ZeroMemory(&item,sizeof(item));
|
|
item.stateMask = LVIS_SELECTED;
|
|
|
|
if ((uView == LVS_LIST) || (uView == LVS_REPORT))
|
|
{
|
|
INT i;
|
|
INT nFirst, nLast;
|
|
|
|
if (infoPtr->nSelectionMark == -1)
|
|
{
|
|
infoPtr->nSelectionMark = nFirst = nLast = nItem;
|
|
}
|
|
else
|
|
{
|
|
nFirst = min(infoPtr->nSelectionMark, nItem);
|
|
nLast = max(infoPtr->nSelectionMark, nItem);
|
|
}
|
|
|
|
for (i = 0; i <= GETITEMCOUNT(infoPtr); i++)
|
|
{
|
|
if ((i < nFirst) || (i > nLast))
|
|
item.state = 0;
|
|
else
|
|
item.state = LVIS_SELECTED;
|
|
LISTVIEW_SetItemState(hwnd,i,&item);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
RECT rcItem;
|
|
RECT rcSelMark;
|
|
RECT rcSel;
|
|
LISTVIEW_GetItemBoundBox(hwnd, nItem, &rcItem);
|
|
LISTVIEW_GetItemBoundBox(hwnd, infoPtr->nSelectionMark, &rcSelMark);
|
|
rcSel.left = min(rcSelMark.left, rcItem.left);
|
|
rcSel.top = min(rcSelMark.top, rcItem.top);
|
|
rcSel.right = max(rcSelMark.right, rcItem.right);
|
|
rcSel.bottom = max(rcSelMark.bottom, rcItem.bottom);
|
|
LISTVIEW_SetSelectionRect(hwnd, rcSel);
|
|
TRACE("item %d (%d,%d)-(%d,%d), mark %d (%d,%d)-(%d,%d), sel (%d,%d)-(%d,%d)\n",
|
|
nItem, rcItem.left, rcItem.top, rcItem.right, rcItem.bottom,
|
|
infoPtr->nSelectionMark,
|
|
rcSelMark.left, rcSelMark.top, rcSelMark.right, rcSelMark.bottom,
|
|
rcSel.left, rcSel.top, rcSel.right, rcSel.bottom);
|
|
|
|
}
|
|
|
|
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 *)GetWindowLongW(hwnd, 0);
|
|
BOOL bResult = FALSE;
|
|
LVITEMW lvItem;
|
|
|
|
if (infoPtr->nFocusedItem != nItem)
|
|
{
|
|
if (infoPtr->nFocusedItem >= 0)
|
|
{
|
|
INT oldFocus = infoPtr->nFocusedItem;
|
|
bResult = TRUE;
|
|
infoPtr->nFocusedItem = -1;
|
|
ZeroMemory(&lvItem, sizeof(lvItem));
|
|
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 *)GetWindowLongW(hwnd, 0);
|
|
LVITEMW lvItem;
|
|
|
|
ZeroMemory(&lvItem, sizeof(lvItem));
|
|
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 *)GetWindowLongW(hwnd, 0);
|
|
LONG lStyle = GetWindowLongW(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 *)GetWindowLongW(hwnd, 0);
|
|
POINT pt;
|
|
|
|
pt.x = (INT)LOWORD(lParam);
|
|
pt.y = (INT)HIWORD(lParam);
|
|
|
|
if(infoPtr->dwExStyle & LVS_EX_TRACKSELECT) {
|
|
/* select the item under the cursor */
|
|
LISTVIEW_MouseSelection(hwnd, pt);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/***
|
|
* DESCRIPTION:
|
|
* Called whenever WM_MOUSEMOVE is received.
|
|
*
|
|
* PARAMETER(S):
|
|
* [I] HWND : window handle
|
|
* [I] wParam : key indicators
|
|
* [I] lParam : cursor position
|
|
*
|
|
* RETURN:
|
|
* 0 if the message is processed, non-zero if there was an error
|
|
*/
|
|
static LRESULT LISTVIEW_MouseMove(HWND hwnd, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
|
|
TRACKMOUSEEVENT trackinfo;
|
|
|
|
/* see if we are supposed to be tracking mouse hovering */
|
|
if(infoPtr->dwExStyle & LVS_EX_TRACKSELECT) {
|
|
/* fill in the trackinfo struct */
|
|
trackinfo.cbSize = sizeof(TRACKMOUSEEVENT);
|
|
trackinfo.dwFlags = TME_QUERY;
|
|
trackinfo.hwndTrack = hwnd;
|
|
trackinfo.dwHoverTime = infoPtr->dwHoverTime;
|
|
|
|
/* see if we are already tracking this hwnd */
|
|
_TrackMouseEvent(&trackinfo);
|
|
|
|
if(!(trackinfo.dwFlags & TME_HOVER)) {
|
|
trackinfo.dwFlags = TME_HOVER;
|
|
|
|
/* call TRACKMOUSEEVENT so we receive WM_MOUSEHOVER messages */
|
|
_TrackMouseEvent(&trackinfo);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/***
|
|
* DESCRIPTION:
|
|
* Selects an item based on coordinates.
|
|
*
|
|
* PARAMETER(S):
|
|
* [I] HWND : window handle
|
|
* [I] POINT : mouse click ccordinates
|
|
*
|
|
* RETURN:
|
|
* SUCCESS : item index
|
|
* FAILURE : -1
|
|
*/
|
|
static LRESULT LISTVIEW_MouseSelection(HWND hwnd, POINT pt)
|
|
{
|
|
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
|
|
RECT rcItem;
|
|
INT i,topindex,bottomindex;
|
|
LONG lStyle = GetWindowLongW(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 (is_textW(lpSubItem->pszText))
|
|
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
|
|
* [I] isW : TRUE if lpLVItem is Unicode, FALSE it it's ANSI
|
|
*
|
|
* RETURN:
|
|
* SUCCCESS : TRUE (EQUAL)
|
|
* FAILURE : FALSE (NOT EQUAL)
|
|
*/
|
|
static UINT LISTVIEW_GetItemChangesT(LISTVIEW_ITEM *lpItem, LPLVITEMW lpLVItem, BOOL isW)
|
|
{
|
|
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_TEXTCALLBACKW)
|
|
{
|
|
if (lpItem->pszText != LPSTR_TEXTCALLBACKW)
|
|
uChanged |= LVIF_TEXT;
|
|
}
|
|
else
|
|
{
|
|
if (lpItem->pszText == LPSTR_TEXTCALLBACKW)
|
|
{
|
|
uChanged |= LVIF_TEXT;
|
|
}
|
|
else
|
|
{
|
|
if (lpLVItem->pszText)
|
|
{
|
|
if (lpItem->pszText)
|
|
{
|
|
LPWSTR pszText = textdupTtoW(lpLVItem->pszText, isW);
|
|
if (pszText && strcmpW(pszText, lpItem->pszText))
|
|
uChanged |= LVIF_TEXT;
|
|
textfreeT(pszText, isW);
|
|
}
|
|
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
|
|
* [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
|
|
*
|
|
* RETURN:
|
|
* SUCCCESS : TRUE
|
|
* FAILURE : FALSE
|
|
*/
|
|
static BOOL LISTVIEW_InitItemT(HWND hwnd, LISTVIEW_ITEM *lpItem,
|
|
LPLVITEMW lpLVItem, BOOL isW)
|
|
{
|
|
LONG lStyle = GetWindowLongW(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_TEXTCALLBACKW)
|
|
{
|
|
if ((lStyle & LVS_SORTASCENDING) || (lStyle & LVS_SORTDESCENDING))
|
|
return FALSE;
|
|
|
|
if (is_textW(lpItem->pszText))
|
|
COMCTL32_Free(lpItem->pszText);
|
|
|
|
lpItem->pszText = LPSTR_TEXTCALLBACKW;
|
|
}
|
|
else
|
|
bResult = textsetptrT(&lpItem->pszText, lpLVItem->pszText, isW);
|
|
}
|
|
}
|
|
|
|
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
|
|
* [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
|
|
*
|
|
* RETURN:
|
|
* SUCCCESS : TRUE
|
|
* FAILURE : FALSE
|
|
*/
|
|
static BOOL LISTVIEW_InitSubItemT(HWND hwnd, LISTVIEW_SUBITEM *lpSubItem,
|
|
LPLVITEMW lpLVItem, BOOL isW)
|
|
{
|
|
LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
|
|
BOOL bResult = FALSE;
|
|
|
|
TRACE("(hwnd=%x, lpSubItem=%p, lpLVItem=%s, isW=%d)\n",
|
|
hwnd, lpSubItem, debuglvitem_t(lpLVItem, isW), isW);
|
|
|
|
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_TEXTCALLBACKW)
|
|
{
|
|
if ((lStyle & LVS_SORTASCENDING) || (lStyle & LVS_SORTDESCENDING))
|
|
return FALSE;
|
|
|
|
if (is_textW(lpSubItem->pszText))
|
|
COMCTL32_Free(lpSubItem->pszText);
|
|
|
|
lpSubItem->pszText = LPSTR_TEXTCALLBACKW;
|
|
}
|
|
else
|
|
bResult = textsetptrT(&lpSubItem->pszText, lpLVItem->pszText, isW);
|
|
}
|
|
}
|
|
}
|
|
|
|
return bResult;
|
|
}
|
|
|
|
/***
|
|
* DESCRIPTION:
|
|
* Adds a subitem at a given position (column index).
|
|
*
|
|
* PARAMETER(S):
|
|
* [I] HWND : window handle
|
|
* [I] LPLVITEM : new subitem atttributes
|
|
* [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
|
|
*
|
|
* RETURN:
|
|
* SUCCESS : TRUE
|
|
* FAILURE : FALSE
|
|
*/
|
|
static BOOL LISTVIEW_AddSubItemT(HWND hwnd, LPLVITEMW lpLVItem, BOOL isW)
|
|
{
|
|
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
|
|
LISTVIEW_SUBITEM *lpSubItem = NULL;
|
|
BOOL bResult = FALSE;
|
|
HDPA hdpaSubItems;
|
|
INT nPosition, nItem;
|
|
LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
|
|
|
|
TRACE("(hwnd=%x, lpLVItem=%s, isW=%d)\n", hwnd, debuglvitem_t(lpLVItem, isW), isW);
|
|
|
|
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_InitSubItemT(hwnd, lpSubItem, lpLVItem, isW))
|
|
{
|
|
nPosition = LISTVIEW_FindInsertPosition(hdpaSubItems,
|
|
lpSubItem->iSubItem);
|
|
nItem = DPA_InsertPtr(hdpaSubItems, nPosition, lpSubItem);
|
|
if (nItem != -1) bResult = TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* cleanup if unsuccessful */
|
|
if (!bResult && lpSubItem) 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 && 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
|
|
* [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
|
|
*
|
|
* RETURN:
|
|
* SUCCESS : TRUE
|
|
* FAILURE : FALSE
|
|
*/
|
|
static BOOL LISTVIEW_SetMainItemT(HWND hwnd, LPLVITEMW lpLVItem, BOOL isW)
|
|
{
|
|
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
|
|
BOOL bResult = FALSE;
|
|
HDPA hdpaSubItems;
|
|
LISTVIEW_ITEM *lpItem;
|
|
NMLISTVIEW nmlv;
|
|
LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
|
|
UINT uChanged;
|
|
UINT uView = lStyle & LVS_TYPEMASK;
|
|
INT item_width;
|
|
RECT rcItem;
|
|
|
|
TRACE("(hwnd=%x, lpLVItem=%s, isW=%d)\n", hwnd, debuglvitem_t(lpLVItem, isW), isW);
|
|
|
|
if (lStyle & LVS_OWNERDATA)
|
|
{
|
|
if ((lpLVItem->iSubItem == 0)&&(lpLVItem->mask == LVIF_STATE))
|
|
{
|
|
LVITEMW itm;
|
|
|
|
ZeroMemory(&itm, sizeof(itm));
|
|
itm.mask = LVIF_STATE | LVIF_PARAM;
|
|
itm.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
|
|
itm.iItem = lpLVItem->iItem;
|
|
itm.iSubItem = 0;
|
|
ListView_GetItemW(hwnd, &itm);
|
|
|
|
|
|
ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
|
|
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))
|
|
{
|
|
/*
|
|
* As per MSDN LVN_ITEMCHANGING notifications are _NOT_ sent
|
|
* by LVS_OWERNDATA list controls
|
|
*/
|
|
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);
|
|
}
|
|
|
|
listview_notify(hwnd, LVN_ITEMCHANGED, &nmlv);
|
|
|
|
rcItem.left = LVIR_BOUNDS;
|
|
LISTVIEW_GetItemRect(hwnd, lpLVItem->iItem, &rcItem);
|
|
if (!infoPtr->bIsDrawing)
|
|
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.lParam = lpItem->lParam;
|
|
uChanged = LISTVIEW_GetItemChangesT(lpItem, lpLVItem, isW);
|
|
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_notify(hwnd, LVN_ITEMCHANGING, &nmlv);
|
|
|
|
/* copy information */
|
|
bResult = LISTVIEW_InitItemT(hwnd, lpItem, lpLVItem, isW);
|
|
|
|
/* 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_GetStringWidthT(hwnd, lpItem->pszText, TRUE);
|
|
|
|
if(item_width > infoPtr->nItemWidth)
|
|
infoPtr->nItemWidth = item_width;
|
|
}
|
|
|
|
/* send LVN_ITEMCHANGED notification */
|
|
listview_notify(hwnd, LVN_ITEMCHANGED, &nmlv);
|
|
}
|
|
else
|
|
{
|
|
bResult = TRUE;
|
|
}
|
|
|
|
if (uChanged)
|
|
{
|
|
rcItem.left = LVIR_BOUNDS;
|
|
LISTVIEW_GetItemRect(hwnd, lpLVItem->iItem, &rcItem);
|
|
if (!infoPtr->bIsDrawing)
|
|
InvalidateRect(hwnd, &rcItem, TRUE);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return bResult;
|
|
}
|
|
|
|
/***
|
|
* DESCRIPTION:
|
|
* Sets subitem attributes.
|
|
*
|
|
* PARAMETER(S):
|
|
* [I] HWND : window handle
|
|
* [I] LPLVITEM : new subitem atttributes
|
|
* [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
|
|
*
|
|
* RETURN:
|
|
* SUCCESS : TRUE
|
|
* FAILURE : FALSE
|
|
*/
|
|
static BOOL LISTVIEW_SetSubItemT(HWND hwnd, LPLVITEMW lpLVItem, BOOL isW)
|
|
{
|
|
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
|
|
LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
|
|
BOOL bResult = FALSE;
|
|
HDPA hdpaSubItems;
|
|
LISTVIEW_SUBITEM *lpSubItem;
|
|
RECT rcItem;
|
|
|
|
TRACE("(hwnd=%x, lpLVItem=%s, isW=%d)\n", hwnd, debuglvitem_t(lpLVItem, isW), isW);
|
|
|
|
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_InitSubItemT(hwnd, lpSubItem, lpLVItem, isW);
|
|
else
|
|
bResult = LISTVIEW_AddSubItemT(hwnd, lpLVItem, isW);
|
|
|
|
rcItem.left = LVIR_BOUNDS;
|
|
LISTVIEW_GetItemRect(hwnd, lpLVItem->iItem, &rcItem);
|
|
InvalidateRect(hwnd, &rcItem, FALSE);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return bResult;
|
|
}
|
|
|
|
/***
|
|
* DESCRIPTION:
|
|
* Sets item attributes.
|
|
*
|
|
* PARAMETER(S):
|
|
* [I] HWND : window handle
|
|
* [I] LPLVITEM : new item atttributes
|
|
* [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
|
|
*
|
|
* RETURN:
|
|
* SUCCESS : TRUE
|
|
* FAILURE : FALSE
|
|
*/
|
|
static BOOL LISTVIEW_SetItemT(HWND hwnd, LPLVITEMW lpLVItem, BOOL isW)
|
|
{
|
|
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
|
|
|
|
if (!lpLVItem || lpLVItem->iItem < 0 ||
|
|
lpLVItem->iItem>=GETITEMCOUNT(infoPtr))
|
|
return FALSE;
|
|
if (lpLVItem->iSubItem == 0)
|
|
return LISTVIEW_SetMainItemT(hwnd, lpLVItem, isW);
|
|
else
|
|
return LISTVIEW_SetSubItemT(hwnd, lpLVItem, isW);
|
|
}
|
|
|
|
/***
|
|
* 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 = GetWindowLongW(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) && GetScrollInfo(hwnd, SB_HORZ, &scrollInfo))
|
|
nItem = scrollInfo.nPos * LISTVIEW_GetCountPerColumn(hwnd);
|
|
}
|
|
else if (uView == LVS_REPORT)
|
|
{
|
|
if ((lStyle & WS_VSCROLL) && GetScrollInfo(hwnd, SB_VERT, &scrollInfo))
|
|
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 *)GetWindowLongW(hwnd, 0);
|
|
WCHAR szDispText[DISP_TEXT_SIZE];
|
|
LVITEMW lvItem;
|
|
LVCOLUMNW lvColumn;
|
|
UINT textoutOptions = ETO_CLIPPED | ETO_OPAQUE;
|
|
RECT rcTemp;
|
|
INT textLeft;
|
|
INT nLabelWidth = 0;
|
|
|
|
TRACE("(hwnd=%x, hdc=%x, nItem=%d, nSubItem=%d)\n", hwnd, hdc,
|
|
nItem, nSubItem);
|
|
|
|
/* get information needed for drawing the item */
|
|
ZeroMemory(&lvItem, sizeof(lvItem));
|
|
lvItem.mask = LVIF_TEXT;
|
|
lvItem.iItem = nItem;
|
|
lvItem.iSubItem = nSubItem;
|
|
lvItem.cchTextMax = DISP_TEXT_SIZE;
|
|
lvItem.pszText = szDispText;
|
|
*lvItem.pszText = '\0';
|
|
LISTVIEW_GetItemW(hwnd, &lvItem, TRUE);
|
|
TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
|
|
|
|
ZeroMemory(&lvColumn, sizeof(lvColumn));
|
|
lvColumn.mask = LVCF_FMT;
|
|
LISTVIEW_GetColumnT(hwnd, nSubItem, &lvColumn, TRUE);
|
|
textLeft = rcItem.left;
|
|
if (lvColumn.fmt != LVCFMT_LEFT)
|
|
{
|
|
if ((nLabelWidth = LISTVIEW_GetStringWidthT(hwnd, lvItem.pszText, TRUE)))
|
|
{
|
|
if (lvColumn.fmt == LVCFMT_RIGHT)
|
|
textLeft = rcItem.right - nLabelWidth;
|
|
else
|
|
textLeft = rcItem.left + (rcItem.right-rcItem.left-nLabelWidth)/2;
|
|
}
|
|
}
|
|
|
|
|
|
/* 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);
|
|
}
|
|
|
|
ExtTextOutW(hdc, textLeft, rcItem.top, textoutOptions,
|
|
&rcItem, lvItem.pszText, lstrlenW(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;
|
|
ExtTextOutW(hdc, rec.left , rec.top, textoutOptions,
|
|
&rec, NULL, 0, NULL);
|
|
}
|
|
CopyRect(&rec,&rcItem);
|
|
rec.right = rec.left;
|
|
rec.left = rec.left - REPORT_MARGINX;
|
|
ExtTextOutW(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 *)GetWindowLongW(hwnd, 0);
|
|
WCHAR szDispText[DISP_TEXT_SIZE];
|
|
INT nLabelWidth;
|
|
LVITEMW 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(lvItem));
|
|
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;
|
|
*lvItem.pszText = '\0';
|
|
LISTVIEW_GetItemW(hwnd, &lvItem, TRUE);
|
|
TRACE(" lvItem=%s\n", debuglvitem_t(&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 ((GetWindowLongW(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_GetStringWidthW(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)
|
|
ExtTextOutW(hdc, dwTextX, rcItem.top, textoutOptions,
|
|
&rcItem, lvItem.pszText, lstrlenW(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;
|
|
ExtTextOutW(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] INT : item index
|
|
* [I] RECT : clipping rectangle
|
|
* [O] RECT * : The text rectangle about which to draw the focus
|
|
*
|
|
* RETURN:
|
|
* None
|
|
*/
|
|
static VOID LISTVIEW_DrawLargeItem(HWND hwnd, HDC hdc, INT nItem, RECT rcItem,
|
|
RECT *SuggestedFocus)
|
|
{
|
|
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
|
|
WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
|
|
LVITEMW lvItem;
|
|
UINT uFormat = DT_TOP | DT_CENTER | DT_WORDBREAK | DT_NOPREFIX |
|
|
DT_EDITCONTROL ;
|
|
/* Maintain this format in line with the one in LISTVIEW_UpdateLargeItemLabelRect*/
|
|
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(lvItem));
|
|
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;
|
|
*lvItem.pszText = '\0';
|
|
LISTVIEW_GetItemW(hwnd, &lvItem, FALSE);
|
|
TRACE(" lvItem=%s\n", debuglvitem_t(&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;
|
|
/* The comment doesn't say WIDTH_PADDING applies to large icons */
|
|
|
|
TRACE("background rect (%d,%d)-(%d,%d)\n",
|
|
rcTemp.left, rcTemp.top, rcTemp.right, rcTemp.bottom);
|
|
|
|
LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
|
|
|
|
|
|
/* Figure out text colours etc. depending on state
|
|
* At least the following states exist; there may be more.
|
|
* Many items may be selected
|
|
* At most one item may have the focus
|
|
* The application may not actually be active currently
|
|
* 1. The item is not selected in any way
|
|
* 2. The cursor is flying over the icon or text and the text is being
|
|
* expanded because it is not fully displayed currently.
|
|
* 3. The item is selected and is focussed, i.e. the user has not clicked
|
|
* in the blank area of the window, and the window (or application?)
|
|
* still has the focus.
|
|
* 4. As 3 except that a different window has the focus
|
|
* 5. The item is the selected item of all the items, but the user has
|
|
* clicked somewhere else on the window.
|
|
* Only a few of these are handled currently. In particular 2 is not yet
|
|
* handled since we do not support the functionality currently (or at least
|
|
* we didn't when I wrote this)
|
|
*/
|
|
|
|
if (lvItem.state & LVIS_SELECTED)
|
|
{
|
|
/* set item colors */
|
|
SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
|
|
SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
|
|
SetBkMode (hdc, OPAQUE);
|
|
/* set raster mode */
|
|
SetROP2(hdc, R2_XORPEN);
|
|
/* When exactly is it in XOR? while being dragged? */
|
|
}
|
|
else
|
|
{
|
|
/* set item colors */
|
|
if ( (infoPtr->clrTextBk == CLR_DEFAULT) || (infoPtr->clrTextBk == CLR_NONE) )
|
|
{
|
|
SetBkMode(hdc, TRANSPARENT);
|
|
}
|
|
else
|
|
{
|
|
SetBkMode(hdc, OPAQUE);
|
|
SetBkColor(hdc, infoPtr->clrTextBk);
|
|
}
|
|
|
|
SetTextColor(hdc, infoPtr->clrText);
|
|
/* set raster mode */
|
|
SetROP2(hdc, R2_COPYPEN);
|
|
}
|
|
|
|
/* In cases 2,3 and 5 (see above) the full text is displayed, with word
|
|
* wrapping and long words split.
|
|
* In cases 1 and 4 only a portion of the text is displayed with word
|
|
* wrapping and both word and end ellipsis. (I don't yet know about path
|
|
* ellipsis)
|
|
*/
|
|
uFormat |= ( (lvItem.state & LVIS_FOCUSED) && infoPtr->bFocus) ?
|
|
DT_NOCLIP
|
|
:
|
|
DT_WORD_ELLIPSIS | DT_END_ELLIPSIS;
|
|
|
|
/* draw the icon */
|
|
if (infoPtr->himlNormal != NULL)
|
|
{
|
|
if (lvItem.iImage >= 0)
|
|
{
|
|
ImageList_Draw (infoPtr->himlNormal, lvItem.iImage, hdc, rcItem.left,
|
|
rcItem.top,
|
|
(lvItem.state & LVIS_SELECTED) ? ILD_SELECTED : ILD_NORMAL);
|
|
}
|
|
}
|
|
|
|
/* Draw the text below the icon */
|
|
|
|
/* Don't bother painting item being edited */
|
|
if ((infoPtr->hwndEdit && (lvItem.state & LVIS_FOCUSED)) ||
|
|
!lstrlenW(lvItem.pszText))
|
|
{
|
|
SetRectEmpty(SuggestedFocus);
|
|
return;
|
|
}
|
|
|
|
/* Since rcItem.left is left point of icon, compute left point of item box */
|
|
rcItem.left -= ((infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2);
|
|
rcItem.right = rcItem.left + infoPtr->nItemWidth;
|
|
rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
|
|
TRACE("bound box for text+icon (%d,%d)-(%d,%d), iS.cx=%ld, nItemWidth=%d\n",
|
|
rcItem.left, rcItem.top, rcItem.right, rcItem.bottom,
|
|
infoPtr->iconSize.cx, infoPtr->nItemWidth);
|
|
TRACE("rcList (%d,%d)-(%d,%d), rcView (%d,%d)-(%d,%d)\n",
|
|
infoPtr->rcList.left, infoPtr->rcList.top,
|
|
infoPtr->rcList.right, infoPtr->rcList.bottom,
|
|
infoPtr->rcView.left, infoPtr->rcView.top,
|
|
infoPtr->rcView.right, infoPtr->rcView.bottom);
|
|
|
|
InflateRect(&rcItem, -(2*CAPTION_BORDER), 0);
|
|
rcItem.top += infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
|
|
|
|
|
|
/* draw label */
|
|
|
|
/* I am sure of most of the uFormat values. However I am not sure about
|
|
* whether we need or do not need the following:
|
|
* DT_EXTERNALLEADING, DT_INTERNAL, DT_CALCRECT, DT_NOFULLWIDTHCHARBREAK,
|
|
* DT_PATH_ELLIPSIS, DT_RTLREADING,
|
|
* We certainly do not need
|
|
* DT_BOTTOM, DT_VCENTER, DT_MODIFYSTRING, DT_LEFT, DT_RIGHT, DT_PREFIXONLY,
|
|
* DT_SINGLELINE, DT_TABSTOP, DT_EXPANDTABS
|
|
*/
|
|
|
|
/* If the text is being drawn without clipping (i.e. the full text) then we
|
|
* need to jump through a few hoops to ensure that it all gets displayed and
|
|
* that the background is complete
|
|
*/
|
|
if (uFormat & DT_NOCLIP)
|
|
{
|
|
RECT rcBack=rcItem;
|
|
HBRUSH hBrush = CreateSolidBrush(GetBkColor (hdc));
|
|
int dx, dy, old_wid, new_wid;
|
|
DrawTextW (hdc, lvItem.pszText, -1, &rcItem, uFormat | DT_CALCRECT);
|
|
/* Microsoft, in their great wisdom, have decided that the rectangle
|
|
* returned by DrawText on DT_CALCRECT will only guarantee the dimension,
|
|
* not the location. So we have to do the centring ourselves (and take
|
|
* responsibility for agreeing off-by-one consistency with them).
|
|
*/
|
|
old_wid = rcItem.right-rcItem.left;
|
|
new_wid = rcBack.right - rcBack.left;
|
|
dx = rcBack.left - rcItem.left + (new_wid-old_wid)/2;
|
|
dy = rcBack.top - rcItem.top;
|
|
OffsetRect (&rcItem, dx, dy);
|
|
FillRect(hdc, &rcItem, hBrush);
|
|
DeleteObject(hBrush);
|
|
}
|
|
/* else ? What if we are losing the focus? will we not get a complete
|
|
* background?
|
|
*/
|
|
DrawTextW (hdc, lvItem.pszText, -1, &rcItem, uFormat);
|
|
|
|
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 *)GetWindowLongW(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 = GetWindowLongW(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 (GetWindowLongW(hwnd,GWL_STYLE) & LVS_OWNERDATA)
|
|
{
|
|
NMLVCACHEHINT nmlv;
|
|
|
|
nmlv.hdr.hwndFrom = hwnd;
|
|
nmlv.hdr.idFrom = GetWindowLongW(hwnd,GWL_ID);
|
|
nmlv.hdr.code = LVN_ODCACHEHINT;
|
|
nmlv.iFrom = nItem;
|
|
nmlv.iTo = nLast;
|
|
|
|
SendMessageW(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 = GetWindowLongW( hwnd, GWL_ID);
|
|
DRAWITEMSTRUCT dis;
|
|
LVITEMW 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(item));
|
|
item.iItem = nItem;
|
|
item.mask = LVIF_PARAM;
|
|
ListView_GetItemW(hwnd, &item);
|
|
|
|
dis.itemData = item.lParam;
|
|
|
|
if (SendMessageW(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 *)GetWindowLongW(hwnd,0);
|
|
UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
|
|
INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
|
|
INT nCountPerRow = 1;
|
|
|
|
if (nListWidth > 0)
|
|
{
|
|
if (uView != LVS_REPORT)
|
|
{
|
|
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 *)GetWindowLongW(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 *)GetWindowLongW(hwnd, 0);
|
|
LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
|
|
INT nColumnCount = 0;
|
|
|
|
if ((lStyle & LVS_TYPEMASK) == LVS_LIST)
|
|
{
|
|
nColumnCount = infoPtr->rcList.right / infoPtr->nItemWidth;
|
|
if (infoPtr->rcList.right % infoPtr->nItemWidth) nColumnCount++;
|
|
}
|
|
|
|
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 *)GetWindowLongW(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 *)GetWindowLongW(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)
|
|
LISTVIEW_DrawItem(hwnd, hdc, i, rcItem, FALSE, &SuggestedFocus);
|
|
else
|
|
LISTVIEW_DrawLargeItem(hwnd, hdc, i, rcItem, &SuggestedFocus);
|
|
/*
|
|
* Draw Focus Rect
|
|
*/
|
|
if (LISTVIEW_GetItemState(hwnd,i,LVIS_FOCUSED) &&
|
|
infoPtr->bFocus && !IsRectEmpty(&SuggestedFocus))
|
|
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 *)GetWindowLongW(hwnd, 0);
|
|
UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
|
|
HFONT hOldFont;
|
|
HPEN hPen, hOldPen;
|
|
DWORD cdmode;
|
|
RECT rect;
|
|
|
|
infoPtr->bIsDrawing = TRUE;
|
|
LISTVIEW_DumpListview (infoPtr, __LINE__);
|
|
|
|
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);
|
|
|
|
infoPtr->bIsDrawing = FALSE;
|
|
}
|
|
|
|
|
|
/***
|
|
* 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 *)GetWindowLongW(hwnd, 0);
|
|
UINT uView = GetWindowLongW(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)
|
|
FIXME("uView == LVS_REPORT: not implemented\n");
|
|
else if (uView == LVS_SMALLICON)
|
|
FIXME("uView == LVS_SMALLICON: not implemented\n");
|
|
else if (uView == LVS_ICON)
|
|
FIXME("uView == LVS_ICON: not implemented\n");
|
|
|
|
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 = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
|
|
BOOL bResult = FALSE;
|
|
|
|
if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
|
|
{
|
|
switch (nAlignCode)
|
|
{
|
|
case LVA_ALIGNLEFT:
|
|
FIXME("nAlignCode=LVA_ALIGNLEFT: not implemented\n");
|
|
break;
|
|
case LVA_ALIGNTOP:
|
|
FIXME("nAlignCode=LVA_ALIGNTOP: not implemented\n");
|
|
break;
|
|
case LVA_DEFAULT:
|
|
FIXME("nAlignCode=LVA_DEFAULT: not implemented\n");
|
|
break;
|
|
case LVA_SNAPTOGRID:
|
|
FIXME("nAlignCode=LVA_SNAPTOGRID: not implemented\n");
|
|
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 *)GetWindowLongW(hwnd, 0);
|
|
LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
|
|
UINT uView = lStyle & LVS_TYPEMASK;
|
|
LISTVIEW_ITEM *lpItem;
|
|
LISTVIEW_SUBITEM *lpSubItem;
|
|
NMLISTVIEW nmlv;
|
|
BOOL bSuppress;
|
|
BOOL bResult = FALSE;
|
|
HDPA hdpaSubItems;
|
|
|
|
TRACE("(hwnd=%x,)\n", hwnd);
|
|
|
|
LISTVIEW_RemoveAllSelections(hwnd);
|
|
infoPtr->nSelectionMark=-1;
|
|
infoPtr->nFocusedItem=-1;
|
|
/* But we are supposed to leave nHotItem as is! */
|
|
|
|
if (lStyle & LVS_OWNERDATA)
|
|
{
|
|
infoPtr->hdpaItems->nItemCount = 0;
|
|
InvalidateRect(hwnd, NULL, TRUE);
|
|
return TRUE;
|
|
}
|
|
|
|
if (GETITEMCOUNT(infoPtr) > 0)
|
|
{
|
|
INT i, j;
|
|
|
|
/* send LVN_DELETEALLITEMS notification */
|
|
/* verify if subsequent LVN_DELETEITEM notifications should be
|
|
suppressed */
|
|
ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
|
|
nmlv.iItem = -1;
|
|
bSuppress = listview_notify(hwnd, LVN_DELETEALLITEMS, &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 (is_textW(lpSubItem->pszText))
|
|
COMCTL32_Free(lpSubItem->pszText);
|
|
|
|
/* free subitem */
|
|
COMCTL32_Free(lpSubItem);
|
|
}
|
|
}
|
|
|
|
lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
|
|
if (lpItem != NULL)
|
|
{
|
|
if (!bSuppress)
|
|
{
|
|
/* send LVN_DELETEITEM notification */
|
|
nmlv.iItem = i;
|
|
nmlv.lParam = lpItem->lParam;
|
|
listview_notify(hwnd, LVN_DELETEITEM, &nmlv);
|
|
}
|
|
|
|
/* free item string */
|
|
if (is_textW(lpItem->pszText))
|
|
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 *)GetWindowLongW(hwnd, 0);
|
|
UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
|
|
UINT uOwnerData = GetWindowLongW(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 *)GetWindowLongW(hwnd, 0);
|
|
LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
|
|
UINT uView = lStyle & LVS_TYPEMASK;
|
|
LONG lCtrlId = GetWindowLongW(hwnd, GWL_ID);
|
|
NMLISTVIEW nmlv;
|
|
BOOL bResult = FALSE;
|
|
HDPA hdpaSubItems;
|
|
LISTVIEW_ITEM *lpItem;
|
|
LISTVIEW_SUBITEM *lpSubItem;
|
|
INT i;
|
|
LVITEMW 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;
|
|
SendMessageW(GetParent(hwnd), WM_NOTIFY, (WPARAM)lCtrlId,
|
|
(LPARAM)&nmlv);
|
|
|
|
|
|
/* remove it from the selection range */
|
|
ZeroMemory(&item,sizeof(item));
|
|
item.stateMask = LVIS_SELECTED;
|
|
LISTVIEW_SetItemState(hwnd,nItem,&item);
|
|
|
|
if (lStyle & LVS_OWNERDATA)
|
|
{
|
|
infoPtr->hdpaItems->nItemCount --;
|
|
InvalidateRect(hwnd, NULL, TRUE);
|
|
return TRUE;
|
|
}
|
|
|
|
if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
|
|
{
|
|
/* initialize memory */
|
|
ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
|
|
|
|
hdpaSubItems = (HDPA)DPA_DeletePtr(infoPtr->hdpaItems, nItem);
|
|
if (hdpaSubItems != NULL)
|
|
{
|
|
for (i = 1; i < hdpaSubItems->nItemCount; i++)
|
|
{
|
|
lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
|
|
if (lpSubItem != NULL)
|
|
{
|
|
/* free item string */
|
|
if (is_textW(lpSubItem->pszText))
|
|
COMCTL32_Free(lpSubItem->pszText);
|
|
|
|
/* free item */
|
|
COMCTL32_Free(lpSubItem);
|
|
}
|
|
}
|
|
|
|
lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
|
|
if (lpItem != NULL)
|
|
{
|
|
/* free item string */
|
|
if (is_textW(lpItem->pszText))
|
|
COMCTL32_Free(lpItem->pszText);
|
|
|
|
/* free item */
|
|
COMCTL32_Free(lpItem);
|
|
}
|
|
|
|
bResult = DPA_Destroy(hdpaSubItems);
|
|
}
|
|
|
|
LISTVIEW_ShiftIndices(hwnd,nItem,-1);
|
|
|
|
/* align items (set position of each item) */
|
|
if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
|
|
{
|
|
if (lStyle & LVS_ALIGNLEFT)
|
|
LISTVIEW_AlignLeft(hwnd);
|
|
else
|
|
LISTVIEW_AlignTop(hwnd);
|
|
}
|
|
|
|
LISTVIEW_UpdateScroll(hwnd);
|
|
|
|
/* refresh client area */
|
|
InvalidateRect(hwnd, NULL, TRUE);
|
|
}
|
|
|
|
return bResult;
|
|
}
|
|
|
|
|
|
/***
|
|
* DESCRIPTION:
|
|
* Return edit control handle of current edit label
|
|
*
|
|
* PARAMETER(S):
|
|
* [I] HWND : window handle
|
|
*
|
|
* RETURN:
|
|
* SUCCESS : HWND
|
|
* FAILURE : 0
|
|
*/
|
|
static LRESULT LISTVIEW_GetEditControl(HWND hwnd)
|
|
{
|
|
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(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
|
|
* [I] isW : TRUE if psxText is Unicode, FALSE if it's ANSI
|
|
*
|
|
* RETURN:
|
|
* SUCCESS : TRUE
|
|
* FAILURE : FALSE
|
|
*/
|
|
static BOOL LISTVIEW_EndEditLabelT(HWND hwnd, LPWSTR pszText, DWORD nItem, BOOL isW)
|
|
{
|
|
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
|
|
LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
|
|
NMLVDISPINFOW dispInfo;
|
|
LISTVIEW_ITEM *lpItem;
|
|
HDPA hdpaSubItems;
|
|
LISTVIEW_ITEM lvItemRef;
|
|
LVITEMW item;
|
|
BOOL bResult = TRUE;
|
|
|
|
TRACE("(hwnd=%x, pszText=%s, nItem=%ld, isW=%d)\n", hwnd, debugstr_t(pszText, isW), nItem, isW);
|
|
|
|
if (!(lStyle & LVS_OWNERDATA))
|
|
{
|
|
if (!(hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem)))
|
|
return FALSE;
|
|
|
|
if (!(lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)))
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
ZeroMemory(&lvItemRef,sizeof(LISTVIEW_ITEM));
|
|
ZeroMemory(&item,sizeof(item));
|
|
item.iItem = nItem;
|
|
item.iSubItem = 0;
|
|
item.mask = LVIF_PARAM | LVIF_STATE;
|
|
ListView_GetItemW(hwnd, &item);
|
|
lvItemRef.state = item.state;
|
|
lvItemRef.iImage = item.iImage;
|
|
lvItemRef.lParam = item.lParam;
|
|
lpItem = &lvItemRef;
|
|
}
|
|
|
|
ZeroMemory(&dispInfo, sizeof(dispInfo));
|
|
dispInfo.item.mask = 0;
|
|
dispInfo.item.iItem = nItem;
|
|
dispInfo.item.state = lpItem->state;
|
|
dispInfo.item.stateMask = 0;
|
|
dispInfo.item.pszText = pszText;
|
|
dispInfo.item.cchTextMax = textlenT(pszText, isW);
|
|
dispInfo.item.iImage = lpItem->iImage;
|
|
dispInfo.item.lParam = lpItem->lParam;
|
|
infoPtr->hwndEdit = 0;
|
|
|
|
/* Do we need to update the Item Text */
|
|
if(dispinfo_notifyT(hwnd, LVN_ENDLABELEDITW, &dispInfo, isW))
|
|
if (lpItem->pszText != LPSTR_TEXTCALLBACKW && !(lStyle & LVS_OWNERDATA))
|
|
bResult = textsetptrT(&lpItem->pszText, pszText, isW);
|
|
|
|
return bResult;
|
|
}
|
|
|
|
/***
|
|
* 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_EndEditLabelW(HWND hwnd, LPWSTR pszText, DWORD nItem)
|
|
{
|
|
return LISTVIEW_EndEditLabelT(hwnd, pszText, nItem, TRUE);
|
|
}
|
|
|
|
/***
|
|
* 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_EndEditLabelA(HWND hwnd, LPSTR pszText, DWORD nItem)
|
|
{
|
|
return LISTVIEW_EndEditLabelT(hwnd, (LPWSTR)pszText, nItem, FALSE);
|
|
}
|
|
|
|
/***
|
|
* DESCRIPTION:
|
|
* Begin in place editing of specified list view item
|
|
*
|
|
* PARAMETER(S):
|
|
* [I] HWND : window handle
|
|
* [I] INT : item index
|
|
* [I] isW : TRUE if it's a Unicode req, FALSE if ASCII
|
|
*
|
|
* RETURN:
|
|
* SUCCESS : TRUE
|
|
* FAILURE : FALSE
|
|
*/
|
|
static HWND LISTVIEW_EditLabelT(HWND hwnd, INT nItem, BOOL isW)
|
|
{
|
|
NMLVDISPINFOW dispInfo;
|
|
RECT rect;
|
|
LISTVIEW_ITEM *lpItem;
|
|
HWND hedit;
|
|
HINSTANCE hinst = GetWindowLongW(hwnd, GWL_HINSTANCE);
|
|
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
|
|
HDPA hdpaSubItems;
|
|
WCHAR szDispText[DISP_TEXT_SIZE];
|
|
LVITEMW lvItem;
|
|
LISTVIEW_ITEM lvItemRef;
|
|
LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
|
|
|
|
if (~GetWindowLongW(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);
|
|
|
|
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
|
|
{
|
|
LVITEMW item;
|
|
ZeroMemory(&lvItemRef,sizeof(LISTVIEW_ITEM));
|
|
ZeroMemory(&item, sizeof(item));
|
|
item.iItem = nItem;
|
|
item.iSubItem = 0;
|
|
item.mask = LVIF_PARAM | LVIF_STATE;
|
|
ListView_GetItemW(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(lvItem));
|
|
lvItem.mask = LVIF_TEXT;
|
|
lvItem.iItem = nItem;
|
|
lvItem.iSubItem = 0;
|
|
lvItem.cchTextMax = DISP_TEXT_SIZE;
|
|
lvItem.pszText = szDispText;
|
|
*lvItem.pszText = '\0';
|
|
LISTVIEW_GetItemT(hwnd, &lvItem, FALSE, isW);
|
|
|
|
ZeroMemory(&dispInfo, sizeof(dispInfo));
|
|
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 = lstrlenW(lvItem.pszText);
|
|
dispInfo.item.iImage = lpItem->iImage;
|
|
dispInfo.item.lParam = lpItem->lParam;
|
|
|
|
if (dispinfo_notifyT(hwnd, LVN_BEGINLABELEDITW, &dispInfo, isW))
|
|
return 0;
|
|
|
|
rect.left = LVIR_LABEL;
|
|
if (!LISTVIEW_GetItemRect(hwnd, nItem, &rect))
|
|
return 0;
|
|
|
|
if (!(hedit = CreateEditLabelT(szDispText , WS_VISIBLE,
|
|
rect.left-2, rect.top-1, 0, rect.bottom - rect.top+2, hwnd, hinst,
|
|
isW ? LISTVIEW_EndEditLabelW : (EditlblCallbackW)LISTVIEW_EndEditLabelA,
|
|
nItem, isW)))
|
|
return 0;
|
|
|
|
infoPtr->hwndEdit = hedit;
|
|
SetFocus(hedit);
|
|
SendMessageW(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 *)GetWindowLongW(hwnd, 0);
|
|
UINT uView = GetWindowLongW(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 *)GetWindowLongW(hwnd, 0);
|
|
LV_INTHIT lvIntHit;
|
|
INT nItem = -1;
|
|
RECT rcView;
|
|
|
|
TRACE("point %ld,%ld, direction %s\n", pt.x, pt.y,
|
|
(vkDirection == VK_DOWN) ? "VK_DOWN" :
|
|
((vkDirection == VK_UP) ? "VK_UP" :
|
|
((vkDirection == VK_LEFT) ? "VK_LEFT" : "VK_RIGHT")));
|
|
|
|
if (LISTVIEW_GetViewRect(hwnd, &rcView) != FALSE)
|
|
{
|
|
ZeroMemory(&lvIntHit, sizeof(lvIntHit));
|
|
LISTVIEW_GetOrigin(hwnd, &lvIntHit.ht.pt);
|
|
lvIntHit.ht.pt.x += pt.x;
|
|
lvIntHit.ht.pt.y += pt.y;
|
|
|
|
if (vkDirection == VK_DOWN)
|
|
lvIntHit.ht.pt.y += infoPtr->nItemHeight;
|
|
else if (vkDirection == VK_UP)
|
|
lvIntHit.ht.pt.y -= infoPtr->nItemHeight;
|
|
else if (vkDirection == VK_LEFT)
|
|
lvIntHit.ht.pt.x -= infoPtr->nItemWidth;
|
|
else if (vkDirection == VK_RIGHT)
|
|
lvIntHit.ht.pt.x += infoPtr->nItemWidth;
|
|
|
|
if (PtInRect(&rcView, lvIntHit.ht.pt) == FALSE)
|
|
return -1;
|
|
else
|
|
{
|
|
nItem = LISTVIEW_SuperHitTestItem(hwnd, &lvIntHit, TRUE);
|
|
return nItem == -1 ? lvIntHit.iDistItem : nItem;
|
|
}
|
|
}
|
|
|
|
return nItem;
|
|
}
|
|
|
|
/***
|
|
* DESCRIPTION:
|
|
* Searches for an item with specific characteristics.
|
|
*
|
|
* PARAMETER(S):
|
|
* [I] hwnd : window handle
|
|
* [I] nStart : base item index
|
|
* [I] lpFindInfo : item information to look for
|
|
*
|
|
* RETURN:
|
|
* SUCCESS : index of item
|
|
* FAILURE : -1
|
|
*/
|
|
static LRESULT LISTVIEW_FindItemW(HWND hwnd, INT nStart,
|
|
LPLVFINDINFOW lpFindInfo)
|
|
{
|
|
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
|
|
POINT ptItem;
|
|
WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
|
|
LVITEMW lvItem;
|
|
BOOL bWrap = FALSE;
|
|
INT nItem = nStart;
|
|
INT nLast = GETITEMCOUNT(infoPtr);
|
|
|
|
if ((nItem >= -1) && (lpFindInfo != NULL))
|
|
{
|
|
ZeroMemory(&lvItem, sizeof(lvItem));
|
|
|
|
if (lpFindInfo->flags & LVFI_PARAM)
|
|
{
|
|
lvItem.mask |= LVIF_PARAM;
|
|
}
|
|
|
|
if (lpFindInfo->flags & (LVFI_STRING | 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_GetItemW(hwnd, &lvItem, TRUE))
|
|
{
|
|
if (lvItem.mask & LVIF_TEXT)
|
|
{
|
|
if (lpFindInfo->flags & LVFI_PARTIAL)
|
|
{
|
|
if (strstrW(lvItem.pszText, lpFindInfo->psz) == NULL)
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
if (lstrcmpW(lvItem.pszText, lpFindInfo->psz) != 0)
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (lvItem.mask & LVIF_PARAM)
|
|
{
|
|
if (lpFindInfo->lParam != lvItem.lParam)
|
|
continue;
|
|
}
|
|
|
|
return nItem;
|
|
}
|
|
}
|
|
|
|
if (bWrap)
|
|
{
|
|
nItem = -1;
|
|
nLast = nStart + 1;
|
|
bWrap = FALSE;
|
|
}
|
|
else
|
|
{
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
/***
|
|
* DESCRIPTION:
|
|
* Searches for an item with specific characteristics.
|
|
*
|
|
* PARAMETER(S):
|
|
* [I] hwnd : window handle
|
|
* [I] nStart : base item index
|
|
* [I] lpFindInfo : item information to look for
|
|
*
|
|
* RETURN:
|
|
* SUCCESS : index of item
|
|
* FAILURE : -1
|
|
*/
|
|
static LRESULT LISTVIEW_FindItemA(HWND hwnd, INT nStart,
|
|
LPLVFINDINFOA lpFindInfo)
|
|
{
|
|
BOOL hasText = lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL);
|
|
LVFINDINFOW fiw;
|
|
LRESULT res;
|
|
|
|
memcpy(&fiw, lpFindInfo, sizeof(fiw));
|
|
if (hasText) fiw.psz = textdupTtoW((LPCWSTR)lpFindInfo->psz, FALSE);
|
|
res = LISTVIEW_FindItemW(hwnd, nStart, &fiw);
|
|
if (hasText) textfreeT((LPWSTR)fiw.psz, FALSE);
|
|
return res;
|
|
}
|
|
|
|
/***
|
|
* 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 *)GetWindowLongW(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 *)GetWindowLongW(hwnd, 0);
|
|
|
|
return infoPtr->uCallbackMask;
|
|
}
|
|
|
|
/***
|
|
* DESCRIPTION:
|
|
* Retrieves column attributes.
|
|
*
|
|
* PARAMETER(S):
|
|
* [I] HWND : window handle
|
|
* [I] INT : column index
|
|
* [IO] LPLVCOLUMNW : column information
|
|
* [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW
|
|
* otherwise it is in fact a LPLVCOLUMNA
|
|
*
|
|
* RETURN:
|
|
* SUCCESS : TRUE
|
|
* FAILURE : FALSE
|
|
*/
|
|
static LRESULT LISTVIEW_GetColumnT(HWND hwnd, INT nItem, LPLVCOLUMNW lpColumn, BOOL isW)
|
|
{
|
|
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
|
|
HDITEMW hdi;
|
|
BOOL bResult = FALSE;
|
|
|
|
if (lpColumn != NULL)
|
|
{
|
|
|
|
TRACE("(hwnd=%x, col=%d, lpColumn=%s, isW=%d)\n",
|
|
hwnd, nItem, debuglvcolumn_t(lpColumn, isW), isW);
|
|
|
|
/* initialize memory */
|
|
ZeroMemory(&hdi, sizeof(hdi));
|
|
|
|
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;
|
|
|
|
if (isW)
|
|
bResult = Header_GetItemW(infoPtr->hwndHeader, nItem, &hdi);
|
|
else
|
|
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;
|
|
}
|
|
|
|
|
|
static LRESULT LISTVIEW_GetColumnOrderArray(HWND hwnd, INT iCount, LPINT lpiArray)
|
|
{
|
|
/* LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(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 *)GetWindowLongW(hwnd, 0);
|
|
UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
|
|
INT nColumnWidth = 0;
|
|
HDITEMW hdi;
|
|
|
|
if (uView == LVS_LIST)
|
|
{
|
|
nColumnWidth = infoPtr->nItemWidth;
|
|
}
|
|
else if (uView == LVS_REPORT)
|
|
{
|
|
/* get column width from header */
|
|
ZeroMemory(&hdi, sizeof(hdi));
|
|
hdi.mask = HDI_WIDTH;
|
|
if (Header_GetItemW(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 *)GetWindowLongW(hwnd, 0);
|
|
UINT uView = GetWindowLongW(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 *)GetWindowLongW(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 *)GetWindowLongW(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 *)GetWindowLongW(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 *)GetWindowLongW(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] lpLVItem : item info
|
|
* [I] internal : if true then we will use tricks that avoid copies
|
|
* but are not compatible with the regular interface
|
|
* [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
|
|
* if FALSE, the lpLVItem is a LPLVITEMA.
|
|
*
|
|
* RETURN:
|
|
* SUCCESS : TRUE
|
|
* FAILURE : FALSE
|
|
*/
|
|
static LRESULT LISTVIEW_GetItemT(HWND hwnd, LPLVITEMW lpLVItem, BOOL internal, BOOL isW)
|
|
{
|
|
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
|
|
LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
|
|
NMLVDISPINFOW dispInfo;
|
|
LISTVIEW_SUBITEM *lpSubItem;
|
|
LISTVIEW_ITEM *lpItem;
|
|
HDPA hdpaSubItems;
|
|
void* null = NULL;
|
|
INT* piImage = (INT*)&null;
|
|
LPWSTR* ppszText= (LPWSTR*)&null;
|
|
LPARAM* plParam = (LPARAM*)&null;
|
|
|
|
if (internal && !isW)
|
|
{
|
|
ERR("We can't have internal non-Unicode GetItem!\n");
|
|
return FALSE;
|
|
}
|
|
|
|
/* 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=%s, internal=%d, isW=%d)\n",
|
|
hwnd, debuglvitem_t(lpLVItem, isW), internal, isW);
|
|
|
|
if ((lpLVItem == NULL) || (lpLVItem->iItem < 0) ||
|
|
(lpLVItem->iItem >= GETITEMCOUNT(infoPtr)))
|
|
return FALSE;
|
|
|
|
ZeroMemory(&dispInfo, sizeof(dispInfo));
|
|
|
|
if (lStyle & LVS_OWNERDATA)
|
|
{
|
|
if (lpLVItem->mask & ~LVIF_STATE)
|
|
{
|
|
memcpy(&dispInfo.item, lpLVItem, sizeof(LVITEMW));
|
|
dispinfo_notifyT(hwnd, LVN_GETDISPINFOW, &dispInfo, isW);
|
|
memcpy(lpLVItem, &dispInfo.item, sizeof(LVITEMW));
|
|
TRACE(" getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem, isW));
|
|
}
|
|
|
|
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;
|
|
|
|
if ( (lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)) == NULL)
|
|
return FALSE;
|
|
|
|
ZeroMemory(&dispInfo.item, sizeof(LVITEMW));
|
|
if (lpLVItem->iSubItem == 0)
|
|
{
|
|
piImage=&lpItem->iImage;
|
|
ppszText=&lpItem->pszText;
|
|
plParam=&lpItem->lParam;
|
|
if ((lpLVItem->mask & LVIF_STATE) && infoPtr->uCallbackMask)
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
|
|
if ((lpLVItem->mask & LVIF_IMAGE) && (*piImage==I_IMAGECALLBACK))
|
|
{
|
|
dispInfo.item.mask |= LVIF_IMAGE;
|
|
}
|
|
|
|
if ((lpLVItem->mask & LVIF_TEXT) && !is_textW(*ppszText))
|
|
{
|
|
dispInfo.item.mask |= LVIF_TEXT;
|
|
dispInfo.item.pszText = lpLVItem->pszText;
|
|
dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
|
|
if (dispInfo.item.pszText && lpLVItem->cchTextMax > 0)
|
|
*dispInfo.item.pszText = '\0';
|
|
if (dispInfo.item.pszText && (*ppszText == NULL))
|
|
*dispInfo.item.pszText = '\0';
|
|
}
|
|
|
|
if (dispInfo.item.mask != 0)
|
|
{
|
|
/* We don't have all the requested info, query the application */
|
|
dispInfo.item.iItem = lpLVItem->iItem;
|
|
dispInfo.item.iSubItem = lpLVItem->iSubItem;
|
|
dispInfo.item.lParam = lpItem->lParam;
|
|
dispinfo_notifyT(hwnd, LVN_GETDISPINFOW, &dispInfo, isW);
|
|
TRACE(" getdispinfo(2):lpLVItem=%s\n", debuglvitem_t(&dispInfo.item, isW));
|
|
}
|
|
|
|
if (dispInfo.item.mask & LVIF_IMAGE)
|
|
{
|
|
lpLVItem->iImage = dispInfo.item.iImage;
|
|
if ((dispInfo.item.mask & LVIF_DI_SETITEM) && (*piImage==I_IMAGECALLBACK))
|
|
*piImage = dispInfo.item.iImage;
|
|
}
|
|
else if (lpLVItem->mask & LVIF_IMAGE)
|
|
{
|
|
lpLVItem->iImage = *piImage;
|
|
}
|
|
|
|
if (dispInfo.item.mask & LVIF_PARAM)
|
|
{
|
|
lpLVItem->lParam = dispInfo.item.lParam;
|
|
if (dispInfo.item.mask & LVIF_DI_SETITEM)
|
|
*plParam = 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)
|
|
textsetptrT(ppszText, dispInfo.item.pszText, isW);
|
|
|
|
/* 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)
|
|
textcpynT(lpLVItem->pszText, isW, dispInfo.item.pszText, isW, lpLVItem->cchTextMax);
|
|
|
|
}
|
|
else if (lpLVItem->mask & LVIF_TEXT)
|
|
{
|
|
if (internal) lpLVItem->pszText = *ppszText;
|
|
else textcpynT(lpLVItem->pszText, isW, *ppszText, TRUE, 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_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 = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
|
|
|
|
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 *)GetWindowLongW(hwnd, 0);
|
|
|
|
return GETITEMCOUNT(infoPtr);
|
|
}
|
|
|
|
/***
|
|
* DESCRIPTION:
|
|
* Retrieves the rectangle enclosing the item icon and text.
|
|
*
|
|
* PARAMETER(S):
|
|
* [I] HWND : window handle
|
|
* [I] INT : item index
|
|
* [O] LPRECT : coordinate information
|
|
*
|
|
* RETURN:
|
|
* SUCCESS : TRUE
|
|
* FAILURE : FALSE
|
|
*/
|
|
static BOOL LISTVIEW_GetItemBoundBox(HWND hwnd, INT nItem, LPRECT lpRect)
|
|
{
|
|
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
|
|
LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
|
|
UINT uView = lStyle & LVS_TYPEMASK;
|
|
BOOL bResult = FALSE;
|
|
HDPA hdpaSubItems;
|
|
LISTVIEW_ITEM *lpItem;
|
|
INT nCountPerColumn;
|
|
INT nRow;
|
|
|
|
TRACE("(hwnd=%x,nItem=%d,lpRect=%p)\n", hwnd, nItem, lpRect);
|
|
|
|
if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)) &&
|
|
(lpRect != 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)
|
|
{
|
|
lpRect->left = nItem / nCountPerColumn * infoPtr->nItemWidth;
|
|
lpRect->top = 0;
|
|
}
|
|
else
|
|
{
|
|
lpRect->left = (nItem / nCountPerColumn -1) * infoPtr->nItemWidth;
|
|
lpRect->top = (nRow + nCountPerColumn) * infoPtr->nItemHeight;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
lpRect->left = nItem / nCountPerColumn * infoPtr->nItemWidth;
|
|
lpRect->top = nItem % nCountPerColumn * infoPtr->nItemHeight;
|
|
}
|
|
}
|
|
else if (uView == LVS_REPORT)
|
|
{
|
|
bResult = TRUE;
|
|
lpRect->left = REPORT_MARGINX;
|
|
lpRect->top = ((nItem - ListView_GetTopIndex(hwnd)) *
|
|
infoPtr->nItemHeight) + infoPtr->rcList.top;
|
|
|
|
if (!(lStyle & LVS_NOSCROLL))
|
|
{
|
|
SCROLLINFO scrollInfo;
|
|
/* Adjust position by scrollbar offset */
|
|
ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
|
|
scrollInfo.cbSize = sizeof(SCROLLINFO);
|
|
scrollInfo.fMask = SIF_POS;
|
|
GetScrollInfo(hwnd, SB_HORZ, &scrollInfo);
|
|
lpRect->left -= scrollInfo.nPos * LISTVIEW_SCROLL_DIV_SIZE;
|
|
}
|
|
}
|
|
else /* either LVS_ICON or LVS_SMALLICON */
|
|
{
|
|
if ((hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem)))
|
|
{
|
|
if ((lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)))
|
|
{
|
|
bResult = TRUE;
|
|
lpRect->left = lpItem->ptPosition.x;
|
|
lpRect->top = lpItem->ptPosition.y;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
lpRect->right = lpRect->left + infoPtr->nItemWidth;
|
|
lpRect->bottom = lpRect->top + infoPtr->nItemHeight;
|
|
TRACE("result %s: (%d,%d)-(%d,%d)\n", bResult ? "TRUE" : "FALSE",
|
|
lpRect->left, lpRect->top, lpRect->right, lpRect->bottom);
|
|
return bResult;
|
|
}
|
|
|
|
/***
|
|
* DESCRIPTION:
|
|
* Retrieves the position (upper-left) of the listview control item.
|
|
* Note that for LVS_ICON style, the upper-left is that of the icon
|
|
* and not the bounding box.
|
|
*
|
|
* 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;
|
|
RECT rcBounding;
|
|
|
|
TRACE("(hwnd=%x, nItem=%d, lpptPosition=%p)\n", hwnd, nItem, lpptPosition);
|
|
|
|
if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)) &&
|
|
(lpptPosition != NULL))
|
|
{
|
|
bResult = LISTVIEW_GetItemBoundBox(hwnd, nItem, &rcBounding);
|
|
lpptPosition->x = rcBounding.left;
|
|
lpptPosition->y = rcBounding.top;
|
|
if (uView == LVS_ICON)
|
|
{
|
|
lpptPosition->y += ICON_TOP_PADDING;
|
|
lpptPosition->x += (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
|
|
}
|
|
TRACE("result %s (%ld,%ld)\n", bResult ? "TRUE" : "FALSE",
|
|
lpptPosition->x, lpptPosition->y);
|
|
}
|
|
return bResult;
|
|
}
|
|
|
|
/***
|
|
* Update the bounding rectangle around the text under a large icon.
|
|
* This depends on whether it has the focus or not.
|
|
* On entry the rectangle's top, left and right should be set.
|
|
* On return the bottom will also be set and the width may have been
|
|
* modified.
|
|
*
|
|
* This appears to be weird, even in the Microsoft implementation.
|
|
*/
|
|
|
|
static void ListView_UpdateLargeItemLabelRect (
|
|
HWND hwnd, /* The window of the listview */
|
|
const LISTVIEW_INFO *infoPtr, /* The listview itself */
|
|
int nItem, /* The item for which we are calculating this */
|
|
RECT *rect) /* The rectangle to be updated */
|
|
{
|
|
HDC hdc = GetDC (hwnd);
|
|
HFONT hOldFont = SelectObject (hdc, infoPtr->hFont);
|
|
|
|
if (infoPtr->bFocus && infoPtr->nFocusedItem == nItem)
|
|
{
|
|
/* We (aim to) display the full text. In Windows 95 it appears to
|
|
* calculate the size assuming the specified font and then it draws
|
|
* the text in that region with the specified font except scaled to
|
|
* 10 point (or the height of the system font or ...). Thus if the
|
|
* window has 24 point Helvetica the highlit rectangle will be
|
|
* taller than the text and if it is 7 point Helvetica then the text
|
|
* will be clipped.
|
|
* For now we will simply say that it is the correct size to display
|
|
* the text in the specified font.
|
|
*/
|
|
LVITEMW lvItem;
|
|
lvItem.mask = LVIF_TEXT;
|
|
lvItem.iItem = nItem;
|
|
lvItem.iSubItem = 0;
|
|
/* We will specify INTERNAL and so will receive back a const
|
|
* pointer to the text, rather than specifying a buffer to which
|
|
* to copy it.
|
|
*/
|
|
LISTVIEW_GetItemW (hwnd, &lvItem, TRUE);
|
|
DrawTextW (hdc, lvItem.pszText, -1, rect, DT_CALCRECT |
|
|
DT_NOCLIP | DT_EDITCONTROL | DT_TOP | DT_CENTER |
|
|
DT_WORDBREAK | DT_NOPREFIX);
|
|
/* Maintain this DT_* list in line with LISTVIEW_DrawLargeItem */
|
|
}
|
|
else
|
|
{
|
|
/* As far as I can see the text region seems to be trying to be
|
|
* "tall enough for two lines of text". Once again (comctl32.dll ver
|
|
* 5.81?) it measures this on the basis of the selected font and then
|
|
* draws it with the same font except in 10 point size. This can lead
|
|
* to more or less than the two rows appearing.
|
|
* Question; are we supposed to be including DT_EXTERNALLEADING?
|
|
* Question; should the width be shrunk to the space required to
|
|
* display the two lines?
|
|
*/
|
|
rect->bottom = rect->top + 2 * infoPtr->ntmHeight;
|
|
}
|
|
|
|
SelectObject (hdc, hOldFont);
|
|
ReleaseDC (hwnd, hdc);
|
|
}
|
|
|
|
/***
|
|
* 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
|
|
*
|
|
* NOTES
|
|
* Note that the bounding rectangle of the label in the LVS_ICON view depends
|
|
* upon whether the window has the focus currently and on whether the item
|
|
* is the one with the focus. Ensure that the control's record of which
|
|
* item has the focus agrees with the items' records.
|
|
*/
|
|
static LRESULT LISTVIEW_GetItemRect(HWND hwnd, INT nItem, LPRECT lprc)
|
|
{
|
|
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
|
|
UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
|
|
BOOL bResult = FALSE;
|
|
POINT ptOrigin;
|
|
POINT ptItem;
|
|
INT nLeftPos;
|
|
INT nLabelWidth;
|
|
INT nIndent;
|
|
LVITEMW lvItem;
|
|
RECT rcInternal;
|
|
|
|
TRACE("(hwnd=%x, nItem=%d, lprc=%p)\n", hwnd, nItem, lprc);
|
|
|
|
if (uView & LVS_REPORT)
|
|
{
|
|
ZeroMemory(&lvItem, sizeof(lvItem));
|
|
lvItem.mask = LVIF_INDENT;
|
|
lvItem.iItem = nItem;
|
|
lvItem.iSubItem = 0;
|
|
LISTVIEW_GetItemW(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))
|
|
{
|
|
switch(lprc->left)
|
|
{
|
|
case LVIR_ICON:
|
|
if (!ListView_GetItemPosition(hwnd, nItem, &ptItem)) break;
|
|
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 (!ListView_GetItemPosition(hwnd, nItem, &ptItem)) break;
|
|
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);
|
|
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;
|
|
ListView_UpdateLargeItemLabelRect (hwnd, infoPtr, nItem, lprc);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
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;
|
|
|
|
if (uView != LVS_REPORT)
|
|
{
|
|
nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
|
|
nLabelWidth += TRAILING_PADDING;
|
|
if (infoPtr->himlSmall)
|
|
nLabelWidth += IMAGE_PADDING;
|
|
}
|
|
else
|
|
nLabelWidth = LISTVIEW_GetColumnWidth(hwnd, 0)-lprc->left;
|
|
if (lprc->left + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
|
|
lprc->right = lprc->left + nLabelWidth;
|
|
else
|
|
lprc->right = nLeftPos + infoPtr->nItemWidth;
|
|
}
|
|
break;
|
|
|
|
case LVIR_BOUNDS:
|
|
if (!LISTVIEW_GetItemBoundBox(hwnd, nItem, &rcInternal)) break;
|
|
ptItem.x = rcInternal.left;
|
|
ptItem.y = rcInternal.top;
|
|
if (uView == LVS_ICON)
|
|
{
|
|
if (infoPtr->himlNormal != NULL)
|
|
{
|
|
if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
|
|
{
|
|
RECT label_rect;
|
|
INT text_left, text_right, icon_left, text_pos_x;
|
|
/* for style LVS_ICON bounds
|
|
* left = min(icon.left, text.left)
|
|
* right = max(icon.right, text.right)
|
|
* top = boundbox.top + NOTHITABLE
|
|
* bottom = text.bottom + 1
|
|
*/
|
|
bResult = TRUE;
|
|
icon_left = text_left = ptItem.x;
|
|
|
|
/* Correct ptItem to icon upper-left */
|
|
icon_left += (infoPtr->nItemWidth - infoPtr->iconSize.cx)/2;
|
|
ptItem.y += ICON_TOP_PADDING;
|
|
|
|
/* Compute the label left and right */
|
|
nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
|
|
text_pos_x = infoPtr->iconSpacing.cx - 2*CAPTION_BORDER - nLabelWidth;
|
|
if (text_pos_x > 1)
|
|
{
|
|
text_left += text_pos_x / 2;
|
|
text_right = text_left + nLabelWidth + 2*CAPTION_BORDER;
|
|
}
|
|
else
|
|
{
|
|
text_left += 1;
|
|
text_right = text_left + infoPtr->iconSpacing.cx - 1;
|
|
}
|
|
|
|
/* Compute rectangle w/o the text height */
|
|
lprc->left = min(icon_left, text_left) + ptOrigin.x;
|
|
lprc->right = max(icon_left + infoPtr->iconSize.cx,
|
|
text_right) + ptOrigin.x;
|
|
lprc->top = ptItem.y + ptOrigin.y - ICON_TOP_PADDING_HITABLE;
|
|
lprc->bottom = lprc->top + ICON_TOP_PADDING_HITABLE
|
|
+ infoPtr->iconSize.cy + 1
|
|
+ ICON_BOTTOM_PADDING;
|
|
|
|
CopyRect (&label_rect, lprc);
|
|
label_rect.top = lprc->bottom;
|
|
ListView_UpdateLargeItemLabelRect (hwnd, infoPtr, nItem, &label_rect);
|
|
UnionRect (lprc, lprc, &label_rect);
|
|
}
|
|
}
|
|
}
|
|
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) || (uView == LVS_REPORT))
|
|
{
|
|
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 (!ListView_GetItemPosition(hwnd, nItem, &ptItem)) break;
|
|
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;
|
|
}
|
|
}
|
|
|
|
TRACE("result %s (%d,%d)-(%d,%d)\n", bResult ? "TRUE" : "FALSE",
|
|
lprc->left, lprc->top, lprc->right, lprc->bottom);
|
|
|
|
return bResult;
|
|
}
|
|
|
|
|
|
static LRESULT LISTVIEW_GetSubItemRect(HWND hwnd, INT nItem, INT nSubItem, INT
|
|
flags, LPRECT lprc)
|
|
{
|
|
UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
|
|
INT count;
|
|
|
|
TRACE("(hwnd=%x, nItem=%d, nSubItem=%d lprc=%p)\n", hwnd, nItem, nSubItem,
|
|
lprc);
|
|
|
|
if (!(uView & LVS_REPORT))
|
|
return FALSE;
|
|
|
|
if (flags & LVIR_ICON)
|
|
{
|
|
FIXME("Unimplemented LVIR_ICON\n");
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
LISTVIEW_GetItemRect(hwnd,nItem,lprc);
|
|
count = 0;
|
|
while (count<(nSubItem-1))
|
|
{
|
|
lprc->left += LISTVIEW_GetColumnWidth(hwnd,count);
|
|
count ++;
|
|
}
|
|
|
|
lprc->right = LISTVIEW_GetColumnWidth(hwnd,(nSubItem-1)) +
|
|
lprc->left;
|
|
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/***
|
|
* 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)
|
|
{
|
|
WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
|
|
INT nLabelWidth = 0;
|
|
LVITEMW lvItem;
|
|
|
|
TRACE("(hwnd=%x, nItem=%d)\n", hwnd, nItem);
|
|
|
|
ZeroMemory(&lvItem, sizeof(lvItem));
|
|
lvItem.mask = LVIF_TEXT;
|
|
lvItem.iItem = nItem;
|
|
lvItem.cchTextMax = DISP_TEXT_SIZE;
|
|
lvItem.pszText = szDispText;
|
|
if (LISTVIEW_GetItemW(hwnd, &lvItem, TRUE))
|
|
nLabelWidth = ListView_GetStringWidthW(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 *)GetWindowLongW(hwnd, 0);
|
|
LONG lResult;
|
|
|
|
if (bSmall == FALSE)
|
|
{
|
|
lResult = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
|
|
}
|
|
else
|
|
{
|
|
LONG style = GetWindowLongW(hwnd, GWL_STYLE);
|
|
if ((style & LVS_TYPEMASK) == LVS_ICON)
|
|
lResult = MAKELONG(DEFAULT_COLUMN_WIDTH, GetSystemMetrics(SM_CXSMICON)+HEIGHT_PADDING);
|
|
else
|
|
lResult = MAKELONG(infoPtr->nItemWidth, 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 *)GetWindowLongW(hwnd, 0);
|
|
LVITEMW lvItem;
|
|
UINT uState = 0;
|
|
|
|
if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
|
|
{
|
|
ZeroMemory(&lvItem, sizeof(lvItem));
|
|
lvItem.iItem = nItem;
|
|
lvItem.stateMask = uMask;
|
|
lvItem.mask = LVIF_STATE;
|
|
if (LISTVIEW_GetItemW(hwnd, &lvItem, TRUE))
|
|
uState = lvItem.state;
|
|
}
|
|
|
|
return uState;
|
|
}
|
|
|
|
/***
|
|
* DESCRIPTION:
|
|
* Retrieves the text of a listview control item or subitem.
|
|
*
|
|
* PARAMETER(S):
|
|
* [I] hwnd : window handle
|
|
* [I] nItem : item index
|
|
* [IO] lpLVItem : item information
|
|
* [I] isW : TRUE if lpLVItem is Unicode
|
|
*
|
|
* RETURN:
|
|
* SUCCESS : string length
|
|
* FAILURE : 0
|
|
*/
|
|
static LRESULT LISTVIEW_GetItemTextT(HWND hwnd, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
|
|
{
|
|
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
|
|
INT nLength = 0;
|
|
|
|
if (lpLVItem != NULL)
|
|
{
|
|
if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
|
|
{
|
|
lpLVItem->mask = LVIF_TEXT;
|
|
lpLVItem->iItem = nItem;
|
|
if (LISTVIEW_GetItemT(hwnd, lpLVItem, FALSE, isW))
|
|
nLength = textlenT(lpLVItem->pszText, isW);
|
|
}
|
|
}
|
|
|
|
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 *)GetWindowLongW(hwnd, 0);
|
|
UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
|
|
UINT uMask = 0;
|
|
LVFINDINFOW 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_FindItemW(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_FindItemW(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_FindItemW(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_FindItemW(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 = GetWindowLongW(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 *)GetWindowLongW(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 *)GetWindowLongW(hwnd, 0);
|
|
|
|
return infoPtr->nSelectionMark;
|
|
}
|
|
|
|
|
|
/***
|
|
* DESCRIPTION:
|
|
* Retrieves the width of a string.
|
|
*
|
|
* PARAMETER(S):
|
|
* [I] hwnd : window handle
|
|
* [I] lpszText : text string to process
|
|
* [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
|
|
*
|
|
* RETURN:
|
|
* SUCCESS : string width (in pixels)
|
|
* FAILURE : zero
|
|
*/
|
|
static LRESULT LISTVIEW_GetStringWidthT(HWND hwnd, LPCWSTR lpszText, BOOL isW)
|
|
{
|
|
if (is_textT(lpszText, isW))
|
|
{
|
|
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
|
|
HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
|
|
HDC hdc = GetDC(hwnd);
|
|
HFONT hOldFont = SelectObject(hdc, hFont);
|
|
SIZE stringSize;
|
|
ZeroMemory(&stringSize, sizeof(SIZE));
|
|
if (isW)
|
|
GetTextExtentPointW(hdc, lpszText, lstrlenW(lpszText), &stringSize);
|
|
else
|
|
GetTextExtentPointA(hdc, (LPCSTR)lpszText, lstrlenA((LPCSTR)lpszText), &stringSize);
|
|
SelectObject(hdc, hOldFont);
|
|
ReleaseDC(hwnd, hdc);
|
|
return stringSize.cx;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/***
|
|
* 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*)GetWindowLongW(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*)GetWindowLongW(hwnd, 0);
|
|
|
|
return infoPtr->clrText;
|
|
}
|
|
|
|
/***
|
|
* DESCRIPTION:
|
|
* Determines item if a hit or closest if not
|
|
*
|
|
* PARAMETER(S):
|
|
* [I] HWND : window handle
|
|
* [IO] LPLV_INTHIT : hit test information
|
|
* [I] subitem : fill out iSubItem.
|
|
*
|
|
* RETURN:
|
|
* SUCCESS : item index of hit
|
|
* FAILURE : -1
|
|
*/
|
|
static INT LISTVIEW_SuperHitTestItem(HWND hwnd, LPLV_INTHIT lpInt, BOOL subitem)
|
|
{
|
|
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
|
|
LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
|
|
UINT uView = lStyle & LVS_TYPEMASK;
|
|
INT i,topindex,bottomindex;
|
|
RECT rcItem;
|
|
DWORD xterm, yterm, dist;
|
|
|
|
TRACE("(hwnd=%x, x=%ld, y=%ld)\n", hwnd, lpInt->ht.pt.x, lpInt->ht.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);
|
|
}
|
|
|
|
lpInt->distance = 0x7fffffff;
|
|
lpInt->iDistItem = -1;
|
|
|
|
for (i = topindex; i < bottomindex; i++)
|
|
{
|
|
rcItem.left = LVIR_BOUNDS;
|
|
if (LISTVIEW_GetItemRect(hwnd, i, &rcItem))
|
|
{
|
|
if (PtInRect(&rcItem, lpInt->ht.pt))
|
|
{
|
|
rcItem.left = LVIR_ICON;
|
|
if (LISTVIEW_GetItemRect(hwnd, i, &rcItem))
|
|
{
|
|
if (PtInRect(&rcItem, lpInt->ht.pt))
|
|
{
|
|
lpInt->ht.flags = LVHT_ONITEMICON;
|
|
lpInt->ht.iItem = i;
|
|
if (subitem) lpInt->ht.iSubItem = 0;
|
|
return i;
|
|
}
|
|
}
|
|
|
|
rcItem.left = LVIR_LABEL;
|
|
if (LISTVIEW_GetItemRect(hwnd, i, &rcItem))
|
|
{
|
|
if (PtInRect(&rcItem, lpInt->ht.pt))
|
|
{
|
|
lpInt->ht.flags = LVHT_ONITEMLABEL;
|
|
lpInt->ht.iItem = i;
|
|
if (subitem) lpInt->ht.iSubItem = 0;
|
|
return i;
|
|
}
|
|
}
|
|
|
|
lpInt->ht.flags = LVHT_ONITEMSTATEICON;
|
|
lpInt->ht.iItem = i;
|
|
if (subitem) lpInt->ht.iSubItem = 0;
|
|
return i;
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* Now compute distance from point to center of boundary
|
|
* box. Since we are only interested in the relative
|
|
* distance, we can skip the nasty square root operation
|
|
*/
|
|
xterm = rcItem.left + (rcItem.right - rcItem.left)/2 - lpInt->ht.pt.x;
|
|
yterm = rcItem.top + (rcItem.bottom - rcItem.top)/2 - lpInt->ht.pt.y;
|
|
dist = xterm * xterm + yterm * yterm;
|
|
if (dist < lpInt->distance)
|
|
{
|
|
lpInt->distance = dist;
|
|
lpInt->iDistItem = i;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
lpInt->ht.flags = LVHT_NOWHERE;
|
|
TRACE("no hit, closest item %d, distance %ld\n", lpInt->iDistItem, lpInt->distance);
|
|
|
|
return -1;
|
|
}
|
|
|
|
/***
|
|
* 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)
|
|
{
|
|
INT ret;
|
|
LV_INTHIT lv_inthit;
|
|
|
|
TRACE("(hwnd=%x, x=%ld, y=%ld)\n", hwnd, lpHitTestInfo->pt.x,
|
|
lpHitTestInfo->pt.y);
|
|
|
|
memcpy(&lv_inthit, lpHitTestInfo, sizeof(LVHITTESTINFO));
|
|
ret = LISTVIEW_SuperHitTestItem(hwnd, &lv_inthit, subitem);
|
|
memcpy(lpHitTestInfo, &lv_inthit, sizeof(LVHITTESTINFO));
|
|
return ret;
|
|
}
|
|
|
|
/***
|
|
* 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 *)GetWindowLongW(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] LPLVCOLUMNW : column information
|
|
*
|
|
* RETURN:
|
|
* SUCCESS : new column index
|
|
* FAILURE : -1
|
|
*/
|
|
static LRESULT LISTVIEW_InsertColumnT(HWND hwnd, INT nColumn,
|
|
LPLVCOLUMNW lpColumn, BOOL isW)
|
|
{
|
|
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
|
|
INT nNewColumn = -1;
|
|
HDITEMW hdi;
|
|
|
|
TRACE("(hwnd=%x, nColumn=%d, lpColumn=%p)\n",hwnd, nColumn, lpColumn);
|
|
|
|
if (lpColumn != NULL)
|
|
{
|
|
/* initialize memory */
|
|
ZeroMemory(&hdi, sizeof(hdi));
|
|
|
|
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;
|
|
if(lpColumn->cx == LVSCW_AUTOSIZE_USEHEADER)
|
|
{
|
|
/* make it fill the remainder of the controls width */
|
|
HDITEMW hdit;
|
|
RECT rcHeader;
|
|
INT item_index;
|
|
|
|
ZeroMemory(&hdit, sizeof(hdit));
|
|
|
|
/* get the width of every item except the current one */
|
|
hdit.mask = HDI_WIDTH;
|
|
hdi.cxy = 0;
|
|
|
|
for(item_index = 0; item_index < (nColumn - 1); item_index++) {
|
|
Header_GetItemW(infoPtr->hwndHeader, item_index, (LPARAM)(&hdit));
|
|
hdi.cxy+=hdit.cxy;
|
|
}
|
|
|
|
/* retrieve the layout of the header */
|
|
GetClientRect(hwnd, &rcHeader);
|
|
/* GetWindowRect(infoPtr->hwndHeader, &rcHeader);*/
|
|
TRACE("start cxy=%d left=%d right=%d\n", hdi.cxy, rcHeader.left, rcHeader.right);
|
|
|
|
hdi.cxy = (rcHeader.right - rcHeader.left) - hdi.cxy;
|
|
}
|
|
else
|
|
hdi.cxy = lpColumn->cx;
|
|
}
|
|
|
|
if (lpColumn->mask & LVCF_TEXT)
|
|
{
|
|
hdi.mask |= HDI_TEXT | HDI_FORMAT;
|
|
hdi.pszText = lpColumn->pszText;
|
|
hdi.cchTextMax = textlenT(lpColumn->pszText, isW);
|
|
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 = SendMessageW(infoPtr->hwndHeader, HDM_INSERTITEMT(isW),
|
|
(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;
|
|
}
|
|
|
|
/* 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)
|
|
{
|
|
LONG lStyle = GetWindowLongW((HWND) lParam, GWL_STYLE);
|
|
LISTVIEW_ITEM* lv_first = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)first, 0 );
|
|
LISTVIEW_ITEM* lv_second = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)second, 0 );
|
|
INT cmpv = lstrcmpW( 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] LPLVITEMW : item information
|
|
* [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
|
|
*
|
|
* RETURN:
|
|
* SUCCESS : new item index
|
|
* FAILURE : -1
|
|
*/
|
|
static LRESULT LISTVIEW_InsertItemT(HWND hwnd, LPLVITEMW lpLVItem, BOOL isW)
|
|
{
|
|
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
|
|
LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
|
|
UINT uView = lStyle & LVS_TYPEMASK;
|
|
INT nItem = -1;
|
|
HDPA hdpaSubItems;
|
|
INT nItemWidth = 0;
|
|
LISTVIEW_ITEM *lpItem = NULL;
|
|
|
|
TRACE("(hwnd=%x, lpLVItem=%s, isW=%d)\n",
|
|
hwnd, debuglvitem_t(lpLVItem, isW), isW);
|
|
|
|
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)
|
|
{
|
|
if ( (lpItem = (LISTVIEW_ITEM *)COMCTL32_Alloc(sizeof(LISTVIEW_ITEM))) )
|
|
{
|
|
ZeroMemory(lpItem, sizeof(LISTVIEW_ITEM));
|
|
if (LISTVIEW_InitItemT(hwnd, lpItem, lpLVItem, isW))
|
|
{
|
|
/* insert item in listview control data structure */
|
|
if ( (hdpaSubItems = DPA_Create(8)) )
|
|
{
|
|
if ( (nItem = DPA_InsertPtr(hdpaSubItems, 0, lpItem)) != -1)
|
|
{
|
|
if ( ((lStyle & LVS_SORTASCENDING) || (lStyle & LVS_SORTDESCENDING))
|
|
&& !(lStyle & LVS_OWNERDRAWFIXED)
|
|
&& (LPSTR_TEXTCALLBACKW != 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)
|
|
{
|
|
NMLISTVIEW nmlv;
|
|
|
|
LISTVIEW_ShiftIndices(hwnd,nItem,1);
|
|
|
|
/* manage item focus */
|
|
if (lpLVItem->mask & LVIF_STATE)
|
|
{
|
|
lpItem->state &= ~(LVIS_FOCUSED|LVIS_SELECTED);
|
|
if (lpLVItem->stateMask & LVIS_SELECTED)
|
|
LISTVIEW_SetSelection(hwnd, nItem);
|
|
else if (lpLVItem->stateMask & LVIS_FOCUSED)
|
|
LISTVIEW_SetItemFocus(hwnd, nItem);
|
|
}
|
|
|
|
/* send LVN_INSERTITEM notification */
|
|
ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
|
|
nmlv.iItem = nItem;
|
|
nmlv.lParam = lpItem->lParam;
|
|
listview_notify(hwnd, LVN_INSERTITEM, &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;
|
|
}
|
|
|
|
/***
|
|
* 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 *)GetWindowLongW(hwnd, 0);
|
|
BOOL bResult = FALSE;
|
|
RECT rcItem;
|
|
INT i;
|
|
|
|
if (nFirst <= nLast)
|
|
{
|
|
if ((nFirst >= 0) && (nFirst < GETITEMCOUNT(infoPtr)))
|
|
{
|
|
if ((nLast >= 0) && (nLast < GETITEMCOUNT(infoPtr)))
|
|
{
|
|
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 *)GetWindowLongW(hwnd, 0);
|
|
|
|
if(infoPtr->clrBk!=clrBk){
|
|
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 *)GetWindowLongW(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] LPLVCOLUMNW : column attributes
|
|
* [I] isW: if TRUE, the lpColumn is a LPLVCOLUMNW,
|
|
* otherwise it is in fact a LPLVCOLUMNA
|
|
*
|
|
* RETURN:
|
|
* SUCCESS : TRUE
|
|
* FAILURE : FALSE
|
|
*/
|
|
static LRESULT LISTVIEW_SetColumnT(HWND hwnd, INT nColumn,
|
|
LPLVCOLUMNW lpColumn, BOOL isW)
|
|
{
|
|
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
|
|
BOOL bResult = FALSE;
|
|
HDITEMW hdi, hdiget;
|
|
|
|
if ((lpColumn != NULL) && (nColumn >= 0) &&
|
|
(nColumn < Header_GetItemCount(infoPtr->hwndHeader)))
|
|
{
|
|
/* initialize memory */
|
|
ZeroMemory(&hdi, sizeof(hdi));
|
|
|
|
if (lpColumn->mask & LVCF_FMT)
|
|
{
|
|
/* format member is valid */
|
|
hdi.mask |= HDI_FORMAT;
|
|
|
|
/* get current format first */
|
|
hdiget.mask = HDI_FORMAT;
|
|
if (Header_GetItemW(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 = textlenT(lpColumn->pszText, isW);
|
|
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 */
|
|
if (isW)
|
|
bResult = Header_SetItemW(infoPtr->hwndHeader, nColumn, &hdi);
|
|
else
|
|
bResult = Header_SetItemA(infoPtr->hwndHeader, nColumn, &hdi);
|
|
}
|
|
|
|
return bResult;
|
|
}
|
|
|
|
/***
|
|
* 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 *)GetWindowLongW(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;
|
|
HDITEMW hdi;
|
|
LRESULT lret;
|
|
LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
|
|
UINT uView = lStyle & LVS_TYPEMASK;
|
|
HDC hdc;
|
|
HFONT header_font;
|
|
HFONT old_font;
|
|
SIZE size;
|
|
WCHAR text_buffer[DISP_TEXT_SIZE];
|
|
INT header_item_count;
|
|
INT item_index;
|
|
INT nLabelWidth;
|
|
RECT rcHeader;
|
|
LVITEMW lvItem;
|
|
WCHAR szDispText[DISP_TEXT_SIZE];
|
|
|
|
/* make sure we can get the listview info */
|
|
if (!(infoPtr = (LISTVIEW_INFO *)GetWindowLongW(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);
|
|
|
|
TRACE("(hwnd=%x, iCol=%d, cx=%d\n", hwnd, iCol, cx);
|
|
|
|
/* 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 column to the width of the widest item */
|
|
if (iCol == 0 || uView == LVS_LIST)
|
|
{
|
|
cx = 0;
|
|
for(item_index = 0; item_index < GETITEMCOUNT(infoPtr); item_index++)
|
|
{
|
|
nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, item_index);
|
|
cx = (nLabelWidth>cx)?nLabelWidth:cx;
|
|
}
|
|
if (infoPtr->himlSmall)
|
|
cx += infoPtr->iconSize.cx + IMAGE_PADDING;
|
|
}
|
|
else
|
|
{
|
|
ZeroMemory(&lvItem, sizeof(lvItem));
|
|
lvItem.iSubItem = iCol;
|
|
lvItem.mask = LVIF_TEXT;
|
|
lvItem.cchTextMax = DISP_TEXT_SIZE;
|
|
lvItem.pszText = szDispText;
|
|
*lvItem.pszText = '\0';
|
|
cx = 0;
|
|
for(item_index = 0; item_index < GETITEMCOUNT(infoPtr); item_index++)
|
|
{
|
|
lvItem.iItem = item_index;
|
|
LISTVIEW_GetItemT(hwnd, &lvItem, FALSE, TRUE);
|
|
nLabelWidth = LISTVIEW_GetStringWidthT(hwnd, lvItem.pszText, TRUE);
|
|
cx = (nLabelWidth>cx)?nLabelWidth:cx;
|
|
}
|
|
}
|
|
cx += TRAILING_PADDING;
|
|
} /* 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_GetItemW(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
|
|
{
|
|
/* Despite what the MS docs say, if this is not the last
|
|
column, then MS resizes the column to the width of the
|
|
largest text string in the column, including headers
|
|
and items. This is different from LVSCW_AUTOSIZE in that
|
|
LVSCW_AUTOSIZE ignores the header string length.
|
|
*/
|
|
|
|
/* retrieve header font */
|
|
header_font = SendMessageW(infoPtr->hwndHeader, WM_GETFONT, 0L, 0L);
|
|
|
|
/* retrieve header text */
|
|
hdi.mask = HDI_TEXT;
|
|
hdi.cchTextMax = sizeof(text_buffer)/sizeof(text_buffer[0]);
|
|
hdi.pszText = text_buffer;
|
|
|
|
Header_GetItemW(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 */
|
|
|
|
GetTextExtentPoint32W(hdc, text_buffer, lstrlenW(text_buffer), &size);
|
|
|
|
SelectObject(hdc, old_font); /* restore the old font */
|
|
ReleaseDC(hwnd, hdc);
|
|
|
|
ZeroMemory(&lvItem, sizeof(lvItem));
|
|
lvItem.iSubItem = iCol;
|
|
lvItem.mask = LVIF_TEXT;
|
|
lvItem.cchTextMax = DISP_TEXT_SIZE;
|
|
lvItem.pszText = szDispText;
|
|
*lvItem.pszText = '\0';
|
|
cx = size.cx;
|
|
for(item_index = 0; item_index < GETITEMCOUNT(infoPtr); item_index++)
|
|
{
|
|
lvItem.iItem = item_index;
|
|
LISTVIEW_GetItemT(hwnd, &lvItem, FALSE, TRUE);
|
|
nLabelWidth = LISTVIEW_GetStringWidthT(hwnd, lvItem.pszText, TRUE);
|
|
nLabelWidth += TRAILING_PADDING;
|
|
/* While it is possible for subitems to have icons, even MS messes
|
|
up the positioning, so I suspect no applications actually use
|
|
them. */
|
|
if (item_index == 0 && infoPtr->himlSmall)
|
|
nLabelWidth += infoPtr->iconSize.cx + IMAGE_PADDING;
|
|
cx = (nLabelWidth>cx)?nLabelWidth:cx;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* call header to update the column change */
|
|
hdi.mask = HDI_WIDTH;
|
|
|
|
hdi.cxy = cx;
|
|
lret = Header_SetItemW(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 = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
|
|
DWORD 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 = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
|
|
INT 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 *)GetWindowLongW(hwnd, 0);
|
|
DWORD oldHoverTime = infoPtr->dwHoverTime;
|
|
|
|
infoPtr->dwHoverTime = dwHoverTime;
|
|
|
|
return oldHoverTime;
|
|
}
|
|
|
|
/***
|
|
* DESCRIPTION:
|
|
* Sets spacing for icons of LVS_ICON style.
|
|
*
|
|
* PARAMETER(S):
|
|
* [I] HWND : window handle
|
|
* [I] DWORD : MAKELONG(cx, cy)
|
|
*
|
|
* RETURN:
|
|
* MAKELONG(oldcx, oldcy)
|
|
*/
|
|
static LRESULT LISTVIEW_SetIconSpacing(HWND hwnd, DWORD spacing)
|
|
{
|
|
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
|
|
INT cy = HIWORD(spacing);
|
|
INT cx = LOWORD(spacing);
|
|
DWORD oldspacing;
|
|
LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
|
|
UINT uView = lStyle & LVS_TYPEMASK;
|
|
|
|
oldspacing = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
|
|
if (cx == -1) /* set to default */
|
|
cx = GetSystemMetrics(SM_CXICONSPACING);
|
|
if (cy == -1) /* set to default */
|
|
cy = GetSystemMetrics(SM_CYICONSPACING);
|
|
|
|
if (cx)
|
|
infoPtr->iconSpacing.cx = cx;
|
|
else
|
|
{ /* if 0 then compute width */
|
|
if (uView == LVS_ICON)
|
|
FIXME("width computation not yet done\n");
|
|
/*
|
|
* Should scan each item and determine max width of
|
|
* icon or label, then make that the width
|
|
*/
|
|
else /* FIXME: unknown computation for non LVS_ICON - this is a guess */
|
|
infoPtr->iconSpacing.cx = LISTVIEW_GetItemWidth(hwnd);
|
|
}
|
|
if (cy)
|
|
infoPtr->iconSpacing.cy = cy;
|
|
else
|
|
{ /* if 0 then compute height */
|
|
if (uView == LVS_ICON)
|
|
infoPtr->iconSpacing.cy = infoPtr->iconSize.cy + infoPtr->ntmHeight
|
|
+ ICON_BOTTOM_PADDING + ICON_TOP_PADDING + LABEL_VERT_OFFSET;
|
|
/* FIXME. I don't think so; I think it is based on twice the ntmHeight */
|
|
else /* FIXME: unknown computation for non LVS_ICON - this is a guess */
|
|
infoPtr->iconSpacing.cy = LISTVIEW_GetItemHeight(hwnd);
|
|
}
|
|
|
|
TRACE("old=(%d,%d), new=(%ld,%ld)\n", LOWORD(oldspacing), HIWORD(oldspacing),
|
|
infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
|
|
|
|
/* these depend on the iconSpacing */
|
|
infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
|
|
infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
|
|
|
|
return oldspacing;
|
|
}
|
|
|
|
/***
|
|
* 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 HIMAGELIST LISTVIEW_SetImageList(HWND hwnd, INT nType, HIMAGELIST himl)
|
|
{
|
|
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(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 himlOld;
|
|
}
|
|
|
|
/***
|
|
* 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*)GetWindowLongW(hwnd, 0);
|
|
|
|
TRACE("(hwnd=%x, nItems=%d, dwFlags=%lx)\n", hwnd, nItems, dwFlags);
|
|
|
|
if (GetWindowLongW(hwnd, GWL_STYLE) & LVS_OWNERDATA)
|
|
{
|
|
int precount,topvisible;
|
|
|
|
TRACE("LVS_OWNERDATA is set!\n");
|
|
if (dwFlags & (LVSICF_NOINVALIDATEALL | LVSICF_NOSCROLL))
|
|
FIXME("flags %s %s not implemented\n",
|
|
(dwFlags & LVSICF_NOINVALIDATEALL) ? "LVSICF_NOINVALIDATEALL"
|
|
: "",
|
|
(dwFlags & LVSICF_NOSCROLL) ? "LVSICF_NOSCROLL" : "");
|
|
|
|
/*
|
|
* 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;
|
|
|
|
infoPtr->nItemWidth = max(LISTVIEW_GetItemWidth(hwnd),
|
|
DEFAULT_COLUMN_WIDTH);
|
|
|
|
LISTVIEW_UpdateSize(hwnd);
|
|
LISTVIEW_UpdateScroll(hwnd);
|
|
|
|
if (min(precount,infoPtr->hdpaItems->nItemCount)<topvisible)
|
|
InvalidateRect(hwnd, NULL, TRUE);
|
|
}
|
|
else
|
|
{
|
|
/* According to MSDN for non-LVS_OWNERDATA this is just
|
|
* a performance issue. The control allocates its internal
|
|
* data structures for the number of items specified. It
|
|
* cuts down on the number of memory allocations. Therefore
|
|
* we will just issue a WARN here
|
|
*/
|
|
WARN("for non-ownerdata performance option not implemented.\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*)GetWindowLongW(hwnd, 0);
|
|
UINT lStyle = GetWindowLongW(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))
|
|
{
|
|
if ( (hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem)) )
|
|
{
|
|
if ( (lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)) )
|
|
{
|
|
POINT orig;
|
|
bResult = TRUE;
|
|
orig = lpItem->ptPosition;
|
|
if ((nPosX == -1) && (nPosY == -1))
|
|
{
|
|
/* This point value seems to be an undocumented feature. The
|
|
* best guess is that it means either at the origin, or at
|
|
* the true beginning of the list. I will assume the origin.
|
|
*/
|
|
POINT pt1;
|
|
if (!LISTVIEW_GetOrigin(hwnd, &pt1))
|
|
{
|
|
pt1.x = 0;
|
|
pt1.y = 0;
|
|
}
|
|
nPosX = pt1.x;
|
|
nPosY = pt1.y;
|
|
if (uView == LVS_ICON)
|
|
{
|
|
nPosX += (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
|
|
nPosY += ICON_TOP_PADDING;
|
|
}
|
|
TRACE("requested special (-1,-1), set to origin (%ld,%ld)\n",
|
|
nPosX, nPosY);
|
|
}
|
|
|
|
lpItem->ptPosition.x = nPosX;
|
|
lpItem->ptPosition.y = nPosY;
|
|
if (uView == LVS_ICON)
|
|
{
|
|
lpItem->ptPosition.y -= ICON_TOP_PADDING;
|
|
lpItem->ptPosition.x -= (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
|
|
if ((lpItem->ptPosition.y < 0) || (lpItem->ptPosition.x < 0))
|
|
{
|
|
FIXME("failed orig (%ld,%ld), intent (%ld,%ld), is (%ld, %ld), setting neg to 0\n",
|
|
orig.x, orig.y, nPosX, nPosY, lpItem->ptPosition.x, lpItem->ptPosition.y);
|
|
|
|
/*
|
|
if (lpItem->ptPosition.x < 0) lpItem->ptPosition.x = 0;
|
|
if (lpItem->ptPosition.y < 0) lpItem->ptPosition.y = 0;
|
|
*/
|
|
}
|
|
else
|
|
{
|
|
TRACE("orig (%ld,%ld), intent (%ld,%ld), is (%ld,%ld)\n",
|
|
orig.x, orig.y, nPosX, nPosY, lpItem->ptPosition.x, lpItem->ptPosition.y);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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, LPLVITEMW lpLVItem)
|
|
{
|
|
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
|
|
BOOL bResult = TRUE;
|
|
LVITEMW lvItem;
|
|
|
|
TRACE("(hwnd=%x, nItem=%d, lpLVItem=%s)\n",
|
|
hwnd, nItem, debuglvitem_t(lpLVItem, TRUE));
|
|
|
|
ZeroMemory(&lvItem, sizeof(lvItem));
|
|
lvItem.mask = LVIF_STATE;
|
|
lvItem.state = lpLVItem->state;
|
|
lvItem.stateMask = lpLVItem->stateMask ;
|
|
lvItem.iItem = nItem;
|
|
|
|
if (nItem == -1)
|
|
{
|
|
/* apply to all items */
|
|
for (lvItem.iItem = 0; lvItem.iItem < GETITEMCOUNT(infoPtr); lvItem.iItem++)
|
|
if (!ListView_SetItemW(hwnd, &lvItem)) bResult = FALSE;
|
|
}
|
|
else
|
|
bResult = ListView_SetItemW(hwnd, &lvItem);
|
|
|
|
return bResult;
|
|
}
|
|
|
|
/***
|
|
* DESCRIPTION:
|
|
* Sets the text of an item or subitem.
|
|
*
|
|
* PARAMETER(S):
|
|
* [I] hwnd : window handle
|
|
* [I] nItem : item index
|
|
* [I] lpLVItem : item or subitem info
|
|
* [I] isW : TRUE if input is Unicode
|
|
*
|
|
* RETURN:
|
|
* SUCCESS : TRUE
|
|
* FAILURE : FALSE
|
|
*/
|
|
static BOOL LISTVIEW_SetItemTextT(HWND hwnd, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
|
|
{
|
|
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
|
|
BOOL bResult = FALSE;
|
|
LVITEMW lvItem;
|
|
|
|
TRACE("(hwnd=%x, nItem=%d, lpLVItem=%s, isW=%d)\n",
|
|
hwnd, nItem, debuglvitem_t(lpLVItem, isW), isW);
|
|
|
|
if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
|
|
{
|
|
ZeroMemory(&lvItem, sizeof(LVITEMW));
|
|
lvItem.mask = LVIF_TEXT;
|
|
lvItem.pszText = lpLVItem->pszText;
|
|
lvItem.iItem = nItem;
|
|
lvItem.iSubItem = lpLVItem->iSubItem;
|
|
if(isW) bResult = ListView_SetItemW(hwnd, &lvItem);
|
|
else bResult = ListView_SetItemA(hwnd, &lvItem);
|
|
}
|
|
|
|
return bResult;
|
|
}
|
|
|
|
/***
|
|
* 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 *)GetWindowLongW(hwnd, 0);
|
|
INT nOldIndex = infoPtr->nSelectionMark;
|
|
|
|
TRACE("(hwnd=%x, nIndex=%d)\n", hwnd, nIndex);
|
|
|
|
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 *)GetWindowLongW(hwnd, 0);
|
|
|
|
TRACE("(hwnd=%x, clrTextBk=%lx)\n", hwnd, clrTextBk);
|
|
|
|
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 *)GetWindowLongW(hwnd, 0);
|
|
|
|
TRACE("(hwnd=%x, clrText=%lx)\n", hwnd, clrText);
|
|
|
|
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)
|
|
{
|
|
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW((HWND)lParam, 0);
|
|
LISTVIEW_ITEM* lv_first = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)first, 0 );
|
|
LISTVIEW_ITEM* lv_second = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)second, 0 );
|
|
|
|
/* Forward the call to the client defined callback */
|
|
return (infoPtr->pfnCompare)( lv_first->lParam , lv_second->lParam, infoPtr->lParamSort );
|
|
}
|
|
|
|
/***
|
|
* 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, PFNLVCOMPARE pfnCompare, LPARAM lParamSort)
|
|
{
|
|
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
|
|
UINT lStyle = GetWindowLongW(hwnd, GWL_STYLE);
|
|
HDPA hdpaSubItems=NULL;
|
|
LISTVIEW_ITEM *pLVItem=NULL;
|
|
LPVOID selectionMarkItem;
|
|
int nCount, i;
|
|
|
|
TRACE("(hwnd=%x, pfnCompare=%p, lParamSort=%lx)\n", hwnd, pfnCompare, lParamSort);
|
|
|
|
if (lStyle & LVS_OWNERDATA) return FALSE;
|
|
|
|
if (!infoPtr || !infoPtr->hdpaItems) return FALSE;
|
|
|
|
nCount = GETITEMCOUNT(infoPtr);
|
|
/* if there are 0 or 1 items, there is no need to sort */
|
|
if (nCount < 2)
|
|
return TRUE;
|
|
|
|
infoPtr->pfnCompare = pfnCompare;
|
|
infoPtr->lParamSort = lParamSort;
|
|
DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompare, hwnd);
|
|
|
|
/* Adjust selections and indices so that they are the way they should
|
|
* be after the sort (otherwise, the list items move around, but
|
|
* whatever is at the item's previous original position will be
|
|
* selected instead)
|
|
*/
|
|
selectionMarkItem=(infoPtr->nSelectionMark>=0)?DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nSelectionMark):NULL;
|
|
for (i=0; i < nCount; i++)
|
|
{
|
|
hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
|
|
pLVItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
|
|
|
|
if (pLVItem->state & LVIS_SELECTED)
|
|
LISTVIEW_AddSelectionRange(hwnd, i, i);
|
|
else
|
|
LISTVIEW_RemoveSelectionRange(hwnd, i, i);
|
|
if (pLVItem->state & LVIS_FOCUSED)
|
|
infoPtr->nFocusedItem=i;
|
|
}
|
|
if (selectionMarkItem != NULL)
|
|
infoPtr->nSelectionMark = DPA_GetPtrIndex(infoPtr->hdpaItems, selectionMarkItem);
|
|
/* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
|
|
|
|
/* align the items */
|
|
LISTVIEW_AlignTop(hwnd);
|
|
|
|
/* refresh the display */
|
|
InvalidateRect(hwnd, NULL, TRUE);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/* LISTVIEW_SubItemHitTest */
|
|
|
|
/***
|
|
* DESCRIPTION:
|
|
* Updates an items or rearranges the listview control.
|
|
*
|
|
* PARAMETER(S):
|
|
* [I] HWND : window handle
|
|
* [I] INT : item index
|
|
*
|
|
* RETURN:
|
|
* SUCCESS : TRUE
|
|
* FAILURE : FALSE
|
|
*/
|
|
static LRESULT LISTVIEW_Update(HWND hwnd, INT nItem)
|
|
{
|
|
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
|
|
LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
|
|
BOOL bResult = FALSE;
|
|
RECT rc;
|
|
|
|
TRACE("(hwnd=%x, nItem=%d)\n", hwnd, nItem);
|
|
|
|
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, LPCREATESTRUCTW lpcs)
|
|
{
|
|
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
|
|
UINT uView = lpcs->style & LVS_TYPEMASK;
|
|
LOGFONTW logFont;
|
|
|
|
TRACE("(hwnd=%x, lpcs=%p)\n", hwnd, lpcs);
|
|
|
|
/* initialize info pointer */
|
|
ZeroMemory(infoPtr, sizeof(LISTVIEW_INFO));
|
|
|
|
/* determine the type of structures to use */
|
|
infoPtr->notifyFormat = SendMessageW(GetParent(hwnd), WM_NOTIFYFORMAT,
|
|
(WPARAM)hwnd, (LPARAM)NF_QUERY);
|
|
|
|
/* initialize color information */
|
|
infoPtr->clrBk = GetSysColor(COLOR_WINDOW);
|
|
infoPtr->clrText = GetSysColor(COLOR_WINDOWTEXT);
|
|
infoPtr->clrTextBk = CLR_DEFAULT;
|
|
|
|
/* set default values */
|
|
infoPtr->hwndSelf = hwnd;
|
|
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;
|
|
infoPtr->bIsDrawing = FALSE;
|
|
|
|
/* get default font (icon title) */
|
|
SystemParametersInfoW(SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
|
|
infoPtr->hDefaultFont = CreateFontIndirectW(&logFont);
|
|
infoPtr->hFont = infoPtr->hDefaultFont;
|
|
LISTVIEW_SaveTextMetrics(hwnd);
|
|
|
|
/* create header */
|
|
infoPtr->hwndHeader = CreateWindowW(WC_HEADERW, (LPCWSTR)NULL,
|
|
WS_CHILD | HDS_HORZ | (DWORD)((LVS_NOSORTHEADER & lpcs->style)?0:HDS_BUTTONS),
|
|
0, 0, 0, 0, hwnd, (HMENU)0,
|
|
lpcs->hInstance, NULL);
|
|
|
|
/* set header unicode format */
|
|
SendMessageW(infoPtr->hwndHeader, HDM_SETUNICODEFORMAT,(WPARAM)TRUE,(LPARAM)NULL);
|
|
|
|
/* set header font */
|
|
SendMessageW(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 */
|
|
SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE,
|
|
GetWindowLongW(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 *)GetWindowLongW(hwnd, 0);
|
|
BOOL bResult;
|
|
|
|
TRACE("(hwnd=%x, wParam=%x, lParam=%lx)\n", hwnd, wParam, lParam);
|
|
|
|
if (infoPtr->clrBk == CLR_NONE)
|
|
{
|
|
bResult = SendMessageW(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 *)GetWindowLongW(hwnd, 0);
|
|
|
|
TRACE("(hwnd=%x, hdc=%x, rc=%p)\n", hwnd, hdc, rc);
|
|
|
|
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 *)GetWindowLongW(hwnd, 0);
|
|
|
|
TRACE("(hwnd=%x)\n", hwnd);
|
|
|
|
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_THUMBPOSITION
|
|
* or SB_THUMBTRACK.
|
|
* [I] HWND : scrollbar control window handle
|
|
*
|
|
* RETURN:
|
|
* Zero
|
|
*/
|
|
static LRESULT LISTVIEW_VScroll(HWND hwnd, INT nScrollCode, SHORT nCurrentPos,
|
|
HWND hScrollWnd)
|
|
{
|
|
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
|
|
SCROLLINFO scrollInfo;
|
|
|
|
TRACE("(hwnd=%x, nScrollCode=%d, nCurrentPos=%d, hScrollWnd=%x)\n",
|
|
hwnd, nScrollCode, nCurrentPos, hScrollWnd);
|
|
|
|
SendMessageW(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_THUMBPOSITION:
|
|
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);
|
|
if (IsWindowVisible(infoPtr->hwndHeader))
|
|
{
|
|
RECT rListview, rcHeader, rDest;
|
|
GetClientRect(hwnd, &rListview);
|
|
GetWindowRect(infoPtr->hwndHeader, &rcHeader);
|
|
MapWindowPoints((HWND) NULL, hwnd, (LPPOINT) &rcHeader, 2);
|
|
SubtractRect(&rDest, &rListview, &rcHeader);
|
|
InvalidateRect(hwnd, &rDest, TRUE);
|
|
}
|
|
else
|
|
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)
|
|
{
|
|
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
|
|
SCROLLINFO scrollInfo;
|
|
|
|
TRACE("(hwnd=%x, nScrollCode=%d, nCurrentPos=%d, hScrollWnd=%x)\n",
|
|
hwnd, nScrollCode, nCurrentPos, hScrollWnd);
|
|
|
|
SendMessageW(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_THUMBPOSITION:
|
|
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 = GetWindowLongW(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)
|
|
{
|
|
UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
|
|
INT gcWheelDelta = 0;
|
|
UINT pulScrollLines = 3;
|
|
SCROLLINFO scrollInfo;
|
|
|
|
TRACE("(hwnd=%x, wheelDelta=%d)\n", hwnd, wheelDelta);
|
|
|
|
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 *)GetWindowLongW(hwnd, 0);
|
|
UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
|
|
INT nItem = -1;
|
|
NMLVKEYDOWN nmKeyDown;
|
|
|
|
TRACE("(hwnd=%x, nVirtualKey=%d, lKeyData=%ld)\n", hwnd, nVirtualKey, lKeyData);
|
|
|
|
/* send LVN_KEYDOWN notification */
|
|
nmKeyDown.wVKey = nVirtualKey;
|
|
nmKeyDown.flags = 0;
|
|
notify(hwnd, LVN_KEYDOWN, &nmKeyDown.hdr);
|
|
|
|
switch (nVirtualKey)
|
|
{
|
|
case VK_RETURN:
|
|
if ((GETITEMCOUNT(infoPtr) > 0) && (infoPtr->nFocusedItem != -1))
|
|
{
|
|
hdr_notify(hwnd, NM_RETURN); /* NM_RETURN notification */
|
|
hdr_notify(hwnd, LVN_ITEMACTIVATE); /* LVN_ITEMACTIVATE notification */
|
|
}
|
|
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))
|
|
{
|
|
if (LISTVIEW_KeySelection(hwnd, nItem))
|
|
UpdateWindow(hwnd); /* update client area */
|
|
}
|
|
|
|
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*)GetWindowLongW(hwnd, 0);
|
|
UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
|
|
INT i,nTop,nBottom;
|
|
|
|
TRACE("(hwnd=%x)\n", hwnd);
|
|
|
|
/* send NM_KILLFOCUS notification */
|
|
hdr_notify(hwnd, NM_KILLFOCUS);
|
|
|
|
/* 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))
|
|
{
|
|
RECT rcItem;
|
|
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)
|
|
{
|
|
LVHITTESTINFO htInfo;
|
|
NMLISTVIEW nmlv;
|
|
|
|
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));
|
|
if (LISTVIEW_HitTestItem(hwnd, &htInfo, TRUE) != -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_notify(hwnd, NM_DBLCLK, &nmlv);
|
|
|
|
|
|
/* To send the LVN_ITEMACTIVATE, it must be on an Item */
|
|
if(nmlv.iItem != -1)
|
|
hdr_notify(hwnd, LVN_ITEMACTIVATE);
|
|
|
|
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 *)GetWindowLongW(hwnd, 0);
|
|
LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
|
|
static BOOL bGroupSelect = TRUE;
|
|
POINT ptPosition;
|
|
INT nItem;
|
|
|
|
TRACE("(hwnd=%x, key=%hu, X=%hu, Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
|
|
|
|
/* send NM_RELEASEDCAPTURE notification */
|
|
hdr_notify(hwnd, NM_RELEASEDCAPTURE);
|
|
|
|
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)
|
|
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 *)GetWindowLongW(hwnd, 0);
|
|
|
|
TRACE("(hwnd=%x, key=%hu, X=%hu, Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
|
|
|
|
if (infoPtr->bLButtonDown != FALSE)
|
|
{
|
|
LVHITTESTINFO lvHitTestInfo;
|
|
NMLISTVIEW nmlv;
|
|
|
|
lvHitTestInfo.pt.x = wPosX;
|
|
lvHitTestInfo.pt.y = wPosY;
|
|
|
|
/* send NM_CLICK notification */
|
|
ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
|
|
if (LISTVIEW_HitTestItem(hwnd, &lvHitTestInfo, TRUE) != -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_notify(hwnd, NM_CLICK, &nmlv);
|
|
|
|
/* set left button flag */
|
|
infoPtr->bLButtonDown = FALSE;
|
|
|
|
if(infoPtr->nEditLabelItem != -1)
|
|
{
|
|
if(lvHitTestInfo.iItem == infoPtr->nEditLabelItem && lvHitTestInfo.flags & LVHT_ONITEMLABEL)
|
|
LISTVIEW_EditLabelT(hwnd, lvHitTestInfo.iItem, TRUE);
|
|
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));
|
|
if (infoPtr == NULL)
|
|
{
|
|
ERR("could not allocate info memory!\n");
|
|
return 0;
|
|
}
|
|
|
|
SetWindowLongW(hwnd, 0, (LONG)infoPtr);
|
|
if ((LISTVIEW_INFO *)GetWindowLongW(hwnd, 0) != infoPtr)
|
|
{
|
|
ERR("pointer assignment error!\n");
|
|
return 0;
|
|
}
|
|
|
|
return DefWindowProcW(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 *)GetWindowLongW(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);
|
|
|
|
SetWindowLongW(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 *)GetWindowLongW(hwnd, 0);
|
|
|
|
TRACE("(hwnd=%x, nCtrlId=%d, lpnmh=%p)\n", hwnd, nCtrlId, lpnmh);
|
|
|
|
if (lpnmh->hwndFrom == infoPtr->hwndHeader)
|
|
{
|
|
/* handle notification from header control */
|
|
if (lpnmh->code == HDN_ENDTRACKW)
|
|
{
|
|
infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
|
|
InvalidateRect(hwnd, NULL, TRUE);
|
|
}
|
|
else if(lpnmh->code == HDN_ITEMCLICKW || lpnmh->code == HDN_ITEMCLICKA)
|
|
{
|
|
/* Handle sorting by Header Column */
|
|
NMLISTVIEW nmlv;
|
|
|
|
ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
|
|
nmlv.iItem = -1;
|
|
nmlv.iSubItem = ((LPNMHEADERW)lpnmh)->iItem;
|
|
listview_notify(hwnd, LVN_COLUMNCLICK, &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 *)GetWindowLongW(hwnd, 0);
|
|
|
|
TRACE("(hwndFrom=%x, hwnd=%x, nCommand=%d)\n", hwndFrom, hwnd, nCommand);
|
|
|
|
if (nCommand == NF_REQUERY)
|
|
infoPtr->notifyFormat = SendMessageW(hwndFrom, WM_NOTIFYFORMAT,
|
|
(WPARAM)hwnd, (LPARAM)NF_QUERY);
|
|
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)
|
|
{
|
|
TRACE("(hwnd=%x,key=%hu,X=%hu,Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
|
|
|
|
/* send NM_RELEASEDCAPTURE notification */
|
|
hdr_notify(hwnd, NM_RELEASEDCAPTURE);
|
|
|
|
/* send NM_RDBLCLK notification */
|
|
hdr_notify(hwnd, NM_RDBLCLK);
|
|
|
|
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 *)GetWindowLongW(hwnd, 0);
|
|
POINT ptPosition;
|
|
INT nItem;
|
|
NMLISTVIEW nmlv;
|
|
LVHITTESTINFO lvHitTestInfo;
|
|
|
|
TRACE("(hwnd=%x,key=%hu,X=%hu,Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
|
|
|
|
/* send NM_RELEASEDCAPTURE notification */
|
|
hdr_notify(hwnd, NM_RELEASEDCAPTURE);
|
|
|
|
/* 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);
|
|
}
|
|
|
|
lvHitTestInfo.pt.x = wPosX;
|
|
lvHitTestInfo.pt.y = wPosY;
|
|
|
|
/* Send NM_RClICK notification */
|
|
ZeroMemory(&nmlv, sizeof(nmlv));
|
|
if (LISTVIEW_HitTestItem(hwnd, &lvHitTestInfo, TRUE) != -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_notify(hwnd, NM_RCLICK, &nmlv);
|
|
|
|
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 *)GetWindowLongW(hwnd, 0);
|
|
|
|
TRACE("(hwnd=%x,key=%hu,X=%hu,Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
|
|
|
|
if (infoPtr->bRButtonDown)
|
|
{
|
|
POINT pt;
|
|
|
|
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 */
|
|
SendMessageW( 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 *)GetWindowLongW(hwnd, 0);
|
|
|
|
TRACE("(hwnd=%x, hwndLoseFocus=%x)\n", hwnd, hwndLoseFocus);
|
|
|
|
/* send NM_SETFOCUS notification */
|
|
hdr_notify(hwnd, NM_SETFOCUS);
|
|
|
|
/* 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 *)GetWindowLongW(hwnd, 0);
|
|
UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
|
|
|
|
TRACE("(hwnd=%x,hfont=%x,redraw=%hu)\n", hwnd, hFont, fRedraw);
|
|
|
|
infoPtr->hFont = hFont ? hFont : infoPtr->hDefaultFont;
|
|
LISTVIEW_SaveTextMetrics(hwnd);
|
|
|
|
if (uView == LVS_REPORT)
|
|
{
|
|
/* set header font */
|
|
SendMessageW(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 = DefWindowProcW(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 = GetWindowLongW(hwnd, GWL_STYLE);
|
|
UINT uView = lStyle & LVS_TYPEMASK;
|
|
|
|
TRACE("(hwnd=%x, width=%d, height=%d)\n", hwnd, Width, Height);
|
|
|
|
if (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 if no size change
|
|
* 1 of size changed
|
|
*/
|
|
static BOOL LISTVIEW_UpdateSize(HWND hwnd)
|
|
{
|
|
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
|
|
LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
|
|
UINT uView = lStyle & LVS_TYPEMASK;
|
|
RECT rcList;
|
|
RECT rcOld;
|
|
|
|
TRACE("(hwnd=%x)\n", hwnd);
|
|
|
|
GetClientRect(hwnd, &rcList);
|
|
CopyRect(&rcOld,&(infoPtr->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 = ℘
|
|
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);
|
|
}
|
|
return (EqualRect(&rcOld,&(infoPtr->rcList)));
|
|
}
|
|
|
|
/***
|
|
* 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 *)GetWindowLongW(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)&&
|
|
((lpss->styleNew & WS_HSCROLL) == 0))
|
|
ShowScrollBar(hwnd, SB_HORZ, FALSE);
|
|
|
|
if (((lpss->styleOld & WS_VSCROLL) != 0)&&
|
|
((lpss->styleNew & 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 = ℘
|
|
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)))
|
|
{
|
|
SendMessageW(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 (!GetWindowLongW(hwnd, 0) && (uMsg != WM_NCCREATE))
|
|
return DefWindowProcW( 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:
|
|
return LISTVIEW_EditLabelT(hwnd, (INT)wParam, TRUE);
|
|
|
|
case LVM_EDITLABELA:
|
|
return LISTVIEW_EditLabelT(hwnd, (INT)wParam, FALSE);
|
|
|
|
case LVM_ENSUREVISIBLE:
|
|
return LISTVIEW_EnsureVisible(hwnd, (INT)wParam, (BOOL)lParam);
|
|
|
|
case LVM_FINDITEMW:
|
|
return LISTVIEW_FindItemW(hwnd, (INT)wParam, (LPLVFINDINFOW)lParam);
|
|
|
|
case LVM_FINDITEMA:
|
|
return LISTVIEW_FindItemA(hwnd, (INT)wParam, (LPLVFINDINFOA)lParam);
|
|
|
|
case LVM_GETBKCOLOR:
|
|
return LISTVIEW_GetBkColor(hwnd);
|
|
|
|
/* case LVM_GETBKIMAGE: */
|
|
|
|
case LVM_GETCALLBACKMASK:
|
|
return LISTVIEW_GetCallbackMask(hwnd);
|
|
|
|
case LVM_GETCOLUMNA:
|
|
return LISTVIEW_GetColumnT(hwnd, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
|
|
|
|
case LVM_GETCOLUMNW:
|
|
return LISTVIEW_GetColumnT(hwnd, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
|
|
|
|
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:
|
|
FIXME("LVM_GETHOTCURSOR: unimplemented\n");
|
|
return FALSE;
|
|
|
|
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_GETISEARCHSTRINGA:
|
|
case LVM_GETISEARCHSTRINGW:
|
|
FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
|
|
return FALSE;
|
|
|
|
case LVM_GETITEMA:
|
|
return LISTVIEW_GetItemT(hwnd, (LPLVITEMW)lParam, FALSE, FALSE);
|
|
|
|
case LVM_GETITEMW:
|
|
return LISTVIEW_GetItemT(hwnd, (LPLVITEMW)lParam, FALSE, TRUE);
|
|
|
|
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:
|
|
return LISTVIEW_GetItemTextT(hwnd, (INT)wParam, (LPLVITEMW)lParam, FALSE);
|
|
|
|
case LVM_GETITEMTEXTW:
|
|
return LISTVIEW_GetItemTextT(hwnd, (INT)wParam, (LPLVITEMW)lParam, TRUE);
|
|
|
|
case LVM_GETNEXTITEM:
|
|
return LISTVIEW_GetNextItem(hwnd, (INT)wParam, LOWORD(lParam));
|
|
|
|
case LVM_GETNUMBEROFWORKAREAS:
|
|
FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
|
|
return 1;
|
|
|
|
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_GetStringWidthT(hwnd, (LPCWSTR)lParam, FALSE);
|
|
|
|
case LVM_GETSTRINGWIDTHW:
|
|
return LISTVIEW_GetStringWidthT(hwnd, (LPCWSTR)lParam, TRUE);
|
|
|
|
case LVM_GETSUBITEMRECT:
|
|
return LISTVIEW_GetSubItemRect(hwnd, (UINT)wParam, ((LPRECT)lParam)->top,
|
|
((LPRECT)lParam)->left, (LPRECT)lParam);
|
|
|
|
case LVM_GETTEXTBKCOLOR:
|
|
return LISTVIEW_GetTextBkColor(hwnd);
|
|
|
|
case LVM_GETTEXTCOLOR:
|
|
return LISTVIEW_GetTextColor(hwnd);
|
|
|
|
case LVM_GETTOOLTIPS:
|
|
FIXME("LVM_GETTOOLTIPS: unimplemented\n");
|
|
return FALSE;
|
|
|
|
case LVM_GETTOPINDEX:
|
|
return LISTVIEW_GetTopIndex(hwnd);
|
|
|
|
/*case LVM_GETUNICODEFORMAT:
|
|
FIXME("LVM_GETUNICODEFORMAT: unimplemented\n");
|
|
return FALSE;*/
|
|
|
|
case LVM_GETVIEWRECT:
|
|
return LISTVIEW_GetViewRect(hwnd, (LPRECT)lParam);
|
|
|
|
case LVM_GETWORKAREAS:
|
|
FIXME("LVM_GETWORKAREAS: unimplemented\n");
|
|
return FALSE;
|
|
|
|
case LVM_HITTEST:
|
|
return LISTVIEW_HitTest(hwnd, (LPLVHITTESTINFO)lParam);
|
|
|
|
case LVM_INSERTCOLUMNA:
|
|
return LISTVIEW_InsertColumnT(hwnd, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
|
|
|
|
case LVM_INSERTCOLUMNW:
|
|
return LISTVIEW_InsertColumnT(hwnd, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
|
|
|
|
case LVM_INSERTITEMA:
|
|
return LISTVIEW_InsertItemT(hwnd, (LPLVITEMW)lParam, FALSE);
|
|
|
|
case LVM_INSERTITEMW:
|
|
return LISTVIEW_InsertItemT(hwnd, (LPLVITEMW)lParam, TRUE);
|
|
|
|
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_SetColumnT(hwnd, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
|
|
|
|
case LVM_SETCOLUMNW:
|
|
return LISTVIEW_SetColumnT(hwnd, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
|
|
|
|
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:
|
|
return LISTVIEW_SetIconSpacing(hwnd, (DWORD)lParam);
|
|
|
|
case LVM_SETIMAGELIST:
|
|
return (LRESULT)LISTVIEW_SetImageList(hwnd, (INT)wParam, (HIMAGELIST)lParam);
|
|
|
|
case LVM_SETITEMA:
|
|
return LISTVIEW_SetItemT(hwnd, (LPLVITEMW)lParam, FALSE);
|
|
|
|
case LVM_SETITEMW:
|
|
return LISTVIEW_SetItemT(hwnd, (LPLVITEMW)lParam, TRUE);
|
|
|
|
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, (LPLVITEMW)lParam);
|
|
|
|
case LVM_SETITEMTEXTA:
|
|
return LISTVIEW_SetItemTextT(hwnd, (INT)wParam, (LPLVITEMW)lParam, FALSE);
|
|
|
|
case LVM_SETITEMTEXTW:
|
|
return LISTVIEW_SetItemTextT(hwnd, (INT)wParam, (LPLVITEMW)lParam, TRUE);
|
|
|
|
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, (PFNLVCOMPARE)lParam, (LPARAM)wParam);
|
|
|
|
/* 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, (LPCREATESTRUCTW)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 DefWindowProcW( 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 DefWindowProcW(hwnd, uMsg, wParam, lParam);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/***
|
|
* DESCRIPTION:
|
|
* Registers the window class.
|
|
*
|
|
* PARAMETER(S):
|
|
* None
|
|
*
|
|
* RETURN:
|
|
* None
|
|
*/
|
|
VOID LISTVIEW_Register(void)
|
|
{
|
|
WNDCLASSW wndClass;
|
|
|
|
ZeroMemory(&wndClass, sizeof(WNDCLASSW));
|
|
wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
|
|
wndClass.lpfnWndProc = (WNDPROC)LISTVIEW_WindowProc;
|
|
wndClass.cbClsExtra = 0;
|
|
wndClass.cbWndExtra = sizeof(LISTVIEW_INFO *);
|
|
wndClass.hCursor = LoadCursorW(0, IDC_ARROWW);
|
|
wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
|
|
wndClass.lpszClassName = WC_LISTVIEWW;
|
|
RegisterClassW(&wndClass);
|
|
}
|
|
|
|
/***
|
|
* DESCRIPTION:
|
|
* Unregisters the window class.
|
|
*
|
|
* PARAMETER(S):
|
|
* None
|
|
*
|
|
* RETURN:
|
|
* None
|
|
*/
|
|
VOID LISTVIEW_Unregister(void)
|
|
{
|
|
UnregisterClassW(WC_LISTVIEWW, (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
|
|
*/
|
|
WCHAR buffer[1024];
|
|
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
|
|
HDC hdc = GetDC(infoPtr->hwndEdit);
|
|
HFONT hFont, hOldFont = 0;
|
|
RECT rect;
|
|
SIZE sz;
|
|
int len;
|
|
|
|
len = GetWindowTextW(infoPtr->hwndEdit, buffer, sizeof(buffer)/sizeof(buffer[0]));
|
|
GetWindowRect(infoPtr->hwndEdit, &rect);
|
|
|
|
/* Select font to get the right dimension of the string */
|
|
hFont = SendMessageW(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
|
|
if(hFont != 0)
|
|
{
|
|
hOldFont = SelectObject(hdc, hFont);
|
|
}
|
|
|
|
if (GetTextExtentPoint32W(hdc, buffer, lstrlenW(buffer), &sz))
|
|
{
|
|
TEXTMETRICW textMetric;
|
|
|
|
/* Add Extra spacing for the next character */
|
|
GetTextMetricsW(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 SendMessageW (GetParent (hwnd), WM_COMMAND, wParam, lParam);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/***
|
|
* DESCRIPTION:
|
|
* Subclassed edit control windproc function
|
|
*
|
|
* PARAMETER(S):
|
|
*
|
|
* RETURN:
|
|
*/
|
|
static LRESULT EditLblWndProcT(HWND hwnd, UINT uMsg,
|
|
WPARAM wParam, LPARAM lParam, BOOL isW)
|
|
{
|
|
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(GetParent(hwnd), 0);
|
|
EDITLABEL_ITEM *einfo = infoPtr->pedititem;
|
|
static BOOL bIgnoreKillFocus = FALSE;
|
|
BOOL cancel = FALSE;
|
|
|
|
TRACE("(hwnd=%x, uMsg=%x, wParam=%x, lParam=%lx, isW=%d)\n",
|
|
hwnd, uMsg, wParam, lParam, isW);
|
|
|
|
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;
|
|
SetWindowLongW(hwnd, GWL_WNDPROC, (LONG)editProc);
|
|
COMCTL32_Free(einfo);
|
|
infoPtr->pedititem = NULL;
|
|
return CallWindowProcT(editProc, hwnd, uMsg, wParam, lParam, isW);
|
|
}
|
|
|
|
case WM_KEYDOWN:
|
|
if (VK_ESCAPE == (INT)wParam)
|
|
{
|
|
cancel = TRUE;
|
|
break;
|
|
}
|
|
else if (VK_RETURN == (INT)wParam)
|
|
break;
|
|
|
|
default:
|
|
return CallWindowProcT(einfo->EditWndProc, hwnd, uMsg, wParam, lParam, isW);
|
|
}
|
|
|
|
if (einfo->EditLblCb)
|
|
{
|
|
LPWSTR buffer = NULL;
|
|
|
|
if (!cancel)
|
|
{
|
|
DWORD len = isW ? GetWindowTextLengthW(hwnd) : GetWindowTextLengthA(hwnd);
|
|
|
|
if (len)
|
|
{
|
|
if ( (buffer = COMCTL32_Alloc((len+1) * (isW ? sizeof(WCHAR) : sizeof(CHAR)))) )
|
|
{
|
|
if (isW) GetWindowTextW(hwnd, buffer, len+1);
|
|
else GetWindowTextA(hwnd, (CHAR*)buffer, len+1);
|
|
}
|
|
}
|
|
}
|
|
/* 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;
|
|
}
|
|
|
|
SendMessageW(hwnd, WM_CLOSE, 0, 0);
|
|
return TRUE;
|
|
}
|
|
|
|
/***
|
|
* DESCRIPTION:
|
|
* Subclassed edit control windproc function
|
|
*
|
|
* PARAMETER(S):
|
|
*
|
|
* RETURN:
|
|
*/
|
|
LRESULT CALLBACK EditLblWndProcW(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
return EditLblWndProcT(hwnd, uMsg, wParam, lParam, TRUE);
|
|
}
|
|
|
|
/***
|
|
* DESCRIPTION:
|
|
* Subclassed edit control windproc function
|
|
*
|
|
* PARAMETER(S):
|
|
*
|
|
* RETURN:
|
|
*/
|
|
LRESULT CALLBACK EditLblWndProcA(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
return EditLblWndProcT(hwnd, uMsg, wParam, lParam, FALSE);
|
|
}
|
|
|
|
/***
|
|
* DESCRIPTION:
|
|
* Creates a subclassed edit cotrol
|
|
*
|
|
* PARAMETER(S):
|
|
*
|
|
* RETURN:
|
|
*/
|
|
HWND CreateEditLabelT(LPCWSTR text, DWORD style, INT x, INT y,
|
|
INT width, INT height, HWND parent, HINSTANCE hinst,
|
|
EditlblCallbackW EditLblCb, DWORD param, BOOL isW)
|
|
{
|
|
LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(parent, 0);
|
|
WCHAR editName[5] = { 'E', 'd', 'i', 't', '\0' };
|
|
HWND hedit;
|
|
SIZE sz;
|
|
HDC hdc;
|
|
HDC hOldFont=0;
|
|
TEXTMETRICW textMetric;
|
|
|
|
TRACE("(text=%s, ..., isW=%d)\n", debugstr_t(text, isW), isW);
|
|
|
|
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 */
|
|
GetTextExtentPoint32W(hdc, text, lstrlenW(text), &sz);
|
|
|
|
/*Add Extra spacing for the next character */
|
|
GetTextMetricsW(hdc, &textMetric);
|
|
sz.cx += (textMetric.tmMaxCharWidth * 2);
|
|
|
|
if(infoPtr->hFont != 0)
|
|
SelectObject(hdc, hOldFont);
|
|
|
|
ReleaseDC(parent, hdc);
|
|
if (isW)
|
|
hedit = CreateWindowW(editName, text, style, x, y, sz.cx, height, parent, 0, hinst, 0);
|
|
else
|
|
hedit = CreateWindowA("Edit", (LPCSTR)text, style, x, y, sz.cx, height, parent, 0, hinst, 0);
|
|
|
|
if (!hedit)
|
|
{
|
|
COMCTL32_Free(infoPtr->pedititem);
|
|
return 0;
|
|
}
|
|
|
|
infoPtr->pedititem->param = param;
|
|
infoPtr->pedititem->EditLblCb = EditLblCb;
|
|
infoPtr->pedititem->EditWndProc = (WNDPROC)
|
|
(isW ? SetWindowLongW(hedit, GWL_WNDPROC, (LONG)EditLblWndProcW) :
|
|
SetWindowLongA(hedit, GWL_WNDPROC, (LONG)EditLblWndProcA) );
|
|
|
|
SendMessageW(hedit, WM_SETFONT, infoPtr->hFont, FALSE);
|
|
|
|
return hedit;
|
|
}
|