mirror of
https://github.com/reactos/reactos.git
synced 2024-12-29 10:35:28 +00:00
0707475f69
For SOME reason comctl32 has been synched manually multiple times to different versions and different pots This PR aims to fix that With the exception of button.c which all in all is a massive fork over wines code entirely. and datetime.c which is at wine 6.0 Comctl32 is now at wine-5.0
2307 lines
66 KiB
C
2307 lines
66 KiB
C
/*
|
|
* Header control
|
|
*
|
|
* Copyright 1998 Eric Kohl
|
|
* Copyright 2000 Eric Kohl for CodeWeavers
|
|
* Copyright 2003 Maxime Bellenge
|
|
* Copyright 2006 Mikolaj Zalewski
|
|
*
|
|
* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
|
*
|
|
* TODO:
|
|
* - Imagelist support (completed?)
|
|
* - Hottrack support (completed?)
|
|
* - Filters support (HDS_FILTER, HDI_FILTER, HDM_*FILTER*, HDN_*FILTER*)
|
|
* - New Windows Vista features
|
|
*/
|
|
|
|
#include <stdarg.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "windef.h"
|
|
#include "winbase.h"
|
|
#include "wingdi.h"
|
|
#include "winuser.h"
|
|
#include "winnls.h"
|
|
#include "commctrl.h"
|
|
#include "comctl32.h"
|
|
#include "vssym32.h"
|
|
#include "uxtheme.h"
|
|
#include "wine/debug.h"
|
|
#include "wine/heap.h"
|
|
|
|
WINE_DEFAULT_DEBUG_CHANNEL(header);
|
|
|
|
typedef struct
|
|
{
|
|
INT cxy;
|
|
HBITMAP hbm;
|
|
LPWSTR pszText;
|
|
INT fmt;
|
|
LPARAM lParam;
|
|
INT iImage;
|
|
INT iOrder; /* see documentation of HD_ITEM */
|
|
|
|
BOOL bDown; /* is item pressed? (used for drawing) */
|
|
RECT rect; /* bounding rectangle of the item */
|
|
DWORD callbackMask; /* HDI_* flags for items that are callback */
|
|
} HEADER_ITEM;
|
|
|
|
|
|
typedef struct
|
|
{
|
|
HWND hwndSelf; /* Control window */
|
|
HWND hwndNotify; /* Owner window to send notifications to */
|
|
INT nNotifyFormat; /* format used for WM_NOTIFY messages */
|
|
UINT uNumItem; /* number of items (columns) */
|
|
INT nHeight; /* height of the header (pixels) */
|
|
HFONT hFont; /* handle to the current font */
|
|
HCURSOR hcurArrow; /* handle to the arrow cursor */
|
|
HCURSOR hcurDivider; /* handle to a cursor (used over dividers) <-|-> */
|
|
HCURSOR hcurDivopen; /* handle to a cursor (used over dividers) <-||-> */
|
|
BOOL bCaptured; /* Is the mouse captured? */
|
|
BOOL bPressed; /* Is a header item pressed (down)? */
|
|
BOOL bDragging; /* Are we dragging an item? */
|
|
BOOL bTracking; /* Is in tracking mode? */
|
|
POINT ptLButtonDown; /* The point where the left button was pressed */
|
|
DWORD dwStyle; /* the cached window GWL_STYLE */
|
|
INT iMoveItem; /* index of tracked item. (Tracking mode) */
|
|
INT xTrackOffset; /* distance between the right side of the tracked item and the cursor */
|
|
INT xOldTrack; /* track offset (see above) after the last WM_MOUSEMOVE */
|
|
INT iHotItem; /* index of hot item (cursor is over this item) */
|
|
INT iHotDivider; /* index of the hot divider (used while dragging an item or by HDM_SETHOTDIVIDER) */
|
|
INT iMargin; /* width of the margin that surrounds a bitmap */
|
|
INT filter_change_timeout; /* change timeout set with HDM_SETFILTERCHANGETIMEOUT */
|
|
|
|
HIMAGELIST himl; /* handle to an image list (may be 0) */
|
|
HEADER_ITEM *items; /* pointer to array of HEADER_ITEM's */
|
|
INT *order; /* array of item IDs indexed by order */
|
|
BOOL bRectsValid; /* validity flag for bounding rectangles */
|
|
} HEADER_INFO;
|
|
|
|
|
|
#define VERT_BORDER 4
|
|
#define DIVIDER_WIDTH 10
|
|
#define HOT_DIVIDER_WIDTH 2
|
|
#define MAX_HEADER_TEXT_LEN 260
|
|
#define HDN_UNICODE_OFFSET 20
|
|
#define HDN_FIRST_UNICODE (HDN_FIRST-HDN_UNICODE_OFFSET)
|
|
|
|
#define HDI_SUPPORTED_FIELDS (HDI_WIDTH|HDI_TEXT|HDI_FORMAT|HDI_LPARAM|HDI_BITMAP|HDI_IMAGE|HDI_ORDER)
|
|
#define HDI_UNSUPPORTED_FIELDS (HDI_FILTER)
|
|
#define HDI_UNKNOWN_FIELDS (~(HDI_SUPPORTED_FIELDS|HDI_UNSUPPORTED_FIELDS|HDI_DI_SETITEM))
|
|
#define HDI_COMCTL32_4_0_FIELDS (HDI_WIDTH|HDI_TEXT|HDI_FORMAT|HDI_LPARAM|HDI_BITMAP)
|
|
|
|
|
|
static BOOL HEADER_PrepareCallbackItems(const HEADER_INFO *infoPtr, INT iItem, INT reqMask);
|
|
static void HEADER_FreeCallbackItems(HEADER_ITEM *lpItem);
|
|
static LRESULT HEADER_SendNotify(const HEADER_INFO *infoPtr, UINT code, NMHDR *hdr);
|
|
static LRESULT HEADER_SendCtrlCustomDraw(const HEADER_INFO *infoPtr, DWORD dwDrawStage, HDC hdc, const RECT *rect);
|
|
|
|
static const WCHAR themeClass[] = {'H','e','a','d','e','r',0};
|
|
|
|
static void HEADER_StoreHDItemInHeader(HEADER_ITEM *lpItem, UINT mask, const HDITEMW *phdi, BOOL fUnicode)
|
|
{
|
|
if (mask & HDI_UNSUPPORTED_FIELDS)
|
|
FIXME("unsupported header fields %x\n", (mask & HDI_UNSUPPORTED_FIELDS));
|
|
|
|
if (mask & HDI_BITMAP)
|
|
lpItem->hbm = phdi->hbm;
|
|
|
|
if (mask & HDI_FORMAT)
|
|
lpItem->fmt = phdi->fmt;
|
|
|
|
if (mask & HDI_LPARAM)
|
|
lpItem->lParam = phdi->lParam;
|
|
|
|
if (mask & HDI_WIDTH)
|
|
lpItem->cxy = phdi->cxy;
|
|
|
|
if (mask & HDI_IMAGE)
|
|
{
|
|
lpItem->iImage = phdi->iImage;
|
|
if (phdi->iImage == I_IMAGECALLBACK)
|
|
lpItem->callbackMask |= HDI_IMAGE;
|
|
else
|
|
lpItem->callbackMask &= ~HDI_IMAGE;
|
|
}
|
|
|
|
if (mask & HDI_TEXT)
|
|
{
|
|
heap_free(lpItem->pszText);
|
|
lpItem->pszText = NULL;
|
|
|
|
if (phdi->pszText != LPSTR_TEXTCALLBACKW) /* covers != TEXTCALLBACKA too */
|
|
{
|
|
static const WCHAR emptyString[] = {0};
|
|
|
|
LPCWSTR pszText = (phdi->pszText != NULL ? phdi->pszText : emptyString);
|
|
if (fUnicode)
|
|
Str_SetPtrW(&lpItem->pszText, pszText);
|
|
else
|
|
Str_SetPtrAtoW(&lpItem->pszText, (LPCSTR)pszText);
|
|
lpItem->callbackMask &= ~HDI_TEXT;
|
|
}
|
|
else
|
|
{
|
|
lpItem->pszText = NULL;
|
|
lpItem->callbackMask |= HDI_TEXT;
|
|
}
|
|
}
|
|
}
|
|
|
|
static inline LRESULT
|
|
HEADER_IndexToOrder (const HEADER_INFO *infoPtr, INT iItem)
|
|
{
|
|
HEADER_ITEM *lpItem = &infoPtr->items[iItem];
|
|
return lpItem->iOrder;
|
|
}
|
|
|
|
|
|
static INT
|
|
HEADER_OrderToIndex(const HEADER_INFO *infoPtr, INT iorder)
|
|
{
|
|
if ((iorder <0) || iorder >= infoPtr->uNumItem)
|
|
return iorder;
|
|
return infoPtr->order[iorder];
|
|
}
|
|
|
|
static void
|
|
HEADER_ChangeItemOrder(const HEADER_INFO *infoPtr, INT iItem, INT iNewOrder)
|
|
{
|
|
HEADER_ITEM *lpItem = &infoPtr->items[iItem];
|
|
INT i, nMin, nMax;
|
|
|
|
TRACE("%d: %d->%d\n", iItem, lpItem->iOrder, iNewOrder);
|
|
if (lpItem->iOrder < iNewOrder)
|
|
{
|
|
memmove(&infoPtr->order[lpItem->iOrder],
|
|
&infoPtr->order[lpItem->iOrder + 1],
|
|
(iNewOrder - lpItem->iOrder) * sizeof(INT));
|
|
}
|
|
if (iNewOrder < lpItem->iOrder)
|
|
{
|
|
memmove(&infoPtr->order[iNewOrder + 1],
|
|
&infoPtr->order[iNewOrder],
|
|
(lpItem->iOrder - iNewOrder) * sizeof(INT));
|
|
}
|
|
infoPtr->order[iNewOrder] = iItem;
|
|
nMin = min(lpItem->iOrder, iNewOrder);
|
|
nMax = max(lpItem->iOrder, iNewOrder);
|
|
for (i = nMin; i <= nMax; i++)
|
|
infoPtr->items[infoPtr->order[i]].iOrder = i;
|
|
}
|
|
|
|
/* Note: if iItem is the last item then this function returns infoPtr->uNumItem */
|
|
static INT
|
|
HEADER_NextItem(const HEADER_INFO *infoPtr, INT iItem)
|
|
{
|
|
return HEADER_OrderToIndex(infoPtr, HEADER_IndexToOrder(infoPtr, iItem)+1);
|
|
}
|
|
|
|
static INT
|
|
HEADER_PrevItem(const HEADER_INFO *infoPtr, INT iItem)
|
|
{
|
|
return HEADER_OrderToIndex(infoPtr, HEADER_IndexToOrder(infoPtr, iItem)-1);
|
|
}
|
|
|
|
/* TRUE when item is not resizable with dividers,
|
|
note that valid index should be supplied */
|
|
static inline BOOL
|
|
HEADER_IsItemFixed(const HEADER_INFO *infoPtr, INT iItem)
|
|
{
|
|
return (infoPtr->dwStyle & HDS_NOSIZING) || (infoPtr->items[iItem].fmt & HDF_FIXEDWIDTH);
|
|
}
|
|
|
|
static void
|
|
HEADER_SetItemBounds (HEADER_INFO *infoPtr)
|
|
{
|
|
HEADER_ITEM *phdi;
|
|
RECT rect;
|
|
unsigned int i;
|
|
int x;
|
|
|
|
infoPtr->bRectsValid = TRUE;
|
|
|
|
if (infoPtr->uNumItem == 0)
|
|
return;
|
|
|
|
GetClientRect (infoPtr->hwndSelf, &rect);
|
|
|
|
x = rect.left;
|
|
for (i = 0; i < infoPtr->uNumItem; i++) {
|
|
phdi = &infoPtr->items[HEADER_OrderToIndex(infoPtr,i)];
|
|
phdi->rect.top = rect.top;
|
|
phdi->rect.bottom = rect.bottom;
|
|
phdi->rect.left = x;
|
|
phdi->rect.right = phdi->rect.left + ((phdi->cxy>0)?phdi->cxy:0);
|
|
x = phdi->rect.right;
|
|
}
|
|
}
|
|
|
|
static LRESULT
|
|
HEADER_Size (HEADER_INFO *infoPtr)
|
|
{
|
|
HEADER_SetItemBounds(infoPtr);
|
|
return 0;
|
|
}
|
|
|
|
static void HEADER_GetHotDividerRect(const HEADER_INFO *infoPtr, RECT *r)
|
|
{
|
|
INT iDivider = infoPtr->iHotDivider;
|
|
if (infoPtr->uNumItem > 0)
|
|
{
|
|
HEADER_ITEM *lpItem;
|
|
|
|
if (iDivider < infoPtr->uNumItem)
|
|
{
|
|
lpItem = &infoPtr->items[iDivider];
|
|
r->left = lpItem->rect.left - HOT_DIVIDER_WIDTH/2;
|
|
r->right = lpItem->rect.left + HOT_DIVIDER_WIDTH/2;
|
|
}
|
|
else
|
|
{
|
|
lpItem = &infoPtr->items[HEADER_OrderToIndex(infoPtr, infoPtr->uNumItem-1)];
|
|
r->left = lpItem->rect.right - HOT_DIVIDER_WIDTH/2;
|
|
r->right = lpItem->rect.right + HOT_DIVIDER_WIDTH/2;
|
|
}
|
|
r->top = lpItem->rect.top;
|
|
r->bottom = lpItem->rect.bottom;
|
|
}
|
|
else
|
|
{
|
|
RECT clientRect;
|
|
GetClientRect(infoPtr->hwndSelf, &clientRect);
|
|
*r = clientRect;
|
|
r->right = r->left + HOT_DIVIDER_WIDTH/2;
|
|
}
|
|
}
|
|
|
|
static void
|
|
HEADER_FillItemFrame(HEADER_INFO *infoPtr, HDC hdc, RECT *r, const HEADER_ITEM *item, BOOL hottrack)
|
|
{
|
|
HTHEME theme = GetWindowTheme (infoPtr->hwndSelf);
|
|
|
|
if (theme) {
|
|
int state = (item->bDown) ? HIS_PRESSED : (hottrack ? HIS_HOT : HIS_NORMAL);
|
|
DrawThemeBackground (theme, hdc, HP_HEADERITEM, state, r, NULL);
|
|
GetThemeBackgroundContentRect (theme, hdc, HP_HEADERITEM, state, r, r);
|
|
}
|
|
else
|
|
{
|
|
HBRUSH hbr = CreateSolidBrush(GetBkColor(hdc));
|
|
FillRect(hdc, r, hbr);
|
|
DeleteObject(hbr);
|
|
}
|
|
}
|
|
|
|
static void
|
|
HEADER_DrawItemFrame(HEADER_INFO *infoPtr, HDC hdc, RECT *r, const HEADER_ITEM *item)
|
|
{
|
|
if (GetWindowTheme(infoPtr->hwndSelf)) return;
|
|
|
|
if (!(infoPtr->dwStyle & HDS_FLAT))
|
|
{
|
|
if (infoPtr->dwStyle & HDS_BUTTONS) {
|
|
if (item->bDown)
|
|
DrawEdge (hdc, r, BDR_RAISEDOUTER, BF_RECT | BF_FLAT | BF_ADJUST);
|
|
else
|
|
DrawEdge (hdc, r, EDGE_RAISED, BF_RECT | BF_SOFT | BF_ADJUST);
|
|
}
|
|
else
|
|
DrawEdge (hdc, r, EDGE_ETCHED, BF_BOTTOM | BF_RIGHT | BF_ADJUST);
|
|
}
|
|
}
|
|
|
|
/* Create a region for the sort arrow with its bounding rect's top-left
|
|
co-ord x,y and its height h. */
|
|
static HRGN create_sort_arrow( INT x, INT y, INT h, BOOL is_up )
|
|
{
|
|
char buffer[256];
|
|
RGNDATA *data = (RGNDATA *)buffer;
|
|
DWORD size = FIELD_OFFSET(RGNDATA, Buffer[h * sizeof(RECT)]);
|
|
INT i, yinc = 1;
|
|
HRGN rgn;
|
|
|
|
if (size > sizeof(buffer))
|
|
{
|
|
data = heap_alloc( size );
|
|
if (!data) return NULL;
|
|
}
|
|
data->rdh.dwSize = sizeof(data->rdh);
|
|
data->rdh.iType = RDH_RECTANGLES;
|
|
data->rdh.nCount = 0;
|
|
data->rdh.nRgnSize = h * sizeof(RECT);
|
|
|
|
if (!is_up)
|
|
{
|
|
y += h - 1;
|
|
yinc = -1;
|
|
}
|
|
|
|
x += h - 1; /* set x to the centre */
|
|
|
|
for (i = 0; i < h; i++, y += yinc)
|
|
{
|
|
RECT *rect = (RECT *)data->Buffer + data->rdh.nCount;
|
|
rect->left = x - i;
|
|
rect->top = y;
|
|
rect->right = x + i + 1;
|
|
rect->bottom = y + 1;
|
|
data->rdh.nCount++;
|
|
}
|
|
rgn = ExtCreateRegion( NULL, size, data );
|
|
if (data != (RGNDATA *)buffer) heap_free( data );
|
|
return rgn;
|
|
}
|
|
|
|
static INT
|
|
HEADER_DrawItem (HEADER_INFO *infoPtr, HDC hdc, INT iItem, BOOL bHotTrack, LRESULT lCDFlags)
|
|
{
|
|
HEADER_ITEM *phdi = &infoPtr->items[iItem];
|
|
RECT r;
|
|
INT oldBkMode;
|
|
HTHEME theme = GetWindowTheme (infoPtr->hwndSelf);
|
|
NMCUSTOMDRAW nmcd;
|
|
int state = 0;
|
|
|
|
TRACE("DrawItem(iItem %d bHotTrack %d unicode flag %d)\n", iItem, bHotTrack, (infoPtr->nNotifyFormat == NFR_UNICODE));
|
|
|
|
r = phdi->rect;
|
|
if (r.right - r.left == 0)
|
|
return phdi->rect.right;
|
|
|
|
if (theme)
|
|
state = (phdi->bDown) ? HIS_PRESSED : (bHotTrack ? HIS_HOT : HIS_NORMAL);
|
|
|
|
/* Set the colors before sending NM_CUSTOMDRAW so that it can change them */
|
|
SetTextColor(hdc, (bHotTrack && !theme) ? comctl32_color.clrHighlight : comctl32_color.clrBtnText);
|
|
SetBkColor(hdc, comctl32_color.clr3dFace);
|
|
|
|
if (lCDFlags & CDRF_NOTIFYITEMDRAW && !(phdi->fmt & HDF_OWNERDRAW))
|
|
{
|
|
LRESULT lCDItemFlags;
|
|
|
|
nmcd.dwDrawStage = CDDS_PREPAINT | CDDS_ITEM;
|
|
nmcd.hdc = hdc;
|
|
nmcd.dwItemSpec = iItem;
|
|
nmcd.rc = r;
|
|
nmcd.uItemState = phdi->bDown ? CDIS_SELECTED : 0;
|
|
nmcd.lItemlParam = phdi->lParam;
|
|
|
|
lCDItemFlags = HEADER_SendNotify(infoPtr, NM_CUSTOMDRAW, (NMHDR *)&nmcd);
|
|
if (lCDItemFlags & CDRF_SKIPDEFAULT)
|
|
return phdi->rect.right;
|
|
}
|
|
|
|
/* Fill background, owner could draw over it. */
|
|
HEADER_FillItemFrame(infoPtr, hdc, &r, phdi, bHotTrack);
|
|
|
|
if (phdi->fmt & HDF_OWNERDRAW)
|
|
{
|
|
DRAWITEMSTRUCT dis;
|
|
BOOL ret;
|
|
|
|
dis.CtlType = ODT_HEADER;
|
|
dis.CtlID = GetWindowLongPtrW (infoPtr->hwndSelf, GWLP_ID);
|
|
dis.itemID = iItem;
|
|
dis.itemAction = ODA_DRAWENTIRE;
|
|
dis.itemState = phdi->bDown ? ODS_SELECTED : 0;
|
|
dis.hwndItem = infoPtr->hwndSelf;
|
|
dis.hDC = hdc;
|
|
dis.rcItem = phdi->rect;
|
|
dis.itemData = phdi->lParam;
|
|
oldBkMode = SetBkMode(hdc, TRANSPARENT);
|
|
ret = SendMessageW (infoPtr->hwndNotify, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
|
|
if (oldBkMode != TRANSPARENT)
|
|
SetBkMode(hdc, oldBkMode);
|
|
|
|
if (!ret)
|
|
HEADER_FillItemFrame(infoPtr, hdc, &r, phdi, bHotTrack);
|
|
|
|
/* Edges are always drawn if we don't have attached theme. */
|
|
HEADER_DrawItemFrame(infoPtr, hdc, &r, phdi);
|
|
/* If application processed WM_DRAWITEM we should skip label painting,
|
|
edges are drawn no matter what. */
|
|
if (ret) return phdi->rect.right;
|
|
}
|
|
else
|
|
HEADER_DrawItemFrame(infoPtr, hdc, &r, phdi);
|
|
|
|
if (phdi->bDown) {
|
|
r.left += 2;
|
|
r.top += 2;
|
|
}
|
|
|
|
/* Now text and image */
|
|
{
|
|
INT rw, rh; /* width and height of r */
|
|
INT *x = NULL; /* x and ... */
|
|
UINT *w = NULL; /* ... width of the pic (bmp or img) which is part of cnt */
|
|
/* cnt,txt,img,bmp */
|
|
INT cx, tx, ix, bx;
|
|
UINT cw, tw, iw, bw;
|
|
INT img_cx, img_cy;
|
|
INT sort_w, sort_x, sort_h;
|
|
BITMAP bmp;
|
|
|
|
HEADER_PrepareCallbackItems(infoPtr, iItem, HDI_TEXT|HDI_IMAGE);
|
|
cw = iw = bw = sort_w = sort_h = 0;
|
|
rw = r.right - r.left;
|
|
rh = r.bottom - r.top;
|
|
|
|
if (phdi->fmt & HDF_STRING) {
|
|
RECT textRect;
|
|
|
|
SetRectEmpty(&textRect);
|
|
|
|
if (theme) {
|
|
GetThemeTextExtent(theme, hdc, HP_HEADERITEM, state, phdi->pszText, -1,
|
|
DT_LEFT|DT_VCENTER|DT_SINGLELINE, NULL, &textRect);
|
|
} else {
|
|
DrawTextW (hdc, phdi->pszText, -1,
|
|
&textRect, DT_LEFT|DT_VCENTER|DT_SINGLELINE|DT_CALCRECT);
|
|
}
|
|
cw = textRect.right - textRect.left + 2 * infoPtr->iMargin;
|
|
}
|
|
|
|
if (phdi->fmt & (HDF_SORTUP | HDF_SORTDOWN)) {
|
|
sort_h = MulDiv( infoPtr->nHeight - VERT_BORDER, 4, 13 );
|
|
sort_w = 2 * sort_h - 1 + infoPtr->iMargin * 2;
|
|
cw += sort_w;
|
|
} else { /* sort arrows take precedent over images/bitmaps */
|
|
if ((phdi->fmt & HDF_IMAGE) && ImageList_GetIconSize( infoPtr->himl, &img_cx, &img_cy )) {
|
|
iw = img_cx + 2 * infoPtr->iMargin;
|
|
x = &ix;
|
|
w = &iw;
|
|
}
|
|
|
|
if ((phdi->fmt & HDF_BITMAP) && (phdi->hbm)) {
|
|
GetObjectW (phdi->hbm, sizeof(BITMAP), &bmp);
|
|
bw = bmp.bmWidth + 2 * infoPtr->iMargin;
|
|
if (!iw) {
|
|
x = &bx;
|
|
w = &bw;
|
|
}
|
|
}
|
|
if (bw || iw)
|
|
cw += *w;
|
|
}
|
|
|
|
/* align cx using the unclipped cw */
|
|
if ((phdi->fmt & HDF_JUSTIFYMASK) == HDF_LEFT)
|
|
cx = r.left;
|
|
else if ((phdi->fmt & HDF_JUSTIFYMASK) == HDF_CENTER)
|
|
cx = r.left + rw / 2 - cw / 2;
|
|
else /* HDF_RIGHT */
|
|
cx = r.right - cw;
|
|
|
|
/* clip cx & cw */
|
|
if (cx < r.left)
|
|
cx = r.left;
|
|
if (cx + cw > r.right)
|
|
cw = r.right - cx;
|
|
|
|
tx = cx + infoPtr->iMargin;
|
|
/* since cw might have changed we have to recalculate tw */
|
|
tw = cw - infoPtr->iMargin * 2;
|
|
|
|
tw -= sort_w;
|
|
sort_x = cx + tw + infoPtr->iMargin * 3;
|
|
|
|
if (iw || bw) {
|
|
tw -= *w;
|
|
if (phdi->fmt & HDF_BITMAP_ON_RIGHT) {
|
|
/* put pic behind text */
|
|
*x = cx + tw + infoPtr->iMargin * 3;
|
|
} else {
|
|
*x = cx + infoPtr->iMargin;
|
|
/* move text behind pic */
|
|
tx += *w;
|
|
}
|
|
}
|
|
|
|
if (iw && bw) {
|
|
/* since we're done with the layout we can
|
|
now calculate the position of bmp which
|
|
has no influence on alignment and layout
|
|
because of img */
|
|
if ((phdi->fmt & HDF_JUSTIFYMASK) == HDF_RIGHT)
|
|
bx = cx - bw + infoPtr->iMargin;
|
|
else
|
|
bx = cx + cw + infoPtr->iMargin;
|
|
}
|
|
|
|
if (sort_w || iw || bw) {
|
|
HDC hClipDC = GetDC(infoPtr->hwndSelf);
|
|
HRGN hClipRgn = CreateRectRgn(r.left, r.top, r.right, r.bottom);
|
|
SelectClipRgn(hClipDC, hClipRgn);
|
|
|
|
if (sort_w) {
|
|
HRGN arrow = create_sort_arrow( sort_x, r.top + (rh - sort_h) / 2,
|
|
sort_h, phdi->fmt & HDF_SORTUP );
|
|
if (arrow) {
|
|
FillRgn( hClipDC, arrow, GetSysColorBrush( COLOR_GRAYTEXT ) );
|
|
DeleteObject( arrow );
|
|
}
|
|
}
|
|
|
|
if (bw) {
|
|
HDC hdcBitmap = CreateCompatibleDC (hClipDC);
|
|
SelectObject (hdcBitmap, phdi->hbm);
|
|
BitBlt (hClipDC, bx, r.top + (rh - bmp.bmHeight) / 2,
|
|
bmp.bmWidth, bmp.bmHeight, hdcBitmap, 0, 0, SRCCOPY);
|
|
DeleteDC (hdcBitmap);
|
|
}
|
|
|
|
if (iw) {
|
|
ImageList_DrawEx (infoPtr->himl, phdi->iImage, hClipDC,
|
|
ix, r.top + (rh - img_cy) / 2,
|
|
img_cx, img_cy, CLR_DEFAULT, CLR_DEFAULT, 0);
|
|
}
|
|
|
|
DeleteObject(hClipRgn);
|
|
ReleaseDC(infoPtr->hwndSelf, hClipDC);
|
|
}
|
|
|
|
if (((phdi->fmt & HDF_STRING)
|
|
|| (!(phdi->fmt & (HDF_OWNERDRAW|HDF_STRING|HDF_BITMAP|
|
|
HDF_BITMAP_ON_RIGHT|HDF_IMAGE)))) /* no explicit format specified? */
|
|
&& (phdi->pszText)) {
|
|
oldBkMode = SetBkMode(hdc, TRANSPARENT);
|
|
r.left = tx;
|
|
r.right = tx + tw;
|
|
if (theme) {
|
|
DrawThemeText(theme, hdc, HP_HEADERITEM, state, phdi->pszText,
|
|
-1, DT_LEFT|DT_END_ELLIPSIS|DT_VCENTER|DT_SINGLELINE,
|
|
0, &r);
|
|
} else {
|
|
DrawTextW (hdc, phdi->pszText, -1,
|
|
&r, DT_LEFT|DT_END_ELLIPSIS|DT_VCENTER|DT_SINGLELINE);
|
|
}
|
|
if (oldBkMode != TRANSPARENT)
|
|
SetBkMode(hdc, oldBkMode);
|
|
}
|
|
HEADER_FreeCallbackItems(phdi);
|
|
}
|
|
|
|
return phdi->rect.right;
|
|
}
|
|
|
|
static void
|
|
HEADER_DrawHotDivider(const HEADER_INFO *infoPtr, HDC hdc)
|
|
{
|
|
HBRUSH brush;
|
|
RECT r;
|
|
|
|
HEADER_GetHotDividerRect(infoPtr, &r);
|
|
brush = CreateSolidBrush(comctl32_color.clrHighlight);
|
|
FillRect(hdc, &r, brush);
|
|
DeleteObject(brush);
|
|
}
|
|
|
|
static void
|
|
HEADER_Refresh (HEADER_INFO *infoPtr, HDC hdc)
|
|
{
|
|
HFONT hFont, hOldFont;
|
|
RECT rect, rcRest;
|
|
HBRUSH hbrBk;
|
|
UINT i;
|
|
INT x;
|
|
LRESULT lCDFlags;
|
|
HTHEME theme = GetWindowTheme (infoPtr->hwndSelf);
|
|
|
|
if (!infoPtr->bRectsValid)
|
|
HEADER_SetItemBounds(infoPtr);
|
|
|
|
/* get rect for the bar, adjusted for the border */
|
|
GetClientRect (infoPtr->hwndSelf, &rect);
|
|
lCDFlags = HEADER_SendCtrlCustomDraw(infoPtr, CDDS_PREPAINT, hdc, &rect);
|
|
|
|
if (infoPtr->bDragging)
|
|
ImageList_DragShowNolock(FALSE);
|
|
|
|
hFont = infoPtr->hFont ? infoPtr->hFont : GetStockObject (SYSTEM_FONT);
|
|
hOldFont = SelectObject (hdc, hFont);
|
|
|
|
/* draw Background */
|
|
if (infoPtr->uNumItem == 0 && theme == NULL) {
|
|
hbrBk = GetSysColorBrush(COLOR_3DFACE);
|
|
FillRect(hdc, &rect, hbrBk);
|
|
}
|
|
|
|
x = rect.left;
|
|
for (i = 0; x <= rect.right && i < infoPtr->uNumItem; i++) {
|
|
int idx = HEADER_OrderToIndex(infoPtr,i);
|
|
if (RectVisible(hdc, &infoPtr->items[idx].rect))
|
|
HEADER_DrawItem(infoPtr, hdc, idx, infoPtr->iHotItem == idx, lCDFlags);
|
|
x = infoPtr->items[idx].rect.right;
|
|
}
|
|
|
|
rcRest = rect;
|
|
rcRest.left = x;
|
|
if ((x <= rect.right) && RectVisible(hdc, &rcRest) && (infoPtr->uNumItem > 0)) {
|
|
if (theme != NULL) {
|
|
DrawThemeBackground(theme, hdc, HP_HEADERITEM, HIS_NORMAL, &rcRest, NULL);
|
|
}
|
|
else if (infoPtr->dwStyle & HDS_FLAT) {
|
|
hbrBk = GetSysColorBrush(COLOR_3DFACE);
|
|
FillRect(hdc, &rcRest, hbrBk);
|
|
}
|
|
else
|
|
{
|
|
if (infoPtr->dwStyle & HDS_BUTTONS)
|
|
DrawEdge (hdc, &rcRest, EDGE_RAISED, BF_TOP|BF_LEFT|BF_BOTTOM|BF_SOFT|BF_MIDDLE);
|
|
else
|
|
DrawEdge (hdc, &rcRest, EDGE_ETCHED, BF_BOTTOM|BF_MIDDLE);
|
|
}
|
|
}
|
|
|
|
if (infoPtr->iHotDivider != -1)
|
|
HEADER_DrawHotDivider(infoPtr, hdc);
|
|
|
|
if (infoPtr->bDragging)
|
|
ImageList_DragShowNolock(TRUE);
|
|
SelectObject (hdc, hOldFont);
|
|
|
|
if (lCDFlags & CDRF_NOTIFYPOSTPAINT)
|
|
HEADER_SendCtrlCustomDraw(infoPtr, CDDS_POSTPAINT, hdc, &rect);
|
|
}
|
|
|
|
|
|
static void
|
|
HEADER_RefreshItem (HEADER_INFO *infoPtr, INT iItem)
|
|
{
|
|
if (!infoPtr->bRectsValid)
|
|
HEADER_SetItemBounds(infoPtr);
|
|
|
|
InvalidateRect(infoPtr->hwndSelf, &infoPtr->items[iItem].rect, FALSE);
|
|
}
|
|
|
|
|
|
static void
|
|
HEADER_InternalHitTest (const HEADER_INFO *infoPtr, const POINT *lpPt, UINT *pFlags, INT *pItem)
|
|
{
|
|
RECT rect, rcTest;
|
|
UINT iCount;
|
|
INT width;
|
|
BOOL bNoWidth;
|
|
|
|
GetClientRect (infoPtr->hwndSelf, &rect);
|
|
|
|
*pFlags = 0;
|
|
bNoWidth = FALSE;
|
|
if (PtInRect (&rect, *lpPt))
|
|
{
|
|
if (infoPtr->uNumItem == 0) {
|
|
*pFlags |= HHT_NOWHERE;
|
|
*pItem = 1;
|
|
TRACE("NOWHERE\n");
|
|
return;
|
|
}
|
|
else {
|
|
/* somewhere inside */
|
|
for (iCount = 0; iCount < infoPtr->uNumItem; iCount++) {
|
|
rect = infoPtr->items[iCount].rect;
|
|
width = rect.right - rect.left;
|
|
if (width == 0) {
|
|
bNoWidth = TRUE;
|
|
continue;
|
|
}
|
|
if (PtInRect (&rect, *lpPt)) {
|
|
if (width <= 2 * DIVIDER_WIDTH) {
|
|
*pFlags |= HHT_ONHEADER;
|
|
*pItem = iCount;
|
|
TRACE("ON HEADER %d\n", iCount);
|
|
return;
|
|
}
|
|
if (HEADER_IndexToOrder(infoPtr, iCount) > 0) {
|
|
rcTest = rect;
|
|
rcTest.right = rcTest.left + DIVIDER_WIDTH;
|
|
if (PtInRect (&rcTest, *lpPt)) {
|
|
if (HEADER_IsItemFixed(infoPtr, HEADER_PrevItem(infoPtr, iCount)))
|
|
{
|
|
*pFlags |= HHT_ONHEADER;
|
|
*pItem = iCount;
|
|
TRACE("ON HEADER %d\n", *pItem);
|
|
return;
|
|
}
|
|
if (bNoWidth) {
|
|
*pFlags |= HHT_ONDIVOPEN;
|
|
*pItem = HEADER_PrevItem(infoPtr, iCount);
|
|
TRACE("ON DIVOPEN %d\n", *pItem);
|
|
return;
|
|
}
|
|
else {
|
|
*pFlags |= HHT_ONDIVIDER;
|
|
*pItem = HEADER_PrevItem(infoPtr, iCount);
|
|
TRACE("ON DIVIDER %d\n", *pItem);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
rcTest = rect;
|
|
rcTest.left = rcTest.right - DIVIDER_WIDTH;
|
|
if (!HEADER_IsItemFixed(infoPtr, iCount) && PtInRect (&rcTest, *lpPt))
|
|
{
|
|
*pFlags |= HHT_ONDIVIDER;
|
|
*pItem = iCount;
|
|
TRACE("ON DIVIDER %d\n", *pItem);
|
|
return;
|
|
}
|
|
|
|
*pFlags |= HHT_ONHEADER;
|
|
*pItem = iCount;
|
|
TRACE("ON HEADER %d\n", iCount);
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* check for last divider part (on nowhere) */
|
|
if (!HEADER_IsItemFixed(infoPtr, infoPtr->uNumItem - 1))
|
|
{
|
|
rect = infoPtr->items[infoPtr->uNumItem-1].rect;
|
|
rect.left = rect.right;
|
|
rect.right += DIVIDER_WIDTH;
|
|
if (PtInRect (&rect, *lpPt)) {
|
|
if (bNoWidth) {
|
|
*pFlags |= HHT_ONDIVOPEN;
|
|
*pItem = infoPtr->uNumItem - 1;
|
|
TRACE("ON DIVOPEN %d\n", *pItem);
|
|
return;
|
|
}
|
|
else {
|
|
*pFlags |= HHT_ONDIVIDER;
|
|
*pItem = infoPtr->uNumItem - 1;
|
|
TRACE("ON DIVIDER %d\n", *pItem);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
*pFlags |= HHT_NOWHERE;
|
|
*pItem = 1;
|
|
TRACE("NOWHERE\n");
|
|
return;
|
|
}
|
|
}
|
|
else {
|
|
if (lpPt->x < rect.left) {
|
|
TRACE("TO LEFT\n");
|
|
*pFlags |= HHT_TOLEFT;
|
|
}
|
|
else if (lpPt->x > rect.right) {
|
|
TRACE("TO RIGHT\n");
|
|
*pFlags |= HHT_TORIGHT;
|
|
}
|
|
|
|
if (lpPt->y < rect.top) {
|
|
TRACE("ABOVE\n");
|
|
*pFlags |= HHT_ABOVE;
|
|
}
|
|
else if (lpPt->y > rect.bottom) {
|
|
TRACE("BELOW\n");
|
|
*pFlags |= HHT_BELOW;
|
|
}
|
|
}
|
|
|
|
*pItem = 1;
|
|
TRACE("flags=0x%X\n", *pFlags);
|
|
return;
|
|
}
|
|
|
|
|
|
static void
|
|
HEADER_DrawTrackLine (const HEADER_INFO *infoPtr, HDC hdc, INT x)
|
|
{
|
|
RECT rect;
|
|
|
|
GetClientRect (infoPtr->hwndSelf, &rect);
|
|
PatBlt( hdc, x, rect.top, 1, rect.bottom - rect.top, DSTINVERT );
|
|
}
|
|
|
|
/***
|
|
* DESCRIPTION:
|
|
* Convert a HDITEM into the correct format (ANSI/Unicode) to send it in a notify
|
|
*
|
|
* PARAMETER(S):
|
|
* [I] infoPtr : the header that wants to send the notify
|
|
* [O] dest : The buffer to store the HDITEM for notify. It may be set to a HDITEMA of HDITEMW
|
|
* [I] src : The source HDITEM. It may be a HDITEMA or HDITEMW
|
|
* [I] fSourceUnicode : is src a HDITEMW or HDITEMA
|
|
* [O] ppvScratch : a pointer to a scratch buffer that needs to be freed after
|
|
* the HDITEM is no longer in use or NULL if none was needed
|
|
*
|
|
* NOTE: We depend on HDITEMA and HDITEMW having the same structure
|
|
*/
|
|
static void HEADER_CopyHDItemForNotify(const HEADER_INFO *infoPtr, HDITEMW *dest,
|
|
const HDITEMW *src, BOOL fSourceUnicode, LPVOID *ppvScratch)
|
|
{
|
|
*ppvScratch = NULL;
|
|
*dest = *src;
|
|
|
|
if (src->mask & HDI_TEXT && src->pszText != LPSTR_TEXTCALLBACKW) /* covers TEXTCALLBACKA as well */
|
|
{
|
|
if (fSourceUnicode && infoPtr->nNotifyFormat != NFR_UNICODE)
|
|
{
|
|
dest->pszText = NULL;
|
|
Str_SetPtrWtoA((LPSTR *)&dest->pszText, src->pszText);
|
|
*ppvScratch = dest->pszText;
|
|
}
|
|
|
|
if (!fSourceUnicode && infoPtr->nNotifyFormat == NFR_UNICODE)
|
|
{
|
|
dest->pszText = NULL;
|
|
Str_SetPtrAtoW(&dest->pszText, (LPSTR)src->pszText);
|
|
*ppvScratch = dest->pszText;
|
|
}
|
|
}
|
|
}
|
|
|
|
static UINT HEADER_NotifyCodeWtoA(UINT code)
|
|
{
|
|
/* we use the fact that all the unicode messages are in HDN_FIRST_UNICODE..HDN_LAST*/
|
|
if (code >= HDN_LAST && code <= HDN_FIRST_UNICODE)
|
|
return code + HDN_UNICODE_OFFSET;
|
|
else
|
|
return code;
|
|
}
|
|
|
|
static LRESULT
|
|
HEADER_SendNotify(const HEADER_INFO *infoPtr, UINT code, NMHDR *nmhdr)
|
|
{
|
|
nmhdr->hwndFrom = infoPtr->hwndSelf;
|
|
nmhdr->idFrom = GetWindowLongPtrW (infoPtr->hwndSelf, GWLP_ID);
|
|
nmhdr->code = code;
|
|
|
|
return SendMessageW(infoPtr->hwndNotify, WM_NOTIFY,
|
|
nmhdr->idFrom, (LPARAM)nmhdr);
|
|
}
|
|
|
|
static BOOL
|
|
HEADER_SendSimpleNotify (const HEADER_INFO *infoPtr, UINT code)
|
|
{
|
|
NMHDR nmhdr;
|
|
return (BOOL)HEADER_SendNotify(infoPtr, code, &nmhdr);
|
|
}
|
|
|
|
static LRESULT
|
|
HEADER_SendCtrlCustomDraw(const HEADER_INFO *infoPtr, DWORD dwDrawStage, HDC hdc, const RECT *rect)
|
|
{
|
|
NMCUSTOMDRAW nm;
|
|
nm.dwDrawStage = dwDrawStage;
|
|
nm.hdc = hdc;
|
|
nm.rc = *rect;
|
|
nm.dwItemSpec = 0;
|
|
nm.uItemState = 0;
|
|
nm.lItemlParam = 0;
|
|
|
|
return HEADER_SendNotify(infoPtr, NM_CUSTOMDRAW, (NMHDR *)&nm);
|
|
}
|
|
|
|
static BOOL
|
|
HEADER_SendNotifyWithHDItemT(const HEADER_INFO *infoPtr, UINT code, INT iItem, HDITEMW *lpItem)
|
|
{
|
|
NMHEADERW nmhdr;
|
|
|
|
if (infoPtr->nNotifyFormat != NFR_UNICODE)
|
|
code = HEADER_NotifyCodeWtoA(code);
|
|
nmhdr.iItem = iItem;
|
|
nmhdr.iButton = 0;
|
|
nmhdr.pitem = lpItem;
|
|
|
|
return (BOOL)HEADER_SendNotify(infoPtr, code, (NMHDR *)&nmhdr);
|
|
}
|
|
|
|
static BOOL
|
|
HEADER_SendNotifyWithIntFieldT(const HEADER_INFO *infoPtr, UINT code, INT iItem, INT mask, INT iValue)
|
|
{
|
|
HDITEMW nmitem;
|
|
|
|
/* copying only the iValue should be ok but to make the code more robust we copy everything */
|
|
nmitem.cxy = infoPtr->items[iItem].cxy;
|
|
nmitem.hbm = infoPtr->items[iItem].hbm;
|
|
nmitem.pszText = NULL;
|
|
nmitem.cchTextMax = 0;
|
|
nmitem.fmt = infoPtr->items[iItem].fmt;
|
|
nmitem.lParam = infoPtr->items[iItem].lParam;
|
|
nmitem.iOrder = infoPtr->items[iItem].iOrder;
|
|
nmitem.iImage = infoPtr->items[iItem].iImage;
|
|
|
|
nmitem.mask = mask;
|
|
switch (mask)
|
|
{
|
|
case HDI_WIDTH:
|
|
nmitem.cxy = iValue;
|
|
break;
|
|
case HDI_ORDER:
|
|
nmitem.iOrder = iValue;
|
|
break;
|
|
default:
|
|
ERR("invalid mask value 0x%x\n", iValue);
|
|
}
|
|
|
|
return HEADER_SendNotifyWithHDItemT(infoPtr, code, iItem, &nmitem);
|
|
}
|
|
|
|
/**
|
|
* Prepare callback items
|
|
* depends on NMHDDISPINFOW having same structure as NMHDDISPINFOA
|
|
* (so we handle the two cases only doing a specific cast for pszText).
|
|
* Checks if any of the required fields is a callback. If this is the case sends a
|
|
* NMHDISPINFO notify to retrieve these items. The items are stored in the
|
|
* HEADER_ITEM pszText and iImage fields. They should be freed with
|
|
* HEADER_FreeCallbackItems.
|
|
*
|
|
* @param hwnd : hwnd header container handler
|
|
* @param iItem : the header item id
|
|
* @param reqMask : required fields. If any of them is callback this function will fetch it
|
|
*
|
|
* @return TRUE on success, else FALSE
|
|
*/
|
|
static BOOL
|
|
HEADER_PrepareCallbackItems(const HEADER_INFO *infoPtr, INT iItem, INT reqMask)
|
|
{
|
|
HEADER_ITEM *lpItem = &infoPtr->items[iItem];
|
|
DWORD mask = reqMask & lpItem->callbackMask;
|
|
NMHDDISPINFOW dispInfo;
|
|
void *pvBuffer = NULL;
|
|
|
|
if (mask == 0)
|
|
return TRUE;
|
|
if (mask&HDI_TEXT && lpItem->pszText != NULL)
|
|
{
|
|
ERR("(): function called without a call to FreeCallbackItems\n");
|
|
heap_free(lpItem->pszText);
|
|
lpItem->pszText = NULL;
|
|
}
|
|
|
|
memset(&dispInfo, 0, sizeof(NMHDDISPINFOW));
|
|
dispInfo.hdr.hwndFrom = infoPtr->hwndSelf;
|
|
dispInfo.hdr.idFrom = GetWindowLongPtrW (infoPtr->hwndSelf, GWLP_ID);
|
|
if (infoPtr->nNotifyFormat == NFR_UNICODE)
|
|
{
|
|
dispInfo.hdr.code = HDN_GETDISPINFOW;
|
|
if (mask & HDI_TEXT)
|
|
pvBuffer = heap_alloc_zero(MAX_HEADER_TEXT_LEN * sizeof(WCHAR));
|
|
}
|
|
else
|
|
{
|
|
dispInfo.hdr.code = HDN_GETDISPINFOA;
|
|
if (mask & HDI_TEXT)
|
|
pvBuffer = heap_alloc_zero(MAX_HEADER_TEXT_LEN * sizeof(CHAR));
|
|
}
|
|
dispInfo.pszText = pvBuffer;
|
|
dispInfo.cchTextMax = (pvBuffer!=NULL?MAX_HEADER_TEXT_LEN:0);
|
|
dispInfo.iItem = iItem;
|
|
dispInfo.mask = mask;
|
|
dispInfo.lParam = lpItem->lParam;
|
|
|
|
TRACE("Sending HDN_GETDISPINFO%c\n", infoPtr->nNotifyFormat == NFR_UNICODE?'W':'A');
|
|
SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, dispInfo.hdr.idFrom, (LPARAM)&dispInfo);
|
|
|
|
TRACE("SendMessage returns(mask:0x%x,str:%s,lParam:%p)\n",
|
|
dispInfo.mask,
|
|
(infoPtr->nNotifyFormat == NFR_UNICODE ? debugstr_w(dispInfo.pszText) : (LPSTR) dispInfo.pszText),
|
|
(void*) dispInfo.lParam);
|
|
|
|
if (mask & HDI_IMAGE)
|
|
lpItem->iImage = dispInfo.iImage;
|
|
if (mask & HDI_TEXT)
|
|
{
|
|
if (infoPtr->nNotifyFormat == NFR_UNICODE)
|
|
{
|
|
lpItem->pszText = pvBuffer;
|
|
|
|
/* the user might have used his own buffer */
|
|
if (dispInfo.pszText != lpItem->pszText)
|
|
Str_GetPtrW(dispInfo.pszText, lpItem->pszText, MAX_HEADER_TEXT_LEN);
|
|
}
|
|
else
|
|
{
|
|
Str_SetPtrAtoW(&lpItem->pszText, (LPSTR)dispInfo.pszText);
|
|
heap_free(pvBuffer);
|
|
}
|
|
}
|
|
|
|
if (dispInfo.mask & HDI_DI_SETITEM)
|
|
{
|
|
/* make the items permanent */
|
|
lpItem->callbackMask &= ~dispInfo.mask;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/***
|
|
* DESCRIPTION:
|
|
* Free the items that might be allocated with HEADER_PrepareCallbackItems
|
|
*
|
|
* PARAMETER(S):
|
|
* [I] lpItem : the item to free the data
|
|
*
|
|
*/
|
|
static void
|
|
HEADER_FreeCallbackItems(HEADER_ITEM *lpItem)
|
|
{
|
|
if (lpItem->callbackMask&HDI_TEXT)
|
|
{
|
|
heap_free(lpItem->pszText);
|
|
lpItem->pszText = NULL;
|
|
}
|
|
|
|
if (lpItem->callbackMask&HDI_IMAGE)
|
|
lpItem->iImage = I_IMAGECALLBACK;
|
|
}
|
|
|
|
static HIMAGELIST
|
|
HEADER_CreateDragImage (HEADER_INFO *infoPtr, INT iItem)
|
|
{
|
|
HEADER_ITEM *lpItem;
|
|
HIMAGELIST himl;
|
|
HBITMAP hMemory, hOldBitmap;
|
|
LRESULT lCDFlags;
|
|
RECT rc;
|
|
HDC hMemoryDC;
|
|
HDC hDeviceDC;
|
|
int height, width;
|
|
HFONT hFont;
|
|
|
|
if (iItem >= infoPtr->uNumItem)
|
|
return NULL;
|
|
|
|
if (!infoPtr->bRectsValid)
|
|
HEADER_SetItemBounds(infoPtr);
|
|
|
|
lpItem = &infoPtr->items[iItem];
|
|
width = lpItem->rect.right - lpItem->rect.left;
|
|
height = lpItem->rect.bottom - lpItem->rect.top;
|
|
|
|
hDeviceDC = GetDC(NULL);
|
|
hMemoryDC = CreateCompatibleDC(hDeviceDC);
|
|
hMemory = CreateCompatibleBitmap(hDeviceDC, width, height);
|
|
ReleaseDC(NULL, hDeviceDC);
|
|
hOldBitmap = SelectObject(hMemoryDC, hMemory);
|
|
SetViewportOrgEx(hMemoryDC, -lpItem->rect.left, -lpItem->rect.top, NULL);
|
|
hFont = infoPtr->hFont ? infoPtr->hFont : GetStockObject(SYSTEM_FONT);
|
|
SelectObject(hMemoryDC, hFont);
|
|
|
|
GetClientRect(infoPtr->hwndSelf, &rc);
|
|
lCDFlags = HEADER_SendCtrlCustomDraw(infoPtr, CDDS_PREPAINT, hMemoryDC, &rc);
|
|
HEADER_DrawItem(infoPtr, hMemoryDC, iItem, FALSE, lCDFlags);
|
|
if (lCDFlags & CDRF_NOTIFYPOSTPAINT)
|
|
HEADER_SendCtrlCustomDraw(infoPtr, CDDS_POSTPAINT, hMemoryDC, &rc);
|
|
|
|
hMemory = SelectObject(hMemoryDC, hOldBitmap);
|
|
DeleteDC(hMemoryDC);
|
|
|
|
if (hMemory == NULL) /* if anything failed */
|
|
return NULL;
|
|
|
|
himl = ImageList_Create(width, height, ILC_COLORDDB, 1, 1);
|
|
ImageList_Add(himl, hMemory, NULL);
|
|
DeleteObject(hMemory);
|
|
return himl;
|
|
}
|
|
|
|
static LRESULT
|
|
HEADER_SetHotDivider(HEADER_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
INT iDivider;
|
|
RECT r;
|
|
|
|
if (wParam)
|
|
{
|
|
POINT pt;
|
|
UINT flags;
|
|
pt.x = (INT)(SHORT)LOWORD(lParam);
|
|
pt.y = 0;
|
|
HEADER_InternalHitTest (infoPtr, &pt, &flags, &iDivider);
|
|
|
|
if (flags & HHT_TOLEFT)
|
|
iDivider = 0;
|
|
else if (flags & HHT_NOWHERE || flags & HHT_TORIGHT)
|
|
iDivider = infoPtr->uNumItem;
|
|
else
|
|
{
|
|
HEADER_ITEM *lpItem = &infoPtr->items[iDivider];
|
|
if (pt.x > (lpItem->rect.left+lpItem->rect.right)/2)
|
|
iDivider = HEADER_NextItem(infoPtr, iDivider);
|
|
}
|
|
}
|
|
else
|
|
iDivider = (INT)lParam;
|
|
|
|
/* Note; wParam==FALSE, lParam==-1 is valid and is used to clear the hot divider */
|
|
if (iDivider<-1 || iDivider>(int)infoPtr->uNumItem)
|
|
return iDivider;
|
|
|
|
if (iDivider != infoPtr->iHotDivider)
|
|
{
|
|
if (infoPtr->iHotDivider != -1)
|
|
{
|
|
HEADER_GetHotDividerRect(infoPtr, &r);
|
|
InvalidateRect(infoPtr->hwndSelf, &r, FALSE);
|
|
}
|
|
infoPtr->iHotDivider = iDivider;
|
|
if (iDivider != -1)
|
|
{
|
|
HEADER_GetHotDividerRect(infoPtr, &r);
|
|
InvalidateRect(infoPtr->hwndSelf, &r, FALSE);
|
|
}
|
|
}
|
|
return iDivider;
|
|
}
|
|
|
|
static LRESULT
|
|
HEADER_DeleteItem (HEADER_INFO *infoPtr, INT iItem)
|
|
{
|
|
INT iOrder;
|
|
UINT i;
|
|
|
|
TRACE("[iItem=%d]\n", iItem);
|
|
|
|
if ((iItem < 0) || (iItem >= (INT)infoPtr->uNumItem))
|
|
return FALSE;
|
|
|
|
for (i = 0; i < infoPtr->uNumItem; i++)
|
|
TRACE("%d: order=%d, iOrder=%d, ->iOrder=%d\n", i, infoPtr->order[i], infoPtr->items[i].iOrder, infoPtr->items[infoPtr->order[i]].iOrder);
|
|
|
|
iOrder = infoPtr->items[iItem].iOrder;
|
|
heap_free(infoPtr->items[iItem].pszText);
|
|
|
|
infoPtr->uNumItem--;
|
|
memmove(&infoPtr->items[iItem], &infoPtr->items[iItem + 1],
|
|
(infoPtr->uNumItem - iItem) * sizeof(HEADER_ITEM));
|
|
memmove(&infoPtr->order[iOrder], &infoPtr->order[iOrder + 1],
|
|
(infoPtr->uNumItem - iOrder) * sizeof(INT));
|
|
infoPtr->items = heap_realloc(infoPtr->items, sizeof(HEADER_ITEM) * infoPtr->uNumItem);
|
|
infoPtr->order = heap_realloc(infoPtr->order, sizeof(INT) * infoPtr->uNumItem);
|
|
|
|
/* Correct the orders */
|
|
for (i = 0; i < infoPtr->uNumItem; i++)
|
|
{
|
|
if (infoPtr->order[i] > iItem)
|
|
infoPtr->order[i]--;
|
|
if (i >= iOrder)
|
|
infoPtr->items[infoPtr->order[i]].iOrder = i;
|
|
}
|
|
for (i = 0; i < infoPtr->uNumItem; i++)
|
|
TRACE("%d: order=%d, iOrder=%d, ->iOrder=%d\n", i, infoPtr->order[i], infoPtr->items[i].iOrder, infoPtr->items[infoPtr->order[i]].iOrder);
|
|
|
|
HEADER_SetItemBounds (infoPtr);
|
|
InvalidateRect(infoPtr->hwndSelf, NULL, FALSE);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
static LRESULT
|
|
HEADER_GetImageList (const HEADER_INFO *infoPtr)
|
|
{
|
|
return (LRESULT)infoPtr->himl;
|
|
}
|
|
|
|
|
|
static LRESULT
|
|
HEADER_GetItemT (const HEADER_INFO *infoPtr, INT nItem, LPHDITEMW phdi, BOOL bUnicode)
|
|
{
|
|
HEADER_ITEM *lpItem;
|
|
UINT mask;
|
|
|
|
if (!phdi)
|
|
return FALSE;
|
|
|
|
TRACE("[nItem=%d]\n", nItem);
|
|
|
|
mask = phdi->mask;
|
|
if (mask == 0)
|
|
return TRUE;
|
|
|
|
if ((nItem < 0) || (nItem >= (INT)infoPtr->uNumItem))
|
|
return FALSE;
|
|
|
|
if (mask & HDI_UNKNOWN_FIELDS)
|
|
{
|
|
TRACE("mask %x contains unknown fields. Using only comctl32 4.0 fields\n", mask);
|
|
mask &= HDI_COMCTL32_4_0_FIELDS;
|
|
}
|
|
|
|
lpItem = &infoPtr->items[nItem];
|
|
HEADER_PrepareCallbackItems(infoPtr, nItem, mask);
|
|
|
|
if (mask & HDI_BITMAP)
|
|
phdi->hbm = lpItem->hbm;
|
|
|
|
if (mask & HDI_FORMAT)
|
|
phdi->fmt = lpItem->fmt;
|
|
|
|
if (mask & HDI_WIDTH)
|
|
phdi->cxy = lpItem->cxy;
|
|
|
|
if (mask & HDI_LPARAM)
|
|
phdi->lParam = lpItem->lParam;
|
|
|
|
if (mask & HDI_IMAGE)
|
|
phdi->iImage = lpItem->iImage;
|
|
|
|
if (mask & HDI_ORDER)
|
|
phdi->iOrder = lpItem->iOrder;
|
|
|
|
if (mask & HDI_TEXT)
|
|
{
|
|
if (bUnicode)
|
|
Str_GetPtrW (lpItem->pszText, phdi->pszText, phdi->cchTextMax);
|
|
else
|
|
Str_GetPtrWtoA (lpItem->pszText, (LPSTR)phdi->pszText, phdi->cchTextMax);
|
|
}
|
|
|
|
HEADER_FreeCallbackItems(lpItem);
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
static inline LRESULT
|
|
HEADER_GetItemCount (const HEADER_INFO *infoPtr)
|
|
{
|
|
return infoPtr->uNumItem;
|
|
}
|
|
|
|
|
|
static LRESULT
|
|
HEADER_GetItemRect (const HEADER_INFO *infoPtr, INT iItem, LPRECT lpRect)
|
|
{
|
|
if ((iItem < 0) || (iItem >= (INT)infoPtr->uNumItem))
|
|
return FALSE;
|
|
|
|
lpRect->left = infoPtr->items[iItem].rect.left;
|
|
lpRect->right = infoPtr->items[iItem].rect.right;
|
|
lpRect->top = infoPtr->items[iItem].rect.top;
|
|
lpRect->bottom = infoPtr->items[iItem].rect.bottom;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
static LRESULT
|
|
HEADER_GetOrderArray(const HEADER_INFO *infoPtr, INT size, LPINT order)
|
|
{
|
|
if ((UINT)size <infoPtr->uNumItem)
|
|
return FALSE;
|
|
|
|
memcpy(order, infoPtr->order, infoPtr->uNumItem * sizeof(INT));
|
|
return TRUE;
|
|
}
|
|
|
|
/* Returns index of first duplicate 'value' from [0,to) range,
|
|
or -1 if there isn't any */
|
|
static INT has_duplicate(const INT *array, INT to, INT value)
|
|
{
|
|
INT i;
|
|
for(i = 0; i < to; i++)
|
|
if (array[i] == value) return i;
|
|
return -1;
|
|
}
|
|
|
|
/* returns next available value from [0,max] not to duplicate in [0,to) */
|
|
static INT get_nextvalue(const INT *array, INT to, INT max)
|
|
{
|
|
INT i;
|
|
for(i = 0; i < max; i++)
|
|
if (has_duplicate(array, to, i) == -1) return i;
|
|
return 0;
|
|
}
|
|
|
|
static LRESULT
|
|
HEADER_SetOrderArray(HEADER_INFO *infoPtr, INT size, const INT *order)
|
|
{
|
|
HEADER_ITEM *lpItem;
|
|
INT i;
|
|
|
|
if ((UINT)size != infoPtr->uNumItem)
|
|
return FALSE;
|
|
|
|
if (TRACE_ON(header))
|
|
{
|
|
TRACE("count=%d, order array={", size);
|
|
for (i = 0; i < size; i++)
|
|
TRACE("%d%c", order[i], i != size-1 ? ',' : '}');
|
|
TRACE("\n");
|
|
}
|
|
|
|
for (i=0; i<size; i++)
|
|
{
|
|
if (order[i] >= size || order[i] < 0)
|
|
/* on invalid index get next available */
|
|
/* FIXME: if i==0 array item is out of range behaviour is
|
|
different, see tests */
|
|
infoPtr->order[i] = get_nextvalue(infoPtr->order, i, size);
|
|
else
|
|
{
|
|
INT j, dup;
|
|
|
|
infoPtr->order[i] = order[i];
|
|
j = i;
|
|
/* remove duplicates */
|
|
while ((dup = has_duplicate(infoPtr->order, j, order[j])) != -1)
|
|
{
|
|
INT next;
|
|
|
|
next = get_nextvalue(infoPtr->order, j, size);
|
|
infoPtr->order[dup] = next;
|
|
j--;
|
|
}
|
|
}
|
|
}
|
|
/* sync with item data */
|
|
for (i=0; i<size; i++)
|
|
{
|
|
lpItem = &infoPtr->items[infoPtr->order[i]];
|
|
lpItem->iOrder = i;
|
|
}
|
|
HEADER_SetItemBounds(infoPtr);
|
|
InvalidateRect(infoPtr->hwndSelf, NULL, FALSE);
|
|
return TRUE;
|
|
}
|
|
|
|
static inline LRESULT
|
|
HEADER_GetUnicodeFormat (const HEADER_INFO *infoPtr)
|
|
{
|
|
return (infoPtr->nNotifyFormat == NFR_UNICODE);
|
|
}
|
|
|
|
|
|
static LRESULT
|
|
HEADER_HitTest (const HEADER_INFO *infoPtr, LPHDHITTESTINFO phti)
|
|
{
|
|
UINT outside = HHT_NOWHERE | HHT_ABOVE | HHT_BELOW | HHT_TOLEFT | HHT_TORIGHT;
|
|
|
|
HEADER_InternalHitTest (infoPtr, &phti->pt, &phti->flags, &phti->iItem);
|
|
|
|
if (phti->flags & outside)
|
|
return phti->iItem = -1;
|
|
else
|
|
return phti->iItem;
|
|
}
|
|
|
|
|
|
static LRESULT
|
|
HEADER_InsertItemT (HEADER_INFO *infoPtr, INT nItem, const HDITEMW *phdi, BOOL bUnicode)
|
|
{
|
|
HEADER_ITEM *lpItem;
|
|
INT iOrder;
|
|
UINT i;
|
|
UINT copyMask;
|
|
|
|
if ((phdi == NULL) || (nItem < 0) || (phdi->mask == 0))
|
|
return -1;
|
|
|
|
if (nItem > infoPtr->uNumItem)
|
|
nItem = infoPtr->uNumItem;
|
|
|
|
iOrder = (phdi->mask & HDI_ORDER) ? phdi->iOrder : nItem;
|
|
if (iOrder < 0)
|
|
iOrder = 0;
|
|
else if (infoPtr->uNumItem < iOrder)
|
|
iOrder = infoPtr->uNumItem;
|
|
|
|
infoPtr->uNumItem++;
|
|
infoPtr->items = heap_realloc(infoPtr->items, sizeof(HEADER_ITEM) * infoPtr->uNumItem);
|
|
infoPtr->order = heap_realloc(infoPtr->order, sizeof(INT) * infoPtr->uNumItem);
|
|
|
|
/* make space for the new item */
|
|
memmove(&infoPtr->items[nItem + 1], &infoPtr->items[nItem],
|
|
(infoPtr->uNumItem - nItem - 1) * sizeof(HEADER_ITEM));
|
|
memmove(&infoPtr->order[iOrder + 1], &infoPtr->order[iOrder],
|
|
(infoPtr->uNumItem - iOrder - 1) * sizeof(INT));
|
|
|
|
/* update the order array */
|
|
infoPtr->order[iOrder] = nItem;
|
|
for (i = 0; i < infoPtr->uNumItem; i++)
|
|
{
|
|
if (i != iOrder && infoPtr->order[i] >= nItem)
|
|
infoPtr->order[i]++;
|
|
infoPtr->items[infoPtr->order[i]].iOrder = i;
|
|
}
|
|
|
|
lpItem = &infoPtr->items[nItem];
|
|
ZeroMemory(lpItem, sizeof(HEADER_ITEM));
|
|
/* cxy, fmt and lParam are copied even if not in the HDITEM mask */
|
|
copyMask = phdi->mask | HDI_WIDTH | HDI_FORMAT | HDI_LPARAM;
|
|
HEADER_StoreHDItemInHeader(lpItem, copyMask, phdi, bUnicode);
|
|
lpItem->iOrder = iOrder;
|
|
|
|
/* set automatically some format bits */
|
|
if (phdi->mask & HDI_TEXT)
|
|
lpItem->fmt |= HDF_STRING;
|
|
else
|
|
lpItem->fmt &= ~HDF_STRING;
|
|
|
|
if (lpItem->hbm != NULL)
|
|
lpItem->fmt |= HDF_BITMAP;
|
|
else
|
|
lpItem->fmt &= ~HDF_BITMAP;
|
|
|
|
if (phdi->mask & HDI_IMAGE)
|
|
lpItem->fmt |= HDF_IMAGE;
|
|
|
|
HEADER_SetItemBounds (infoPtr);
|
|
InvalidateRect(infoPtr->hwndSelf, NULL, FALSE);
|
|
|
|
return nItem;
|
|
}
|
|
|
|
|
|
static LRESULT
|
|
HEADER_Layout (HEADER_INFO *infoPtr, LPHDLAYOUT lpLayout)
|
|
{
|
|
lpLayout->pwpos->hwnd = infoPtr->hwndSelf;
|
|
lpLayout->pwpos->hwndInsertAfter = 0;
|
|
lpLayout->pwpos->x = lpLayout->prc->left;
|
|
lpLayout->pwpos->y = lpLayout->prc->top;
|
|
lpLayout->pwpos->cx = lpLayout->prc->right - lpLayout->prc->left;
|
|
if (infoPtr->dwStyle & HDS_HIDDEN)
|
|
lpLayout->pwpos->cy = 0;
|
|
else {
|
|
lpLayout->pwpos->cy = infoPtr->nHeight;
|
|
lpLayout->prc->top += infoPtr->nHeight;
|
|
}
|
|
lpLayout->pwpos->flags = SWP_NOZORDER;
|
|
|
|
TRACE("Layout x=%d y=%d cx=%d cy=%d\n",
|
|
lpLayout->pwpos->x, lpLayout->pwpos->y,
|
|
lpLayout->pwpos->cx, lpLayout->pwpos->cy);
|
|
|
|
infoPtr->bRectsValid = FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
static LRESULT
|
|
HEADER_SetImageList (HEADER_INFO *infoPtr, HIMAGELIST himl)
|
|
{
|
|
HIMAGELIST himlOld;
|
|
|
|
TRACE("(himl %p)\n", himl);
|
|
himlOld = infoPtr->himl;
|
|
infoPtr->himl = himl;
|
|
|
|
/* FIXME: Refresh needed??? */
|
|
|
|
return (LRESULT)himlOld;
|
|
}
|
|
|
|
|
|
static LRESULT
|
|
HEADER_GetBitmapMargin(const HEADER_INFO *infoPtr)
|
|
{
|
|
return infoPtr->iMargin;
|
|
}
|
|
|
|
static LRESULT
|
|
HEADER_SetBitmapMargin(HEADER_INFO *infoPtr, INT iMargin)
|
|
{
|
|
INT oldMargin = infoPtr->iMargin;
|
|
|
|
infoPtr->iMargin = iMargin;
|
|
|
|
return oldMargin;
|
|
}
|
|
|
|
static LRESULT
|
|
HEADER_SetItemT (HEADER_INFO *infoPtr, INT nItem, const HDITEMW *phdi, BOOL bUnicode)
|
|
{
|
|
HEADER_ITEM *lpItem;
|
|
HDITEMW hdNotify;
|
|
void *pvScratch;
|
|
|
|
if (phdi == NULL)
|
|
return FALSE;
|
|
if ((nItem < 0) || (nItem >= (INT)infoPtr->uNumItem))
|
|
return FALSE;
|
|
|
|
TRACE("[nItem=%d]\n", nItem);
|
|
|
|
HEADER_CopyHDItemForNotify(infoPtr, &hdNotify, phdi, bUnicode, &pvScratch);
|
|
if (HEADER_SendNotifyWithHDItemT(infoPtr, HDN_ITEMCHANGINGW, nItem, &hdNotify))
|
|
{
|
|
heap_free(pvScratch);
|
|
return FALSE;
|
|
}
|
|
|
|
lpItem = &infoPtr->items[nItem];
|
|
HEADER_StoreHDItemInHeader(lpItem, phdi->mask, phdi, bUnicode);
|
|
|
|
if (phdi->mask & HDI_ORDER)
|
|
if (phdi->iOrder >= 0 && phdi->iOrder < infoPtr->uNumItem)
|
|
HEADER_ChangeItemOrder(infoPtr, nItem, phdi->iOrder);
|
|
|
|
HEADER_SendNotifyWithHDItemT(infoPtr, HDN_ITEMCHANGEDW, nItem, &hdNotify);
|
|
|
|
HEADER_SetItemBounds (infoPtr);
|
|
|
|
InvalidateRect(infoPtr->hwndSelf, NULL, FALSE);
|
|
|
|
heap_free(pvScratch);
|
|
return TRUE;
|
|
}
|
|
|
|
static inline LRESULT
|
|
HEADER_SetUnicodeFormat (HEADER_INFO *infoPtr, WPARAM wParam)
|
|
{
|
|
BOOL bTemp = (infoPtr->nNotifyFormat == NFR_UNICODE);
|
|
|
|
infoPtr->nNotifyFormat = ((BOOL)wParam ? NFR_UNICODE : NFR_ANSI);
|
|
|
|
return bTemp;
|
|
}
|
|
|
|
|
|
static LRESULT
|
|
HEADER_Create (HWND hwnd, const CREATESTRUCTW *lpcs)
|
|
{
|
|
HEADER_INFO *infoPtr;
|
|
TEXTMETRICW tm;
|
|
HFONT hOldFont;
|
|
HDC hdc;
|
|
|
|
infoPtr = heap_alloc_zero (sizeof(*infoPtr));
|
|
SetWindowLongPtrW (hwnd, 0, (DWORD_PTR)infoPtr);
|
|
|
|
infoPtr->hwndSelf = hwnd;
|
|
infoPtr->hwndNotify = lpcs->hwndParent;
|
|
infoPtr->uNumItem = 0;
|
|
infoPtr->hFont = 0;
|
|
infoPtr->items = 0;
|
|
infoPtr->order = 0;
|
|
infoPtr->bRectsValid = FALSE;
|
|
infoPtr->hcurArrow = LoadCursorW (0, (LPWSTR)IDC_ARROW);
|
|
infoPtr->hcurDivider = LoadCursorW (COMCTL32_hModule, MAKEINTRESOURCEW(IDC_DIVIDER));
|
|
infoPtr->hcurDivopen = LoadCursorW (COMCTL32_hModule, MAKEINTRESOURCEW(IDC_DIVIDEROPEN));
|
|
infoPtr->bPressed = FALSE;
|
|
infoPtr->bTracking = FALSE;
|
|
infoPtr->dwStyle = lpcs->style;
|
|
infoPtr->iMoveItem = 0;
|
|
infoPtr->himl = 0;
|
|
infoPtr->iHotItem = -1;
|
|
infoPtr->iHotDivider = -1;
|
|
infoPtr->iMargin = 3*GetSystemMetrics(SM_CXEDGE);
|
|
infoPtr->nNotifyFormat =
|
|
SendMessageW (infoPtr->hwndNotify, WM_NOTIFYFORMAT, (WPARAM)hwnd, NF_QUERY);
|
|
infoPtr->filter_change_timeout = 1000;
|
|
|
|
hdc = GetDC (0);
|
|
hOldFont = SelectObject (hdc, GetStockObject (SYSTEM_FONT));
|
|
GetTextMetricsW (hdc, &tm);
|
|
infoPtr->nHeight = tm.tmHeight + VERT_BORDER;
|
|
SelectObject (hdc, hOldFont);
|
|
ReleaseDC (0, hdc);
|
|
|
|
OpenThemeData(hwnd, themeClass);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static LRESULT
|
|
HEADER_Destroy (HEADER_INFO *infoPtr)
|
|
{
|
|
HTHEME theme = GetWindowTheme(infoPtr->hwndSelf);
|
|
CloseThemeData(theme);
|
|
return 0;
|
|
}
|
|
|
|
static LRESULT
|
|
HEADER_NCDestroy (HEADER_INFO *infoPtr)
|
|
{
|
|
HEADER_ITEM *lpItem;
|
|
INT nItem;
|
|
|
|
if (infoPtr->items) {
|
|
lpItem = infoPtr->items;
|
|
for (nItem = 0; nItem < infoPtr->uNumItem; nItem++, lpItem++)
|
|
heap_free(lpItem->pszText);
|
|
heap_free(infoPtr->items);
|
|
}
|
|
|
|
heap_free(infoPtr->order);
|
|
|
|
SetWindowLongPtrW (infoPtr->hwndSelf, 0, 0);
|
|
heap_free(infoPtr);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static inline LRESULT
|
|
HEADER_GetFont (const HEADER_INFO *infoPtr)
|
|
{
|
|
return (LRESULT)infoPtr->hFont;
|
|
}
|
|
|
|
|
|
static BOOL
|
|
HEADER_IsDragDistance(const HEADER_INFO *infoPtr, const POINT *pt)
|
|
{
|
|
/* Windows allows for a mouse movement before starting the drag. We use the
|
|
* SM_CXDOUBLECLICK/SM_CYDOUBLECLICK as that distance.
|
|
*/
|
|
return (abs(infoPtr->ptLButtonDown.x - pt->x)>GetSystemMetrics(SM_CXDOUBLECLK) ||
|
|
abs(infoPtr->ptLButtonDown.y - pt->y)>GetSystemMetrics(SM_CYDOUBLECLK));
|
|
}
|
|
|
|
static LRESULT
|
|
HEADER_LButtonDblClk (const HEADER_INFO *infoPtr, INT x, INT y)
|
|
{
|
|
POINT pt;
|
|
UINT flags;
|
|
INT nItem;
|
|
|
|
pt.x = x;
|
|
pt.y = y;
|
|
HEADER_InternalHitTest (infoPtr, &pt, &flags, &nItem);
|
|
|
|
if ((infoPtr->dwStyle & HDS_BUTTONS) && (flags == HHT_ONHEADER))
|
|
HEADER_SendNotifyWithHDItemT(infoPtr, HDN_ITEMDBLCLICKW, nItem, NULL);
|
|
else if ((flags == HHT_ONDIVIDER) || (flags == HHT_ONDIVOPEN))
|
|
HEADER_SendNotifyWithHDItemT(infoPtr, HDN_DIVIDERDBLCLICKW, nItem, NULL);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static LRESULT
|
|
HEADER_LButtonDown (HEADER_INFO *infoPtr, INT x, INT y)
|
|
{
|
|
POINT pt;
|
|
UINT flags;
|
|
INT nItem;
|
|
HDC hdc;
|
|
|
|
pt.x = x;
|
|
pt.y = y;
|
|
HEADER_InternalHitTest (infoPtr, &pt, &flags, &nItem);
|
|
|
|
if ((infoPtr->dwStyle & HDS_BUTTONS) && (flags == HHT_ONHEADER)) {
|
|
SetCapture (infoPtr->hwndSelf);
|
|
infoPtr->bCaptured = TRUE;
|
|
infoPtr->bPressed = TRUE;
|
|
infoPtr->bDragging = FALSE;
|
|
infoPtr->iMoveItem = nItem;
|
|
infoPtr->ptLButtonDown = pt;
|
|
|
|
infoPtr->items[nItem].bDown = TRUE;
|
|
|
|
/* Send WM_CUSTOMDRAW */
|
|
hdc = GetDC (infoPtr->hwndSelf);
|
|
HEADER_RefreshItem (infoPtr, nItem);
|
|
ReleaseDC (infoPtr->hwndSelf, hdc);
|
|
|
|
TRACE("Pressed item %d.\n", nItem);
|
|
}
|
|
else if ((flags == HHT_ONDIVIDER) || (flags == HHT_ONDIVOPEN)) {
|
|
INT iCurrWidth = infoPtr->items[nItem].cxy;
|
|
if (!HEADER_SendNotifyWithIntFieldT(infoPtr, HDN_BEGINTRACKW, nItem, HDI_WIDTH, iCurrWidth))
|
|
{
|
|
SetCapture (infoPtr->hwndSelf);
|
|
infoPtr->bCaptured = TRUE;
|
|
infoPtr->bTracking = TRUE;
|
|
infoPtr->iMoveItem = nItem;
|
|
infoPtr->xTrackOffset = infoPtr->items[nItem].rect.right - pt.x;
|
|
|
|
if (!(infoPtr->dwStyle & HDS_FULLDRAG)) {
|
|
infoPtr->xOldTrack = infoPtr->items[nItem].rect.right;
|
|
hdc = GetDC (infoPtr->hwndSelf);
|
|
HEADER_DrawTrackLine (infoPtr, hdc, infoPtr->xOldTrack);
|
|
ReleaseDC (infoPtr->hwndSelf, hdc);
|
|
}
|
|
|
|
TRACE("Begin tracking item %d.\n", nItem);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static LRESULT
|
|
HEADER_LButtonUp (HEADER_INFO *infoPtr, INT x, INT y)
|
|
{
|
|
POINT pt;
|
|
UINT flags;
|
|
INT nItem;
|
|
HDC hdc;
|
|
|
|
pt.x = x;
|
|
pt.y = y;
|
|
HEADER_InternalHitTest (infoPtr, &pt, &flags, &nItem);
|
|
|
|
if (infoPtr->bPressed) {
|
|
|
|
infoPtr->items[infoPtr->iMoveItem].bDown = FALSE;
|
|
|
|
if (infoPtr->bDragging)
|
|
{
|
|
HEADER_ITEM *lpItem = &infoPtr->items[infoPtr->iMoveItem];
|
|
INT iNewOrder;
|
|
|
|
ImageList_DragShowNolock(FALSE);
|
|
ImageList_EndDrag();
|
|
|
|
if (infoPtr->iHotDivider == -1)
|
|
iNewOrder = -1;
|
|
else if (infoPtr->iHotDivider == infoPtr->uNumItem)
|
|
iNewOrder = infoPtr->uNumItem-1;
|
|
else
|
|
{
|
|
iNewOrder = HEADER_IndexToOrder(infoPtr, infoPtr->iHotDivider);
|
|
if (iNewOrder > lpItem->iOrder)
|
|
iNewOrder--;
|
|
}
|
|
|
|
if (iNewOrder != -1 &&
|
|
!HEADER_SendNotifyWithIntFieldT(infoPtr, HDN_ENDDRAG, infoPtr->iMoveItem, HDI_ORDER, iNewOrder))
|
|
{
|
|
HEADER_ChangeItemOrder(infoPtr, infoPtr->iMoveItem, iNewOrder);
|
|
infoPtr->bRectsValid = FALSE;
|
|
InvalidateRect(infoPtr->hwndSelf, NULL, FALSE);
|
|
}
|
|
else
|
|
InvalidateRect(infoPtr->hwndSelf, &infoPtr->items[infoPtr->iMoveItem].rect, FALSE);
|
|
|
|
infoPtr->bDragging = FALSE;
|
|
HEADER_SetHotDivider(infoPtr, FALSE, -1);
|
|
}
|
|
else
|
|
{
|
|
hdc = GetDC (infoPtr->hwndSelf);
|
|
HEADER_RefreshItem (infoPtr, infoPtr->iMoveItem);
|
|
ReleaseDC (infoPtr->hwndSelf, hdc);
|
|
|
|
if (!(infoPtr->dwStyle & HDS_DRAGDROP) || !HEADER_IsDragDistance(infoPtr, &pt))
|
|
HEADER_SendNotifyWithHDItemT(infoPtr, HDN_ITEMCLICKW, infoPtr->iMoveItem, NULL);
|
|
}
|
|
|
|
TRACE("Released item %d.\n", infoPtr->iMoveItem);
|
|
infoPtr->bPressed = FALSE;
|
|
}
|
|
else if (infoPtr->bTracking) {
|
|
INT iNewWidth = pt.x - infoPtr->items[infoPtr->iMoveItem].rect.left + infoPtr->xTrackOffset;
|
|
if (iNewWidth < 0)
|
|
iNewWidth = 0;
|
|
TRACE("End tracking item %d.\n", infoPtr->iMoveItem);
|
|
infoPtr->bTracking = FALSE;
|
|
|
|
HEADER_SendNotifyWithIntFieldT(infoPtr, HDN_ENDTRACKW, infoPtr->iMoveItem, HDI_WIDTH, iNewWidth);
|
|
|
|
if (!(infoPtr->dwStyle & HDS_FULLDRAG)) {
|
|
hdc = GetDC (infoPtr->hwndSelf);
|
|
HEADER_DrawTrackLine (infoPtr, hdc, infoPtr->xOldTrack);
|
|
ReleaseDC (infoPtr->hwndSelf, hdc);
|
|
}
|
|
|
|
if (!HEADER_SendNotifyWithIntFieldT(infoPtr, HDN_ITEMCHANGINGW, infoPtr->iMoveItem, HDI_WIDTH, iNewWidth))
|
|
{
|
|
infoPtr->items[infoPtr->iMoveItem].cxy = iNewWidth;
|
|
HEADER_SendNotifyWithIntFieldT(infoPtr, HDN_ITEMCHANGEDW, infoPtr->iMoveItem, HDI_WIDTH, iNewWidth);
|
|
}
|
|
|
|
HEADER_SetItemBounds (infoPtr);
|
|
InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
|
|
}
|
|
|
|
if (infoPtr->bCaptured) {
|
|
infoPtr->bCaptured = FALSE;
|
|
ReleaseCapture ();
|
|
HEADER_SendSimpleNotify (infoPtr, NM_RELEASEDCAPTURE);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static LRESULT
|
|
HEADER_NotifyFormat (HEADER_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
switch (lParam)
|
|
{
|
|
case NF_QUERY:
|
|
return infoPtr->nNotifyFormat;
|
|
|
|
case NF_REQUERY:
|
|
infoPtr->nNotifyFormat =
|
|
SendMessageW ((HWND)wParam, WM_NOTIFYFORMAT,
|
|
(WPARAM)infoPtr->hwndSelf, NF_QUERY);
|
|
return infoPtr->nNotifyFormat;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static LRESULT
|
|
HEADER_MouseLeave (HEADER_INFO *infoPtr)
|
|
{
|
|
/* Reset hot-tracked item when mouse leaves control. */
|
|
INT oldHotItem = infoPtr->iHotItem;
|
|
HDC hdc = GetDC (infoPtr->hwndSelf);
|
|
|
|
infoPtr->iHotItem = -1;
|
|
if (oldHotItem != -1) HEADER_RefreshItem (infoPtr, oldHotItem);
|
|
ReleaseDC (infoPtr->hwndSelf, hdc);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static LRESULT
|
|
HEADER_MouseMove (HEADER_INFO *infoPtr, LPARAM lParam)
|
|
{
|
|
POINT pt;
|
|
UINT flags;
|
|
INT nItem, nWidth;
|
|
HDC hdc;
|
|
/* With theming, hottracking is always enabled */
|
|
BOOL hotTrackEnabled =
|
|
((infoPtr->dwStyle & HDS_BUTTONS) && (infoPtr->dwStyle & HDS_HOTTRACK))
|
|
|| (GetWindowTheme (infoPtr->hwndSelf) != NULL);
|
|
INT oldHotItem = infoPtr->iHotItem;
|
|
|
|
pt.x = (INT)(SHORT)LOWORD(lParam);
|
|
pt.y = (INT)(SHORT)HIWORD(lParam);
|
|
HEADER_InternalHitTest (infoPtr, &pt, &flags, &nItem);
|
|
|
|
if (hotTrackEnabled) {
|
|
if (flags & (HHT_ONHEADER | HHT_ONDIVIDER | HHT_ONDIVOPEN))
|
|
infoPtr->iHotItem = nItem;
|
|
else
|
|
infoPtr->iHotItem = -1;
|
|
}
|
|
|
|
if (infoPtr->bCaptured) {
|
|
/* check if we should drag the header */
|
|
if (infoPtr->bPressed && !infoPtr->bDragging && (infoPtr->dwStyle & HDS_DRAGDROP)
|
|
&& HEADER_IsDragDistance(infoPtr, &pt))
|
|
{
|
|
if (!HEADER_SendNotifyWithHDItemT(infoPtr, HDN_BEGINDRAG, infoPtr->iMoveItem, NULL))
|
|
{
|
|
HIMAGELIST hDragItem = HEADER_CreateDragImage(infoPtr, infoPtr->iMoveItem);
|
|
if (hDragItem != NULL)
|
|
{
|
|
HEADER_ITEM *lpItem = &infoPtr->items[infoPtr->iMoveItem];
|
|
TRACE("Starting item drag\n");
|
|
ImageList_BeginDrag(hDragItem, 0, pt.x - lpItem->rect.left, 0);
|
|
ImageList_DragShowNolock(TRUE);
|
|
ImageList_Destroy(hDragItem);
|
|
infoPtr->bDragging = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (infoPtr->bDragging)
|
|
{
|
|
POINT drag;
|
|
drag.x = pt.x;
|
|
drag.y = 0;
|
|
ClientToScreen(infoPtr->hwndSelf, &drag);
|
|
ImageList_DragMove(drag.x, drag.y);
|
|
HEADER_SetHotDivider(infoPtr, TRUE, lParam);
|
|
}
|
|
|
|
if (infoPtr->bPressed && !infoPtr->bDragging) {
|
|
BOOL oldState = infoPtr->items[infoPtr->iMoveItem].bDown;
|
|
if ((nItem == infoPtr->iMoveItem) && (flags == HHT_ONHEADER))
|
|
infoPtr->items[infoPtr->iMoveItem].bDown = TRUE;
|
|
else
|
|
infoPtr->items[infoPtr->iMoveItem].bDown = FALSE;
|
|
if (oldState != infoPtr->items[infoPtr->iMoveItem].bDown) {
|
|
hdc = GetDC (infoPtr->hwndSelf);
|
|
HEADER_RefreshItem (infoPtr, infoPtr->iMoveItem);
|
|
ReleaseDC (infoPtr->hwndSelf, hdc);
|
|
}
|
|
|
|
TRACE("Moving pressed item %d.\n", infoPtr->iMoveItem);
|
|
}
|
|
else if (infoPtr->bTracking) {
|
|
if (infoPtr->dwStyle & HDS_FULLDRAG) {
|
|
HEADER_ITEM *lpItem = &infoPtr->items[infoPtr->iMoveItem];
|
|
nWidth = pt.x - lpItem->rect.left + infoPtr->xTrackOffset;
|
|
if (!HEADER_SendNotifyWithIntFieldT(infoPtr, HDN_ITEMCHANGINGW, infoPtr->iMoveItem, HDI_WIDTH, nWidth))
|
|
{
|
|
INT nOldWidth = lpItem->rect.right - lpItem->rect.left;
|
|
RECT rcClient;
|
|
RECT rcScroll;
|
|
|
|
if (nWidth < 0) nWidth = 0;
|
|
infoPtr->items[infoPtr->iMoveItem].cxy = nWidth;
|
|
#ifdef __REACTOS__
|
|
InvalidateRect(infoPtr->hwndSelf, &lpItem->rect, FALSE);
|
|
#endif
|
|
HEADER_SetItemBounds(infoPtr);
|
|
|
|
GetClientRect(infoPtr->hwndSelf, &rcClient);
|
|
rcScroll = rcClient;
|
|
rcScroll.left = lpItem->rect.left + nOldWidth;
|
|
#ifdef __REACTOS__
|
|
ScrollWindowEx(infoPtr->hwndSelf, nWidth - nOldWidth, 0, &rcScroll, &rcClient,
|
|
NULL, NULL, SW_INVALIDATE);
|
|
#else
|
|
ScrollWindowEx(infoPtr->hwndSelf, nWidth - nOldWidth, 0, &rcScroll, &rcClient, NULL, NULL, 0);
|
|
InvalidateRect(infoPtr->hwndSelf, &lpItem->rect, FALSE);
|
|
#endif
|
|
UpdateWindow(infoPtr->hwndSelf);
|
|
|
|
HEADER_SendNotifyWithIntFieldT(infoPtr, HDN_ITEMCHANGEDW, infoPtr->iMoveItem, HDI_WIDTH, nWidth);
|
|
}
|
|
}
|
|
else {
|
|
INT iTrackWidth;
|
|
hdc = GetDC (infoPtr->hwndSelf);
|
|
HEADER_DrawTrackLine (infoPtr, hdc, infoPtr->xOldTrack);
|
|
infoPtr->xOldTrack = pt.x + infoPtr->xTrackOffset;
|
|
if (infoPtr->xOldTrack < infoPtr->items[infoPtr->iMoveItem].rect.left)
|
|
infoPtr->xOldTrack = infoPtr->items[infoPtr->iMoveItem].rect.left;
|
|
HEADER_DrawTrackLine (infoPtr, hdc, infoPtr->xOldTrack);
|
|
ReleaseDC (infoPtr->hwndSelf, hdc);
|
|
iTrackWidth = infoPtr->xOldTrack - infoPtr->items[infoPtr->iMoveItem].rect.left;
|
|
/* FIXME: should stop tracking if HDN_TRACK returns TRUE */
|
|
HEADER_SendNotifyWithIntFieldT(infoPtr, HDN_TRACKW, infoPtr->iMoveItem, HDI_WIDTH, iTrackWidth);
|
|
}
|
|
|
|
TRACE("Tracking item %d.\n", infoPtr->iMoveItem);
|
|
}
|
|
}
|
|
|
|
if (hotTrackEnabled) {
|
|
TRACKMOUSEEVENT tme;
|
|
if (oldHotItem != infoPtr->iHotItem && !infoPtr->bDragging) {
|
|
hdc = GetDC (infoPtr->hwndSelf);
|
|
if (oldHotItem != -1) HEADER_RefreshItem (infoPtr, oldHotItem);
|
|
if (infoPtr->iHotItem != -1) HEADER_RefreshItem (infoPtr, infoPtr->iHotItem);
|
|
ReleaseDC (infoPtr->hwndSelf, hdc);
|
|
}
|
|
tme.cbSize = sizeof( tme );
|
|
tme.dwFlags = TME_LEAVE;
|
|
tme.hwndTrack = infoPtr->hwndSelf;
|
|
TrackMouseEvent( &tme );
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static LRESULT
|
|
HEADER_Paint (HEADER_INFO *infoPtr, HDC hdcParam)
|
|
{
|
|
HDC hdc;
|
|
PAINTSTRUCT ps;
|
|
|
|
hdc = hdcParam==0 ? BeginPaint (infoPtr->hwndSelf, &ps) : hdcParam;
|
|
HEADER_Refresh (infoPtr, hdc);
|
|
if(!hdcParam)
|
|
EndPaint (infoPtr->hwndSelf, &ps);
|
|
return 0;
|
|
}
|
|
|
|
|
|
static LRESULT
|
|
HEADER_RButtonUp (HEADER_INFO *infoPtr, INT x, INT y)
|
|
{
|
|
BOOL bRet;
|
|
POINT pt;
|
|
|
|
pt.x = x;
|
|
pt.y = y;
|
|
|
|
/* Send a Notify message */
|
|
bRet = HEADER_SendSimpleNotify (infoPtr, NM_RCLICK);
|
|
|
|
/* Change to screen coordinate for WM_CONTEXTMENU */
|
|
ClientToScreen(infoPtr->hwndSelf, &pt);
|
|
|
|
/* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
|
|
SendMessageW( infoPtr->hwndSelf, WM_CONTEXTMENU, (WPARAM) infoPtr->hwndSelf, MAKELPARAM(pt.x, pt.y));
|
|
|
|
return bRet;
|
|
}
|
|
|
|
|
|
static LRESULT
|
|
HEADER_SetCursor (HEADER_INFO *infoPtr, LPARAM lParam)
|
|
{
|
|
POINT pt;
|
|
UINT flags;
|
|
INT nItem;
|
|
|
|
TRACE("code=0x%X id=0x%X\n", LOWORD(lParam), HIWORD(lParam));
|
|
|
|
GetCursorPos (&pt);
|
|
ScreenToClient (infoPtr->hwndSelf, &pt);
|
|
|
|
HEADER_InternalHitTest (infoPtr, &pt, &flags, &nItem);
|
|
|
|
if (flags == HHT_ONDIVIDER)
|
|
SetCursor (infoPtr->hcurDivider);
|
|
else if (flags == HHT_ONDIVOPEN)
|
|
SetCursor (infoPtr->hcurDivopen);
|
|
else
|
|
SetCursor (infoPtr->hcurArrow);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static LRESULT
|
|
HEADER_SetFont (HEADER_INFO *infoPtr, HFONT hFont, WORD Redraw)
|
|
{
|
|
TEXTMETRICW tm;
|
|
HFONT hOldFont;
|
|
HDC hdc;
|
|
|
|
infoPtr->hFont = hFont;
|
|
|
|
hdc = GetDC (0);
|
|
hOldFont = SelectObject (hdc, infoPtr->hFont ? infoPtr->hFont : GetStockObject (SYSTEM_FONT));
|
|
GetTextMetricsW (hdc, &tm);
|
|
infoPtr->nHeight = tm.tmHeight + VERT_BORDER;
|
|
SelectObject (hdc, hOldFont);
|
|
ReleaseDC (0, hdc);
|
|
|
|
infoPtr->bRectsValid = FALSE;
|
|
|
|
if (Redraw) {
|
|
InvalidateRect(infoPtr->hwndSelf, NULL, FALSE);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static LRESULT HEADER_SetRedraw(HEADER_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
/* ignoring the InvalidateRect calls is handled by user32. But some apps expect
|
|
* that we invalidate the header and this has to be done manually */
|
|
LRESULT ret;
|
|
|
|
ret = DefWindowProcW(infoPtr->hwndSelf, WM_SETREDRAW, wParam, lParam);
|
|
if (wParam)
|
|
InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
|
|
return ret;
|
|
}
|
|
|
|
static INT HEADER_StyleChanged(HEADER_INFO *infoPtr, WPARAM wStyleType,
|
|
const STYLESTRUCT *lpss)
|
|
{
|
|
TRACE("(styletype=%lx, styleOld=0x%08x, styleNew=0x%08x)\n",
|
|
wStyleType, lpss->styleOld, lpss->styleNew);
|
|
|
|
if (wStyleType != GWL_STYLE) return 0;
|
|
|
|
infoPtr->dwStyle = lpss->styleNew;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Update the theme handle after a theme change */
|
|
static LRESULT HEADER_ThemeChanged(const HEADER_INFO *infoPtr)
|
|
{
|
|
HTHEME theme = GetWindowTheme(infoPtr->hwndSelf);
|
|
CloseThemeData(theme);
|
|
OpenThemeData(infoPtr->hwndSelf, themeClass);
|
|
InvalidateRect(infoPtr->hwndSelf, NULL, FALSE);
|
|
return 0;
|
|
}
|
|
|
|
static INT HEADER_SetFilterChangeTimeout(HEADER_INFO *infoPtr, INT timeout)
|
|
{
|
|
INT old_timeout = infoPtr->filter_change_timeout;
|
|
|
|
if (timeout != 0)
|
|
infoPtr->filter_change_timeout = timeout;
|
|
return old_timeout;
|
|
}
|
|
|
|
static LRESULT WINAPI
|
|
HEADER_WindowProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
HEADER_INFO *infoPtr = (HEADER_INFO *)GetWindowLongPtrW(hwnd, 0);
|
|
|
|
TRACE("hwnd=%p msg=%x wparam=%lx lParam=%lx\n", hwnd, msg, wParam, lParam);
|
|
if (!infoPtr && (msg != WM_CREATE))
|
|
return DefWindowProcW (hwnd, msg, wParam, lParam);
|
|
switch (msg) {
|
|
/* case HDM_CLEARFILTER: */
|
|
|
|
case HDM_CREATEDRAGIMAGE:
|
|
return (LRESULT)HEADER_CreateDragImage (infoPtr, (INT)wParam);
|
|
|
|
case HDM_DELETEITEM:
|
|
return HEADER_DeleteItem (infoPtr, (INT)wParam);
|
|
|
|
/* case HDM_EDITFILTER: */
|
|
|
|
case HDM_GETBITMAPMARGIN:
|
|
return HEADER_GetBitmapMargin(infoPtr);
|
|
|
|
case HDM_GETIMAGELIST:
|
|
return HEADER_GetImageList (infoPtr);
|
|
|
|
case HDM_GETITEMA:
|
|
case HDM_GETITEMW:
|
|
return HEADER_GetItemT (infoPtr, (INT)wParam, (LPHDITEMW)lParam, msg == HDM_GETITEMW);
|
|
|
|
case HDM_GETITEMCOUNT:
|
|
return HEADER_GetItemCount (infoPtr);
|
|
|
|
case HDM_GETITEMRECT:
|
|
return HEADER_GetItemRect (infoPtr, (INT)wParam, (LPRECT)lParam);
|
|
|
|
case HDM_GETORDERARRAY:
|
|
return HEADER_GetOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
|
|
|
|
case HDM_GETUNICODEFORMAT:
|
|
return HEADER_GetUnicodeFormat (infoPtr);
|
|
|
|
case HDM_HITTEST:
|
|
return HEADER_HitTest (infoPtr, (LPHDHITTESTINFO)lParam);
|
|
|
|
case HDM_INSERTITEMA:
|
|
case HDM_INSERTITEMW:
|
|
return HEADER_InsertItemT (infoPtr, (INT)wParam, (LPHDITEMW)lParam, msg == HDM_INSERTITEMW);
|
|
|
|
case HDM_LAYOUT:
|
|
return HEADER_Layout (infoPtr, (LPHDLAYOUT)lParam);
|
|
|
|
case HDM_ORDERTOINDEX:
|
|
return HEADER_OrderToIndex(infoPtr, (INT)wParam);
|
|
|
|
case HDM_SETBITMAPMARGIN:
|
|
return HEADER_SetBitmapMargin(infoPtr, (INT)wParam);
|
|
|
|
case HDM_SETFILTERCHANGETIMEOUT:
|
|
return HEADER_SetFilterChangeTimeout(infoPtr, (INT)lParam);
|
|
|
|
case HDM_SETHOTDIVIDER:
|
|
return HEADER_SetHotDivider(infoPtr, wParam, lParam);
|
|
|
|
case HDM_SETIMAGELIST:
|
|
return HEADER_SetImageList (infoPtr, (HIMAGELIST)lParam);
|
|
|
|
case HDM_SETITEMA:
|
|
case HDM_SETITEMW:
|
|
return HEADER_SetItemT (infoPtr, (INT)wParam, (LPHDITEMW)lParam, msg == HDM_SETITEMW);
|
|
|
|
case HDM_SETORDERARRAY:
|
|
return HEADER_SetOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
|
|
|
|
case HDM_SETUNICODEFORMAT:
|
|
return HEADER_SetUnicodeFormat (infoPtr, wParam);
|
|
|
|
case WM_CREATE:
|
|
return HEADER_Create (hwnd, (LPCREATESTRUCTW)lParam);
|
|
|
|
case WM_DESTROY:
|
|
return HEADER_Destroy (infoPtr);
|
|
|
|
case WM_NCDESTROY:
|
|
return HEADER_NCDestroy (infoPtr);
|
|
|
|
case WM_ERASEBKGND:
|
|
return 1;
|
|
|
|
case WM_GETDLGCODE:
|
|
return DLGC_WANTTAB | DLGC_WANTARROWS;
|
|
|
|
case WM_GETFONT:
|
|
return HEADER_GetFont (infoPtr);
|
|
|
|
case WM_LBUTTONDBLCLK:
|
|
return HEADER_LButtonDblClk (infoPtr, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
|
|
|
|
case WM_LBUTTONDOWN:
|
|
return HEADER_LButtonDown (infoPtr, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
|
|
|
|
case WM_LBUTTONUP:
|
|
return HEADER_LButtonUp (infoPtr, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
|
|
|
|
case WM_MOUSELEAVE:
|
|
return HEADER_MouseLeave (infoPtr);
|
|
|
|
case WM_MOUSEMOVE:
|
|
return HEADER_MouseMove (infoPtr, lParam);
|
|
|
|
case WM_NOTIFYFORMAT:
|
|
return HEADER_NotifyFormat (infoPtr, wParam, lParam);
|
|
|
|
case WM_SIZE:
|
|
return HEADER_Size (infoPtr);
|
|
|
|
case WM_THEMECHANGED:
|
|
return HEADER_ThemeChanged (infoPtr);
|
|
|
|
case WM_PRINTCLIENT:
|
|
case WM_PAINT:
|
|
return HEADER_Paint (infoPtr, (HDC)wParam);
|
|
|
|
case WM_RBUTTONUP:
|
|
return HEADER_RButtonUp (infoPtr, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
|
|
|
|
case WM_SETCURSOR:
|
|
return HEADER_SetCursor (infoPtr, lParam);
|
|
|
|
case WM_SETFONT:
|
|
return HEADER_SetFont (infoPtr, (HFONT)wParam, (WORD)lParam);
|
|
|
|
case WM_SETREDRAW:
|
|
return HEADER_SetRedraw(infoPtr, wParam, lParam);
|
|
|
|
case WM_STYLECHANGED:
|
|
return HEADER_StyleChanged(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
|
|
|
|
case WM_SYSCOLORCHANGE:
|
|
COMCTL32_RefreshSysColors();
|
|
return 0;
|
|
|
|
default:
|
|
if ((msg >= WM_USER) && (msg < WM_APP) && !COMCTL32_IsReflectedMessage(msg))
|
|
ERR("unknown msg %04x wp=%04lx lp=%08lx\n",
|
|
msg, wParam, lParam );
|
|
return DefWindowProcW(hwnd, msg, wParam, lParam);
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
HEADER_Register (void)
|
|
{
|
|
WNDCLASSW wndClass;
|
|
|
|
ZeroMemory (&wndClass, sizeof(WNDCLASSW));
|
|
wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
|
|
wndClass.lpfnWndProc = HEADER_WindowProc;
|
|
wndClass.cbClsExtra = 0;
|
|
wndClass.cbWndExtra = sizeof(HEADER_INFO *);
|
|
wndClass.hCursor = LoadCursorW (0, (LPWSTR)IDC_ARROW);
|
|
wndClass.lpszClassName = WC_HEADERW;
|
|
|
|
RegisterClassW (&wndClass);
|
|
}
|
|
|
|
|
|
VOID
|
|
HEADER_Unregister (void)
|
|
{
|
|
UnregisterClassW (WC_HEADERW, NULL);
|
|
}
|