diff --git a/redo/comctl32_windows.c b/redo/comctl32_windows.c index 68d58cb..e3e02cf 100644 --- a/redo/comctl32_windows.c +++ b/redo/comctl32_windows.c @@ -10,6 +10,12 @@ BOOL (*WINAPI fv_SetWindowSubclass)(HWND, SUBCLASSPROC, UINT_PTR, DWORD_PTR); BOOL (*WINAPI fv_RemoveWindowSubclass)(HWND, SUBCLASSPROC, UINT_PTR); LRESULT (*WINAPI fv_DefSubclassProc)(HWND, UINT, WPARAM, LPARAM); +#define wantedICCClasses ( \ + ICC_PROGRESS_CLASS | /* progress bars */ \ + ICC_TAB_CLASSES | /* tabs */ \ + ICC_LISTVIEW_CLASSES | /* list views */ \ + 0) + DWORD initCommonControls(LPCWSTR manifest, char **errmsg) { ACTCTX actctx; @@ -35,7 +41,7 @@ DWORD initCommonControls(LPCWSTR manifest, char **errmsg) ZeroMemory(&icc, sizeof (INITCOMMONCONTROLSEX)); icc.dwSize = sizeof (INITCOMMONCONTROLSEX); - icc.dwICC = ICC_PROGRESS_CLASS | ICC_TAB_CLASSES; + icc.dwICC = wantedICCClasses; comctl32 = LoadLibraryW(L"comctl32.dll"); if (comctl32 == NULL) { diff --git a/redo/table.go b/redo/table.go index 52024da..7a0e174 100644 --- a/redo/table.go +++ b/redo/table.go @@ -13,6 +13,8 @@ import ( // Tables maintain their own storage behind a sync.RWMutex-compatible sync.Locker; use Table.Lock()/Table.Unlock() to make changes and Table.RLock()/Table.RUnlock() to merely read values. // TODO headers type Table interface { + Control + // Lock and Unlock lock and unlock Data for reading or writing. // RLock and RUnlock lock and unlock Data for reading only. // These methods have identical semantics to the analogous methods of sync.RWMutex. @@ -41,16 +43,18 @@ func NewTable(ty reflect.Type) Table { panic(fmt.Errorf("unknown or unsupported type %v given to NewTable()", ty)) } b := new(tablebase) - // arbitrary starting capacity - b.data = reflect.NewSlice(ty, 0, 512).Addr().Interface() - return finishNewTable(b) + // we want a pointer to a slice + b.data = reflect.New(reflect.SliceOf(ty)).Interface() + return finishNewTable(b, ty) } func (b *tablebase) Lock() { b.lock.Lock() } -func (b *tablebase) Unlock() { +// Unlock() is defined on each backend implementation of Table +// they should all call this, however +func (b *tablebase) unlock() { b.lock.Unlock() } @@ -62,6 +66,6 @@ func (b *tablebase) RUnlock() { b.lock.RUnlock() } -func (b *tablebase) Data() { +func (b *tablebase) Data() interface{} { return b.data } diff --git a/redo/table_windows.c b/redo/table_windows.c new file mode 100644 index 0000000..a9039f0 --- /dev/null +++ b/redo/table_windows.c @@ -0,0 +1,51 @@ +/* 28 july 2014 */ + +#include "winapi_windows.h" + +/* provided for cgo's benefit */ +LPCWSTR xWC_LISTVIEW = WC_LISTVIEW; + +static LRESULT CALLBACK tableSubProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR id, DWORD_PTR data) +{ + switch (uMsg) { + case msgCOMMAND: + /* TODO */ + return (*fv_DefSubclassProc)(hwnd, uMsg, wParam, lParam); + case msgNOTIFY: + /* TODO */ + return (*fv_DefSubclassProc)(hwnd, uMsg, wParam, lParam); + case WM_NCDESTROY: + if ((*fv_RemoveWindowSubclass)(hwnd, tableSubProc, id) == FALSE) + xpanic("error removing Table subclass (which was for its own event handler)", GetLastError()); + return (*fv_DefSubclassProc)(hwnd, uMsg, wParam, lParam); + default: + return (*fv_DefSubclassProc)(hwnd, uMsg, wParam, lParam); + } + xmissedmsg("Button", "tableSubProc()", uMsg); + return 0; /* unreached */ +} + +void setTableSubclass(HWND hwnd, void *data) +{ + if ((*fv_SetWindowSubclass)(hwnd, tableSubProc, 0, (DWORD_PTR) data) == FALSE) + xpanic("error subclassing Table to give it its own event handler", GetLastError()); +} + +void tableAppendColumn(HWND hwnd, int index, LPCWSTR name) +{ + LVCOLUMNW col; + + ZeroMemory(&col, sizeof (LVCOLUMNW)); + col.mask = LVCF_FMT | LVCF_TEXT | LVCF_SUBITEM | LVCF_ORDER; + col.fmt = LVCFMT_LEFT; + col.pszText = name; + col.iSubItem = index; + col.iOrder = index; + if (SendMessageW(hwnd, LVM_INSERTCOLUMN, (WPARAM) index, (LPARAM) (&col)) == (LRESULT) -1) + xpanic("error adding column to Table", GetLastError()); +} + +void tableUpdate(HWND table, int nItems) +{ + /* TODO */ +} diff --git a/redo/table_windows.go b/redo/table_windows.go new file mode 100644 index 0000000..4711e37 --- /dev/null +++ b/redo/table_windows.go @@ -0,0 +1,39 @@ +// 28 july 2014 + +package ui + +import ( + "unsafe" + "reflect" +) + +// #include "winapi_windows.h" +import "C" + +type table struct { + *widgetbase + *tablebase +} + +func finishNewTable(b *tablebase, ty reflect.Type) Table { + t := &table{ + widgetbase: newWidget(C.xWC_LISTVIEW, + C.LVS_REPORT | C.LVS_OWNERDATA | C.LVS_NOSORTHEADER | C.LVS_SHOWSELALWAYS | C.WS_HSCROLL | C.WS_VSCROLL, + 0), // TODO WS_EX_CLIENTEDGE? + tablebase: b, + } + C.setTableSubclass(t.hwnd, unsafe.Pointer(&t)) + for i := 0; i < ty.NumField(); i++ { + C.tableAppendColumn(t.hwnd, C.int(i), toUTF16(ty.Field(i).Name)) + } + return t +} + +func (t *table) Unlock() { + t.unlock() + // TODO RACE CONDITION HERE + // I think there's a way to set the item count without causing a refetch of data that works around this... + t.RLock() + defer t.RUnlock() + C.tableUpdate(t.hwnd, C.int(reflect.Indirect(reflect.ValueOf(t.data)).Len())) +} diff --git a/redo/winapi_windows.h b/redo/winapi_windows.h index 0129d37..c8726e7 100644 --- a/redo/winapi_windows.h +++ b/redo/winapi_windows.h @@ -87,4 +87,10 @@ extern void setTabSubclass(HWND, void *); extern void tabAppend(HWND, LPCWSTR); extern void tabGetContentRect(HWND, RECT *); +/* table_windows.go */ +extern LPCWSTR xWC_LISTVIEW; +extern void setTableSubclass(HWND, void *); +extern void tableAppendColumn(HWND, int, LPCWSTR); +extern void tableUpdate(HWND, int); + #endif diff --git a/redo/zz_test.go b/redo/zz_test.go index 327aec2..1d23f35 100644 --- a/redo/zz_test.go +++ b/redo/zz_test.go @@ -7,6 +7,7 @@ package ui import ( "fmt" "flag" + "reflect" "testing" )