1217 lines
29 KiB
C
1217 lines
29 KiB
C
#include "ctlspriv.h"
|
|
|
|
typedef struct {
|
|
short x;
|
|
short cxy;
|
|
short fmt;
|
|
LPSTR pszText;
|
|
HBITMAP hbm;
|
|
LPARAM lParam;
|
|
} HDI;
|
|
|
|
// BUGBUG: store the style here too, set at create time
|
|
typedef struct {
|
|
HWND hwnd;
|
|
HWND hwndParent;
|
|
UINT flags;
|
|
int iRepaint; // First item needing repaint
|
|
int cxEllipses;
|
|
int cxDividerSlop;
|
|
int cyChar;
|
|
HFONT hfont;
|
|
HDSA hdsaHDI; // list of HDI's
|
|
} HD;
|
|
|
|
LRESULT CALLBACK Header_WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
|
|
|
|
// Message handler functions
|
|
|
|
BOOL NEAR Header_OnCreate(HD* phd, CREATESTRUCT FAR* lpCreateStruct);
|
|
void NEAR Header_OnDestroy(HD* phd);
|
|
void NEAR Header_OnPaint(HD* phd, HDC hdcIn);
|
|
#if 0
|
|
BOOL NEAR Header_OnEraseBkgnd(HD* phd, HDC hdc);
|
|
#endif
|
|
void NEAR Header_OnCommand(HD* phd, int id, HWND hwndCtl, UINT codeNotify);
|
|
void NEAR Header_OnEnable(HD* phd, BOOL fEnable);
|
|
UINT NEAR Header_OnGetDlgCode(HD* phd, MSG FAR* lpmsg);
|
|
void NEAR Header_OnLButtonDown(HD* phd, BOOL fDoubleClick, int x, int y, UINT keyFlags);
|
|
BOOL NEAR Header_StartDrag(HD* phd, int i, int x, int y);
|
|
BOOL NEAR Header_IsTracking(HD* phd);
|
|
void NEAR Header_OnMouseMove(HD* phd, int x, int y, UINT keyFlags);
|
|
void NEAR Header_OnLButtonUp(HD* phd, int x, int y, UINT keyFlags);
|
|
void NEAR Header_OnCancelMode(HD* phd);
|
|
void NEAR Header_OnSetFont(HD* plv, HFONT hfont, BOOL fRedraw);
|
|
int NEAR PASCAL Header_OnHitTest(HD* phd, HD_HITTESTINFO FAR *phdht);
|
|
HFONT NEAR Header_OnGetFont(HD* plv);
|
|
|
|
BOOL NEAR Header_GetItemRect(HD* phd, int i, RECT FAR* prc);
|
|
void NEAR Header_Redraw(HD* phd, HDC hdc, RECT FAR* prcClip, int i);
|
|
void NEAR Header_RedrawItem(HD* phd, int i);
|
|
|
|
// HDM_* Message handler functions
|
|
|
|
int NEAR Header_OnInsertItem(HD* phd, int i, const HD_ITEM FAR* pitem);
|
|
BOOL NEAR Header_OnDeleteItem(HD* phd, int i);
|
|
BOOL NEAR Header_OnGetItem(HD* phd, int i, HD_ITEM FAR* pitem);
|
|
BOOL NEAR Header_OnSetItem(HD* phd, int i, const HD_ITEM FAR* pitem);
|
|
BOOL NEAR Header_OnLayout(HD* phd, HD_LAYOUT FAR* playout);
|
|
BOOL NEAR Header_OnSetCursor(HD* phd, HWND hwndCursor, UINT codeHitTest, UINT msg);
|
|
|
|
|
|
#define Header_GetItemPtr(phd, i) (HDI FAR*)DSA_GetItemPtr((phd)->hdsaHDI, (i))
|
|
|
|
|
|
#pragma code_seg(CODESEG_INIT)
|
|
|
|
BOOL FAR PASCAL Header_Init(HINSTANCE hinst)
|
|
{
|
|
WNDCLASS wc;
|
|
|
|
if (!GetClassInfo(hinst, c_szHeaderClass, &wc)) {
|
|
#ifndef WIN32
|
|
#ifndef IEWIN31
|
|
|
|
// Use stab WndProc to avoid loading segment on init.
|
|
|
|
LRESULT CALLBACK _Header_WndProc(HWND hwnd, UINT wMsg, WPARAM wParam, LPARAM lParam);
|
|
wc.lpfnWndProc = _Header_WndProc;
|
|
#else
|
|
wc.lpfnWndProc = Header_WndProc;
|
|
#endif
|
|
#else
|
|
wc.lpfnWndProc = Header_WndProc;
|
|
#endif
|
|
wc.hCursor = NULL; // we do WM_SETCURSOR handling
|
|
wc.hIcon = NULL;
|
|
wc.lpszMenuName = NULL;
|
|
wc.hInstance = hinst;
|
|
wc.lpszClassName = c_szHeaderClass;
|
|
wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1);
|
|
wc.style = CS_DBLCLKS | CS_GLOBALCLASS;
|
|
wc.cbWndExtra = sizeof(HD*);
|
|
wc.cbClsExtra = 0;
|
|
|
|
return RegisterClass(&wc);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
#pragma code_seg()
|
|
|
|
|
|
LRESULT CALLBACK Header_WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
HD* phd = (HD*)GetWindowInt(hwnd, 0);
|
|
|
|
if (phd == NULL)
|
|
{
|
|
if (msg == WM_NCCREATE)
|
|
{
|
|
phd = (HD*)NearAlloc(sizeof(HD));
|
|
|
|
if (phd == NULL)
|
|
return 0L;
|
|
|
|
phd->hwnd = hwnd;
|
|
phd->hwndParent = GetParent(hwnd);
|
|
SetWindowInt(hwnd, 0, (int)phd);
|
|
}
|
|
else
|
|
{
|
|
return DefWindowProc(hwnd, msg, wParam, lParam);
|
|
}
|
|
}
|
|
|
|
if (msg == WM_NCDESTROY)
|
|
{
|
|
//DWORD result = HANDLE_MSG(hwnd, WM_NCDESTROY, Header_OnNCDestroy);
|
|
|
|
NearFree(phd);
|
|
phd = NULL;
|
|
SetWindowInt(hwnd, 0, 0);
|
|
|
|
//return result;
|
|
}
|
|
|
|
switch (msg)
|
|
{
|
|
HANDLE_MSG(phd, WM_CREATE, Header_OnCreate);
|
|
HANDLE_MSG(phd, WM_DESTROY, Header_OnDestroy);
|
|
#if 0
|
|
HANDLE_MSG(phd, WM_ERASEBKGND, Header_OnEraseBkgnd);
|
|
#endif
|
|
HANDLE_MSG(phd, WM_ENABLE, Header_OnEnable);
|
|
HANDLE_MSG(phd, WM_SETCURSOR, Header_OnSetCursor);
|
|
HANDLE_MSG(phd, WM_MOUSEMOVE, Header_OnMouseMove);
|
|
HANDLE_MSG(phd, WM_LBUTTONDOWN, Header_OnLButtonDown);
|
|
HANDLE_MSG(phd, WM_LBUTTONDBLCLK, Header_OnLButtonDown);
|
|
HANDLE_MSG(phd, WM_LBUTTONUP, Header_OnLButtonUp);
|
|
HANDLE_MSG(phd, WM_CANCELMODE, Header_OnCancelMode);
|
|
HANDLE_MSG(phd, WM_GETDLGCODE, Header_OnGetDlgCode);
|
|
HANDLE_MSG(phd, WM_SETFONT, Header_OnSetFont);
|
|
HANDLE_MSG(phd, WM_GETFONT, Header_OnGetFont);
|
|
|
|
case WM_PRINTCLIENT:
|
|
case WM_PAINT:
|
|
Header_OnPaint(phd, (HDC)wParam);
|
|
return(0);
|
|
|
|
case WM_RBUTTONUP:
|
|
if (!SendNotify(phd->hwndParent, phd->hwnd, NM_RCLICK, NULL))
|
|
goto DoDefWindowProc;
|
|
break;
|
|
|
|
case WM_STYLECHANGED:
|
|
// we don't cache our style so relay out and invaidate
|
|
InvalidateRect(phd->hwnd, NULL, TRUE);
|
|
break;
|
|
|
|
case HDM_GETITEMCOUNT:
|
|
return (LPARAM)(UINT)DSA_GetItemCount(phd->hdsaHDI);
|
|
|
|
case HDM_INSERTITEM:
|
|
return (LPARAM)Header_OnInsertItem(phd, (int)wParam, (const HD_ITEM FAR*)lParam);
|
|
|
|
case HDM_DELETEITEM:
|
|
return (LPARAM)Header_OnDeleteItem(phd, (int)wParam);
|
|
|
|
case HDM_GETITEM:
|
|
return (LPARAM)Header_OnGetItem(phd, (int)wParam, (HD_ITEM FAR*)lParam);
|
|
|
|
case HDM_SETITEM:
|
|
return (LPARAM)Header_OnSetItem(phd, (int)wParam, (const HD_ITEM FAR*)lParam);
|
|
|
|
case HDM_LAYOUT:
|
|
return (LPARAM)Header_OnLayout(phd, (HD_LAYOUT FAR*)lParam);
|
|
|
|
case HDM_HITTEST:
|
|
return (LPARAM)Header_OnHitTest(phd, (HD_HITTESTINFO FAR *)lParam);
|
|
|
|
default:
|
|
DoDefWindowProc:
|
|
return DefWindowProc(hwnd, msg, wParam, lParam);
|
|
}
|
|
}
|
|
|
|
BOOL NEAR Header_SendChange(HD* phd, int i, int code, const HD_ITEM FAR* pitem)
|
|
{
|
|
HD_NOTIFY nm;
|
|
|
|
nm.iItem = i;
|
|
nm.pitem = (HD_ITEM FAR*)pitem;
|
|
nm.iButton = 0;
|
|
|
|
return !(BOOL)SendNotify(phd->hwndParent, phd->hwnd, code, &nm.hdr);
|
|
}
|
|
|
|
void NEAR Header_Notify(HD* phd, int i, int iButton, int code)
|
|
{
|
|
HD_NOTIFY nm;
|
|
nm.iItem = i;
|
|
nm.iButton = iButton;
|
|
nm.pitem = NULL;
|
|
|
|
SendNotify(phd->hwndParent, phd->hwnd, code, &nm.hdr);
|
|
}
|
|
|
|
|
|
void NEAR Header_NewFont(HD* phd, HFONT hfont)
|
|
{
|
|
HDC hdc;
|
|
SIZE siz;
|
|
|
|
hdc = GetDC(HWND_DESKTOP);
|
|
|
|
if (hfont)
|
|
SelectFont(hdc, hfont);
|
|
|
|
GetTextExtentPoint(hdc, c_szEllipses, CCHELLIPSES, &siz);
|
|
|
|
phd->cxEllipses = siz.cx;
|
|
phd->cyChar = siz.cy;
|
|
phd->hfont = hfont;
|
|
|
|
ReleaseDC(HWND_DESKTOP, hdc);
|
|
}
|
|
|
|
BOOL NEAR Header_OnCreate(HD* phd, CREATESTRUCT FAR* lpCreateStruct)
|
|
{
|
|
phd->iRepaint = 0;
|
|
phd->flags = 0;
|
|
phd->hfont = NULL;
|
|
|
|
phd->hdsaHDI = DSA_Create(sizeof(HDI), 4);
|
|
phd->cxDividerSlop = 8 * g_cxBorder;
|
|
Header_NewFont(phd, NULL);
|
|
return TRUE;
|
|
}
|
|
|
|
void NEAR Header_OnDestroy(HD* phd)
|
|
{
|
|
int j;
|
|
HDI FAR * phdi;
|
|
|
|
// We must walk through and destroy all of the string pointers that
|
|
// are contained in the structures before we pass it off to the
|
|
// DSA_Destroy function...
|
|
for (j = DSA_GetItemCount(phd->hdsaHDI) - 1; j >= 0 ; j--) {
|
|
phdi = Header_GetItemPtr(phd, j);
|
|
if (phdi && phdi->pszText)
|
|
{
|
|
Str_Set(&phdi->pszText, NULL);
|
|
}
|
|
}
|
|
|
|
DSA_Destroy(phd->hdsaHDI);
|
|
}
|
|
|
|
void NEAR Header_OnPaint(HD* phd, HDC hdc)
|
|
{
|
|
PAINTSTRUCT ps;
|
|
|
|
if (hdc)
|
|
{
|
|
GetClientRect(phd->hwnd, &ps.rcPaint);
|
|
Header_Redraw(phd, hdc, &ps.rcPaint, 0);
|
|
}
|
|
else
|
|
{
|
|
Header_Redraw(phd, BeginPaint(phd->hwnd, &ps), &ps.rcPaint, 0);
|
|
EndPaint(phd->hwnd, &ps);
|
|
}
|
|
}
|
|
|
|
#if 0
|
|
BOOL NEAR Header_OnEraseBkgnd(HD* phd, HDC hdc)
|
|
{
|
|
RECT rc;
|
|
|
|
GetClientRect(phd->hwnd, &rc);
|
|
FillRect(hdc, &rc, g_hbrBtnFace);
|
|
return TRUE;
|
|
}
|
|
#endif
|
|
|
|
void NEAR Header_OnCommand(HD* phd, int id, HWND hwndCtl, UINT codeNotify)
|
|
{
|
|
}
|
|
|
|
void NEAR Header_OnEnable(HD* phd, BOOL fEnable)
|
|
{
|
|
}
|
|
|
|
UINT NEAR Header_OnGetDlgCode(HD* phd, MSG FAR* lpmsg)
|
|
{
|
|
return DLGC_WANTTAB | DLGC_WANTARROWS;
|
|
}
|
|
|
|
|
|
int NEAR Header_HitTest(HD* phd, int x, int y, UINT FAR* pflags)
|
|
{
|
|
UINT flags = 0;
|
|
POINT pt;
|
|
RECT rc;
|
|
HDI FAR* phdi;
|
|
int i;
|
|
|
|
pt.x = x; pt.y = y;
|
|
|
|
GetClientRect(phd->hwnd, &rc);
|
|
|
|
flags = 0;
|
|
i = -1;
|
|
if (x < rc.left)
|
|
flags |= HHT_TOLEFT;
|
|
else if (x >= rc.right)
|
|
flags |= HHT_TORIGHT;
|
|
if (y < rc.top)
|
|
flags |= HHT_ABOVE;
|
|
else if (y >= rc.bottom)
|
|
flags |= HHT_BELOW;
|
|
|
|
if (flags == 0)
|
|
{
|
|
int cItems = DSA_GetItemCount(phd->hdsaHDI);
|
|
int xPrev = 0;
|
|
BOOL fPrevZero = FALSE;
|
|
int xItem;
|
|
int cxSlop;
|
|
|
|
phdi = Header_GetItemPtr(phd, 0);
|
|
//DebugMsg(DM_TRACE, "Hit Test begin");
|
|
for (i = 0; i <= cItems; i++, phdi++, xPrev = xItem)
|
|
{
|
|
xItem = (i == cItems ? rc.right : phdi->x);
|
|
|
|
//DebugMsg(DM_TRACE, "x = %d xItem = %d xPrev = %d fPrevZero = %d", x, xItem, xPrev, xPrev == xItem);
|
|
if (xItem == xPrev)
|
|
{
|
|
// Skip zero width items...
|
|
fPrevZero = TRUE;
|
|
continue;
|
|
}
|
|
|
|
cxSlop = min((xItem - xPrev) / 4, phd->cxDividerSlop);
|
|
|
|
if (x >= xPrev && x < xItem)
|
|
{
|
|
flags = HHT_ONHEADER;
|
|
|
|
if (i > 0 && x < xPrev + cxSlop)
|
|
{
|
|
i--;
|
|
flags = HHT_ONDIVIDER;
|
|
|
|
if (fPrevZero && x > xPrev)
|
|
{
|
|
flags = HHT_ONDIVOPEN;
|
|
}
|
|
}
|
|
else if (x >= xItem - cxSlop)
|
|
{
|
|
flags = HHT_ONDIVIDER;
|
|
}
|
|
break;
|
|
}
|
|
fPrevZero = FALSE;
|
|
}
|
|
if (i == cItems)
|
|
{
|
|
i = -1;
|
|
flags = HHT_NOWHERE;
|
|
}
|
|
}
|
|
*pflags = flags;
|
|
return i;
|
|
}
|
|
|
|
int NEAR PASCAL Header_OnHitTest(HD* phd, HD_HITTESTINFO FAR *phdht)
|
|
{
|
|
if (phdht) {
|
|
phdht->iItem = Header_HitTest(phd, phdht->pt.x, phdht->pt.y, &phdht->flags);
|
|
return phdht->iItem;
|
|
} else
|
|
return -1;
|
|
}
|
|
|
|
BOOL NEAR Header_OnSetCursor(HD* phd, HWND hwndCursor, UINT codeHitTest, UINT msg)
|
|
{
|
|
POINT pt;
|
|
UINT flags;
|
|
DWORD dw;
|
|
LPCSTR lpCur;
|
|
HINSTANCE hinst;
|
|
|
|
if (phd->hwnd != hwndCursor || codeHitTest >= 0x8000)
|
|
return FALSE;
|
|
|
|
dw = GetMessagePos();
|
|
|
|
pt.x = LOWORD(dw);
|
|
pt.y = HIWORD(dw);
|
|
|
|
ScreenToClient(hwndCursor, &pt);
|
|
|
|
Header_HitTest(phd, pt.x, pt.y, &flags);
|
|
|
|
hinst = HINST_THISDLL;
|
|
switch (flags)
|
|
{
|
|
case HHT_ONDIVIDER:
|
|
lpCur = MAKEINTRESOURCE(IDC_DIVIDER);
|
|
break;
|
|
case HHT_ONDIVOPEN:
|
|
lpCur = MAKEINTRESOURCE(IDC_DIVOPEN);
|
|
break;
|
|
default:
|
|
lpCur = IDC_ARROW;
|
|
hinst = NULL;
|
|
break;
|
|
}
|
|
SetCursor(LoadCursor(hinst, lpCur));
|
|
return TRUE;
|
|
}
|
|
|
|
void NEAR Header_DrawDivider(HD* phd, int x)
|
|
{
|
|
RECT rc;
|
|
HDC hdc = GetDC(phd->hwnd);
|
|
|
|
GetClientRect(phd->hwnd, &rc);
|
|
rc.left = x;
|
|
rc.right = x + g_cxBorder;
|
|
|
|
InvertRect(hdc, &rc);
|
|
|
|
ReleaseDC(phd->hwnd, hdc);
|
|
}
|
|
|
|
// BUGBUG: no need for most of these globals.
|
|
|
|
HWND g_hwndTrack = NULL;
|
|
int g_iTrack = 0;
|
|
int g_bTrackPress = FALSE; // is the button pressed?
|
|
UINT g_flagsTrack = 0;
|
|
int g_xTrack = 0;
|
|
int g_dxTrack = 0;
|
|
int g_xMinTrack = 0;
|
|
|
|
int NEAR Header_GetItemPosition(HD* phd, int i)
|
|
{
|
|
if (i < 0)
|
|
return 0;
|
|
return (Header_GetItemPtr(phd, i))->x;
|
|
}
|
|
|
|
int NEAR Header_PinDividerPos(HD* phd, int x)
|
|
{
|
|
x += g_dxTrack;
|
|
if (x < g_xMinTrack)
|
|
x = g_xMinTrack;
|
|
return x;
|
|
}
|
|
|
|
void NEAR Header_OnLButtonDown(HD* phd, BOOL fDoubleClick, int x, int y, UINT keyFlags)
|
|
{
|
|
HD_ITEM hd;
|
|
int i;
|
|
UINT flags;
|
|
|
|
i = Header_HitTest(phd, x, y, &flags);
|
|
if (flags & (HHT_ONDIVIDER))
|
|
{
|
|
if (fDoubleClick) {
|
|
Header_SendChange(phd, i, HDN_DIVIDERDBLCLICK, NULL);
|
|
}
|
|
}
|
|
if (flags & (HHT_ONDIVIDER | HHT_ONHEADER | HHT_ONDIVOPEN))
|
|
{
|
|
g_hwndTrack = phd->hwnd;
|
|
g_iTrack = i;
|
|
g_flagsTrack = flags;
|
|
SetCapture(phd->hwnd);
|
|
}
|
|
if (flags & (HHT_ONDIVIDER | HHT_ONDIVOPEN))
|
|
{
|
|
|
|
// We should first send out the HDN_BEGINTRACK notification
|
|
|
|
g_xMinTrack = Header_GetItemPosition(phd, i - 1);
|
|
g_xTrack = Header_GetItemPosition(phd, i);
|
|
g_dxTrack = g_xTrack - x;
|
|
|
|
hd.mask = HDI_WIDTH;
|
|
hd.cxy = x - g_xMinTrack;
|
|
if (!Header_SendChange(phd, i, HDN_BEGINTRACK, &hd))
|
|
{
|
|
// They said no!
|
|
ReleaseCapture();
|
|
g_hwndTrack = NULL;
|
|
return;
|
|
}
|
|
|
|
|
|
x = Header_PinDividerPos(phd, x);
|
|
Header_DrawDivider(phd, x);
|
|
}
|
|
else if ((flags & HHT_ONHEADER) && (GetWindowStyle(phd->hwnd) & HDS_BUTTONS))
|
|
{
|
|
if (fDoubleClick) {
|
|
Header_SendChange(phd, i, HDN_ITEMDBLCLICK, NULL);
|
|
} else {
|
|
g_bTrackPress = TRUE;
|
|
Header_RedrawItem(phd, g_iTrack);
|
|
}
|
|
}
|
|
}
|
|
|
|
void NEAR Header_OnMouseMove(HD* phd, int x, int y, UINT keyFlags)
|
|
{
|
|
UINT flags;
|
|
int i;
|
|
HD_ITEM hd;
|
|
|
|
if (Header_IsTracking(phd))
|
|
{
|
|
if (g_flagsTrack & (HHT_ONDIVIDER | HHT_ONDIVOPEN))
|
|
{
|
|
x = Header_PinDividerPos(phd, x);
|
|
|
|
|
|
// Let the Owner have a chance to update this.
|
|
|
|
hd.mask = HDI_WIDTH;
|
|
hd.cxy = x - g_xMinTrack;
|
|
if (!Header_SendChange(phd, g_iTrack, HDN_TRACK, &hd))
|
|
{
|
|
// We need to cancel tracking
|
|
ReleaseCapture();
|
|
g_hwndTrack = NULL;
|
|
|
|
// Undraw the last divider we displayed
|
|
Header_DrawDivider(phd, g_xTrack);
|
|
return;
|
|
}
|
|
|
|
// We should update our x depending on what caller did
|
|
x = hd.cxy + g_xMinTrack;
|
|
|
|
Header_DrawDivider(phd, g_xTrack);
|
|
Header_DrawDivider(phd, x);
|
|
g_xTrack = x;
|
|
}
|
|
else if (g_flagsTrack & HHT_ONHEADER)
|
|
{
|
|
i = Header_HitTest(phd, x, y, &flags);
|
|
// if pressing on button and it's not pressed, press it
|
|
if (flags & HHT_ONHEADER && i == g_iTrack)
|
|
{
|
|
if ((!g_bTrackPress) && (GetWindowStyle(phd->hwnd) & HDS_BUTTONS))
|
|
{
|
|
g_bTrackPress = TRUE;
|
|
Header_RedrawItem(phd, g_iTrack);
|
|
}
|
|
}
|
|
// tracked off of button. if pressed, pop it
|
|
else if ((g_bTrackPress) && (GetWindowStyle(phd->hwnd) & HDS_BUTTONS))
|
|
{
|
|
g_bTrackPress = FALSE;
|
|
Header_RedrawItem(phd, g_iTrack);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void NEAR Header_OnLButtonUp(HD* phd, int x, int y, UINT keyFlags)
|
|
{
|
|
if (Header_IsTracking(phd))
|
|
{
|
|
ReleaseCapture();
|
|
g_hwndTrack = NULL;
|
|
|
|
if (g_flagsTrack & (HHT_ONDIVIDER | HHT_ONDIVOPEN))
|
|
{
|
|
HD_ITEM item;
|
|
|
|
Header_DrawDivider(phd, g_xTrack);
|
|
|
|
item.mask = HDI_WIDTH;
|
|
item.cxy = g_xTrack - g_xMinTrack;
|
|
|
|
// Let the owner have a chance to say yes.
|
|
|
|
|
|
if (Header_SendChange(phd, g_iTrack, HDN_ENDTRACK, &item))
|
|
Header_OnSetItem(phd, g_iTrack, &item);
|
|
|
|
RedrawWindow(phd->hwnd, NULL, NULL, RDW_INVALIDATE | RDW_ERASE);
|
|
}
|
|
else if ((g_flagsTrack & HHT_ONHEADER && g_bTrackPress) && (GetWindowStyle(phd->hwnd) & HDS_BUTTONS))
|
|
{
|
|
// Notify the owner that the item has been clicked
|
|
Header_Notify(phd, g_iTrack, 0, HDN_ITEMCLICK);
|
|
g_bTrackPress = FALSE;
|
|
Header_RedrawItem(phd, g_iTrack);
|
|
}
|
|
}
|
|
}
|
|
|
|
BOOL NEAR Header_StartDrag(HD* phd, int i, int x, int y)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL NEAR Header_IsTracking(HD* phd)
|
|
{
|
|
if (!g_hwndTrack || phd->hwnd != g_hwndTrack)
|
|
return FALSE;
|
|
|
|
if (GetCapture() != phd->hwnd)
|
|
{
|
|
g_flagsTrack = 0;
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
// BUGBUG: use this to check capture loss
|
|
void NEAR Header_OnCancelMode(HD* phd)
|
|
{
|
|
}
|
|
|
|
void NEAR Header_OnSetFont(HD* phd, HFONT hfont, BOOL fRedraw)
|
|
{
|
|
if (hfont != phd->hfont)
|
|
{
|
|
Header_NewFont(phd, hfont);
|
|
|
|
if (fRedraw)
|
|
RedrawWindow(phd->hwnd, NULL, NULL, RDW_INVALIDATE | RDW_ERASE);
|
|
}
|
|
}
|
|
|
|
HFONT NEAR Header_OnGetFont(HD* phd)
|
|
{
|
|
return phd->hfont;
|
|
}
|
|
|
|
|
|
|
|
int NEAR Header_OnInsertItem(HD* phd, int i, const HD_ITEM FAR* pitem)
|
|
{
|
|
HDI hdi;
|
|
int x;
|
|
HDI FAR* phdi;
|
|
int j;
|
|
|
|
if (!pitem)
|
|
return -1;
|
|
|
|
if (pitem->mask == 0)
|
|
return -1;
|
|
|
|
x = pitem->cxy;
|
|
|
|
if (i > DSA_GetItemCount(phd->hdsaHDI))
|
|
i = DSA_GetItemCount(phd->hdsaHDI);
|
|
|
|
if (i > 0)
|
|
{
|
|
|
|
phdi = Header_GetItemPtr(phd, i - 1);
|
|
if (phdi)
|
|
x += phdi->x;
|
|
|
|
}
|
|
|
|
// move everything else over
|
|
for (j = DSA_GetItemCount(phd->hdsaHDI) - 1; j >= i ; j--) {
|
|
phdi = Header_GetItemPtr(phd, j);
|
|
phdi->x += pitem->cxy;
|
|
}
|
|
|
|
hdi.x = x;
|
|
hdi.lParam = pitem->lParam;
|
|
hdi.fmt = pitem->fmt;
|
|
hdi.pszText = NULL;
|
|
hdi.cxy = pitem->cxy;
|
|
if ((pitem->mask & HDI_TEXT) && (pitem->pszText != NULL))
|
|
{
|
|
if (!Str_Set(&hdi.pszText, pitem->pszText))
|
|
return -1;
|
|
|
|
// Unless ownerdraw make sure the text bit is on!
|
|
if ((pitem->mask & HDF_OWNERDRAW) == 0)
|
|
hdi.fmt |= HDF_STRING;
|
|
}
|
|
else
|
|
{
|
|
hdi.fmt &= ~(HDF_STRING);
|
|
}
|
|
|
|
if ((pitem->mask & HDI_BITMAP) && (pitem->hbm != NULL))
|
|
{
|
|
hdi.hbm = pitem->hbm;
|
|
|
|
// Unless ownerdraw make sure the text bit is on!
|
|
if ((pitem->mask & HDF_OWNERDRAW) == 0)
|
|
hdi.fmt |= HDF_BITMAP;
|
|
}
|
|
else
|
|
{
|
|
hdi.hbm = NULL;
|
|
hdi.fmt &= ~(HDF_BITMAP);
|
|
}
|
|
|
|
i = DSA_InsertItem(phd->hdsaHDI, i, &hdi);
|
|
if (i == -1) {
|
|
// failed to add
|
|
if (hdi.pszText)
|
|
Str_Set(&hdi.pszText, NULL);
|
|
} else {
|
|
// succeeded! redraw
|
|
RECT rc;
|
|
GetClientRect(phd->hwnd, &rc);
|
|
rc.left = x - pitem->cxy;
|
|
RedrawWindow(phd->hwnd, &rc, NULL, RDW_INVALIDATE | RDW_ERASE);
|
|
}
|
|
|
|
return i;
|
|
}
|
|
|
|
BOOL NEAR Header_OnDeleteItem(HD* phd, int i)
|
|
{
|
|
HDI hdi;
|
|
RECT rc;
|
|
int iWidth;
|
|
HDI FAR * phdi;
|
|
int j;
|
|
|
|
if (!DSA_GetItem(phd->hdsaHDI, i, &hdi))
|
|
return FALSE;
|
|
|
|
GetClientRect(phd->hwnd, &rc);
|
|
iWidth = rc.right;
|
|
Header_GetItemRect(phd, i, &rc);
|
|
InflateRect(&rc, g_cxBorder, g_cyBorder);
|
|
|
|
if (!DSA_DeleteItem(phd->hdsaHDI, i))
|
|
return FALSE;
|
|
|
|
if (hdi.pszText)
|
|
Str_Set(&hdi.pszText, NULL);
|
|
|
|
// move everything else over
|
|
for (j = DSA_GetItemCount(phd->hdsaHDI) - 1; j >= i ; j--) {
|
|
phdi = Header_GetItemPtr(phd, j);
|
|
phdi->x -= hdi.cxy;
|
|
}
|
|
|
|
rc.right = iWidth;
|
|
InvalidateRect(phd->hwnd, &rc, TRUE);
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL NEAR Header_OnGetItem(HD* phd, int i, HD_ITEM FAR* pitem)
|
|
{
|
|
HDI FAR* phdi;
|
|
UINT mask;
|
|
|
|
Assert(pitem);
|
|
|
|
if (!pitem)
|
|
return FALSE;
|
|
|
|
mask = pitem->mask;
|
|
|
|
phdi = Header_GetItemPtr(phd, i);
|
|
if (!phdi)
|
|
return FALSE;
|
|
|
|
if (mask & HDI_WIDTH)
|
|
{
|
|
int cx = phdi->x;
|
|
|
|
if (i > 0)
|
|
{
|
|
Assert((phdi - 1) == Header_GetItemPtr(phd, i - 1));
|
|
cx -= (phdi - 1)->x;
|
|
}
|
|
|
|
pitem->cxy = cx;
|
|
}
|
|
|
|
if (mask & HDI_FORMAT)
|
|
{
|
|
pitem->fmt = phdi->fmt;
|
|
}
|
|
|
|
if (mask & HDI_LPARAM)
|
|
{
|
|
pitem->lParam = phdi->lParam;
|
|
}
|
|
|
|
if (mask & HDI_TEXT)
|
|
{
|
|
if (!Str_GetPtr(phdi->pszText, pitem->pszText, pitem->cchTextMax))
|
|
return FALSE;
|
|
}
|
|
|
|
if (mask & HDI_BITMAP)
|
|
{
|
|
pitem->hbm = phdi->hbm;
|
|
}
|
|
|
|
pitem->mask = mask;
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL NEAR Header_OnSetItem(HD* phd, int i, const HD_ITEM FAR* pitem)
|
|
{
|
|
HDI FAR* phdi;
|
|
UINT mask;
|
|
int xOld;
|
|
BOOL fInvalidate;
|
|
|
|
Assert(pitem);
|
|
|
|
if (!pitem)
|
|
return FALSE;
|
|
|
|
phdi = Header_GetItemPtr(phd, i);
|
|
if (!phdi)
|
|
return FALSE;
|
|
|
|
mask = pitem->mask;
|
|
|
|
if (mask == 0)
|
|
return TRUE;
|
|
|
|
if (!Header_SendChange(phd, i, HDN_ITEMCHANGING, pitem))
|
|
return FALSE;
|
|
|
|
xOld = phdi->x;
|
|
if (mask & HDI_WIDTH)
|
|
{
|
|
int j;
|
|
HDI FAR* phdiLocal;
|
|
int dx = pitem->cxy;
|
|
|
|
if (dx < 0)
|
|
dx = 0;
|
|
|
|
if (i > 0)
|
|
{
|
|
Assert((phdi - 1) == Header_GetItemPtr(phd, i - 1));
|
|
dx += (phdi - 1)->x;
|
|
}
|
|
|
|
dx -= phdi->x;
|
|
|
|
phdi->cxy = pitem->cxy;
|
|
for (phdiLocal = phdi, j = i; j < DSA_GetItemCount(phd->hdsaHDI); j++, phdiLocal++)
|
|
{
|
|
phdiLocal->x += dx;
|
|
}
|
|
fInvalidate = TRUE;
|
|
}
|
|
if (mask & HDI_FORMAT) {
|
|
phdi->fmt = pitem->fmt;
|
|
fInvalidate = TRUE;
|
|
}
|
|
if (mask & HDI_LPARAM)
|
|
phdi->lParam = pitem->lParam;
|
|
|
|
if (mask & HDI_TEXT)
|
|
{
|
|
if (!Str_Set(&phdi->pszText, pitem->pszText))
|
|
return FALSE;
|
|
fInvalidate = TRUE;
|
|
}
|
|
|
|
if (mask & HDI_BITMAP)
|
|
{
|
|
phdi->hbm = pitem->hbm;
|
|
fInvalidate = TRUE;
|
|
}
|
|
|
|
Header_SendChange(phd, i, HDN_ITEMCHANGED, pitem);
|
|
|
|
if (fInvalidate) {
|
|
if (xOld == phdi->x) {
|
|
// no change in x
|
|
Header_RedrawItem(phd, i);
|
|
} else {
|
|
RECT rc;
|
|
GetClientRect(phd->hwnd, &rc);
|
|
|
|
if (i > 0) {
|
|
HDI FAR * phdiTemp;
|
|
phdiTemp = Header_GetItemPtr(phd, i - 1);
|
|
if (phdiTemp) {
|
|
rc.left = phdi->x;
|
|
}
|
|
}
|
|
RedrawWindow(phd->hwnd, &rc, NULL, RDW_INVALIDATE | RDW_ERASE);
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
// Compute layout for header bar, and leftover rectangle.
|
|
|
|
BOOL NEAR Header_OnLayout(HD* phd, HD_LAYOUT FAR* playout)
|
|
{
|
|
int cyHeader;
|
|
WINDOWPOS FAR* pwpos;
|
|
RECT FAR* prc;
|
|
|
|
Assert(playout);
|
|
|
|
if (!(playout && phd))
|
|
return FALSE;
|
|
|
|
if (!(playout->pwpos && playout->prc))
|
|
return FALSE;
|
|
|
|
pwpos = playout->pwpos;
|
|
prc = playout->prc;
|
|
|
|
cyHeader = phd->cyChar + 2 * g_cyEdge;
|
|
|
|
// BUGBUG: we should store the style at creat time
|
|
// internal hack style for use with LVS_REPORT|LVS_NOCOLUMNHEADER! edh
|
|
if (GetWindowStyle(phd->hwnd) & HDS_HIDDEN)
|
|
cyHeader = 0;
|
|
|
|
pwpos->hwndInsertAfter = NULL;
|
|
pwpos->flags = SWP_NOZORDER | SWP_NOACTIVATE;
|
|
|
|
// BUGBUG: Assert(phd->style & HDS_HORZ);
|
|
|
|
pwpos->x = prc->left;
|
|
pwpos->cx = prc->right - prc->left;
|
|
pwpos->y = prc->top;
|
|
pwpos->cy = cyHeader;
|
|
|
|
prc->top += cyHeader;
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL NEAR Header_GetItemRect(HD* phd, int i, RECT FAR* prc)
|
|
{
|
|
HDI FAR* phdi;
|
|
|
|
phdi = Header_GetItemPtr(phd, i);
|
|
if (!phdi)
|
|
return FALSE;
|
|
|
|
GetClientRect(phd->hwnd, prc);
|
|
|
|
prc->right = phdi->x;
|
|
prc->left = prc->right - phdi->cxy;
|
|
#ifdef DEBUG
|
|
if (i > 0)
|
|
Assert(prc->left == (phdi - 1)->x);
|
|
#endif
|
|
return TRUE;
|
|
}
|
|
|
|
void NEAR Header_RedrawItem(HD* phd, int i)
|
|
{
|
|
RECT rc;
|
|
|
|
Header_GetItemRect(phd, i, &rc);
|
|
InflateRect(&rc, g_cxBorder, g_cyBorder);
|
|
RedrawWindow(phd->hwnd, &rc, NULL, RDW_INVALIDATE | RDW_ERASE);
|
|
}
|
|
|
|
void NEAR _Header_DrawBitmap(HDC hdc, HBITMAP hbm, HDC hdcMem, RECT FAR *prc,
|
|
int fmt, UINT flags)
|
|
{
|
|
BITMAP bm;
|
|
RECT rc;
|
|
int xBitmap = 0;
|
|
int yBitmap = 0;
|
|
HBITMAP hbmOld;
|
|
|
|
if (GetObject(hbm, sizeof(bm), &bm) != sizeof(bm))
|
|
return; // could not get the info about bitmap.
|
|
|
|
if ((hbmOld = SelectObject(hdcMem, hbm)) == ERROR)
|
|
return; // an error happened.
|
|
|
|
rc = *prc;
|
|
rc.left += g_cxLabelMargin;
|
|
rc.right -= g_cxLabelMargin;
|
|
|
|
// If needed, add in a little extra margin...
|
|
|
|
if (flags & SHDT_EXTRAMARGIN)
|
|
{
|
|
rc.left += g_cxLabelMargin * 2;
|
|
rc.right -= g_cxLabelMargin * 2;
|
|
}
|
|
|
|
if (flags & SHDT_DEPRESSED)
|
|
OffsetRect(&rc, g_cxBorder, g_cyBorder);
|
|
|
|
|
|
if (fmt == LVCFMT_LEFT)
|
|
{
|
|
if (bm.bmWidth > (rc.right-rc.left))
|
|
{
|
|
bm.bmWidth = rc.right - rc.left;
|
|
}
|
|
}
|
|
else if (fmt == LVCFMT_CENTER)
|
|
{
|
|
if (bm.bmWidth > (rc.right - rc.left))
|
|
{
|
|
xBitmap = (bm.bmWidth - (rc.right - rc.left)) / 2;
|
|
bm.bmWidth = rc.right - rc.left;
|
|
}
|
|
else
|
|
rc.left = (rc.left + rc.right - bm.bmWidth) / 2;
|
|
}
|
|
else // fmt == LVCFMT_RIGHT
|
|
{
|
|
|
|
if (bm.bmWidth > (rc.right - rc.left))
|
|
{
|
|
xBitmap = bm.bmWidth - (rc.right - rc.left);
|
|
}
|
|
else
|
|
rc.left = rc.right - bm.bmWidth;
|
|
}
|
|
|
|
// Now setup horizontally
|
|
if (bm.bmHeight > (rc.bottom - rc.top))
|
|
{
|
|
yBitmap = (bm.bmHeight - (rc.bottom - rc.top)) / 2;
|
|
bm.bmHeight = rc.bottom - rc.top;
|
|
}
|
|
else
|
|
rc.top = (rc.bottom - rc.top - bm.bmHeight) / 2;
|
|
|
|
// Last but not least we will do the bitblt.
|
|
BitBlt(hdc, rc.left, rc.top, bm.bmWidth, bm.bmHeight,
|
|
hdcMem, xBitmap, yBitmap, SRCCOPY);
|
|
|
|
// Unselect our object from the DC
|
|
SelectObject(hdcMem, hbmOld);
|
|
|
|
}
|
|
|
|
void NEAR Header_Redraw(HD* phd, HDC hdc, RECT FAR* prcClip, int i)
|
|
{
|
|
int cItems;
|
|
HDI FAR* phdi;
|
|
RECT rc;
|
|
BOOL fTracking, fItemSunken;
|
|
UINT uDrawTextFlags;
|
|
HFONT hfontOld = NULL;
|
|
HDC hdcMem = NULL;
|
|
WORD wID;
|
|
|
|
fTracking = Header_IsTracking(phd);
|
|
|
|
SetTextColor(hdc, g_clrBtnText);
|
|
SetBkColor(hdc, g_clrBtnFace);
|
|
if (phd->hfont)
|
|
hfontOld = SelectFont(hdc, phd->hfont);
|
|
|
|
cItems = DSA_GetItemCount(phd->hdsaHDI);
|
|
|
|
wID = GetWindowID(phd->hwnd);
|
|
|
|
Header_GetItemRect(phd, i, &rc);
|
|
phdi = Header_GetItemPtr(phd, i);
|
|
for ( ; i <= cItems; i++)
|
|
{
|
|
if (prcClip)
|
|
{
|
|
if (rc.right < prcClip->left)
|
|
goto NextItem;
|
|
if (rc.left >= prcClip->right)
|
|
break;
|
|
}
|
|
|
|
fItemSunken = (fTracking && (g_flagsTrack & HHT_ONHEADER) &&
|
|
(g_iTrack == i) && g_bTrackPress);
|
|
|
|
if (i < cItems)
|
|
{
|
|
if(fItemSunken)
|
|
uDrawTextFlags = SHDT_ELLIPSES | SHDT_DEPRESSED | SHDT_EXTRAMARGIN;
|
|
else
|
|
uDrawTextFlags = SHDT_ELLIPSES | SHDT_EXTRAMARGIN;
|
|
|
|
phdi = Header_GetItemPtr(phd, i);
|
|
|
|
if (phdi->fmt & HDF_OWNERDRAW)
|
|
{
|
|
DRAWITEMSTRUCT dis;
|
|
|
|
dis.CtlType = ODT_HEADER;
|
|
dis.CtlID = wID;
|
|
dis.itemID = i;
|
|
dis.itemAction = ODA_DRAWENTIRE;
|
|
dis.itemState = (uDrawTextFlags & SHDT_DEPRESSED) ? ODS_SELECTED : 0;
|
|
dis.hwndItem = phd->hwnd;
|
|
dis.hDC = hdc;
|
|
dis.rcItem = rc;
|
|
dis.itemData = phdi->lParam;
|
|
|
|
// Now send it off to my parent...
|
|
if (SendMessage(phd->hwndParent, WM_DRAWITEM, wID,
|
|
(LPARAM)(DRAWITEMSTRUCT FAR *)&dis))
|
|
{
|
|
goto DrawEdges; //Ick, but it works
|
|
}
|
|
}
|
|
|
|
|
|
// Now neet to handle the different combinatations of
|
|
// text and bitmaps...
|
|
|
|
if (phdi->fmt & HDF_BITMAP)
|
|
{
|
|
if (hdcMem == NULL)
|
|
hdcMem = CreateCompatibleDC(hdc);
|
|
|
|
if (hdcMem != NULL)
|
|
{
|
|
_Header_DrawBitmap(hdc, phdi->hbm, hdcMem, &rc,
|
|
phdi->fmt & HDF_JUSTIFYMASK, uDrawTextFlags);
|
|
|
|
}
|
|
// BUGBUG: Not Implemented
|
|
Assert (FALSE);
|
|
}
|
|
|
|
if (phdi->fmt & HDF_STRING)
|
|
{
|
|
#if defined(WINDOWS_ME)
|
|
int oldAlign;
|
|
|
|
if (phdi->fmt & HDF_RTLREADING)
|
|
{
|
|
oldAlign = GetTextAlign(hdc);
|
|
SetTextAlign(hdc, oldAlign | TA_RTLREADING);
|
|
}
|
|
#endif
|
|
SHDrawText(hdc, phdi->pszText, &rc,
|
|
phdi->fmt & HDF_JUSTIFYMASK,
|
|
uDrawTextFlags, phd->cyChar, phd->cxEllipses,
|
|
GetSysColor( COLOR_BTNTEXT ),
|
|
GetSysColor( COLOR_BTNFACE ) );
|
|
#if defined(WINDOWS_ME)
|
|
if (phdi->fmt & HDF_RTLREADING)
|
|
SetTextAlign(hdc, oldAlign);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
DrawEdges:
|
|
if (GetWindowStyle(phd->hwnd) & HDS_BUTTONS)
|
|
{
|
|
if (fItemSunken)
|
|
DrawEdge(hdc, &rc, EDGE_SUNKEN, BF_RECT | BF_SOFT | BF_FLAT);
|
|
else
|
|
DrawEdge(hdc, &rc, EDGE_RAISED, BF_RECT | BF_SOFT);
|
|
}
|
|
else
|
|
{
|
|
DrawEdge(hdc, &rc, EDGE_ETCHED, BF_BOTTOMRIGHT);
|
|
}
|
|
|
|
|
|
NextItem:
|
|
rc.left = rc.right;
|
|
if (i < cItems - 1)
|
|
{
|
|
phdi++;
|
|
rc.right = phdi->x;
|
|
}
|
|
else
|
|
{
|
|
rc.right = 30000;
|
|
}
|
|
}
|
|
|
|
if (fTracking && (g_flagsTrack & (HHT_ONDIVIDER | HHT_ONDIVOPEN)))
|
|
{
|
|
Header_DrawDivider(phd, g_xTrack);
|
|
}
|
|
|
|
if (hfontOld)
|
|
SelectFont(hdc, hfontOld);
|
|
|
|
// Also free any memory dcs we may have created
|
|
if (hdcMem != NULL)
|
|
DeleteDC(hdcMem);
|
|
}
|