2014-10-19 13:44:27 -05:00
// 19 october 2014
# define UNICODE
# define _UNICODE
# define STRICT
# define STRICT_TYPED_ITEMIDS
2014-11-09 12:04:04 -06:00
# define CINTERFACE
2014-10-19 13:44:27 -05:00
// 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>
2014-11-14 21:41:25 -06:00
extern HIMAGELIST makeCheckboxImageList ( HWND hwnddc , HTHEME * theme , int * , int * ) ;
2014-11-10 13:57:26 -06:00
enum {
checkboxStateChecked = 1 < < 0 ,
checkboxStateHot = 1 < < 1 ,
checkboxStatePushed = 1 < < 2 ,
checkboxnStates = 1 < < 3 ,
} ;
2014-10-19 13:44:27 -05:00
# include <windowsx.h>
# include <vsstyle.h>
# include <vssym32.h>
2014-11-09 12:04:04 -06:00
# include <oleacc.h>
2014-10-19 13:44:27 -05:00
2014-11-10 13:57:26 -06:00
// #qo LIBS: user32 kernel32 gdi32 comctl32 uxtheme
2014-10-19 13:44:27 -05:00
2014-10-20 09:32:11 -05:00
// TODO
// - http://blogs.msdn.com/b/oldnewthing/archive/2003/09/09/54826.aspx (relies on the integrality parts? IDK)
// - might want to http://blogs.msdn.com/b/oldnewthing/archive/2003/09/17/54944.aspx instead
2014-10-20 15:08:56 -05:00
// - http://msdn.microsoft.com/en-us/library/windows/desktop/bb775574%28v=vs.85%29.aspx
2014-11-07 17:19:27 -06:00
// - hscroll
2014-10-21 11:48:32 -05:00
// - keyboard navigation
2014-11-20 17:27:03 -06:00
// - how will this affect hot-tracking?
2014-11-27 09:36:04 -06:00
// - automatic hscroll when scrolling columns
2014-11-07 17:19:27 -06:00
// - accessibility
2014-11-09 01:11:39 -06:00
// - must use MSAA as UI Automation is not included by default on Windows XP (and apparently requires SP3?)
2014-11-11 12:54:34 -06:00
// - try horizontally scrolling the initail window and watch the selection rect corrupt itself *sometimes*
2014-11-11 12:57:16 -06:00
// - preallocate t->columnTypes instead of keeping it at exactly the right size
2014-11-12 21:37:56 -06:00
// - checkbox events
// - space to toggle (TODO); + or = to set; - to clear (see http://msdn.microsoft.com/en-us/library/windows/desktop/bb775941%28v=vs.85%29.aspx)
// - TODO figure out which notification is needed
2014-11-13 11:14:29 -06:00
// - http://blogs.msdn.com/b/oldnewthing/archive/2006/01/03/508694.aspx
2014-11-18 12:52:54 -06:00
// - free all allocated resources on WM_DESTROY
2014-11-20 17:28:01 -06:00
// - rename lastmouse
// - or perhaps do a general cleanup of the checkbox and mouse event code...
2014-11-22 19:32:30 -06:00
// - figure out why initial draw pretends there is no header
2014-11-23 11:43:11 -06:00
// - find places where the top-left corner of the client rect is assumed to be (0, 0)
2014-12-03 18:55:00 -06:00
// - rewrite a lot of this crap to make better sense of coordinates and item counts, get rid of needless resizing and redrawing, etc.
2014-10-20 09:32:11 -05:00
2014-10-19 13:44:27 -05:00
# define tableWindowClass L"gouitable"
2014-12-01 22:51:09 -06:00
// 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)
2014-11-11 12:54:34 -06:00
enum {
// wParam - one of the type constants
// lParam - column name as a Unicode string
2014-12-01 22:51:09 -06:00
tableAddColumn = WM_USER + 20 ,
2014-11-11 12:54:34 -06:00
} ;
enum {
tableColumnText ,
tableColumnImage ,
tableColumnCheckbox ,
nTableColumnTypes ,
} ;
2014-10-19 13:44:27 -05:00
struct table {
HWND hwnd ;
HFONT defaultFont ;
HFONT font ;
intptr_t selected ;
2014-10-19 18:40:23 -05:00
intptr_t count ;
2014-10-19 21:20:53 -05:00
intptr_t firstVisible ;
2014-10-20 12:06:26 -05:00
intptr_t pagesize ; // in rows
2014-10-20 09:21:47 -05:00
int wheelCarry ;
2014-10-20 12:40:57 -05:00
HWND header ;
int headerHeight ;
2014-10-21 10:52:31 -05:00
intptr_t nColumns ;
2014-10-21 13:31:44 -05:00
HIMAGELIST imagelist ;
2014-10-21 13:39:46 -05:00
int imagelistHeight ;
2014-11-06 19:28:34 -06:00
intptr_t width ;
2014-11-07 09:12:06 -06:00
intptr_t hpagesize ;
2014-11-06 19:28:34 -06:00
intptr_t hpos ;
2014-11-10 13:57:26 -06:00
HIMAGELIST checkboxes ;
HTHEME theme ;
2014-11-11 12:54:34 -06:00
int * columnTypes ;
2014-11-12 20:58:21 -06:00
intptr_t focusedColumn ;
2014-11-14 21:41:25 -06:00
int checkboxWidth ;
int checkboxHeight ;
2014-10-19 13:44:27 -05:00
} ;
2014-11-30 20:27:32 -06:00
# define HANDLER(what) static BOOL what ## Handler(struct table *t, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *lResult)
2014-11-29 17:29:50 -06:00
# include "util.h"
# include "hscroll.h"
# include "vscroll.h"
2014-11-30 16:05:42 -06:00
# include "selection.h"
# include "draw.h"
2014-11-30 20:11:12 -06:00
# include "api.h"
2014-10-19 22:33:08 -05:00
2014-11-30 20:27:32 -06:00
typedef BOOL ( * handlerfunc ) ( struct table * , UINT , WPARAM , LPARAM , LRESULT * ) ;
2014-12-01 14:18:12 -06:00
const handlerfunc handlerfuncs [ ] = {
2014-12-03 18:38:08 -06:00
hscrollHandler ,
2014-12-01 14:34:26 -06:00
vscrollHandler ,
2014-11-30 20:27:32 -06:00
APIHandler ,
NULL ,
} ;
2014-11-30 16:05:42 -06:00
// TODO create a system where each of the above modules provide their own window procedures
2014-10-19 13:44:27 -05:00
static LRESULT CALLBACK tableWndProc ( HWND hwnd , UINT uMsg , WPARAM wParam , LPARAM lParam )
{
struct table * t ;
2014-11-30 20:27:32 -06:00
handlerfunc * hf ;
LRESULT lResult ;
2014-10-19 13:44:27 -05:00
HDC dc ;
PAINTSTRUCT ps ;
2014-10-20 23:29:50 -05:00
NMHDR * nmhdr = ( NMHDR * ) lParam ;
NMHEADERW * nm = ( NMHEADERW * ) lParam ;
2014-10-19 13:44:27 -05:00
t = ( struct table * ) GetWindowLongPtrW ( hwnd , GWLP_USERDATA ) ;
if ( t = = NULL ) {
2014-10-20 12:40:57 -05:00
// 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)
if ( uMsg = = WM_NCCREATE ) {
CREATESTRUCTW * cs = ( CREATESTRUCTW * ) lParam ;
t = ( struct table * ) malloc ( sizeof ( struct table ) ) ;
if ( t = = NULL )
abort ( ) ;
ZeroMemory ( t , sizeof ( struct table ) ) ;
t - > hwnd = hwnd ;
// TODO this should be a global
t - > defaultFont = ( HFONT ) GetStockObject ( SYSTEM_FONT ) ;
if ( t - > defaultFont = = NULL )
abort ( ) ;
t - > font = t - > defaultFont ;
2014-10-19 18:40:23 -05:00
t - > selected = 5 ; t - > count = 100 ; //TODO
2014-10-20 12:40:57 -05:00
t - > header = CreateWindowExW ( 0 ,
WC_HEADERW , L " " ,
// TODO is HOTTRACK needed?
WS_CHILD | HDS_FULLDRAG | HDS_HORZ | HDS_HOTTRACK ,
0 , 0 , 0 , 0 ,
t - > hwnd , ( HMENU ) 100 , cs - > hInstance , NULL ) ;
if ( t - > header = = NULL )
abort ( ) ;
2014-11-11 12:54:34 -06:00
{ t - > imagelist = ImageList_Create ( GetSystemMetrics ( SM_CXSMICON ) , GetSystemMetrics ( SM_CYSMICON ) , ILC_COLOR32 , 1 , 1 ) ;
2014-10-21 13:31:44 -05:00
if ( t - > imagelist = = NULL ) abort ( ) ;
{
HICON icon ;
2014-10-21 13:39:46 -05:00
int unused ;
2014-10-21 13:31:44 -05:00
icon = LoadIconW ( NULL , IDI_ERROR ) ;
if ( icon = = NULL ) abort ( ) ;
if ( ImageList_AddIcon ( t - > imagelist , icon ) = = - 1 ) abort ( ) ;
2014-10-21 13:39:46 -05:00
if ( ImageList_GetIconSize ( t - > imagelist , & unused , & ( t - > imagelistHeight ) ) = = 0 ) abort ( ) ;
2014-10-21 13:31:44 -05:00
}
}
2014-11-14 21:41:25 -06:00
t - > checkboxes = makeCheckboxImageList ( t - > hwnd , & ( t - > theme ) , & ( t - > checkboxWidth ) , & ( t - > checkboxHeight ) ) ;
2014-11-12 20:58:21 -06:00
t - > focusedColumn = - 1 ;
2014-11-24 09:49:36 -06:00
//TODO retrack(t);
2014-10-20 12:40:57 -05:00
SetWindowLongPtrW ( hwnd , GWLP_USERDATA , ( LONG_PTR ) t ) ;
}
// even if we did the above, fall through
return DefWindowProcW ( hwnd , uMsg , wParam , lParam ) ;
2014-10-19 13:44:27 -05:00
}
2014-11-30 20:27:32 -06:00
for ( hf = handlerfuncs ; * hf ! = NULL ; hf + + )
if ( ( * hf ) ( t , uMsg , wParam , lParam , & lResult ) )
return lResult ;
2014-10-19 13:44:27 -05:00
switch ( uMsg ) {
case WM_PAINT :
dc = BeginPaint ( hwnd , & ps ) ;
if ( dc = = NULL )
abort ( ) ;
2014-10-19 20:49:27 -05:00
drawItems ( t , dc , ps . rcPaint ) ;
2014-10-19 13:44:27 -05:00
EndPaint ( hwnd , & ps ) ;
return 0 ;
2014-10-19 22:33:08 -05:00
case WM_SIZE :
2014-10-19 22:48:25 -05:00
resize ( t ) ;
2014-10-19 22:33:08 -05:00
return 0 ;
2014-10-20 10:34:33 -05:00
case WM_LBUTTONDOWN :
selectItem ( t , wParam , lParam ) ;
return 0 ;
2014-10-20 11:10:30 -05:00
case WM_SETFOCUS :
case WM_KILLFOCUS :
// all we need to do here is redraw the highlight
// TODO ensure giving focus works right
2014-12-03 18:38:08 -06:00
// TODO figure out hwat I meant by this
2014-11-17 09:21:43 -06:00
redrawRow ( t , t - > selected ) ;
2014-10-20 11:10:30 -05:00
return 0 ;
2014-10-20 12:06:26 -05:00
case WM_KEYDOWN :
keySelect ( t , wParam , lParam ) ;
return 0 ;
2014-10-20 23:29:50 -05:00
// TODO header double-click
case WM_NOTIFY :
if ( nmhdr - > hwndFrom = = t - > header )
switch ( nmhdr - > code ) {
2014-10-21 08:18:08 -05:00
// I could use HDN_TRACK but wine doesn't emit that
2014-10-20 23:29:50 -05:00
case HDN_ITEMCHANGING :
2014-10-21 08:18:08 -05:00
case HDN_ITEMCHANGED : // TODO needed?
2014-10-21 11:13:26 -05:00
recomputeHScroll ( t ) ;
2014-10-20 23:29:50 -05:00
redrawAll ( t ) ;
return FALSE ;
}
2014-11-09 01:36:43 -06:00
return DefWindowProcW ( hwnd , uMsg , wParam , lParam ) ;
2014-11-10 13:57:26 -06:00
// TODO others?
case WM_WININICHANGE :
case WM_SYSCOLORCHANGE :
case WM_THEMECHANGED :
if ( ImageList_Destroy ( t - > checkboxes ) = = 0 )
abort ( ) ;
2014-11-14 21:41:25 -06:00
t - > checkboxes = makeCheckboxImageList ( t - > hwnd , & ( t - > theme ) , & ( t - > checkboxWidth ) , & ( t - > checkboxHeight ) ) ;
2014-11-10 13:57:26 -06:00
resize ( t ) ; // TODO needed?
redrawAll ( t ) ;
// now defer back to DefWindowProc() in case other things are needed
// TODO needed?
return DefWindowProcW ( hwnd , uMsg , wParam , lParam ) ;
2014-11-09 01:36:43 -06:00
case WM_GETOBJECT : // accessibility
/*
if ( ( ( DWORD ) lParam ) = = OBJID_CLIENT ) {
TODO * server ;
LRESULT lResult ;
// TODO create the server object
lResult = LresultFromObject ( IID_IAccessible , wParam , server ) ;
2014-11-10 13:57:26 -06:00
if ( /* TODO failure *|/)
2014-11-09 01:36:43 -06:00
abort ( ) ;
// TODO release object
return lResult ;
}
*/
return DefWindowProcW ( hwnd , uMsg , wParam , lParam ) ;
2014-10-19 13:44:27 -05:00
default :
return DefWindowProcW ( hwnd , uMsg , wParam , lParam ) ;
}
abort ( ) ;
return 0 ; // unreached
}
void makeTableWindowClass ( void )
{
WNDCLASSW wc ;
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?
2014-10-19 20:49:27 -05:00
wc . style = CS_HREDRAW | CS_VREDRAW ;
2014-10-19 13:44:27 -05:00
wc . hInstance = GetModuleHandle ( NULL ) ;
if ( RegisterClassW ( & wc ) = = 0 )
abort ( ) ;
}
2014-11-23 19:28:52 -06:00
int main ( int argc , char * argv [ ] )
2014-10-19 13:44:27 -05:00
{
HWND mainwin ;
MSG msg ;
2014-10-20 12:40:57 -05:00
INITCOMMONCONTROLSEX icc ;
2014-10-19 13:44:27 -05:00
2014-10-20 12:40:57 -05:00
ZeroMemory ( & icc , sizeof ( INITCOMMONCONTROLSEX ) ) ;
icc . dwSize = sizeof ( INITCOMMONCONTROLSEX ) ;
icc . dwICC = ICC_LISTVIEW_CLASSES ;
if ( InitCommonControlsEx ( & icc ) = = 0 )
abort ( ) ;
2014-10-19 13:44:27 -05:00
makeTableWindowClass ( ) ;
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 )
abort ( ) ;
2014-11-11 12:54:34 -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 " ) ;
2014-11-23 19:28:52 -06:00
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 )
abort ( ) ;
font = CreateFontIndirectW ( & ncm . lfMessageFont ) ;
if ( font = = NULL )
abort ( ) ;
SendMessageW ( mainwin , WM_SETFONT , ( WPARAM ) font , TRUE ) ;
}
2014-10-19 13:44:27 -05:00
ShowWindow ( mainwin , SW_SHOWDEFAULT ) ;
if ( UpdateWindow ( mainwin ) = = 0 )
abort ( ) ;
while ( GetMessageW ( & msg , NULL , 0 , 0 ) > 0 ) {
TranslateMessage ( & msg ) ;
DispatchMessageW ( & msg ) ;
}
return 0 ;
}