2987 lines
108 KiB
C
2987 lines
108 KiB
C
|
// ==================================================================================================
|
||
|
// 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 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)
|
||
|
{
|
||
|
DWORD 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)
|
||
|
{
|
||
|
DWORD 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;
|
||
|
int 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(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:
|
||
|
{
|
||
|
int 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;
|
||
|
int 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 = GetSystemMetrics(SM_CXBORDER);
|
||
|
ncyBorder = 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 = 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
|
||
|
//
|
||
|
SetWindowLong(hwnd, 0, (DWORD)lpColumnLB);
|
||
|
|
||
|
memset(lpColumnLB, '\0', sizeof(COLUMNLBSTRUCT));
|
||
|
|
||
|
//
|
||
|
// fill in pertinent info
|
||
|
//
|
||
|
lpColumnLB->Style = lpCreateStruct->style;
|
||
|
|
||
|
lpColumnLB->hInstance = (HINSTANCE)GetWindowLong(hwnd, GWL_HINSTANCE);
|
||
|
|
||
|
lpColumnLB->fUseVlist = fUseVlist;
|
||
|
lpColumnLB->fSorting = FALSE;
|
||
|
lpColumnLB->fMouseState = 0;
|
||
|
|
||
|
lpColumnLB->hwndList = hwndList;
|
||
|
lpColumnLB->OldListboxProc = (FARPROC)GetWindowLong(hwndList, GWL_WNDPROC);
|
||
|
lpColumnLB->NewListboxProc = MakeProcInstance((FARPROC)ColumnLBClass_ListBoxWndProc, hInst);
|
||
|
|
||
|
lpColumnLB->hwndTitleList = hwndTitleList;
|
||
|
lpColumnLB->OldTitleListboxProc = (FARPROC)GetWindowLong(hwndTitleList, GWL_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...
|
||
|
//
|
||
|
SetWindowLong(hwndList, GWL_WNDPROC,(DWORD)lpColumnLB->NewListboxProc);
|
||
|
|
||
|
//
|
||
|
// sub-class the title listbox window by substituting our window proc for the
|
||
|
// normal one...
|
||
|
//
|
||
|
SetWindowLong(hwndTitleList, GWL_WNDPROC,(DWORD)lpColumnLB->NewTitleListboxProc);
|
||
|
|
||
|
//
|
||
|
// add the lpColumnLB struct as the only item in the title listbox
|
||
|
//
|
||
|
ListBox_AddString(lpColumnLB->hwndTitleList, (DWORD)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 = GetSystemMetrics(SM_CXBORDER);
|
||
|
ncyBorder = 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 = 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 = 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;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
// ------------------------------------------------------------------
|
||
|
// 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 = 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 = 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(SendMessage( GetParent(hwnd),
|
||
|
CLBN_CHARTOITEM,
|
||
|
MAKEWPARAM(ch, lpColumnLB->SortColumn),
|
||
|
(LPARAM)hwnd));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
// ------------------------------------------------------------------
|
||
|
// 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;
|
||
|
LPDWORD lpListboxContents;
|
||
|
DWORD i;
|
||
|
int nCurSel;
|
||
|
DWORD 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 = (LPDWORD) GlobalAllocPtr(GPTR, sizeof(DWORD) * 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 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 = GetWindowLong(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 = GetWindowLong(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, (LONG) &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);
|
||
|
}
|
||
|
|