2014-12-05 21:23:39 -06:00
// 19 october 2014
# define UNICODE
# define _UNICODE
# define STRICT
# define STRICT_TYPED_ITEMIDS
# define CINTERFACE
// get Windows version right; right now Windows XP
# define WINVER 0x0501
# define _WIN32_WINNT 0x0501
# define _WIN32_WINDOWS 0x0501 /* according to Microsoft's winperf.h */
# define _WIN32_IE 0x0600 /* according to Microsoft's sdkddkver.h */
# define NTDDI_VERSION 0x05010000 /* according to Microsoft's sdkddkver.h */
# include <windows.h>
# include <commctrl.h>
# include <stdint.h>
# include <uxtheme.h>
# include <string.h>
# include <wchar.h>
# include <windowsx.h>
# include <vsstyle.h>
# include <vssym32.h>
# include <oleacc.h>
// #qo LIBS: user32 kernel32 gdi32 comctl32 uxtheme
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-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 ,
} ;
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-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-05 21:23:39 -06:00
} ;
# include "util.h"
# include "coord.h"
# include "events.h"
2014-12-09 19:47:03 -06:00
# include "hscroll.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-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-05 21:23:39 -06:00
NULL ,
} ;
2014-12-08 07:50:42 -06:00
static void initDummyTableStuff ( struct table * t )
{
2014-12-09 19:32:49 -06:00
// nothing yet...
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-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-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 ) ;
fprintf ( stderr , " This is the default Table error handler function; programs that use Table should provide their own instead. \n The program will now abort. \n " ) ;
# undef abort
abort ( ) ;
# define abort $$$$
}
void initTable ( void ( * panicfunc ) ( const char * msg , DWORD lastError ) )
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-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
}
int main ( int argc , char * argv [ ] )
{
HWND mainwin ;
MSG msg ;
INITCOMMONCONTROLSEX icc ;
ZeroMemory ( & icc , sizeof ( INITCOMMONCONTROLSEX ) ) ;
icc . dwSize = sizeof ( INITCOMMONCONTROLSEX ) ;
icc . dwICC = ICC_LISTVIEW_CLASSES ;
if ( InitCommonControlsEx ( & icc ) = = 0 )
2014-12-07 12:23:39 -06:00
panic ( " (test program) error initializing comctl32.dll " ) ;
initTable ( NULL ) ;
2014-12-05 21:23:39 -06:00
mainwin = CreateWindowExW ( 0 ,
tableWindowClass , L " Main Window " ,
WS_OVERLAPPEDWINDOW | WS_HSCROLL | WS_VSCROLL ,
CW_USEDEFAULT , CW_USEDEFAULT ,
400 , 400 ,
NULL , NULL , GetModuleHandle ( NULL ) , NULL ) ;
if ( mainwin = = NULL )
2014-12-07 12:23:39 -06:00
panic ( " (test program) error creating Table " ) ;
2014-12-05 21:23:39 -06:00
SendMessageW ( mainwin , tableAddColumn , tableColumnText , ( LPARAM ) L " Column " ) ;
SendMessageW ( mainwin , tableAddColumn , tableColumnImage , ( LPARAM ) L " Column 2 " ) ;
SendMessageW ( mainwin , tableAddColumn , tableColumnCheckbox , ( LPARAM ) L " Column 3 " ) ;
if ( argc > 1 ) {
NONCLIENTMETRICSW ncm ;
HFONT font ;
ZeroMemory ( & ncm , sizeof ( NONCLIENTMETRICSW ) ) ;
ncm . cbSize = sizeof ( NONCLIENTMETRICSW ) ;
if ( SystemParametersInfoW ( SPI_GETNONCLIENTMETRICS , sizeof ( NONCLIENTMETRICSW ) , & ncm , sizeof ( NONCLIENTMETRICSW ) ) = = 0 )
2014-12-07 12:23:39 -06:00
panic ( " (test program) error getting non-client metrics " ) ;
2014-12-05 21:23:39 -06:00
font = CreateFontIndirectW ( & ncm . lfMessageFont ) ;
if ( font = = NULL )
2014-12-07 12:23:39 -06:00
panic ( " (test program) error creating lfMessageFont HFONT " ) ;
2014-12-05 21:23:39 -06:00
SendMessageW ( mainwin , WM_SETFONT , ( WPARAM ) font , TRUE ) ;
}
ShowWindow ( mainwin , SW_SHOWDEFAULT ) ;
if ( UpdateWindow ( mainwin ) = = 0 )
2014-12-07 12:23:39 -06:00
panic ( " (test program) error updating window " ) ;
2014-12-05 21:23:39 -06:00
while ( GetMessageW ( & msg , NULL , 0 , 0 ) > 0 ) {
TranslateMessage ( & msg ) ;
DispatchMessageW ( & msg ) ;
}
return 0 ;
}