📄 header.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 "wine/unicode.h"
#include "wingdi.h"
#include "winuser.h"
#include "winnls.h"
#include "commctrl.h"
#include "comctl32.h"
#include "imagelist.h"
#include "tmschema.h"
#include "uxtheme.h"
#include "wine/debug.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 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 */
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 */
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)
#define HEADER_GetInfoPtr(hwnd) ((HEADER_INFO *)GetWindowLongPtrW(hwnd,0))
static BOOL HEADER_PrepareCallbackItems(HWND hwnd, INT iItem, INT reqMask);
static void HEADER_FreeCallbackItems(HEADER_ITEM *lpItem);
static LRESULT HEADER_SendNotify(HWND hwnd, UINT code, NMHDR *hdr);
static LRESULT HEADER_SendCtrlCustomDraw(HWND hwnd, DWORD dwDrawStage, HDC hdc, RECT *rect);
static const WCHAR themeClass[] = {'H','e','a','d','e','r',0};
static WCHAR emptyString[] = {0};
static void HEADER_DisposeItem(HEADER_ITEM *lpItem)
{
if (lpItem->pszText)
{
Free(lpItem->pszText);
}
}
static void HEADER_StoreHDItemInHeader(HEADER_ITEM *lpItem, UINT mask, 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)
{
if (lpItem->pszText)
{
Free(lpItem->pszText);
lpItem->pszText = NULL;
}
if (phdi->pszText != LPSTR_TEXTCALLBACKW) /* covers != TEXTCALLBACKA too */
{
LPWSTR pszText = (phdi->pszText != NULL ? phdi->pszText : emptyString);
if (fUnicode)
Str_SetPtrW(&lpItem->pszText, pszText);
else
Str_SetPtrAtoW(&lpItem->pszText, (LPSTR)pszText);
lpItem->callbackMask &= ~HDI_TEXT;
}
else
{
lpItem->pszText = NULL;
lpItem->callbackMask |= HDI_TEXT;
}
}
}
inline static LRESULT
HEADER_IndexToOrder (HWND hwnd, INT iItem)
{
HEADER_INFO *infoPtr = HEADER_GetInfoPtr (hwnd);
HEADER_ITEM *lpItem = &infoPtr->items[iItem];
return lpItem->iOrder;
}
static INT
HEADER_OrderToIndex(HWND hwnd, WPARAM wParam)
{
HEADER_INFO *infoPtr = HEADER_GetInfoPtr (hwnd);
INT iorder = (INT)wParam;
if ((iorder <0) || iorder >= infoPtr->uNumItem)
return iorder;
return infoPtr->order[iorder];
}
static void
HEADER_ChangeItemOrder(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(HWND hwnd, INT iItem)
{
return HEADER_OrderToIndex(hwnd, HEADER_IndexToOrder(hwnd, iItem)+1);
}
static INT
HEADER_PrevItem(HWND hwnd, INT iItem)
{
return HEADER_OrderToIndex(hwnd, HEADER_IndexToOrder(hwnd, iItem)-1);
}
static void
HEADER_SetItemBounds (HWND hwnd)
{
HEADER_INFO *infoPtr = HEADER_GetInfoPtr (hwnd);
HEADER_ITEM *phdi;
RECT rect;
unsigned int i;
int x;
infoPtr->bRectsValid = TRUE;
if (infoPtr->uNumItem == 0)
return;
GetClientRect (hwnd, &rect);
x = rect.left;
for (i = 0; i < infoPtr->uNumItem; i++) {
phdi = &infoPtr->items[HEADER_OrderToIndex(hwnd,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 (HWND hwnd, WPARAM wParam)
{
HEADER_INFO *infoPtr = HEADER_GetInfoPtr (hwnd);
infoPtr->bRectsValid = FALSE;
return 0;
}
static void HEADER_GetHotDividerRect(HWND hwnd, 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(hwnd, 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(hwnd, &clientRect);
*r = clientRect;
r->right = r->left + HOT_DIVIDER_WIDTH/2;
}
}
static INT
HEADER_DrawItem (HWND hwnd, HDC hdc, INT iItem, BOOL bHotTrack, LRESULT lCDFlags)
{
HEADER_INFO *infoPtr = HEADER_GetInfoPtr (hwnd);
HEADER_ITEM *phdi = &infoPtr->items[iItem];
RECT r;
INT oldBkMode;
HTHEME theme = GetWindowTheme (hwnd);
NMCUSTOMDRAW nmcd;
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;
/* Set the colors before sending NM_CUSTOMDRAW so that it can change them */
SetTextColor(hdc, (bHotTrack && !theme) ? COLOR_HIGHLIGHT : COLOR_BTNTEXT);
SetBkColor(hdc, GetSysColor(COLOR_3DFACE));
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(hwnd, NM_CUSTOMDRAW, (NMHDR *)&nmcd);
if (lCDItemFlags & CDRF_SKIPDEFAULT)
return phdi->rect.right;
}
if (theme != NULL) {
int state = (phdi->bDown) ? HIS_PRESSED :
(bHotTrack ? HIS_HOT : HIS_NORMAL);
DrawThemeBackground (theme, hdc, HP_HEADERITEM, state,
&r, NULL);
GetThemeBackgroundContentRect (theme, hdc, HP_HEADERITEM, state,
&r, &r);
}
else {
HBRUSH hbr;
if (GetWindowLongW (hwnd, GWL_STYLE) & HDS_BUTTONS) {
if (phdi->bDown) {
DrawEdge (hdc, &r, BDR_RAISEDOUTER,
BF_RECT | BF_FLAT | BF_MIDDLE | BF_ADJUST);
}
else
DrawEdge (hdc, &r, EDGE_RAISED,
BF_RECT | BF_SOFT | BF_MIDDLE | BF_ADJUST);
}
else
DrawEdge (hdc, &r, EDGE_ETCHED, BF_BOTTOM | BF_RIGHT | BF_ADJUST);
hbr = CreateSolidBrush(GetBkColor(hdc));
FillRect(hdc, &r, hbr);
DeleteObject(hbr);
}
if (phdi->bDown) {
r.left += 2;
r.top += 2;
}
if (phdi->fmt & HDF_OWNERDRAW) {
DRAWITEMSTRUCT dis;
dis.CtlType = ODT_HEADER;
dis.CtlID = GetWindowLongPtrW (hwnd, GWLP_ID);
dis.itemID = iItem;
dis.itemAction = ODA_DRAWENTIRE;
dis.itemState = phdi->bDown ? ODS_SELECTED : 0;
dis.hwndItem = hwnd;
dis.hDC = hdc;
dis.rcItem = phdi->rect;
dis.itemData = phdi->lParam;
oldBkMode = SetBkMode(hdc, TRANSPARENT);
SendMessageW (infoPtr->hwndNotify, WM_DRAWITEM,
(WPARAM)dis.CtlID, (LPARAM)&dis);
if (oldBkMode != TRANSPARENT)
SetBkMode(hdc, oldBkMode);
}
else {
UINT rw, rh, /* width and height of r */
*x = NULL, *w = NULL; /* x and width of the pic (bmp or img) which is part of cnt */
/* cnt,txt,img,bmp */
UINT cx, tx, ix, bx,
cw, tw, iw, bw;
BITMAP bmp;
HEADER_PrepareCallbackItems(hwnd, iItem, HDI_TEXT|HDI_IMAGE);
cw = tw = iw = bw = 0;
rw = r.right - r.left;
rh = r.bottom - r.top;
if (phdi->fmt & HDF_STRING) {
RECT textRect;
SetRectEmpty(&textRect);
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_IMAGE) && (infoPtr->himl)) {
iw = infoPtr->himl->cx + 2 * infoPtr->iMargin;
x = &ix;
w = &iw;
}
if ((phdi->fmt & HDF_BITMAP) && (phdi->hbm)) {
GetObjectW (phdi->hbm, sizeof(BITMAP), (LPVOID)&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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -