forked from andlabs/ui
-
Notifications
You must be signed in to change notification settings - Fork 0
/
table_windows.go
204 lines (184 loc) · 5.68 KB
/
table_windows.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
// 28 july 2014
package ui
// TODO
// - why are we not getting keyboard input (focus?) on a mouse click?
// - are we getting keyboard input (focus?) on tab?
// - random freezes on Windows 7 when resizing headers or clicking new rows; likely another package ui infrastructure issue though...
// - investigate japanese display in the table headers on wine (it has worked in the past in ANSI mode via Shift-JIS with what I assume is the same font, so huh?)
import (
"fmt"
"reflect"
"unsafe"
"sync"
"image"
)
// #include "winapi_windows.h"
import "C"
type table struct {
*tablebase
*controlSingleHWND
noautosize bool
colcount C.int
selected *event
chainresize func(x int, y int, width int, height int, d *sizing)
free map[C.uintptr_t]bool
freeLock sync.Mutex
}
func finishNewTable(b *tablebase, ty reflect.Type) Table {
hwnd := C.newControl(C.xtableWindowClass,
C.WS_HSCROLL|C.WS_VSCROLL|C.WS_TABSTOP,
C.WS_EX_CLIENTEDGE) // WS_EX_CLIENTEDGE without WS_BORDER will show the canonical visual styles border (thanks to MindChild in irc.efnet.net/#winprog)
t := &table{
controlSingleHWND: newControlSingleHWND(hwnd),
tablebase: b,
selected: newEvent(),
free: make(map[C.uintptr_t]bool),
}
t.fpreferredSize = t.xpreferredSize
t.chainresize = t.fresize
t.fresize = t.xresize
C.setTableSubclass(t.hwnd, unsafe.Pointer(t))
// TODO listview didn't need this; someone mentioned (TODO) it uses the small caption font???
C.controlSetControlFont(t.hwnd)
for i := 0; i < ty.NumField(); i++ {
coltype := C.WPARAM(C.tableColumnText)
switch {
case ty.Field(i).Type == reflect.TypeOf((*image.RGBA)(nil)):
coltype = C.tableColumnImage
case ty.Field(i).Type.Kind() == reflect.Bool:
coltype = C.tableColumnCheckbox
}
colname := ty.Field(i).Tag.Get("uicolumn")
if colname == "" {
colname = ty.Field(i).Name
}
ccolname := toUTF16(colname)
C.SendMessageW(t.hwnd, C.tableAddColumn, coltype, C.LPARAM(uintptr(unsafe.Pointer(ccolname))))
// TODO free ccolname
}
t.colcount = C.int(ty.NumField())
return t
}
func (t *table) Unlock() {
t.unlock()
// there's a possibility that user actions can happen at this point, before the view is updated
// alas, this is something we have to deal with, because Unlock() can be called from any thread
go func() {
Do(func() {
t.RLock()
defer t.RUnlock()
C.gotableSetRowCount(t.hwnd, C.intptr_t(reflect.Indirect(reflect.ValueOf(t.data)).Len()))
})
}()
}
func (t *table) Selected() int {
t.RLock()
defer t.RUnlock()
return int(C.tableSelectedItem(t.hwnd))
}
func (t *table) Select(index int) {
t.RLock()
defer t.RUnlock()
C.tableSelectItem(t.hwnd, C.intptr_t(index))
}
func (t *table) OnSelected(f func()) {
t.selected.set(f)
}
//export tableGetCell
func tableGetCell(data unsafe.Pointer, tnm *C.tableNM) C.LRESULT {
t := (*table)(data)
t.RLock()
defer t.RUnlock()
d := reflect.Indirect(reflect.ValueOf(t.data))
datum := d.Index(int(tnm.row)).Field(int(tnm.column))
switch {
case datum.Type() == reflect.TypeOf((*image.RGBA)(nil)):
i := datum.Interface().(*image.RGBA)
hbitmap := C.toBitmap(unsafe.Pointer(i), C.intptr_t(i.Rect.Dx()), C.intptr_t(i.Rect.Dy()))
bitmap := C.uintptr_t(uintptr(unsafe.Pointer(hbitmap)))
t.freeLock.Lock()
t.free[bitmap] = true // bitmap freed with C.freeBitmap()
t.freeLock.Unlock()
return C.LRESULT(bitmap)
case datum.Kind() == reflect.Bool:
if datum.Bool() == true {
return C.TRUE
}
return C.FALSE
default:
s := fmt.Sprintf("%v", datum)
text := C.uintptr_t(uintptr(unsafe.Pointer(toUTF16(s))))
t.freeLock.Lock()
t.free[text] = false // text freed with C.free()
t.freeLock.Unlock()
return C.LRESULT(text)
}
}
//export tableFreeCellData
func tableFreeCellData(gotable unsafe.Pointer, data C.uintptr_t) {
t := (*table)(gotable)
t.freeLock.Lock()
defer t.freeLock.Unlock()
b, ok := t.free[data]
if !ok {
panic(fmt.Errorf("undefined data %p in tableFreeData()", data))
}
if b == false {
C.free(unsafe.Pointer(uintptr(data)))
} else {
C.freeBitmap(data)
}
delete(t.free, data)
}
// the column autoresize policy is simple:
// on every table.commitResize() call, if the columns have not been resized by the user, autoresize
func (t *table) autoresize() {
t.RLock()
defer t.RUnlock()
if !t.noautosize {
//TODO C.tableAutosizeColumns(t.hwnd, t.colcount)
}
}
//export tableStopColumnAutosize
func tableStopColumnAutosize(data unsafe.Pointer) {
t := (*table)(data)
t.noautosize = true
}
//export tableColumnCount
func tableColumnCount(data unsafe.Pointer) C.int {
t := (*table)(data)
return t.colcount
}
//export tableToggled
func tableToggled(data unsafe.Pointer, row C.intptr_t, col C.intptr_t) {
t := (*table)(data)
t.Lock()
defer t.Unlock()
d := reflect.Indirect(reflect.ValueOf(t.data))
datum := d.Index(int(row)).Field(int(col))
if datum.Kind() == reflect.Bool {
datum.SetBool(!datum.Bool())
return
}
panic(fmt.Errorf("tableSetHot() on non-checkbox at (%d, %d)", row, col))
}
//export tableSelectionChanged
func tableSelectionChanged(data unsafe.Pointer) {
t := (*table)(data)
t.selected.fire()
}
const (
// from C++ Template 05 in http://msdn.microsoft.com/en-us/library/windows/desktop/bb226818%28v=vs.85%29.aspx as this is the best I can do for now
// there IS a message LVM_APPROXIMATEVIEWRECT that can do calculations, but it doesn't seem to work right when asked to base its calculations on the current width/height on Windows and wine...
tableWidth = 183
tableHeight = 50
)
func (t *table) xpreferredSize(d *sizing) (width, height int) {
return fromdlgunitsX(tableWidth, d), fromdlgunitsY(tableHeight, d)
}
func (t *table) xresize(x int, y int, width int, height int, d *sizing) {
t.chainresize(x, y, width, height, d)
t.RLock()
defer t.RUnlock()
t.autoresize()
}