2015-01-07 11:43:58 -06:00
// 7 january 2015
2014-12-05 21:23:39 -06:00
2014-12-07 12:46:35 -06:00
// TODO
// - should tablePanic be CALLBACK or some other equivalent macro? and definitely export initTable somehow, but which alias macro to use?
2014-12-10 17:32:28 -06:00
// - make panic messages grammatically correct ("Table error: adding...")
2014-12-12 09:47:23 -06:00
// - make access to column widths consistent; see whether HDITEMW.cxy == (ITEMRECT.right - ITEMRECT.left)
2014-12-12 15:43:56 -06:00
// - make sure all uses of t->headerHeight are ADDED to RECT.top
2014-12-18 18:28:12 -06:00
// - WM_THEMECHANGED, etc.
2014-12-18 18:49:42 -06:00
// - see if vertical centering is really what we want or if we just want to offset by a few pixels or so
2014-12-20 20:09:01 -06:00
// - going right from column 0 to column 2 with the right arrow key deselects
2014-12-22 19:15:10 -06:00
// - make sure all error messages involving InvalidateRect() are consistent with regards to "redrawing" and "queueing for redraw"
2014-12-23 13:23:28 -06:00
// - collect all resize-related tasks in a single function (so things like adding columns will refresh everything, not just horizontal scrolls; also would fix initial coordinates)
2014-12-23 13:43:33 -06:00
// - checkbox columns don't clip to the column width
2015-01-07 16:05:38 -06:00
// - send standard notification codes
2014-12-07 12:46:35 -06:00
2014-12-05 21:23:39 -06:00
# define tableWindowClass L"gouitable"
// start at WM_USER + 20 just in case for whatever reason we ever get the various dialog manager messages (see also http://blogs.msdn.com/b/oldnewthing/archive/2003/10/21/55384.aspx)
enum {
// wParam - one of the type constants
// lParam - column name as a Unicode string
tableAddColumn = WM_USER + 20 ,
} ;
enum {
tableColumnText ,
tableColumnImage ,
tableColumnCheckbox ,
nTableColumnTypes ,
} ;
2015-01-07 16:05:38 -06:00
// notification codes
// note that these are positive; see http://blogs.msdn.com/b/oldnewthing/archive/2009/08/21/9877791.aspx
// each of these is of type tableNM
// all fields except data will always be set
enum {
// data parameter is always 0
// for tableColumnText return should be WCHAR *
// for tableColumnImage return should be HBITMAP
// for tableColumnCheckbox return is nonzero for checked, zero for unchecked
2015-01-07 16:16:39 -06:00
tableNotificationGetCellData ,
// data parameter is pointer, same as tableNotificationGetCellData
2015-01-07 16:05:38 -06:00
// not sent for checkboxes
// no return
2015-01-07 18:00:56 -06:00
tableNotificationFinishedWithCellData ,
2015-01-07 16:05:38 -06:00
// data is zero
// no return
2015-01-07 20:34:12 -06:00
tableNotificationCellCheckboxToggled ,
2015-01-07 16:05:38 -06:00
} ;
typedef struct tableNM tableNM ;
struct tableNM {
NMHDR nmhdr ;
intptr_t row ;
intptr_t column ;
int columnType ;
uintptr_t data ;
} ;
2014-12-07 12:24:12 -06:00
static void ( * tablePanic ) ( const char * , DWORD ) = NULL ;
2014-12-07 12:23:39 -06:00
# define panic(...) (*tablePanic)(__VA_ARGS__, GetLastError())
# define abort $$$$ // prevent accidental use of abort()
2014-12-21 09:38:52 -06:00
static BOOL ( * WINAPI tableTrackMouseEvent ) ( LPTRACKMOUSEEVENT ) ;
2014-12-24 19:15:45 -06:00
// forward declaration
struct tableAcc ;
2014-12-05 21:23:39 -06:00
struct table {
HWND hwnd ;
2014-12-06 21:01:04 -06:00
HWND header ;
2014-12-08 14:04:43 -06:00
HFONT font ;
2014-12-09 19:32:49 -06:00
intptr_t nColumns ;
int * columnTypes ;
2014-12-10 09:07:08 -06:00
intptr_t width ;
2014-12-10 13:07:14 -06:00
intptr_t headerHeight ;
2014-12-12 09:47:23 -06:00
intptr_t hscrollpos ; // in logical units
intptr_t hpagesize ; // in logical units
2014-12-11 17:58:37 -06:00
intptr_t count ;
2014-12-12 09:47:23 -06:00
intptr_t vscrollpos ; // in rows
intptr_t vpagesize ; // in rows
2014-12-12 21:17:20 -06:00
int hwheelCarry ;
int vwheelCarry ;
2014-12-13 10:49:36 -06:00
intptr_t selectedRow ;
intptr_t selectedColumn ;
2014-12-17 20:56:37 -06:00
HTHEME theme ;
int checkboxWidth ;
int checkboxHeight ;
2014-12-21 17:45:37 -06:00
BOOL checkboxMouseOverLast ;
LPARAM checkboxMouseOverLastPoint ;
2014-12-22 19:15:10 -06:00
BOOL checkboxMouseDown ;
intptr_t checkboxMouseDownRow ;
intptr_t checkboxMouseDownColumn ;
2014-12-24 19:15:45 -06:00
struct tableAcc * ta ;
2014-12-05 21:23:39 -06:00
} ;
2015-01-07 16:05:38 -06:00
// forward declaration (TODO needed?)
static LRESULT notify ( struct table * , UINT , intptr_t , intptr_t , uintptr_t ) ;
2014-12-05 21:23:39 -06:00
# include "util.h"
# include "coord.h"
2014-12-12 13:53:58 -06:00
# include "scroll.h"
2014-12-09 19:47:03 -06:00
# include "hscroll.h"
2014-12-12 14:20:34 -06:00
# include "vscroll.h"
2014-12-14 10:09:59 -06:00
# include "select.h"
2014-12-18 18:27:03 -06:00
# include "checkboxes.h"
2014-12-14 10:09:59 -06:00
# include "events.h"
2014-12-07 17:27:35 -06:00
# include "header.h"
2014-12-07 17:43:05 -06:00
# include "children.h"
2014-12-07 19:16:58 -06:00
# include "resize.h"
2014-12-08 09:01:41 -06:00
# include "draw.h"
2014-12-08 14:04:43 -06:00
# include "api.h"
2014-12-24 19:15:45 -06:00
# include "accessibility.h"
2014-12-05 21:23:39 -06:00
static const handlerfunc handlers [ ] = {
eventHandlers ,
2014-12-07 17:43:05 -06:00
childrenHandlers ,
2014-12-07 19:16:58 -06:00
resizeHandler ,
2014-12-08 09:01:41 -06:00
drawHandlers ,
2014-12-08 14:04:43 -06:00
apiHandlers ,
2014-12-10 14:37:52 -06:00
hscrollHandler ,
2014-12-12 14:20:34 -06:00
vscrollHandler ,
2014-12-24 19:15:45 -06:00
accessibilityHandler ,
2014-12-05 21:23:39 -06:00
NULL ,
} ;
2014-12-08 07:50:42 -06:00
static void initDummyTableStuff ( struct table * t )
{
2014-12-11 17:58:37 -06:00
t - > count = 100 ;
2014-12-13 10:49:36 -06:00
t - > selectedRow = 2 ;
t - > selectedColumn = 1 ;
2014-12-08 07:50:42 -06:00
}
2014-12-05 21:23:39 -06:00
static LRESULT CALLBACK tableWndProc ( HWND hwnd , UINT uMsg , WPARAM wParam , LPARAM lParam )
{
struct table * t ;
LRESULT lResult ;
t = ( struct table * ) GetWindowLongPtrW ( hwnd , GWLP_USERDATA ) ;
if ( t = = NULL ) {
// we have to do things this way because creating the header control will fail mysteriously if we create it first thing
// (which is fine; we can get the parent hInstance this way too)
2014-12-07 17:32:13 -06:00
// we use WM_CREATE because we have to use WM_DESTROY to destroy the header; we can't do it in WM_NCDESTROY because Windows will have destroyed it for us by then, and let's match message pairs to be safe
2014-12-07 17:27:35 -06:00
if ( uMsg = = WM_CREATE ) {
2014-12-05 21:23:39 -06:00
CREATESTRUCTW * cs = ( CREATESTRUCTW * ) lParam ;
2014-12-07 15:22:51 -06:00
t = ( struct table * ) tableAlloc ( sizeof ( struct table ) , " error allocating internal Table data structure " ) ;
2014-12-05 21:23:39 -06:00
t - > hwnd = hwnd ;
2014-12-07 17:27:35 -06:00
makeHeader ( t , cs - > hInstance ) ;
2014-12-13 10:49:36 -06:00
t - > selectedRow = - 1 ;
t - > selectedColumn = - 1 ;
2014-12-18 18:24:32 -06:00
loadCheckboxThemeData ( t ) ;
2014-12-24 19:15:45 -06:00
t - > ta = newTableAcc ( t ) ;
2014-12-08 07:50:42 -06:00
initDummyTableStuff ( t ) ;
2014-12-05 21:23:39 -06:00
SetWindowLongPtrW ( hwnd , GWLP_USERDATA , ( LONG_PTR ) t ) ;
}
// even if we did the above, fall through
return DefWindowProcW ( hwnd , uMsg , wParam , lParam ) ;
}
if ( uMsg = = WM_DESTROY ) {
2014-12-07 17:27:35 -06:00
printf ( " destroy \n " ) ;
2014-12-05 21:23:39 -06:00
// TODO free appropriate (after figuring this part out) components of t
2014-12-07 17:32:13 -06:00
// TODO send EVENT_OBJECT_DESTROY events to accessibility listeners (when appropriate); see the note on proxy objects as well
2014-12-24 19:15:45 -06:00
freeTableAcc ( t - > ta ) ;
t - > ta = NULL ;
2014-12-18 18:24:32 -06:00
freeCheckboxThemeData ( t ) ;
2014-12-07 17:27:35 -06:00
destroyHeader ( t ) ;
2014-12-07 15:22:51 -06:00
tableFree ( t , " error allocating internal Table data structure " ) ;
2014-12-05 21:23:39 -06:00
return DefWindowProcW ( hwnd , uMsg , wParam , lParam ) ;
}
if ( runHandlers ( handlers , t , uMsg , wParam , lParam , & lResult ) )
return lResult ;
return DefWindowProcW ( hwnd , uMsg , wParam , lParam ) ;
}
2014-12-07 12:23:39 -06:00
static void deftablePanic ( const char * msg , DWORD lastError )
{
fprintf ( stderr , " Table error: %s (last error %d) \n " , msg , lastError ) ;
2014-12-16 14:07:55 -06:00
fprintf ( stderr , " This is the default Table error handler function; programs that use Table should provide their own instead. \n The program will now break into the debugger. \n " ) ;
DebugBreak ( ) ;
2014-12-07 12:23:39 -06:00
}
2015-01-07 15:24:17 -06:00
// TODO have hInstance passed in
2014-12-21 09:38:52 -06:00
void initTable ( void ( * panicfunc ) ( const char * msg , DWORD lastError ) , BOOL ( * WINAPI tme ) ( LPTRACKMOUSEEVENT ) )
2014-12-05 21:23:39 -06:00
{
WNDCLASSW wc ;
2014-12-07 12:23:39 -06:00
tablePanic = panicfunc ;
if ( tablePanic = = NULL )
tablePanic = deftablePanic ;
2014-12-21 09:38:52 -06:00
if ( tme = = NULL )
// TODO errorless version
panic ( " must provide a TrackMouseEvent() to initTable() " ) ;
tableTrackMouseEvent = tme ;
2014-12-05 21:23:39 -06:00
ZeroMemory ( & wc , sizeof ( WNDCLASSW ) ) ;
wc . lpszClassName = tableWindowClass ;
wc . lpfnWndProc = tableWndProc ;
wc . hCursor = LoadCursorW ( NULL , IDC_ARROW ) ;
wc . hIcon = LoadIconW ( NULL , IDI_APPLICATION ) ;
wc . hbrBackground = ( HBRUSH ) ( COLOR_WINDOW + 1 ) ; // TODO correct?
wc . style = CS_HREDRAW | CS_VREDRAW ;
wc . hInstance = GetModuleHandle ( NULL ) ;
if ( RegisterClassW ( & wc ) = = 0 )
2014-12-07 12:23:39 -06:00
panic ( " error registering Table window class " ) ;
2014-12-05 21:23:39 -06:00
}