Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

2992 lines
108 KiB

// ==================================================================================================
// COPYRIGHT 1992,1993 MICROSOFT CORP ALL RIGHTS RESERVED
// ==================================================================================================
//
// - Custom control to display Columns/Titles above a list box
//
// TERMINOLOGY:
// PHYSICAL COLUMNS: The index of the original columns in their original order
// VIRtUAL COLUMNS: The index of the column as the display is currently showing it based on
// the current order.
//
// History:
// -------
// RickD 04/10/92 created TitleListBox
// Tom Laird-McConnell 12/30/92 Ported to Win32, added to BH project
// Tom Laird-McConnell 05/01/93 gutted titlelist and used as base for complete rewrite as ColumnLB
// Tom Laird-McConnell 08/18/93 Added tabbed-delimited string support
// Arth 03/24/94 Added Unicode support
// ==================================================================================================
#define STRICT 1
#include "switches.h"
#include <windows.h>
#include <windowsx.h>
#include <string.h>
#include <stdlib.h>
// #include <dprintf.h>
#include "columnlb.h"
#include "vlist.h"
#include "utils.h"
#include "debug.h"
#include "mem.h"
//#define DEBUG_HSCROLL 1
#define WHITESPACE 8
#define IDL_COLUMNLISTBOX (CLB_BASE + 1)
#define IDL_COLUMNTITLELISTBOX (CLB_BASE + 2)
#define LB16_ADDSTRING (WM_USER+1)
#define LB16_INSERTSTRING (WM_USER+2)
typedef struct _COLRECORD
{
DWORD_PTR itemData;
LPTSTR pString[];
} COLRECORD;
typedef COLRECORD *LPCOLRECORD;
// -------------------------------------------------------------------------------------
//
// window procs
//
LRESULT CALLBACK ColumnLBClass_ListBoxWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK ColumnLBClass_WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
//
// system message handlers
//
BOOL ColumnLBClass_OnNCCreate(HWND hwnd, LPCREATESTRUCT lpCreateStruct);
void ColumnLBClass_OnNCDestroy(HWND hwnd);
void ColumnLBClass_OnDestroy(HWND hwnd);
void ColumnLBClass_OnPaint(HWND hwnd);
void ColumnLBClass_OnSize(HWND hwnd, UINT state, int cx, int cy);
void ColumnLBClass_OnSetFont(HWND hwnd, HFONT hfont, BOOL fRedraw);
LRESULT ColumnLBClass_OnHScroll(HWND hwnd, HWND hwndCtl, UINT code, int pos);
void ColumnLBClass_OnDrawItem(HWND hwnd, const DRAWITEMSTRUCT * lpDrawItem);
void ColumnLBClass_OnMeasureItem(HWND hwnd, LPMEASUREITEMSTRUCT lpMeasureItem);
void ColumnLBClass_OnDeleteItem(HWND hwnd, const DELETEITEMSTRUCT *lpDeleteItem);
int ColumnLBClass_OnCharToItem(HWND hwnd, UINT ch, HWND hwndListbox, int iCaret);
//
// WM_USER message handlers
//
BYTE ColumnLBClass_OnNumberCols(HWND hwnd, BYTE NewNumberCols, BOOL fSetColumns);
int ColumnLBClass_OnColWidth(HWND hwnd, BYTE Column, int NewWidth, BOOL fSetWidth);
LPTSTR ColumnLBClass_OnColTitle(HWND hwnd, BYTE Column, LPTSTR lpTitle, BOOL fSetTitle);
BYTE ColumnLBClass_OnSortCol(HWND hwnd, BYTE NewSortCol, BOOL fSetSortCol);
LPBYTE ColumnLBClass_OnColOrder(HWND hwnd, LPBYTE NewColOrder, BOOL fSetOrder);
LPINT ColumnLBClass_OnColOffsets(HWND hwnd, LPINT NewOffsetTable, BOOL fSetOffsets);
LRESULT ColumnLBClass_OnAutoWidth(HWND hwnd, BYTE ColumnToCompute);
//
// mouse movement messages
//
void ColumnLBClass_OnMouseMove(HWND hwnd, int x, int y, UINT keyFlags);
void ColumnLBClass_OnLButtonDown(HWND hwnd, BOOL fDoubleClick, int x, int y, UINT keyFlags);
void ColumnLBClass_OnLButtonUp(HWND hwnd, int x, int y, UINT keyFlags);
void ColumnLBClass_OnRButtonDown (HWND hwnd, BOOL fDoubleClick, int x, int y, UINT keyFlags);
// helper functions
int ColumnLBClass_ComputeOffsets(HWND hwnd);
// -------------------------------------------------------------------------------------
//
// Locals
//
BOOL fWIN32s; // flag for whether win32s (instead of win32/NT)
// -----------------------------------------------------------------
//
// ColumnLBClass_Register()
//
// This function is used to register the Column LB class with the system
//
// HISTORY:
// Tom Laird-McConnell 4/17/93 Created
//
// -----------------------------------------------------------------
BOOL ColumnLBClass_Register(HINSTANCE hInstance)
{
WNDCLASS WndClass;
fWIN32s = ((DWORD)GetVersion() & 0x80000000) ? TRUE : FALSE;
//
// Create the COLUMNLISTBOX class
//
WndClass.style = CS_PARENTDC | CS_DBLCLKS; // CS_GLOBALCLASS;
WndClass.lpfnWndProc = ColumnLBClass_WndProc;
WndClass.cbClsExtra = 0;
WndClass.cbWndExtra = sizeof(LPCOLUMNLBSTRUCT); // we store a pointer as instance data
WndClass.hInstance = hInstance;
WndClass.hIcon = 0;
WndClass.hCursor = LoadCursor(0, IDC_ARROW);
WndClass.hbrBackground = 0;
WndClass.lpszMenuName = 0;
WndClass.lpszClassName = COLUMNLBCLASS_CLASSNAME;
/* Register the normal title list box control */
return RegisterClass((LPWNDCLASS)&WndClass);
}
// -----------------------------------------------------------------
//
// ColumnVLBClass_Register()
//
// This function is used to register the Column VLIST LB class with the system
//
// HISTORY:
// Tom Laird-McConnell 4/17/93 Created
//
// -----------------------------------------------------------------
BOOL ColumnVLBClass_Register(HINSTANCE hInstance)
{
WNDCLASS WndClass;
fWIN32s = ((DWORD)GetVersion() & 0x80000000) ? TRUE : FALSE;
//
// Create the COLUMNVLISTBOX class
//
WndClass.style = CS_PARENTDC | CS_DBLCLKS; // CS_GLOBALCLASS;
WndClass.lpfnWndProc = ColumnLBClass_WndProc;
WndClass.cbClsExtra = 0;
WndClass.cbWndExtra = sizeof(LPCOLUMNLBSTRUCT); // we store a pointer as instance data
WndClass.hInstance = hInstance;
WndClass.hIcon = 0;
WndClass.hCursor = LoadCursor(0, IDC_ARROW);
WndClass.hbrBackground = 0;
WndClass.lpszMenuName = 0;
WndClass.lpszClassName = COLUMNVLBCLASS_CLASSNAME; // NOTE: this is a different name
/* Register the new control */
return(RegisterClass((LPWNDCLASS)&WndClass));
}
// -----------------------------------------------------------------
//
// ColumnLBClass_Unregister()
//
// This function is used to deregister the Column LB class with the system
//
// HISTORY:
// Tom Laird-McConnell 4/17/93 Created
//
// -----------------------------------------------------------------
BOOL ColumnLBClass_Unregister(HINSTANCE hInstance)
{
return(UnregisterClass(COLUMNLBCLASS_CLASSNAME, hInstance));
}
// -----------------------------------------------------------------
//
// ColumnVLBClass_Unregister()
//
// This function is used to deregister the Column VLIST LB class with the system
//
// HISTORY:
// Tom Laird-McConnell 4/17/93 Created
//
// -----------------------------------------------------------------
BOOL ColumnVLBClass_Unregister(HINSTANCE hInstance)
{
return(UnregisterClass(COLUMNVLBCLASS_CLASSNAME, hInstance));
}
// -----------------------------------------------------------------
// ColumnLBClass_ListBoxWndProc
//
// Window proc used in sub-classing the internal listbox to catch
// internal scroll events to keep title in sync with it...
//
// HISTORY:
// Tom Laird-McConnell 4/17/93 Created
//
// -----------------------------------------------------------------
LRESULT CALLBACK ColumnLBClass_ListBoxWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
LRESULT result;
LPCOLUMNLBSTRUCT lpColumnLB;
// Everthing goes to normal listbox for processing
lpColumnLB = (LPCOLUMNLBSTRUCT)GetWindowLong(GetParent(hwnd), (DWORD)0);
// preprocessing
switch (msg)
{
case WM_HSCROLL:
// do title hscroll first..
result = SendMessage(lpColumnLB->hwndTitleList, WM_HSCROLL, wParam, lParam);
break;
case WM_SETFOCUS:
lpColumnLB->fHasFocus = TRUE;
//dprintf ("SetFocus to ColumnLB\n");
break;
case WM_KILLFOCUS:
lpColumnLB->fHasFocus = FALSE;
//dprintf ("KillFocus to ColumnLB\n");
break;
}
//
// call the original listbox window proc
//
result = CallWindowProc((WNDPROC)(lpColumnLB->OldListboxProc), hwnd, msg, (WPARAM) wParam, (LPARAM)lParam);
//
// or if our parent has CLBS_NOTIFYLMOUSE style, then foward LMOUSE buttons
// or if our parent has CLBS_NOTIFYRMOUSE style, then foward RMOUSE buttons
//
switch (msg)
{
case WM_HSCROLL:
//
// if it is a Horizontal scrolls, then we foward to our parent so title can be moved
// in sync with listbox....
//
SendMessage(GetParent(hwnd), CLB_HSCROLL, wParam, lParam );
break;
case VLB_RESETCONTENT:
case LB_RESETCONTENT:
//
// if it is a LB_RESETCONTENT, or VLB_RESETCONTENT, then reset x position
//
SendMessage(GetParent(hwnd), CLB_HSCROLL, (WPARAM)SB_TOP, (LPARAM)NULL);
break;
case WM_LBUTTONDOWN :
case WM_LBUTTONUP :
case WM_LBUTTONDBLCLK :
//
// forward message to parent
//
if (GetWindowLong(GetParent(hwnd), GWL_EXSTYLE) & CLBS_NOTIFYLMOUSE)
SendMessage(GetParent(hwnd), msg, wParam, lParam);
break;
case WM_RBUTTONDOWN :
// case WM_RBUTTONUP :
case WM_RBUTTONDBLCLK :
//
// forward message to parent
//
// if (GetWindowLong(GetParent(hwnd), GWL_EXSTYLE) & CLBS_NOTIFYRMOUSE)
SendMessage(GetParent(hwnd), msg, wParam, lParam);
break;
default:
break;
}
return(result);
}
// -----------------------------------------------------------------
// ColumnLBClass_TitleListBoxWndProc
//
// Window proc used in sub-classing the internal Title listbox to catch
// internal mouse click events...
//
// HISTORY:
// Tom Laird-McConnell 4/17/93 Created
//
// -----------------------------------------------------------------
LRESULT CALLBACK ColumnLBClass_TitleListBoxWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
LRESULT result;
LPCOLUMNLBSTRUCT lpColumnLB;
// Everthing goes to normal listbox for processing
lpColumnLB = (LPCOLUMNLBSTRUCT)GetWindowLong(GetParent(hwnd), (DWORD)0);
//
// call the original listbox window proc
//
result = CallWindowProc((WNDPROC)(lpColumnLB->OldTitleListboxProc) , hwnd, msg, (WPARAM) wParam, (LPARAM)lParam);
//
// foward LMOUSE buttons, foward RMOUSE buttons
//
switch (msg)
{
#ifdef DEBUG_HSCROLL
case WM_HSCROLL:
dprintf(TEXT("ColumnLBClass_TitleListBoxProc: CallWindowProc(OldListboxProc) returned %ld to hwnd=%lx, wParam=%u, lParam=%u\n"), hwnd, wParam, lParam);
break;
#endif
case WM_MOUSEMOVE:
case WM_LBUTTONDOWN :
case WM_LBUTTONUP :
case WM_LBUTTONDBLCLK :
case WM_RBUTTONDOWN :
case WM_RBUTTONUP :
case WM_RBUTTONDBLCLK :
SendMessage(GetParent(hwnd), msg, wParam, lParam);
break;
case WM_SETFOCUS:
// we don't ever want the focus, so give it back to the parent...
SendMessage(GetParent(hwnd), msg, wParam, lParam);
break;
case WM_SIZE:
// we need to reset our idea of what our current scroll position is...
break;
}
return(result);
}
// -----------------------------------------------------------------
// ColumnLBClass_WndProc
//
// Main window proc for handling messages for both the ColumnLB and
// ColumnVLB classes...
//
// HISTORY:
// Tom Laird-McConnell 4/17/93 Created
//
// -----------------------------------------------------------------
LRESULT CALLBACK ColumnLBClass_WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
LPCOLUMNLBSTRUCT lpColumnLB = (LPCOLUMNLBSTRUCT)GetWindowLong(hwnd, 0);
LPCOLRECORD lpRecord;
LRESULT result;
//
// check for ListBox message coming from application
// and forward them onto the listbox itself...
//
if ( ((fWIN32s == TRUE) && (msg >= WM_USER && msg < (WM_USER + LB_MSGMAX - LB_ADDSTRING + 1)) ) || // WIN32s version BUGBUG
((fWIN32s == FALSE) && (msg >= LB_ADDSTRING && msg < LB_MSGMAX)) || // NT version BUGBUG
((msg >= VLB_TOVLIST_MSGMIN) && (msg <= VLB_TOVLIST_MSGMAX)) // vlb sepcific APP->VLIST messages should be fowarded...
)
{
//
// OWNERDRAW parent, so just send it to the hwnd list child
//
return(SendMessage(lpColumnLB->hwndList, msg, wParam, lParam));
}
//
// check to see if message is a TO APPLCATION message from the child listbox
// which should be forwarded to application parent window
//
if ((msg >= VLB_TOAPP_MSGMIN) && (msg <= VLB_TOAPP_MSGMAX))
return(SendMessage(GetParent(hwnd), msg, wParam, lParam)); // forward to parent...
//
// since it's not a message passing through, then process this message
// as our own...
//
switch (msg)
{
HANDLE_MSG(hwnd, WM_NCCREATE, ColumnLBClass_OnNCCreate);
HANDLE_MSG(hwnd, WM_NCDESTROY, ColumnLBClass_OnNCDestroy);
HANDLE_MSG(hwnd, WM_DESTROY, ColumnLBClass_OnDestroy);
HANDLE_MSG(hwnd, WM_PAINT, ColumnLBClass_OnPaint);
HANDLE_MSG(hwnd, WM_SIZE, ColumnLBClass_OnSize);
HANDLE_MSG(hwnd, WM_DRAWITEM, ColumnLBClass_OnDrawItem);
HANDLE_MSG(hwnd, WM_MEASUREITEM, ColumnLBClass_OnMeasureItem);
HANDLE_MSG(hwnd, WM_DELETEITEM, ColumnLBClass_OnDeleteItem);
HANDLE_MSG(hwnd, WM_LBUTTONDOWN, ColumnLBClass_OnLButtonDown);
HANDLE_MSG(hwnd, WM_LBUTTONDBLCLK, ColumnLBClass_OnLButtonDown);
HANDLE_MSG(hwnd, WM_LBUTTONUP, ColumnLBClass_OnLButtonUp);
HANDLE_MSG(hwnd, WM_MOUSEMOVE, ColumnLBClass_OnMouseMove);
case WM_RBUTTONDOWN:
// figure out what item we are on and tell our parent.
HANDLE_WM_RBUTTONDOWN ( hwnd, wParam, lParam, ColumnLBClass_OnRButtonDown );
break;
case WM_CREATE:
{
LPCREATESTRUCT lpCreate = (LPCREATESTRUCT) lParam;
ColumnLBClass_OnSize(hwnd, SIZE_RESTORED, lpCreate->cx, lpCreate->cy);
}
break;
// -------------------------------------------------------------------
// put System messages here which explicitly need to be passed to LISTBOX
//
case WM_SETFONT:
HANDLE_WM_SETFONT(hwnd, wParam, lParam, ColumnLBClass_OnSetFont);
break;
// put the focus on the list box if we get it
case WM_SETFOCUS:
lpColumnLB = (LPCOLUMNLBSTRUCT)GetWindowLong(hwnd, 0);
SetFocus(lpColumnLB->hwndList);
break;
case SBM_SETPOS :
case SBM_SETRANGE :
case SBM_SETRANGEREDRAW :
//
// need to foward SBM messages to both listboxes...
//
SendMessage(lpColumnLB->hwndTitleList, msg, wParam, lParam);
return(SendMessage(lpColumnLB->hwndList, msg, wParam, lParam));
case SBM_GETPOS :
case SBM_GETRANGE :
case SBM_ENABLE_ARROWS :
return(SendMessage(lpColumnLB->hwndList, msg, wParam, lParam));
// ------------------------------------------------------------------------------
//
// LB_XXXXXXX Messages (disguised as CLB_XXXXXX messages)
// to pass on to child listbox, if the parent didn't want us to
// be owner draw, then we implement ownerddraw default behavior ourself
//
// ------------------------------------------------------------------------------
case CLB_ADDSTRING:
case CLB_INSERTSTRING:
//
// if the parent is NOT handling OWNERDRAW, then we need to handle
//
if ( ! (lpColumnLB->Style & (LBS_OWNERDRAWFIXED | VLBS_OWNERDRAWFIXED)) )
{
LPTSTR lpColStart,lpTab;
LPTSTR lpStringBuffer;
int i;
lpRecord = (LPCOLRECORD)GlobalAllocPtr(GPTR, sizeof(COLRECORD) + sizeof(LPTSTR) * lpColumnLB->nColumns);
lpStringBuffer = (LPTSTR) GlobalAllocPtr(GPTR, (lstrlen((LPTSTR)lParam) * sizeof(TCHAR)) + sizeof(TCHAR));
if ((lpRecord) && (lpStringBuffer))
{
// now parse the tab-deliminated string and put into each pointer
lstrcpy(lpStringBuffer, (LPTSTR)lParam);
lpColStart = lpStringBuffer;
lpTab = lstrchr(lpStringBuffer, TEXT('\t'));
// fill in pointer table
for (i=0; i < lpColumnLB->nColumns; i++)
{
if (lpTab)
*lpTab = '\0';
else
{
// there was an error, not enough tabs!
GlobalFreePtr(lpRecord);
GlobalFreePtr(lpStringBuffer);
return(LB_ERR);
}
// store this pointer.
lpRecord->pString[i] = lpColStart;
// advance to next column text
lpColStart = lpTab + 1;
lpTab = lstrchr(lpColStart, TEXT('\t'));
}
lpRecord->itemData = 0;
// and now pass on our new lpRecord as the item being added to the listbox
return(SendMessage(lpColumnLB->hwndList, msg - CLB_BASE, wParam, (LPARAM)lpRecord));
}
else // an error has occured, free up any memory and return failure
{
if (lpStringBuffer)
GlobalFreePtr(lpStringBuffer);
if (lpRecord)
GlobalFreePtr(lpRecord);
return(LB_ERR);
}
}
else
//
// Parent is owner draw, so send it to the hwnd list child,
// but translate the message first to real LB_ message
//
return(SendMessage(lpColumnLB->hwndList, msg - CLB_BASE, wParam, lParam));
// and we also need to check for LB_GETTEXT to make it look like a string
case CLB_GETTEXT:
//
// if the parent is NOT handling OWNERDRAW, then we need to handle
//
if ( ! (lpColumnLB->Style & (LBS_OWNERDRAWFIXED | VLBS_OWNERDRAWFIXED)) )
{
LPTSTR p = (LPTSTR)lParam;
DWORD Length = 0;
DWORD x;
int i;
*p = '\0';
// and now pass on to get the text...
lpRecord = (LPCOLRECORD)SendMessage(lpColumnLB->hwndList, LB_GETITEMDATA, wParam, 0);
if (lpRecord == (LPCOLRECORD)LB_ERR)
return(LB_ERR);
for (i=0; i < lpColumnLB->nColumns ; i++ )
{
lstrcpy(p, lpRecord->pString[lpColumnLB->ColumnOrderTable[i]]);
lstrcat(p, TEXT("\t"));
x = lstrlen(p);
Length += x + sizeof(TCHAR);
p += x;
}
return(Length);
}
else
//
// Parent is owner draw, so send it to the hwnd list child,
// but translate the message first to real LB_ message
//
return(SendMessage(lpColumnLB->hwndList, msg - CLB_BASE, wParam, lParam));
case CLB_GETTEXTPTRS:
if ( ! (lpColumnLB->Style & (LBS_OWNERDRAWFIXED | VLBS_OWNERDRAWFIXED)) )
{
lpRecord = (LPCOLRECORD)SendMessage(lpColumnLB->hwndList, LB_GETITEMDATA, wParam, 0);
if (lpRecord == (LPCOLRECORD)LB_ERR)
return(LB_ERR);
return (LRESULT)lpRecord->pString;
}
else
return (LRESULT)NULL;
// we need to handle LB_GETTEXTLEN to return full tabbed length...
case CLB_GETTEXTLEN:
//
// if the parent is NOT handling OWNERDRAW, then we need to handle
//
if ( ! (lpColumnLB->Style & (LBS_OWNERDRAWFIXED | VLBS_OWNERDRAWFIXED)) )
{
LPTSTR p = (LPTSTR)lParam;
DWORD Length = 0;
int i;
// and now pass on to get the text...
lpRecord = (LPCOLRECORD)SendMessage(lpColumnLB->hwndList, LB_GETITEMDATA, wParam, 0);
if (lpRecord == (LPCOLRECORD)LB_ERR)
return(LB_ERR);
for (i=0; i < lpColumnLB->nColumns ; i++ )
{
Length += lstrlen(lpRecord->pString[lpColumnLB->ColumnOrderTable[i]]) + sizeof(TCHAR);
}
return(Length);
}
else
//
// Parent is owner draw, so send it to the hwnd list child,
// but translate the message first to real LB_ message
//
return(SendMessage(lpColumnLB->hwndList, msg - CLB_BASE, wParam, lParam));
case CLB_GETITEMDATA:
//
// if the parent is NOT handling OWNERDRAW, then we need to handle
//
if ( ! (lpColumnLB->Style & (LBS_OWNERDRAWFIXED | VLBS_OWNERDRAWFIXED)) )
{
lpRecord = (LPCOLRECORD)SendMessage(lpColumnLB->hwndList, LB_GETITEMDATA, wParam, 0);
if (lpRecord != (LPCOLRECORD)LB_ERR)
return(lpRecord->itemData);
else
return(LB_ERR);
}
else
//
// Parent is owner draw, so send it to the hwnd list child,
// but translate the message first to real LB_ message
//
return(SendMessage(lpColumnLB->hwndList, msg - CLB_BASE, wParam, lParam));
case CLB_SETITEMDATA:
//
// if the parent is NOT handling OWNERDRAW, then we need to handle
//
if ( ! (lpColumnLB->Style & (LBS_OWNERDRAWFIXED | VLBS_OWNERDRAWFIXED)) )
{
lpRecord = (LPCOLRECORD)SendMessage(lpColumnLB->hwndList, LB_GETITEMDATA, wParam, 0);
if (lpRecord != (LPCOLRECORD)LB_ERR)
return (LRESULT)(lpRecord->itemData = lParam);
else
return(LB_ERR);
}
else
//
// Parent is owner draw, so send it to the hwnd list child,
// but translate the message first to real LB_ message
//
return(SendMessage(lpColumnLB->hwndList, msg - CLB_BASE, wParam, lParam));
//
// if it is a HORIZONTAL exntent message, then we need to massage...
//
case CLB_SETHORIZONTALEXTENT :
//
// send the message to the title listbox as well
//
SendMessage(lpColumnLB->hwndTitleList, LB_SETHORIZONTALEXTENT, wParam, lParam);
//
// pass it on to the child listbox, using VLB_SETHOR if appropriate...
//
return(SendMessage(lpColumnLB->hwndList,
(lpColumnLB->fUseVlist) ? VLB_SETHORIZONTALEXTENT : LB_SETHORIZONTALEXTENT,
wParam, lParam));
//
// we need to massage the GETITEMRECT to handle the incorrect rect returned due to titlelistbox.
//
case CLB_GETITEMRECT:
{
LRESULT retcode;
int height;
LPRECT lpRect = (LPRECT)lParam;
//
// send it to the hwnd list child, but translate the message first to real LB_ message
//
retcode = SendMessage(lpColumnLB->hwndList, msg - CLB_BASE, wParam, lParam);
height = lpRect->bottom-lpRect->top;
lpRect->top = lpRect->bottom + 1;
lpRect->bottom = lpRect->top + height;
return(retcode);
}
break;
case CLB_DELETESTRING :
case CLB_SELITEMRANGEEX :
case CLB_RESETCONTENT :
case CLB_SETSEL :
case CLB_SETCURSEL :
case CLB_GETSEL :
case CLB_GETCURSEL :
case CLB_GETCOUNT :
case CLB_SELECTSTRING :
case CLB_DIR :
case CLB_GETTOPINDEX :
case CLB_FINDSTRING :
case CLB_GETSELCOUNT :
case CLB_GETSELITEMS :
case CLB_SETTABSTOPS :
case CLB_GETHORIZONTALEXTENT :
case CLB_SETCOLUMNWIDTH :
case CLB_ADDFILE :
case CLB_SETTOPINDEX :
case CLB_SELITEMRANGE :
case CLB_SETANCHORINDEX :
case CLB_GETANCHORINDEX :
case CLB_SETCARETINDEX :
case CLB_GETCARETINDEX :
case CLB_SETITEMHEIGHT :
case CLB_GETITEMHEIGHT :
case CLB_FINDSTRINGEXACT :
case CLB_SETLOCALE :
case CLB_GETLOCALE :
case CLB_SETCOUNT :
//
// Simply send it to the hwnd list child, but translate the message first to real LB_ message
//
return(SendMessage(lpColumnLB->hwndList, msg - CLB_BASE, wParam, lParam));
// -------------------------------------------------------------------
// put messages here which explicitly need to be passed to our PARENT
//
case WM_COMMAND:
/* if this is a notification message from our child translate */
/* it to look like it is from us and pass on to our parent */
if (LOWORD(wParam) == IDL_COLUMNLISTBOX)
return(SendMessage( GetParent(hwnd),
msg,
MAKELONG( GetDlgCtrlID(hwnd) ,HIWORD(wParam)),
(LPARAM)hwnd )); // make it look like we were the ones sending the message...
else
return(SendMessage(GetParent(hwnd), msg, wParam, (LPARAM)hwnd));
case WM_VKEYTOITEM:
// pass on to our parent but using our hwnd...
if (lpColumnLB->Style & (LBS_WANTKEYBOARDINPUT | VLBS_WANTKEYBOARDINPUT) )
return(SendMessage(GetParent(hwnd), msg, wParam, (LPARAM)hwnd));
else
return(-1); // perform default action...
case WM_CHARTOITEM:
if (lpColumnLB->Style & (LBS_WANTKEYBOARDINPUT | VLBS_WANTKEYBOARDINPUT) )
if ((result = SendMessage(GetParent(hwnd), msg, wParam, (LPARAM)hwnd)) != -1)
return(result);
return HANDLE_WM_CHARTOITEM(hwnd, wParam, lParam, ColumnLBClass_OnCharToItem);
case WM_COMPAREITEM:
{
LPCOMPAREITEMSTRUCT lpCompareItem = (LPCOMPAREITEMSTRUCT)lParam;
LRESULT result;
if ((lpColumnLB->Style & LBS_OWNERDRAWFIXED) ||
(lpColumnLB->Style & VLBS_OWNERDRAWFIXED))
{
//
// substitute our values in the COMPAREITEMSTRUCT...
//
lpCompareItem->CtlID = GetDlgCtrlID(hwnd);
lpCompareItem->hwndItem = hwnd;
//
// then pass to our parent as our WM_COMPAREITEM, with the current physcial sort column as wParam
//
result = (int)SendMessage(GetParent(hwnd), WM_COMPAREITEM, (WPARAM)lpColumnLB->SortColumn, (LPARAM)lpCompareItem);
return(result);
}
else
{
LPTSTR lpString1;
LPTSTR lpString2;
LPCOLRECORD lpColRecord1;
LPCOLRECORD lpColRecord2;
// handle ourselves assuming item data is pointer to array of LPTSTR's
lpColRecord1 = (LPCOLRECORD)lpCompareItem->itemData1;
lpColRecord2 = (LPCOLRECORD)lpCompareItem->itemData2;
lpString1 = lpColRecord1->pString[lpColumnLB->SortColumn];
lpString2 = lpColRecord2->pString[lpColumnLB->SortColumn];
if (lpString1 && lpString2)
return(lstrcmpi(lpString1, lpString2));
else
return(0);
}
}
break;
// ---------------------------------------------------------
// handle our own messages
// ---------------------------------------------------------
//
// NUMBER COLUMNS
//
case CLB_GETNUMBERCOLS : // get the number of columns (ret=NumCols)
return ColumnLBClass_OnNumberCols(hwnd, 0, FALSE);
case CLB_SETNUMBERCOLS : // set the number of columns (wparam=NumCols)
return ColumnLBClass_OnNumberCols(hwnd, (BYTE)wParam, TRUE);
// ----------------------------------------------------------------
// Following messages use physical column numbers
// ----------------------------------------------------------------
//
// COLUMN WIDTH
//
case CLB_GETCOLWIDTH : // get a column width (wParm=Physical Column ret=ColWidth in LU's)
return ColumnLBClass_OnColWidth(hwnd, (BYTE)wParam, (int)0, FALSE);
case CLB_SETCOLWIDTH : // set a column width (wParm=Physical Column lParam=Width)
return ColumnLBClass_OnColWidth(hwnd, (BYTE)wParam, (int)lParam, TRUE);
case CLB_AUTOWIDTH : // auto-matically set column widths using titles... (wParam = Physical Column to auto width)
return ColumnLBClass_OnAutoWidth(hwnd, (BYTE)wParam);
//
// COLUMN TITLE
//
case CLB_GETCOLTITLE : // get a column's title (wParm=Physical Column, ret=Title)
return (LRESULT)ColumnLBClass_OnColTitle(hwnd, (BYTE)wParam, (LPTSTR)NULL, FALSE);
case CLB_SETCOLTITLE : // set a column's title (wParm=Physical Col, lParm=Title)
return (LRESULT)ColumnLBClass_OnColTitle(hwnd, (BYTE)wParam, (LPTSTR)lParam, TRUE);
case CLB_GETROWCOLTEXT:
//
// if the parent is NOT handling OWNERDRAW, then we need to handle
//
if ( ! (lpColumnLB->Style & (LBS_OWNERDRAWFIXED | VLBS_OWNERDRAWFIXED)) )
{
INT WhichCol = (INT)(wParam);
INT WhichRow = (INT)(lParam);
// and now pass on to get the text...
lpRecord = (LPCOLRECORD)SendMessage(lpColumnLB->hwndList, LB_GETITEMDATA, WhichRow, 0);
if (lpRecord == (LPCOLRECORD)LB_ERR)
return((LRESULT)NULL);
return (LRESULT)lpRecord->pString[WhichCol];
}
return((LRESULT)NULL); // owner draw, the owner has to figure this out themselves
//
// SORT COLUMN
//
case CLB_GETSORTCOL : // get the physical sort column (ret=Physical Col)
return (LRESULT)ColumnLBClass_OnSortCol(hwnd, 0, FALSE);
case CLB_SETSORTCOL : // set the physical sort column (wParm=Physical Col)
return (LRESULT)ColumnLBClass_OnSortCol(hwnd, (BYTE)wParam, TRUE);
//
// COLUMN ORDER
//
case CLB_GETCOLORDER : // get the virtual order of physical columns (ret = LPDWORD order table)
return (LRESULT)ColumnLBClass_OnColOrder(hwnd, (LPBYTE)0, FALSE);
case CLB_SETCOLORDER : // get the virtual order of physical columns (wParam = LPDWORD order table)
return (LRESULT)ColumnLBClass_OnColOrder(hwnd, (LPBYTE)lParam, TRUE);
case CLB_CHECKFOCUS: // does the listbox have the focus?
// if (lpColumnLB->fUseVlist) // then we have to ask vlist the info
// return
// else
return lpColumnLB->fHasFocus;
// ----------------------------------------------------------------
// Following messages use virtual column numbers
// ----------------------------------------------------------------
//
// COLUMN OFFSETS
//
case CLB_GETCOLOFFSETS : // gets the incremental virtual col offsets (ret=LPDWORD)
return (LRESULT)ColumnLBClass_OnColOffsets(hwnd, (LPINT)wParam, FALSE);
// case CLB_SETCOLOFFSETS : // gets the incremental virtual col offsets (ret=LPDWORD)
// return (LRESULT)ColumnLBClass_OnColOffsets(hwnd, (LPDWORD)wParam, TRUE);
// =================================================================
// INTERNAL
//
case CLB_HSCROLL : // a hscroll event (INTERNAL)
return ColumnLBClass_OnHScroll(hwnd, (HWND)(lParam), (int)LOWORD(wParam), (int)HIWORD(wParam));
//
// GET FOCUS
//
case CLB_GETFOCUS : // get the handle for the window of CLB with the key focus
if ( lpColumnLB->fUseVlist )
// we must ask the column list box below us for the information.
return SendMessage ( lpColumnLB->hwndList, VLB_GETFOCUSHWND, 0,0 );
return (LRESULT) lpColumnLB->hwndList;
default:
return(DefWindowProc(hwnd, msg, wParam, lParam));
}
return(TRUE);
}
// ------------------------------------------------------------------
// ColumnLBClass_OnNCCreate()
//
// Handles WM_NCCCREATE message
//
// HISTORY:
// Tom Laird-McConnell 4/18/93 Created
// ------------------------------------------------------------------
BOOL ColumnLBClass_OnNCCreate(HWND hwnd, LPCREATESTRUCT lpCreateStruct)
{
LPCOLUMNLBSTRUCT lpColumnLB;
HWND hwndList;
HWND hwndTitleList;
BOOL fUseVlist;
HDC hdc;
TEXTMETRIC tm;
RECT rect;
WORD ncxBorder;
WORD ncyBorder;
WORD yTitle;
ncxBorder = (WORD) GetSystemMetrics(SM_CXBORDER);
ncyBorder = (WORD) GetSystemMetrics(SM_CYBORDER);
//
// if the classname is for ColumnVLB class, then they want the Column Virtual list box...
//
if (lstrcmpi(lpCreateStruct->lpszClass, COLUMNVLBCLASS_CLASSNAME) == 0)
fUseVlist = TRUE;
else
fUseVlist = FALSE;
hdc = GetDC(hwnd);
GetTextMetrics(hdc, &tm);
ReleaseDC(hwnd, hdc);
yTitle = (WORD)(tm.tmHeight + 2*ncyBorder);
GetClientRect(hwnd, &rect);
//
// create the title list box window...
//
hwndTitleList = CreateWindow( (LPTSTR) TEXT("LISTBOX"),
(LPTSTR) TEXT(""),
(lpCreateStruct->style & ~WS_BORDER) |
LBS_NOINTEGRALHEIGHT |
LBS_OWNERDRAWFIXED |
WS_VISIBLE |
WS_CHILD,
ncxBorder,
ncyBorder,
lpCreateStruct->cx - (2*ncxBorder) - GetSystemMetrics(SM_CXVSCROLL),
yTitle,
hwnd,
(HMENU)IDL_COLUMNTITLELISTBOX,
lpCreateStruct->hInstance,
NULL);
if (fUseVlist == TRUE)
//
// create a Vlist window...
//
hwndList = CreateWindow((LPTSTR)VLIST_CLASSNAME,
(LPTSTR) TEXT(""),
(lpCreateStruct->style & ~(WS_BORDER | VLBS_HASSTRINGS)) | // NOTE: This can _never_ be hasstrings
VLBS_NOTIFY |
VLBS_USETABSTOPS |
VLBS_NOINTEGRALHEIGHT |
VLBS_OWNERDRAWFIXED | // we always force this as either we will owner draw, or our parent will
WS_HSCROLL |
WS_VSCROLL |
VLBS_DISABLENOSCROLL |
WS_VISIBLE |
WS_CHILD,
ncxBorder,
yTitle + ncyBorder,
lpCreateStruct->cx - ncxBorder, // -(2*ncxBorder)
lpCreateStruct->cy - yTitle - ncyBorder,
hwnd,
(HMENU)IDL_COLUMNLISTBOX,
lpCreateStruct->hInstance,
NULL);
else
//
// create a list box window...
//
#ifdef H_SCROLL
hwndList = CreateWindow((LPTSTR) TEXT("LISTBOX"),
(LPTSTR) TEXT(""),
(lpCreateStruct->style & ~(WS_BORDER | LBS_HASSTRINGS)) | // NOTE: This can _never_ be hasstrings
LBS_NOTIFY |
LBS_USETABSTOPS |
LBS_NOINTEGRALHEIGHT |
LBS_OWNERDRAWFIXED | // we always force this as either we will owner draw, or our parent will
WS_HSCROLL |
WS_VSCROLL |
LBS_DISABLENOSCROLL |
WS_VISIBLE |
WS_CHILD,
ncxBorder,
yTitle + ncyBorder,
lpCreateStruct->cx - ncxBorder, // -(2*ncxBorder)
lpCreateStruct->cy - yTitle - ncyBorder,
hwnd,
(HMENU)IDL_COLUMNLISTBOX,
lpCreateStruct->hInstance,
NULL);
#else
hwndList = CreateWindow((LPTSTR) TEXT("LISTBOX"),
(LPTSTR) TEXT(""),
(lpCreateStruct->style & ~(WS_BORDER | LBS_HASSTRINGS)) | // NOTE: This can _never_ be hasstrings
LBS_NOTIFY |
LBS_USETABSTOPS |
LBS_NOINTEGRALHEIGHT |
LBS_OWNERDRAWFIXED | // we always force this as either we will owner draw, or our parent will
WS_VSCROLL |
LBS_DISABLENOSCROLL |
WS_VISIBLE |
WS_CHILD,
ncxBorder,
yTitle + ncyBorder,
lpCreateStruct->cx - ncxBorder, // -(2*ncxBorder)
lpCreateStruct->cy - yTitle - ncyBorder,
hwnd,
(HMENU)IDL_COLUMNLISTBOX,
lpCreateStruct->hInstance,
NULL);
#endif
//
// if we succeeded...
//
if (hwndList)
{
//
// create a ColumnLB struct to keep track of all of the pertinent instance
// info for this instance of a ColumnLB window
//
lpColumnLB = (LPCOLUMNLBSTRUCT)GlobalAllocPtr(GPTR, sizeof(COLUMNLBSTRUCT));
if (lpColumnLB)
{
BYTE i;
//
// store it in the window data for this window
//
SetWindowLongPtr(hwnd, 0, (LONG_PTR) lpColumnLB);
memset(lpColumnLB, '\0', sizeof(COLUMNLBSTRUCT));
//
// fill in pertinent info
//
lpColumnLB->Style = lpCreateStruct->style;
lpColumnLB->hInstance = (HINSTANCE)GetWindowLong(hwnd, GWLP_HINSTANCE);
lpColumnLB->fUseVlist = (UCHAR) fUseVlist;
lpColumnLB->fSorting = FALSE;
lpColumnLB->fMouseState = 0;
lpColumnLB->hwndList = hwndList;
lpColumnLB->OldListboxProc = (FARPROC)GetWindowLong(hwndList, GWLP_WNDPROC);
lpColumnLB->NewListboxProc = MakeProcInstance((FARPROC)ColumnLBClass_ListBoxWndProc, hInst);
lpColumnLB->hwndTitleList = hwndTitleList;
lpColumnLB->OldTitleListboxProc = (FARPROC)GetWindowLong(hwndTitleList, GWLP_WNDPROC);
lpColumnLB->NewTitleListboxProc = MakeProcInstance((FARPROC)ColumnLBClass_TitleListBoxWndProc, hInst);
lpColumnLB->nColumns=1;
lpColumnLB->yTitle = yTitle;
//
// init sort order
//
for (i=0; i < MAX_COLUMNS ; i++ )
lpColumnLB->ColumnOrderTable[i] = i;
//
// sub-class the listbox window by substituting our window proc for the
// normal one...
//
SetWindowLongPtr(hwndList, GWLP_WNDPROC,(DWORD_PTR)lpColumnLB->NewListboxProc);
//
// sub-class the title listbox window by substituting our window proc for the
// normal one...
//
SetWindowLongPtr(hwndTitleList, GWLP_WNDPROC,(DWORD_PTR)lpColumnLB->NewTitleListboxProc);
//
// add the lpColumnLB struct as the only item in the title listbox
//
ListBox_AddString(lpColumnLB->hwndTitleList, (DWORD_PTR)lpColumnLB);
//
// pass this on to the default window proc
//
return(FORWARD_WM_NCCREATE(hwnd, lpCreateStruct, DefWindowProc));
}
else
{
return(FALSE);
}
}
else
return(FALSE);
}
// ------------------------------------------------------------------
// ColumnLBClass_OnDestroy()
//
// Handles WM_DESTROY message
//
// HISTORY:
// Tom Laird-McConnell 7/18/93 Created
// ------------------------------------------------------------------
void ColumnLBClass_OnDestroy(HWND hwnd)
{
LPCOLUMNLBSTRUCT lpColumnLB = (LPCOLUMNLBSTRUCT)GetWindowLong(hwnd, 0);
DestroyWindow(lpColumnLB->hwndTitleList);
DestroyWindow(lpColumnLB->hwndList);
}
// ------------------------------------------------------------------
// ColumnLBClass_OnNCDestroy()
//
// Handles WM_NCDESTROY
//
// HISTORY:
// Tom Laird-McConnell 4/18/93 Created
// ------------------------------------------------------------------
void ColumnLBClass_OnNCDestroy(HWND hwnd)
{
LPCOLUMNLBSTRUCT lpColumnLB = (LPCOLUMNLBSTRUCT)GetWindowLong(hwnd, 0);
FreeProcInstance(lpColumnLB->NewListboxProc);
GlobalFreePtr(lpColumnLB);
}
// ------------------------------------------------------------------
// ColumnLBClass_OnPaint()
//
// Handles WM_PAINT message, draws column titles as appropriate...
//
// HISTORY:
// Tom Laird-McConnell 4/18/93 Created
// ------------------------------------------------------------------
void ColumnLBClass_OnPaint(HWND hwnd)
{
LPCOLUMNLBSTRUCT lpColumnLB = (LPCOLUMNLBSTRUCT)GetWindowLong(hwnd, 0);
PAINTSTRUCT ps;
HBRUSH hFrameBrush, hGreyBrush;
RECT rect;
HDC hdc;
int ncxBorder = GetSystemMetrics(SM_CXBORDER);
int ncyBorder = GetSystemMetrics(SM_CYBORDER);
BeginPaint(hwnd, (LPPAINTSTRUCT)&ps);
// Draw border around title and listbox
GetClientRect(hwnd, (LPRECT)&rect);
hdc = ps.hdc;
hFrameBrush = CreateSolidBrush(GetSysColor(COLOR_WINDOWFRAME));
FrameRect(hdc, (LPRECT)&rect, hFrameBrush);
// make bottom the height of a HSCROLL bar
// make left side the width of a VSCROLL bar
rect.top += ncyBorder;
rect.right -= ncxBorder;
rect.left = rect.right - GetSystemMetrics(SM_CXVSCROLL);
rect.bottom = lpColumnLB->yTitle+ncyBorder;
hGreyBrush = CreateSolidBrush(GetSysColor(COLOR_SCROLLBAR));
FillRect(hdc, &rect, hGreyBrush);
rect.right = rect.left+1;
FillRect(hdc, &rect, hFrameBrush);
// destroy brushes...
DeleteBrush(hFrameBrush);
DeleteBrush(hGreyBrush);
EndPaint(hwnd, (LPPAINTSTRUCT)&ps);
}
// ------------------------------------------------------------------
// ColumnLBClass_OnSize()
//
// Handles WM_SIZE message
//
// HISTORY:
// Tom Laird-McConnell 4/18/93 Created
// ------------------------------------------------------------------
void ColumnLBClass_OnSize(HWND hwnd, UINT state, int cx, int cy)
{
WORD ncxBorder;
WORD ncyBorder;
RECT rect;
DWORD cxExtent;
LPCOLUMNLBSTRUCT lpColumnLB = (LPCOLUMNLBSTRUCT)GetWindowLong(hwnd, 0);
if (lpColumnLB->hwndList != (HWND)NULL)
{
ncxBorder = (WORD)GetSystemMetrics(SM_CXBORDER);
ncyBorder = (WORD)GetSystemMetrics(SM_CYBORDER);
// position title listbox at top
MoveWindow(lpColumnLB->hwndTitleList,
ncxBorder,
ncyBorder,
cx-(2*ncxBorder) - GetSystemMetrics(SM_CXVSCROLL),
lpColumnLB->yTitle,
TRUE);
// position list box below title listbox
MoveWindow(lpColumnLB->hwndList,
ncxBorder,
lpColumnLB->yTitle + ncyBorder,
cx - ncxBorder, // -(2*ncxBorder)
cy-lpColumnLB->yTitle - ncyBorder,
TRUE);
cxExtent = ColumnLBClass_ComputeOffsets(hwnd);
GetClientRect(hwnd, &rect);
//
// if the new extent is smaller then the space available, move the position
//
if ((DWORD)rect.right > cxExtent)
{
// #ifdef DEBUG
// dprintf(TEXT("Reset HSCROLL pos to far left\n"));
// #endif
// move position to far left
SendMessage(lpColumnLB->hwndList,
(lpColumnLB->fUseVlist) ? VLB_HSCROLL : WM_HSCROLL,
MAKEWPARAM(SB_TOP, 0), 0);
// do the same for the title list
SendMessage(lpColumnLB->hwndTitleList,
WM_HSCROLL, MAKEWPARAM(SB_TOP, 0), 0);
}
InvalidateRect(lpColumnLB->hwndList, NULL, TRUE);
InvalidateRect(lpColumnLB->hwndTitleList, NULL, TRUE);
}
InvalidateRect(lpColumnLB->hwndTitleList, 0, TRUE);
InvalidateRect(lpColumnLB->hwndList, 0, TRUE);
}
// ------------------------------------------------------------------
// ColumnLBClass_OnSetFont()
//
// Handles WM_SETFONT message
//
// HISTORY:
// Tom Laird-McConnell 4/18/93 Created
// ------------------------------------------------------------------
void ColumnLBClass_OnSetFont(HWND hwnd, HFONT hFont, BOOL fRedraw)
{
LPCOLUMNLBSTRUCT lpColumnLB = (LPCOLUMNLBSTRUCT)GetWindowLong(hwnd, 0);
HDC hdc;
TEXTMETRIC tm;
RECT rect;
lpColumnLB->hFont = hFont;
hdc = GetDC(hwnd);
SelectFont(hdc, (HFONT)hFont);
GetTextMetrics(hdc, &tm);
//
// forward on to listbox window
//
if (lpColumnLB->hwndList != (HWND)NULL)
FORWARD_WM_SETFONT(lpColumnLB->hwndList, hFont, fRedraw, SendMessage);
if (lpColumnLB->hwndTitleList != (HWND)NULL)
FORWARD_WM_SETFONT(lpColumnLB->hwndTitleList, hFont, fRedraw, SendMessage);
//
// store text height...
//
lpColumnLB->yTitle = tm.tmHeight + 2*GetSystemMetrics(SM_CYBORDER);
//
// change height appropriately
//
ListBox_SetItemHeight(lpColumnLB->hwndTitleList, 0, lpColumnLB->yTitle);
SendMessage(lpColumnLB->hwndList,
(lpColumnLB->fUseVlist) ? VLB_SETITEMHEIGHT : LB_SETITEMHEIGHT,
0,
tm.tmHeight);
//
// since we changed the font size, forze a WM_SIZE to recalc the size of the window
//
GetClientRect(hwnd, &rect);
ColumnLBClass_OnSize(hwnd, SIZE_RESTORED, rect.right-rect.left, rect.bottom-rect.top);
ReleaseDC(hwnd, hdc);
}
// ------------------------------------------------------------------
// ColumnLBClass_OnHScroll()
//
//
// Handles OnHScroll messages to keep title bar in ssync with listbox...
//
// HISTORY:
// Tom Laird-McConnell 5/1/93 Created
// ------------------------------------------------------------------
LRESULT ColumnLBClass_OnHScroll(HWND hwnd, HWND hwndCtl, UINT code, int pos)
{
LPCOLUMNLBSTRUCT lpColumnLB = (LPCOLUMNLBSTRUCT)GetWindowLong(hwnd, 0);
long lPos;
WORD nPos;
RECT rect;
int cxExtent;
switch (code)
{
case SB_THUMBPOSITION:
case SB_THUMBTRACK:
nPos = (WORD) pos;
break;
case SB_LINEUP:
case SB_LINEDOWN:
case SB_PAGEUP:
case SB_PAGEDOWN:
case SB_TOP:
case SB_BOTTOM:
case SB_ENDSCROLL:
if (lpColumnLB->fUseVlist)
nPos = (WORD)SendMessage(lpColumnLB->hwndList, VLB_GETSCROLLPOS, 0, 0);
else
nPos = (WORD) GetScrollPos((hwndCtl) ? hwndCtl : lpColumnLB->hwndList, SB_HORZ);
// nPos = GetScrollPos(lpColumnLB->hwndList, SB_HORZ);
break;
default:
return(TRUE);
}
GetClientRect(lpColumnLB->hwndList, (LPRECT)&rect);
//... if it is a VLIST, then there is an error in the client calculation when it has VSCROLL bars, so
// we need to adjust ourselves by width of VSCROLL bar...
if (lpColumnLB->fUseVlist)
rect.right -= GetSystemMetrics(SM_CXVSCROLL);
cxExtent = (DWORD)SendMessage(lpColumnLB->hwndList,
(lpColumnLB->fUseVlist) ? VLB_GETHORIZONTALEXTENT : LB_GETHORIZONTALEXTENT, 0, 0L);
if (cxExtent >= rect.right)
{
// then the listbox size is > then client's display size
// so we need to calculate how much is not on the client display
cxExtent -= rect.right;
}
else
// else set the amount left over to 0 to nullify (technical term) the
// effects of this calculation...
cxExtent = 0;
lPos = -(((LONG)nPos*(LONG)cxExtent)/100);
if (lPos > 0)
lpColumnLB->xPos = 0;
else
if (lPos < -cxExtent)
lpColumnLB->xPos = -cxExtent;
else
lpColumnLB->xPos = (int)lPos;
return(TRUE);
}
// ------------------------------------------------------------------
// ColumnLBClass_OnMeasureItem()
//
//
// Handles telling the parent how to draw each column accordingly...
//
//
// HISTORY:
// Tom Laird-McConnell 4/18/93 Created
// ------------------------------------------------------------------
void ColumnLBClass_OnMeasureItem(HWND hwnd, LPMEASUREITEMSTRUCT lpMeasureItem)
{
LPCOLUMNLBSTRUCT lpColumnLB = (LPCOLUMNLBSTRUCT)GetWindowLong(hwnd, 0L);
TEXTMETRIC tm;
HDC hdc;
if (lpMeasureItem->CtlID == IDL_COLUMNTITLELISTBOX)
{
if (lpColumnLB)
lpMeasureItem->itemHeight = lpColumnLB->yTitle;
else
{
hdc = GetDC(hwnd);
GetTextMetrics(hdc, &tm);
ReleaseDC(hwnd, hdc);
lpMeasureItem->itemHeight = tm.tmHeight;
}
}
else
//
// it should be passed to parent
//
FORWARD_WM_MEASUREITEM(GetParent(hwnd), lpMeasureItem, SendMessage);
}
// ------------------------------------------------------------------
// ColumnLBClass_OnDeleteItem()
//
//
// Handles deleting items if necessary...
//
//
// HISTORY:
// Tom Laird-McConnell 08/18/93 Created
// ------------------------------------------------------------------
void ColumnLBClass_OnDeleteItem(HWND hwnd, const DELETEITEMSTRUCT *lpDeleteItem)
{
LPCOLUMNLBSTRUCT lpColumnLB = (LPCOLUMNLBSTRUCT)GetWindowLong(hwnd, 0L);
LPCOLRECORD lpRecord;
// don't actually do the delete if we are sorting...
if (lpColumnLB->fSorting == TRUE)
return;
if (lpDeleteItem->CtlID == IDL_COLUMNLISTBOX)
{
// if the style is that the owner is handling the owner draw stuff
// then we need to pass to the parent ELSE free our memory...
if ((lpColumnLB) && (lpColumnLB->Style & LBS_OWNERDRAWFIXED))
//
// it should be passed to parent
//
FORWARD_WM_DELETEITEM(GetParent(hwnd), lpDeleteItem, SendMessage);
else
// this is our item data, so we need to free it
if (lpDeleteItem->itemData)
{
lpRecord = (LPCOLRECORD)lpDeleteItem->itemData;
// NOTE that the first pointer is actually the string buffer...
if (lpRecord->pString[0])
GlobalFreePtr(lpRecord->pString[0]);
GlobalFreePtr(lpRecord);
}
}
}
// ------------------------------------------------------------------
// ColumnLBClass_OnDrawItem()
//
//
// Handles telling the parent to draw each column accordingly...
//
//
// HISTORY:
// Tom Laird-McConnell 4/18/93 Created
// ------------------------------------------------------------------
void ColumnLBClass_OnDrawItem(HWND hwnd, const DRAWITEMSTRUCT * lpDrawItem)
{
LPCOLUMNLBSTRUCT lpColumnLB = (LPCOLUMNLBSTRUCT)GetWindowLong(hwnd, 0L);
HWND hwndParent = GetParent(hwnd);
BYTE i;
BYTE PhysCol;
CLBDRAWITEMSTRUCT CLBDrawItemStruct;
RECT rect;
int ncxBorder = GetSystemMetrics(SM_CXBORDER);
int ncyBorder = GetSystemMetrics(SM_CYBORDER);
HPEN hFramePen;
HPEN hShadowPen;
HPEN hHighlightPen;
HPEN hOldPen;
HBRUSH hBackgroundBrush;
DWORD Col;
DWORD cyChar;
TEXTMETRIC tm;
BYTE PhysColumn;
RECT ClientRect;
GetClientRect(lpDrawItem->hwndItem, &ClientRect);
//
// figure out which window sent us the DrawItem
//
switch (lpDrawItem->CtlID)
{
//
// handle drawing the title listbox
//
case IDL_COLUMNTITLELISTBOX:
{
LPCOLUMNLBSTRUCT lpColumnLB = (LPCOLUMNLBSTRUCT)GetWindowLong(hwnd, 0);
if (lpDrawItem->itemAction == ODA_DRAWENTIRE)
{
GetTextMetrics(lpDrawItem->hDC, &tm);
cyChar = tm.tmHeight;
//
// create all of our pens for our drawing
//
hHighlightPen = CreatePen(PS_SOLID, ncyBorder, GetSysColor(COLOR_BTNHIGHLIGHT));
hShadowPen = CreatePen(PS_SOLID, ncyBorder, GetSysColor(COLOR_BTNSHADOW));
hFramePen = CreatePen(PS_SOLID, ncyBorder, GetSysColor(COLOR_WINDOWFRAME));
hBackgroundBrush = CreateSolidBrush(GetSysColor(COLOR_BTNFACE));
//
// get the window rect we are going to work with
//
CopyRect(&rect, &lpDrawItem->rcItem);
FillRect(lpDrawItem->hDC, &rect, hBackgroundBrush);
//
// Draw frame color line below title section of property window
//
hOldPen = SelectObject(lpDrawItem->hDC, hFramePen);
MoveToEx(lpDrawItem->hDC, rect.left, rect.bottom, NULL);
LineTo(lpDrawItem->hDC, rect.right, rect.bottom);
//
// we start at the current left
//
rect.top += 2*ncyBorder;
SetTextColor(lpDrawItem->hDC, GetSysColor(COLOR_BTNTEXT));
SetBkColor(lpDrawItem->hDC, GetSysColor(COLOR_BTNFACE));
SetBkMode(lpDrawItem->hDC, TRANSPARENT);
for (Col=0; Col < lpColumnLB->nColumns; Col++)
{
//
// get the index number of the current column
//
PhysColumn = lpColumnLB->ColumnOrderTable[Col];
//
// adjust right side to be left side plus width of current column
//
rect.right = rect.left + lpColumnLB->ColumnInfoTable[PhysColumn].Width;
//
// if the button is dpressed, then draw it that way
//
if (lpColumnLB->ColumnInfoTable[PhysColumn].fDepressed)
{
//
// pick the shadow pen and draw the top-left depressed
//
SelectObject(lpDrawItem->hDC, hShadowPen);
MoveToEx(lpDrawItem->hDC, rect.left, rect.bottom, NULL);
LineTo(lpDrawItem->hDC, rect.left, rect.top-2*ncyBorder); // bottom-left --> top-left
LineTo(lpDrawItem->hDC, rect.right, rect.top-2*ncyBorder); // top-left --> top-right
//
// pick the Frame pen and draw the Column seperater
//
SelectObject(lpDrawItem->hDC, hFramePen);
MoveToEx(lpDrawItem->hDC, rect.right+ncxBorder, rect.top-2*ncyBorder, NULL);
LineTo(lpDrawItem->hDC, rect.right+ncxBorder, rect.bottom); // bottom-left --> top-left
//
// move the cursor for whitespace to draw text
//
rect.left += WHITESPACE/2;
// draw the title of the column in the current slot
DrawText(lpDrawItem->hDC,
lpColumnLB->ColumnInfoTable[PhysColumn].lpTitle,
-1,
(LPRECT)&rect,
DT_SINGLELINE | DT_LEFT | DT_TOP);
rect.left -= WHITESPACE/2; // restore the left position...
}
else
{
// it is not depressed, draw it normal
//
// pick the white pen and draw the top-left highlight
//
SelectObject(lpDrawItem->hDC, hHighlightPen);
MoveToEx(lpDrawItem->hDC, rect.left, rect.bottom-ncyBorder, NULL);
LineTo(lpDrawItem->hDC, rect.left, rect.top-2*ncyBorder); // bottom-left --> top-left
LineTo(lpDrawItem->hDC, rect.right, rect.top-2*ncyBorder); // top-left --> top-right
//
// pick the shadow pen and draw the bottom-right dark shadow
//
SelectObject(lpDrawItem->hDC, hShadowPen);
LineTo(lpDrawItem->hDC, rect.right, rect.bottom-ncyBorder); // top-right --> bottom-right
LineTo(lpDrawItem->hDC, rect.left, rect.bottom-ncyBorder); // bottom-right --> bottom-left
//
// pick the Frame pen and draw the Column seperater
//
SelectObject(lpDrawItem->hDC, hFramePen);
MoveToEx(lpDrawItem->hDC, rect.right+ncxBorder, rect.top-2*ncyBorder, NULL);
LineTo(lpDrawItem->hDC, rect.right+ncxBorder, rect.bottom); // bottom-left --> top-left
//
// move the cursor for whitespace to draw text
//
rect.left += WHITESPACE/4;
rect.top -= ncyBorder;
// draw the title of the column in the current slot
DrawText(lpDrawItem->hDC,
lpColumnLB->ColumnInfoTable[PhysColumn].lpTitle,
-1,
(LPRECT)&rect,
DT_SINGLELINE | DT_LEFT | DT_TOP);
rect.top += ncyBorder;
}
//
// adjust the left side of the rect for the width of this column
//
rect.left = rect.right+2*ncxBorder;
}
// select the original brush
SelectObject(lpDrawItem->hDC, hOldPen);
// delete my pens
DeletePen(hFramePen);
DeletePen(hHighlightPen);
DeletePen(hShadowPen);
DeleteBrush(hBackgroundBrush);
}
}
break;
//
// handle sending CLB_DRAWITEM MESSAGES to parent
//
case IDL_COLUMNLISTBOX:
{
//
// make a copy of the drawitem portion of the struct
//
memcpy(&CLBDrawItemStruct.DrawItemStruct, lpDrawItem, sizeof(DRAWITEMSTRUCT));
//
// fake parent window into thinking our id is the listbox
//
CLBDrawItemStruct.DrawItemStruct.CtlID = GetDlgCtrlID(hwnd);
CLBDrawItemStruct.lpColOrder = lpColumnLB->ColumnOrderTable;
CLBDrawItemStruct.nColumns = lpColumnLB->nColumns;
CopyRect(&rect, &lpDrawItem->rcItem);
//
// move the cursor for whitespace to draw text
//
rect.left += WHITESPACE/4;
//
// tell the parent to draw each physical column in the appropriate rectangle
//
for (i=0; i < lpColumnLB->nColumns ;i++ )
{
//
// get physical column number
//
PhysCol = lpColumnLB->ColumnOrderTable[i];
//
// massage the rect's right to be the left plus the width of the column
//
rect.right = rect.left + lpColumnLB->ColumnInfoTable[PhysCol].Width - WHITESPACE/4;
//
// copy it
//
CopyRect(&CLBDrawItemStruct.rect[i], &rect);
//
// massage the rect's left to be the right + 1
//
rect.left = rect.right + WHITESPACE/4 + 2*ncxBorder ;
}
if ((lpColumnLB->Style & LBS_OWNERDRAWFIXED) ||
(lpColumnLB->Style & VLBS_OWNERDRAWFIXED) )
//
// send a draw message with the physical column order list
// to the parent as they want to draw it
//
SendMessage(hwndParent, CLBN_DRAWITEM, (WPARAM)0, (WPARAM)&CLBDrawItemStruct);
else
{
//
// we want to draw it ourselves...
// NOTE: This assumes that we are LBS_HASSTRINGS and NOT LBS_OWNERDRAWFIXED
//
switch(lpDrawItem->itemAction)
{
case ODA_FOCUS:
DrawFocusRect(lpDrawItem->hDC,(LPRECT)&(lpDrawItem->rcItem));
break;
case ODA_DRAWENTIRE:
case ODA_SELECT:
// only if we have data...
if (lpDrawItem->itemData)
{
LPCOLRECORD lpColRecord = (LPCOLRECORD)lpDrawItem->itemData;
if ((lpColRecord == NULL) ||
(lpColRecord == (LPCOLRECORD)LB_ERR))
break; // bogus data
// Are we highlighted? (highlit?)
if (lpDrawItem->itemState & ODS_SELECTED)
{
hBackgroundBrush = CreateSolidBrush(GetSysColor(COLOR_HIGHLIGHT));
SetBkColor(lpDrawItem->hDC, GetSysColor(COLOR_HIGHLIGHT));
SetTextColor(lpDrawItem->hDC, GetSysColor(COLOR_HIGHLIGHTTEXT));
}
else
{
hBackgroundBrush = CreateSolidBrush(GetSysColor(COLOR_WINDOW));
SetBkColor(lpDrawItem->hDC, GetSysColor(COLOR_WINDOW));
SetTextColor(lpDrawItem->hDC, GetSysColor(COLOR_WINDOWTEXT));
}
// FillRect(lpDrawItem->hDC,(LPRECT)&(lpDrawItem->rcItem), hBackgroundBrush);
//
// either way, draw column borders now...
//
hFramePen = CreatePen(PS_SOLID, ncyBorder, GetSysColor(COLOR_WINDOWFRAME));
hOldPen = SelectObject(lpDrawItem->hDC, hFramePen);
//
// now draw each column in the approved order...
//
for (i=0; i < CLBDrawItemStruct.nColumns ; i++)
{
//
// draw line of text...
//
ExtTextOut( lpDrawItem->hDC,
CLBDrawItemStruct.rect[i].left,
CLBDrawItemStruct.rect[i].top,
ETO_CLIPPED | ETO_OPAQUE,
&CLBDrawItemStruct.rect[i],
lpColRecord->pString[CLBDrawItemStruct.lpColOrder[i]], // pointer to string
lstrlen(lpColRecord->pString[CLBDrawItemStruct.lpColOrder[i]]), // length
(LPINT)NULL);
// draw column seperator
ColumnLB_DrawColumnBorder( lpDrawItem->hDC, &CLBDrawItemStruct.rect[i], ClientRect.bottom, hBackgroundBrush);
}
// restore old pen
SelectObject(lpDrawItem->hDC, hOldPen);
// destroy pen
DeletePen(hFramePen);
DeleteBrush(hBackgroundBrush);
}
break;
} // end of switch on drawitem action
}
}
break;
}
}
// ------------------------------------------------------------------
// ColumnLBClass_OnCharToItem()
//
// Handles converting keystrokes to items
//
// HISTORY:
// Tom Laird-McConnell 10/18/93 Created
// ------------------------------------------------------------------
int ColumnLBClass_OnCharToItem(HWND hwnd, UINT ch, HWND hwndListbox, int iCaret)
{
LPCOLUMNLBSTRUCT lpColumnLB = (LPCOLUMNLBSTRUCT)GetWindowLong(hwnd, 0);
LPCOLRECORD lpColRecord;
int nCount;
int nCurSel;
int nNewSel;
TCHAR cKey;
TCHAR cLBText;
if (hwndListbox != lpColumnLB->hwndTitleList)
{
RECT ClientRect;
GetClientRect(hwnd, &ClientRect);
//
// if the parent is NOT ownerdraw, then we are doing it ourselves, and
// so need to translate the WM_CHAR --> the correct item based on the
// current sort column...
//
if (! (lpColumnLB->Style & (LBS_OWNERDRAWFIXED | VLBS_OWNERDRAWFIXED)) )
{
nCurSel = ListBox_GetCurSel(lpColumnLB->hwndList);
if (IsCharAlphaNumeric((TCHAR)ch))
{
nNewSel = nCurSel + 1;
nCount = ListBox_GetCount(lpColumnLB->hwndList);
cKey = (TCHAR) toupper( (TCHAR)ch );
// loop thru items starting with the one just after
// the current selection, until we are too far along,
// then wrap around to the beginning and
// keep going until we hit our original selection.
for (; nNewSel != nCurSel ; nNewSel++ )
{
// make sure that we do't try to compare at location -1
if( nNewSel == -1)
continue;
lpColRecord = (LPCOLRECORD)ListBox_GetItemData(lpColumnLB->hwndList, nNewSel);
// if this comes back as LB_ERR then we are off the end of the list
if( lpColRecord == (LPCOLRECORD)LB_ERR )
{
nNewSel = -1; // increment will move to 0
continue;
}
cLBText = (TCHAR) toupper( *lpColRecord->pString[
lpColumnLB->ColumnOrderTable[
lpColumnLB->SortColumn ]] );
if ( cLBText == cKey )
{
// we found it ...
// change the current selection
if( lpColumnLB->Style & LBS_MULTIPLESEL )
{
// multiple selection LB, just move fuzzy rect
ListBox_SetCaretIndex(lpColumnLB->hwndList, nNewSel);
// BUGBUG change of caret does not have a notification?
}
else
{
// single sel LB, change the sel
ListBox_SetCurSel(lpColumnLB->hwndList, nNewSel);
// notify our parent if we need to
if( lpColumnLB->Style & LBS_NOTIFY )
{
SendMessage( GetParent( hwnd ),
WM_COMMAND,
MAKEWPARAM( GetDlgCtrlID( hwnd), LBN_SELCHANGE),
(LPARAM)hwnd); // NOTE: substitute ourselves
// as the source of the message
}
}
return(-1); // we handled it...
}
else if (nNewSel == nCount-1)
{
// we have gone beyond it
// or are at the end of the list...
// we need to wrap to the beginning
// (this will get incremented above prior to use)
nNewSel = -1;
continue;
}
}
// we did not find our target
return(nCurSel);
}
else
// not an alphanumeric, just return the current selection
return(nCurSel);
}
else
//
// pass on to parent as a WM_CHARTOITEM, but with the HIWORD(wParam) == SORT COLUMN
//
return((int)SendMessage( GetParent(hwnd),
CLBN_CHARTOITEM,
MAKEWPARAM(ch, lpColumnLB->SortColumn),
(LPARAM)hwnd));
} else {
return 0;
}
}
// ------------------------------------------------------------------
// ColumnLBClass_OnNumberCols()
//
// case CLB_GETNUMBERCOLS : // get the number of columns (ret=NumCols)
// case CLB_SETNUMBERCOLS : // set the number of columns (wparam=NumCols)
//
// HISTORY:
// Tom Laird-McConnell 4/18/93 Created
// ------------------------------------------------------------------
BYTE ColumnLBClass_OnNumberCols(HWND hwnd, BYTE NewNumberCols, BOOL fSetColumns)
{
LPCOLUMNLBSTRUCT lpColumnLB = (LPCOLUMNLBSTRUCT)GetWindowLong(hwnd, 0L);
//
// if we are modifying it
//
if (fSetColumns)
{
//
// if the value is a new value
//
if (lpColumnLB->nColumns != NewNumberCols)
{
lpColumnLB->nColumns = NewNumberCols;
// force a redraw of the entire columnlb...
InvalidateRect(hwnd, NULL, TRUE);
}
}
return lpColumnLB->nColumns;
}
// ------------------------------------------------------------------
// ColumnLBClass_OnColWidth()
//
// case CLB_GETCOLWIDTH : // get a column width (wParm=Physical Column ret=ColWidth in DU's)
// case CLB_SETCOLWIDTH : // set a column width (wParm=Physical Column lParam=Width)
//
// HISTORY:
// Tom Laird-McConnell 4/18/93 Created
// ------------------------------------------------------------------
int ColumnLBClass_OnColWidth(HWND hwnd, BYTE Column, int NewWidth, BOOL fSetWidth)
{
LPCOLUMNLBSTRUCT lpColumnLB = (LPCOLUMNLBSTRUCT)GetWindowLong(hwnd, 0L);
int cxExtent;
RECT rect;
//
// if we are modifying it
//
if (fSetWidth)
{
//
// if the value is a new value
//
if (lpColumnLB->ColumnInfoTable[Column].Width != NewWidth)
{
lpColumnLB->ColumnInfoTable[Column].Width = NewWidth;
cxExtent = ColumnLBClass_ComputeOffsets(hwnd);
GetClientRect(hwnd, &rect);
//
// send the message to the title listbox as well
//
SendMessage(lpColumnLB->hwndTitleList, LB_SETHORIZONTALEXTENT, cxExtent, 0L);
//
// pass it on to the child listbox, using VLB_SETHOR if appropriate...
//
SendMessage(lpColumnLB->hwndList,
(lpColumnLB->fUseVlist) ? VLB_SETHORIZONTALEXTENT : LB_SETHORIZONTALEXTENT, cxExtent, 0L);
//
// if the new extent is smaller then the space available, move the position
//
if (rect.right > cxExtent)
{
// #ifdef DEBUG
// dprintf(TEXT("Reset HSCROLL pos to far left\n"));
// #endif
// move position to far left
SendMessage(lpColumnLB->hwndList,
(lpColumnLB->fUseVlist) ? VLB_HSCROLL : WM_HSCROLL,
MAKEWPARAM(SB_TOP, 0), 0);
// do the same for the title list
SendMessage(lpColumnLB->hwndTitleList,
WM_HSCROLL, MAKEWPARAM(SB_TOP, 0), 0);
}
InvalidateRect(lpColumnLB->hwndList, NULL, TRUE);
InvalidateRect(lpColumnLB->hwndTitleList, NULL, TRUE);
}
}
return (DWORD)lpColumnLB->ColumnInfoTable[Column].Width;
}
// ------------------------------------------------------------------
// ColumnLBClass_OnColTitle()
//
// case CLB_GETCOLTITLE : // get a column's title (wParm=Physical Column, ret=Title)
// case CLB_SETCOLTITLE : // set a column's title (wParm=Physical Col, lParm=Title)
//
// HISTORY:
// Tom Laird-McConnell 4/18/93 Created
// ------------------------------------------------------------------
LPTSTR ColumnLBClass_OnColTitle(HWND hwnd, BYTE Column, LPTSTR lpTitle, BOOL fSetTitle)
{
LPCOLUMNLBSTRUCT lpColumnLB = (LPCOLUMNLBSTRUCT)GetWindowLong(hwnd, 0L);
//
// if we are modifying it
//
if (fSetTitle)
{
//
// if the value is a new value
//
if (lpColumnLB->ColumnInfoTable[Column].lpTitle != lpTitle)
{
//
// BUGBUG, is there more to do here?
//
lpColumnLB->ColumnInfoTable[Column].lpTitle = lpTitle;
//
// invalidate the title
//
InvalidateRect(lpColumnLB->hwndTitleList, NULL, TRUE);
}
}
return (LPTSTR)lpColumnLB->ColumnInfoTable[Column].lpTitle;
}
// ------------------------------------------------------------------
// ColumnLBClass_OnSortCol()
//
// case CLB_GETSORTCOL : // get the sort column (ret=Physical Col)
// case CLB_SETSORTCOL : // set the sort column (wParm=Physical Col)
//
// HISTORY:
// Tom Laird-McConnell 4/18/93 Created
// ------------------------------------------------------------------
BYTE ColumnLBClass_OnSortCol(HWND hwnd, BYTE NewSortCol, BOOL fSetSortCol)
{
LPCOLUMNLBSTRUCT lpColumnLB = (LPCOLUMNLBSTRUCT)GetWindowLong(hwnd, 0L);
DWORD nCount;
PDWORD_PTR lpListboxContents;
DWORD i;
int nCurSel;
DWORD_PTR ItemData;
HCURSOR hCursor;
//
// if we are modifying it
//
if (fSetSortCol)
{
hCursor = SetCursor(LoadCursor(0, IDC_WAIT));
// set new sort value
lpColumnLB->SortColumn = NewSortCol;
// need to resort listbox
nCount = ListBox_GetCount(lpColumnLB->hwndList);
// need to get current select
nCurSel = ListBox_GetCurSel(lpColumnLB->hwndList);
// and it's item data
ItemData = ListBox_GetItemData(lpColumnLB->hwndList, nCurSel);
SetWindowRedraw(lpColumnLB->hwndList, FALSE);
//
// allocate space for the listbox contents
//
lpListboxContents = (PDWORD_PTR) GlobalAllocPtr(GPTR, sizeof(DWORD_PTR) * nCount);
//
// retrieve all of the data values
//
for (i=0; i<nCount; i++)
lpListboxContents[i] = ListBox_GetItemData(lpColumnLB->hwndList, i);
//
// reset the listbox contents
//
lpColumnLB->fSorting = TRUE; // disable deleting while sorting...
SendMessage(lpColumnLB->hwndList, LB_RESETCONTENT, 0, 0);
lpColumnLB->fSorting = FALSE; // reenable it...
//
// now re-add all of the items, with the new sort column
//
for (i=0; i<nCount ; i++ )
{
nCurSel = ListBox_AddString(lpColumnLB->hwndList, lpListboxContents[i]);
}
// reselect selected item...
for (i=0; i < nCount ; i++)
{
if (ItemData == (DWORD)ListBox_GetItemData(lpColumnLB->hwndList, i))
// then select it
ListBox_SetCurSel(lpColumnLB->hwndList, i);
}
GlobalFreePtr(lpListboxContents);
SetWindowRedraw(lpColumnLB->hwndList, TRUE);
InvalidateRect(lpColumnLB->hwndList, NULL, TRUE);
SetCursor(hCursor);
}
return lpColumnLB->SortColumn;
}
// ------------------------------------------------------------------
// ColumnLBClass_OnColOrder()
//
// case CLB_GETCOLORDER : // get the virtual order of the physical columns (ret=LPDWORD order table)
// case CLB_SETCOLORDER : // set the virtual order of the physical columns (ret=LPDWORD order table, wParamn=LPDWORD new order)
//
// HISTORY:
// Tom Laird-McConnell 4/18/93 Created
// ------------------------------------------------------------------
LPBYTE ColumnLBClass_OnColOrder(HWND hwnd, LPBYTE NewColOrder, BOOL fSetOrder)
{
LPCOLUMNLBSTRUCT lpColumnLB = (LPCOLUMNLBSTRUCT)GetWindowLong(hwnd, 0L);
//
// if we are modifying it
//
if (fSetOrder)
{
//
// copy the new order over the old order
//
memcpy(lpColumnLB->ColumnOrderTable, NewColOrder, lpColumnLB->nColumns);
ColumnLBClass_ComputeOffsets(hwnd);
//
// cause listbox to be redrawn
//
InvalidateRect(lpColumnLB->hwndTitleList, NULL, TRUE);
InvalidateRect(lpColumnLB->hwndList, NULL, TRUE);
}
return lpColumnLB->ColumnOrderTable;
}
// ------------------------------------------------------------------
// ColumnLBClass_OnColOffsets()
//
// case CLB_GETCOLOFFSETS : // gets the incremental col offsets (ret=LPDWORD)
// case CLB_SETCOLOFFSETS : // sets the incremental col offsets (wParam = LPDWORD)
//
// HISTORY:
// Tom Laird-McConnell 4/18/93 Created
// ------------------------------------------------------------------
LPINT ColumnLBClass_OnColOffsets(HWND hwnd, LPINT NewOffsetTable, BOOL fSetOffsets)
{
LPCOLUMNLBSTRUCT lpColumnLB = (LPCOLUMNLBSTRUCT)GetWindowLong(hwnd, 0L);
//
// if we are modifying it
//
// if (fSetOffsets)
// {
// for (i=0; i < lpColumnLB->nColumns ; i++ )
// {
// lpColumnLB->ColumnOffsetTable[i] = NewOffsetTable[i];
// }
// }
return (lpColumnLB->ColumnOffsetTable);
}
// ------------------------------------------------------------------
// ColumnLBClass_OnAutoWidths()
//
//
// Handles CLB_AUTOWIDTHS messages to calculate the width of each field, and
// to calculate the offsets automatically... (if column is -1 , then all columns)
// ColumnToCompute is in Physical Columns
//
// returns: The horiztonal extent of all of the columns...
//
// HISTORY:
// Tom Laird-McConnell 5/1/93 Created
// ------------------------------------------------------------------
LRESULT ColumnLBClass_OnAutoWidth(HWND hwnd, BYTE ColumnToCompute)
{
HDC hdc;
BYTE nColumn;
LONG cxExtent;
SIZE Size;
TEXTMETRIC tm;
LPCOLUMNINFO lpColumnInfo, lpPrevColumnInfo;
LPCOLUMNLBSTRUCT lpColumnLB = (LPCOLUMNLBSTRUCT)GetWindowLong(hwnd, 0);
HFONT hOldFont;
DWORD_PTR OldStyle, NewStyle;
hdc = GetDC(hwnd);
GetTextMetrics(hdc, &tm);
hOldFont = SelectFont(hdc, lpColumnLB->hFont);
lpPrevColumnInfo = NULL;
//
// based on column order, compute the widths and offsets of each column
// NOTE: nColumn is the physical column
//
lpColumnInfo = lpColumnLB->ColumnInfoTable;
cxExtent = 0;
for (nColumn=0; nColumn < lpColumnLB->nColumns; nColumn++, lpColumnInfo++)
{
// bail out if column title is not there...
if ((lpColumnInfo->lpTitle == NULL) ||
(lpColumnInfo->lpTitle[0] == '\0'))
continue; // try next column
//
// only if it is a column we are supposed to change
//
if ((ColumnToCompute == (BYTE)-1) ||
(nColumn == ColumnToCompute))
{
GetTextExtentPoint( hdc,
(LPTSTR)lpColumnInfo->lpTitle,
lstrlen(lpColumnInfo->lpTitle),
&Size);
//
// the width is the text extent of the string plus some whitespace
//
lpColumnInfo->Width = (WHITESPACE/2) + Size.cx;
}
}
SelectFont(hdc, hOldFont);
ReleaseDC(hwnd, hdc);
//
// now adjust the offsets to show new values
//
cxExtent = ColumnLBClass_ComputeOffsets(hwnd);
if (lpColumnLB->fUseVlist)
OldStyle = SendMessage(lpColumnLB->hwndList, VLB_GETLISTBOXSTYLE, 0L, 0L);
else
OldStyle = GetWindowLongPtr(lpColumnLB->hwndList, GWL_STYLE);
//
// send the message to the title listbox as well
//
SendMessage(lpColumnLB->hwndTitleList, LB_SETHORIZONTALEXTENT, cxExtent, 0L);
SendMessage(lpColumnLB->hwndList,
(lpColumnLB->fUseVlist) ? VLB_SETHORIZONTALEXTENT : LB_SETHORIZONTALEXTENT, cxExtent, 0L);
if (lpColumnLB->fUseVlist)
NewStyle = SendMessage(lpColumnLB->hwndList, VLB_GETLISTBOXSTYLE, 0L, 0L);
else
NewStyle = GetWindowLongPtr(lpColumnLB->hwndList, GWL_STYLE);
//
// if the horizontal scroll bar is gone, then reset hscroll position
//
if ((NewStyle & WS_HSCROLL) !=
(OldStyle & WS_HSCROLL))
{
// move position to far left
SendMessage(lpColumnLB->hwndList,
(lpColumnLB->fUseVlist) ? VLB_HSCROLL : WM_HSCROLL,
MAKEWPARAM(SB_TOP, 0), 0);
}
InvalidateRect(lpColumnLB->hwndList, NULL, TRUE);
InvalidateRect(lpColumnLB->hwndTitleList, NULL, TRUE);
return(cxExtent);
}
// ------------------------------------------------------------------
// ColumnLBClass_ComputeOffsets()
//
// returns text extent...
//
// HISTORY:
// Tom Laird-McConnell 5/3/93 Created
// ------------------------------------------------------------------
int ColumnLBClass_ComputeOffsets(HWND hwnd)
{
BYTE i;
LPCOLUMNLBSTRUCT lpColumnLB = (LPCOLUMNLBSTRUCT)GetWindowLong(hwnd, 0L);
LPINT lpOffset;
LPBYTE lpOrder;
LPCOLUMNINFO lpColumnInfo;
BYTE PhysColumn;
int ncxBorder = GetSystemMetrics(SM_CXBORDER);
//
// recalc the offsets table using the current virtual order
//
lpOffset = lpColumnLB->ColumnOffsetTable;
lpOrder = lpColumnLB->ColumnOrderTable;
lpColumnInfo = lpColumnLB->ColumnInfoTable;
//
// first offset is always 0
//
lpOffset[0] = 0;
for (i=1; i < lpColumnLB->nColumns + 1 ; i++ )
{
PhysColumn = lpOrder[i-1];
//
// this offset is the previous offset plus the previous width
//
lpOffset[i] = lpOffset[i-1] + lpColumnInfo[PhysColumn].Width + 2 * ncxBorder;
}
//
// last offset is also new text extent...
return(lpOffset[lpColumnLB->nColumns]);
}
// ------------------------------------------------------------------
// ColumnLBClass_OnLButtonDown()
//
// Handles WM_LBUTTONDOWN and WM_LBUTTONDBLCLK messages from the client
// area above the listbox
//
//
// HISTORY:
// Tom Laird-McConnell 5/3/93 Created
// ------------------------------------------------------------------
void ColumnLBClass_OnLButtonDown(HWND hwnd, BOOL fDoubleClick, int x, int y, UINT keyFlags)
{
LPCOLUMNLBSTRUCT lpColumnLB = (LPCOLUMNLBSTRUCT)GetWindowLong(hwnd, 0L);
BYTE i;
int AdjustedX = x - lpColumnLB->xPos;
HCURSOR hCursor;
BYTE PhysColumn;
BYTE VirtColumn;
RECT rect;
POINT point;
point.x = x;
point.y = y;
GetClientRect(lpColumnLB->hwndTitleList, &rect);
// only if this is a right mouse button from
if (PtInRect(&rect, point))
{
//
// if this is a down-click, and it is on a column border, then go into resize mode
//
for(i=1; i < lpColumnLB->nColumns+1; i++)
{
//
// check to see if this is a column offset
//
if ((AdjustedX > lpColumnLB->ColumnOffsetTable[i]-4) &&
(AdjustedX < lpColumnLB->ColumnOffsetTable[i]+4))
{
VirtColumn = i-1;
PhysColumn = lpColumnLB->ColumnOrderTable[VirtColumn];
//
// x is the right-side of the column i-1
//
lpColumnLB->fMouseState = MOUSE_COLUMNRESIZE;
lpColumnLB->xPrevPos = 0;
lpColumnLB->ColClickStart = VirtColumn; // virtual column
SetCapture(hwnd);
hCursor = LoadCursor(lpColumnLB->hInstance, TEXT("SizebarHCursor"));
SetCursor(hCursor);
return;
}
#ifdef DRAG
else
//
// if this is a down-click, and it is on a column title,
//
if ((AdjustedX > lpColumnLB->ColumnOffsetTable[i-1]) &&
(AdjustedX < lpColumnLB->ColumnOffsetTable[i]))
{
//
// whether it is a double-or single click, we need to draw down button state
//
VirtColumn = i-1;
PhysColumn = lpColumnLB->ColumnOrderTable[VirtColumn];
lpColumnLB->ColumnInfoTable[PhysColumn].fDepressed = TRUE;
GetClientRect(lpColumnLB->hwndTitleList, &rect);
rect.left = lpColumnLB->ColumnOffsetTable[VirtColumn] + lpColumnLB->xPos;
rect.right = lpColumnLB->ColumnOffsetTable[VirtColumn+1] + lpColumnLB->xPos;
//
// if this is a double-click, AND we are in sort mode then handle this as a sort request on the
// column double-clicked on
//
if (fDoubleClick)
{
if (GetWindowLong(hwnd, GWL_STYLE) & LBS_SORT)
{
//
// then default to doing a sort
//
SendMessage(hwnd, CLB_SETSORTCOL, (WPARAM)PhysColumn, (LPARAM)0);
}
else
{
//
// tell parent that the user double-clicked on PhysColumn
//
SendMessage(GetParent(hwnd), CLBN_TITLEDBLCLK, (WPARAM)GetDlgCtrlID(hwnd), (LPARAM) PhysColumn);
}
//
// we are done with double-click, so redraw window
//
lpColumnLB->ColumnInfoTable[PhysColumn].fDepressed = FALSE;
InvalidateRect(lpColumnLB->hwndTitleList, &rect, FALSE);
return;
}
else
{
// then go into single click mode/or column drag mode
//
// then x, y is in column i-1
//
lpColumnLB->fMouseState = MOUSE_COLUMNCLICK;
lpColumnLB->ColClickStart = VirtColumn;
CopyRect(&lpColumnLB->ColClickRect, &rect);
// lpColumnLB->ColClickRect.left += (lpColumnLB->ColClickRect.right - lpColumnLB->ColClickRect.left)/3;
// lpColumnLB->ColClickRect.right -= (lpColumnLB->ColClickRect.right - lpColumnLB->ColClickRect.left)/3;
SetCapture(hwnd);
InvalidateRect(lpColumnLB->hwndTitleList, &rect, FALSE);
GetWindowRect(lpColumnLB->hwndTitleList, &rect);
ClipCursor(&rect);
return;
}
}
#endif
}
}
}
// ------------------------------------------------------------------
// ColumnLBClass_OnMouseMove()
//
// Handles Mouse movement messages from the client
// area above the listbox
//
//
// HISTORY:
// Tom Laird-McConnell 5/3/93 Created
// ------------------------------------------------------------------
void ColumnLBClass_OnMouseMove(HWND hwnd, int x, int y, UINT keyFlags)
{
LPCOLUMNLBSTRUCT lpColumnLB = (LPCOLUMNLBSTRUCT)GetWindowLong(hwnd, 0L);
RECT rect;
HDC hdc;
BYTE i;
int AdjustedX = x - lpColumnLB->xPos;
POINT Point;
HCURSOR hCursor;
switch (lpColumnLB->fMouseState)
{
case 0 : // not in mouse mode at all, so just track changing cursor when over column border
for(i=1; i < lpColumnLB->nColumns + 1; i++)
{
//
// check to see if this is a column offset
//
if ((AdjustedX > lpColumnLB->ColumnOffsetTable[i]-4) &&
(AdjustedX < lpColumnLB->ColumnOffsetTable[i]+4))
{
//
// it is, so set the cursor and return
//
hCursor = LoadCursor(lpColumnLB->hInstance, TEXT("SizebarHCursor"));
SetCursor(hCursor);
return;
}
}
SetCursor(LoadCursor(0,IDC_ARROW));
break;
case MOUSE_COLUMNRESIZE:
GetClientRect(hwnd, &rect);
//
// as long as we haven't moved past the previous column, and we haven't moved out of the rect
//
if (AdjustedX < lpColumnLB->ColumnOffsetTable[lpColumnLB->ColClickStart]+8)
{
x += (lpColumnLB->ColumnOffsetTable[lpColumnLB->ColClickStart]+8)-AdjustedX;
AdjustedX = lpColumnLB->ColumnOffsetTable[lpColumnLB->ColClickStart]+8;
}
if (x < rect.right)
{
hdc = GetDC(hwnd);
// un invert previous postion
if (lpColumnLB->xPrevPos)
{
rect.left = lpColumnLB->xPrevPos;
rect.right = rect.left+1;
InvertRect(hdc, &rect);
}
lpColumnLB->xPrevPos = x;
// invert new position
rect.left = x;
rect.right = rect.left+1;
InvertRect(hdc, &rect);
ReleaseDC(hwnd, hdc);
}
break;
case MOUSE_COLUMNDRAG:
//
// if this is a column drag, we track the messages until the mouse has moved
// back INTO the original column rectangle, if it does this, then we switchback to
// COLUMNCLICK mode, until they let go, or move back out
//
Point.x = x;
Point.y = y;
GetClientRect(lpColumnLB->hwndTitleList, &rect);
// if it is on far RIGHT generate WM_HSCROLL right message
if (x >= rect.right-2)
{
SendMessage(lpColumnLB->hwndList, (lpColumnLB->fUseVlist) ? VLB_HSCROLL : WM_HSCROLL, MAKEWPARAM(SB_LINEDOWN, 0), (LPARAM)NULL);
return;
}
// if it is on far RIGHT generate WM_HSCROLL left message
if (x <= rect.left+2)
{
SendMessage(lpColumnLB->hwndList, (lpColumnLB->fUseVlist) ? VLB_HSCROLL : WM_HSCROLL, MAKEWPARAM(SB_LINEUP, 0), (LPARAM)NULL);
return;
}
// rect.right -= lpColumnLB->xPos;
//
// //
// // it if is out of the title area, or if it is in the original column
// //
// if ((PtInRect(&lpColumnLB->ColClickRect, Point) == TRUE) || // original column
// (PtInRect(&rect, Point) == FALSE) ) // title area
// {
// //
// // then it has moved back into the original column, switch to
// //COLUMNCLICK mode
// //
// lpColumnLB->fMouseState = MOUSE_COLUMNCLICK;
//
// SetCursor(LoadCursor(0, IDC_ARROW));
// return;
// }
break;
case MOUSE_COLUMNCLICK:
//
// if this is a column click, we track the messages until the mouse has moved
// outside of the original column rectangle, if it does this, then we switch to
// COLUMNDRAG mode, until they let go, or until they move back to the original
// column.
//
Point.x = x;
Point.y = y;
GetClientRect(lpColumnLB->hwndTitleList, &rect);
rect.right -= lpColumnLB->xPos;
//
// if it is outside of the original column, and inside title area, then swtich to
// DRAG mode
//
if ((PtInRect(&lpColumnLB->ColClickRect, Point) == FALSE) && //
(PtInRect(&rect, Point) == TRUE) ) // title area
{
//
// then it has moved outside of the column, switch to
//COLUMNDRAG mode
//
lpColumnLB->fMouseState = MOUSE_COLUMNDRAG;
hCursor = LoadCursor(lpColumnLB->hInstance, TEXT("ColDragCursor"));
SetCursor(hCursor);
}
break;
}
}
// ------------------------------------------------------------------
// ColumnLBClass_OnLButtonUp()
//
// Handles WM_LBUTTONUp messages from the client
// area above the listbox
//
//
// HISTORY:
// Tom Laird-McConnell 5/3/93 Created
// ------------------------------------------------------------------
void ColumnLBClass_OnLButtonUp(HWND hwnd, int x, int y, UINT keyFlags)
{
LPCOLUMNLBSTRUCT lpColumnLB = (LPCOLUMNLBSTRUCT)GetWindowLong(hwnd, 0L);
BYTE PhysColumn = lpColumnLB->ColumnOrderTable[lpColumnLB->ColClickStart];
BYTE PhysSourceColumn;
int AdjustedX = x - lpColumnLB->xPos;
POINT Point;
BYTE NewOrderTable[MAX_COLUMNS];
LPBYTE lpNewOrderTable = NewOrderTable;
LPBYTE lpOrderTable = lpColumnLB->ColumnOrderTable;
BYTE CurrentCol;
BYTE DestCol;
BYTE SourceCol;
TCHAR Direction;
BYTE i;
HDC hdc;
RECT rect;
SetCursor(LoadCursor(0, IDC_ARROW)); // go back to arrow
switch (lpColumnLB->fMouseState)
{
case MOUSE_COLUMNRESIZE:
//
// if we were in resize column mode, then resize the column to the left of the border
//
ReleaseCapture();
ClipCursor(NULL);
lpColumnLB->fMouseState = 0;
// clean up line
GetClientRect(hwnd, &rect);
hdc = GetDC(hwnd);
// massage the value to make sure it's in the right range...
if (AdjustedX < lpColumnLB->ColumnOffsetTable[lpColumnLB->ColClickStart]+8)
AdjustedX = lpColumnLB->ColumnOffsetTable[lpColumnLB->ColClickStart]+8;
// un invert previous postion
if (lpColumnLB->xPrevPos)
{
rect.left = lpColumnLB->xPrevPos;
rect.right = rect.left+1;
InvertRect(hdc, &rect);
}
ReleaseDC(hwnd, hdc);
//
// set the physical column width to be the current x position - the current virtual column offset
//
SendMessage(hwnd,
CLB_SETCOLWIDTH,
(WPARAM)PhysColumn,
(LPARAM)AdjustedX - lpColumnLB->ColumnOffsetTable[lpColumnLB->ColClickStart]);
break;
case MOUSE_COLUMNDRAG:
lpColumnLB->fMouseState = 0;
ReleaseCapture();
ClipCursor(NULL);
lpColumnLB->ColumnInfoTable[PhysColumn].fDepressed = FALSE;
//
// we need to figure out what column we ended up on
//
for(i=1; i < lpColumnLB->nColumns+1; i++)
{
//
// if it fits in this columns area, then this is the destination column
//
if ((AdjustedX > lpColumnLB->ColumnOffsetTable[i-1]) &&
(AdjustedX < lpColumnLB->ColumnOffsetTable[i]))
{
//
// make duplicate of the table
//
memcpy(NewOrderTable, lpOrderTable, sizeof(BYTE)*lpColumnLB->nColumns);
//
// i-1 is the destination column! (virtual)
//
SourceCol = lpColumnLB->ColClickStart; // virtual
DestCol = i-1; // virtual
PhysSourceColumn = lpColumnLB->ColumnOrderTable[SourceCol]; // physical
Direction = (SourceCol > DestCol) ? -1 : 1;
CurrentCol = SourceCol;
while (CurrentCol != DestCol)
{
NewOrderTable[CurrentCol] = NewOrderTable[CurrentCol + Direction];
CurrentCol += Direction;
}
//
// ok, it's equal to destination, so let's put the source Physical value into the destination
//
NewOrderTable[CurrentCol] = PhysSourceColumn;
//
// ok, so now set the order to the new order
//
SendMessage(hwnd, CLB_SETCOLORDER, (WPARAM)0, (LPARAM)NewOrderTable);
}
}
GetClientRect(lpColumnLB->hwndTitleList, &rect);
rect.left = lpColumnLB->ColumnOffsetTable[lpColumnLB->ColClickStart] + lpColumnLB->xPos;
rect.right = lpColumnLB->ColumnOffsetTable[lpColumnLB->ColClickStart+1] + lpColumnLB->xPos;
InvalidateRect(lpColumnLB->hwndTitleList, &rect, FALSE);
break;
case MOUSE_COLUMNCLICK:
//
// if this is a column click, we track the messages until the mouse has moved
//
lpColumnLB->fMouseState = 0;
ReleaseCapture();
ClipCursor(NULL);
lpColumnLB->ColumnInfoTable[PhysColumn].fDepressed = FALSE;
GetClientRect(lpColumnLB->hwndTitleList, &rect);
rect.left = lpColumnLB->ColumnOffsetTable[lpColumnLB->ColClickStart] + lpColumnLB->xPos;
rect.right = lpColumnLB->ColumnOffsetTable[lpColumnLB->ColClickStart+1] + lpColumnLB->xPos;
InvalidateRect(lpColumnLB->hwndTitleList, &rect, FALSE);
//
// now send a CLBN_SINGLECLICK message to the parent, only if the mousebutton was let up in the original
// column
//
Point.x = AdjustedX;
Point.y = y;
if (PtInRect(&lpColumnLB->ColClickRect, Point) == TRUE)
SendMessage(GetParent(hwnd), CLBN_TITLESINGLECLK, (WPARAM)GetDlgCtrlID(hwnd), (LPARAM)PhysColumn);
return;
}
}
// ------------------------------------------------------------------
// ColumnLBClass_OnRButtonDown()
//
// Handles WM_RBUTTON_DOWN messages
// alg:
//
// figure out where we are
// determine listbox item
// find height of rows
// translate mouse Y into a row number
// are we VLIST?
// Yes, Get TopIndex
// are we Owner Draw?
// Yes
// Send message to VLIST to get the data for row number
// No
// item number + TopIndex is the info the parent needs.
// No
// item number is the info the parent needs
// determine column
// calc which column taking into account scrolling
// send message to parent with info
// The parent will receive the column in wParam and the item in lParam. lParam
// needs to be the item because it might be the owner draw data.
//
//
//
// HISTORY:
// Steve Hiskey 10/19/93 Created
// ------------------------------------------------------------------
void ColumnLBClass_OnRButtonDown (HWND hwnd, BOOL fDoubleClick, int x, int y, UINT keyFlags)
{
LPCOLUMNLBSTRUCT lpColumnLB = (LPCOLUMNLBSTRUCT)GetWindowLong(hwnd, 0L);
// get the item height here and not when the OnFont message is
// processed. ?? we get different/wrong results otherwise.
int ItemHeight = (int)ListBox_GetItemHeight(hwnd,1);
int Item = y / ItemHeight;
BYTE i;
int AdjustedX = x - lpColumnLB->xPos;
BYTE VirtColumn;
DWORD TopIndex;
CLBRBUTTONSTRUCT RButtonStruct;
BOOL GotOne = FALSE;
BOOL fTemp;
RButtonStruct.x = x;
RButtonStruct.y = y + lpColumnLB->yTitle;
RButtonStruct.hwndChild = hwnd;
//
// we already have the item (non VList), figure out which column
//
for(i=1; i < lpColumnLB->nColumns+1; i++)
{
if ((AdjustedX > lpColumnLB->ColumnOffsetTable[i-1]) &&
(AdjustedX < lpColumnLB->ColumnOffsetTable[i]))
{
//
// we have our column. Get the Physical Column. The parent of this column
// list box know what columns are interesting... and how the physical columns
// map to the virtual columns.
//
VirtColumn = i-1;
RButtonStruct.PhysColumn = lpColumnLB->ColumnOrderTable[VirtColumn];
GotOne = TRUE;
break;
}
}
if ( !GotOne)
return;
// are we VLIST?
if ( lpColumnLB->fUseVlist )
{
DWORD Style;
// are we owner draw? If so, then we don't care about TopIndex, we just want
// the instance data
Style = (DWORD)SendMessage(lpColumnLB->hwndList, VLB_GETVLISTSTYLE, 0, 0L);
if ( Style && VLBS_USEDATAVALUES )
{
// we are use data values. This means that we must ask the VList for the
// data and this is the data is the identifier of the row. ie, the data the
// VList stores is the structure needed to identify and display the line.
RButtonStruct.Index = ListBox_GetItemData(lpColumnLB->hwndList, Item );
}
else
{
// we are a normal vlist box. Get the top index and add our offset
// from top of listbox to it.
TopIndex = (DWORD)SendMessage(lpColumnLB->hwndList, LB_GETTOPINDEX, 0, 0L);
RButtonStruct.Index = TopIndex + Item;
}
}
else
{
// we are a normal list box. We need to know what item we are looking at.
// ask the listbox for the top index.
TopIndex = (DWORD)SendMessage(lpColumnLB->hwndList, LB_GETTOPINDEX, 0, 0L);
RButtonStruct.Index = TopIndex + Item;
}
// if they have hit rButton, we should set the focus to this item (lButton)... since
// WE are providing the CLB_SETCURSEL, we must tell the parent... some weird rule about
// if the user does a set cur sel, then the parent is notified, but if the parent does
// the set cur sel, the parent is not notified... since we are neither the parent or the
// user, we have to do both.
// if VLIST, we need to send just the item, top TopIndex + Item...
if ( lpColumnLB->fUseVlist )
fTemp = ListBox_SetCurSel(lpColumnLB->hwndList, Item);
else
fTemp = ListBox_SetCurSel(lpColumnLB->hwndList, RButtonStruct.Index);
if ( fTemp )
SendMessage(GetParent(hwnd), WM_COMMAND,
GetDlgCtrlID( lpColumnLB->hwndList),
MAKELPARAM(lpColumnLB->hwndList, LBN_SELCHANGE));
// we are ready to send which column and which row to the parent.
SendMessage ( GetParent (hwnd), CLBN_RBUTTONDOWN, (WORD)0, (LPARAM) &RButtonStruct );
}
// ------------------------------------------------------------------
// ColumnLBClass_DrawColumnBorder()
//
//
// HISTORY:
// Tom Laird-McConnell 3/15/94 created
// ------------------------------------------------------------------
void ColumnLB_DrawColumnBorder(HDC hDC, RECT *lpRect, int Bottom, HBRUSH hBackgroundBrush)
{
int ncxBorder = GetSystemMetrics(SM_CXBORDER);
RECT rect;
CopyRect(&rect, lpRect);
// fill in left side of rect
rect.right = rect.left;
rect.left -= (WHITESPACE/4);
FillRect(hDC, &rect, hBackgroundBrush);
// fill in right side of rect
rect.left = lpRect->right;
rect.right = lpRect->right + ncxBorder;
FillRect(hDC, &rect, hBackgroundBrush);
// draw the line itself
MoveToEx( hDC, rect.right, rect.top, NULL);
LineTo( hDC, rect.right, Bottom);
}