2014-07-28 19:52:32 -05:00
// 28 july 2014
package ui
import (
2014-07-28 22:29:06 -05:00
"fmt"
2014-07-28 19:52:32 -05:00
"reflect"
2014-10-02 09:05:53 -05:00
"unsafe"
2015-02-17 20:29:41 -06:00
"sync"
2014-07-28 19:52:32 -05:00
)
// #include "winapi_windows.h"
import "C"
type table struct {
* tablebase
2014-10-18 16:03:07 -05:00
* controlSingleHWND
2014-10-02 09:05:53 -05:00
noautosize bool
colcount C . int
selected * event
2014-10-18 16:03:07 -05:00
chainresize func ( x int , y int , width int , height int , d * sizing )
2015-02-17 20:29:41 -06:00
freeTexts map [ unsafe . Pointer ] bool
freeLock sync . Mutex
2014-07-28 19:52:32 -05:00
}
func finishNewTable ( b * tablebase , ty reflect . Type ) Table {
2015-02-17 20:29:41 -06:00
hwnd := C . newControl ( C . xtableWindowClass ,
C . WS_HSCROLL | C . WS_VSCROLL | C . WS_TABSTOP ,
2014-10-18 16:03:07 -05:00
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)
2014-07-28 19:52:32 -05:00
t := & table {
2014-10-18 16:03:07 -05:00
controlSingleHWND : newControlSingleHWND ( hwnd ) ,
2014-10-02 09:05:53 -05:00
tablebase : b ,
selected : newEvent ( ) ,
2015-02-17 20:29:41 -06:00
free : make ( map [ unsafe . Pointer ] bool ) ,
2014-07-28 19:52:32 -05:00
}
2014-10-18 16:03:07 -05:00
t . fpreferredSize = t . xpreferredSize
t . chainresize = t . fresize
t . fresize = t . xresize
C . setTableSubclass ( t . hwnd , unsafe . Pointer ( t ) )
2014-07-28 19:52:32 -05:00
for i := 0 ; i < ty . NumField ( ) ; i ++ {
2015-02-17 20:29:41 -06:00
coltype := C . WPARAM ( C . tableColumnText )
switch ty . Field ( i ) . Type {
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 := toUTF16 ( ty . Field ( i ) . Name )
C . SendMessageW ( t . hwnd , C . tableAddColumn , coltype , C . LPARAM ( uintptr ( unsafe . Pointer ( colname ) ) ) )
2014-07-28 19:52:32 -05:00
}
2014-08-06 09:42:26 -05:00
t . colcount = C . int ( ty . NumField ( ) )
2014-07-28 19:52:32 -05:00
return t
}
func ( t * table ) Unlock ( ) {
t . unlock ( )
2014-08-11 18:38:21 -05:00
// 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 ( )
2015-02-17 20:29:41 -06:00
C . SendMessageW ( t . hwnd , C . tableSetRowCount , 0 , C . LPARAM ( C . intptr_t ( reflect . Indirect ( reflect . ValueOf ( t . data ) ) . Len ( ) ) ) )
2014-08-11 18:38:21 -05:00
} )
} ( )
2014-07-28 19:52:32 -05:00
}
2014-07-28 22:29:06 -05:00
2014-08-18 01:22:27 -05:00
func ( t * table ) Selected ( ) int {
t . RLock ( )
defer t . RUnlock ( )
2015-02-17 20:29:41 -06:00
//TODO return int(C.tableSelectedItem(t.hwnd))
return - 1
2014-08-18 01:22:27 -05:00
}
func ( t * table ) Select ( index int ) {
t . RLock ( )
defer t . RUnlock ( )
2015-02-17 20:29:41 -06:00
//TODO C.tableSelectItem(t.hwnd, C.intptr_t(index))
2014-08-18 01:22:27 -05:00
}
2014-08-20 20:21:45 -05:00
func ( t * table ) OnSelected ( f func ( ) ) {
t . selected . set ( f )
}
2014-08-16 16:37:21 -05:00
//export tableGetCell
2015-02-17 20:29:41 -06:00
func tableGetCell ( data unsafe . Pointer , item * C . LVITEMW ) C . LRESULT {
2014-07-28 22:29:06 -05:00
t := ( * table ) ( data )
t . RLock ( )
defer t . RUnlock ( )
d := reflect . Indirect ( reflect . ValueOf ( t . data ) )
2014-08-16 16:37:21 -05:00
datum := d . Index ( int ( item . iItem ) ) . Field ( int ( item . iSubItem ) )
2014-08-30 10:46:13 -05:00
isText := true
2015-02-17 20:29:41 -06:00
switch {
case datum . Type ( ) == reflect . TypeOf ( ( * image . RGBA ) ( nil ) ) :
2015-02-17 20:38:55 -06:00
i := datum . Interface ( ) . ( * image . RGBA )
hbitmap := C . toBitmap ( unsafe . Pointer ( i ) , C . intptr_t ( i . Dx ( ) ) , C . intptr_t ( i . Dy ( ) ) )
bitmap := unsafe . Pointer ( hbitmap )
2015-02-17 20:29:41 -06:00
t . freeLock . Lock ( )
t . free [ bitmap ] = true // bitmap freed with C.freeBitmap()
t . freeLock . Unlock ( )
return C . LRESULT ( uintptr ( bmp ) )
case datum . Kind ( ) == reflect . Bool :
if datum . Bool ( ) == true {
return C . TRUE
2014-08-17 14:30:10 -05:00
}
2015-02-17 20:29:41 -06:00
return C . FALSE
default :
s := fmt . Sprintf ( "%v" , datum )
text := unsafe . Pointer ( toUTF16 ( s ) )
t . freeLock . Lock ( )
t . free [ text ] = false // text freed with C.free()
t . freeLock . Unlock ( )
return C . LRESULT ( uintptr ( text ) )
2014-08-30 10:46:13 -05:00
}
2015-02-17 20:29:41 -06:00
}
//export tableFreeData
func tableFreeData ( gotable unsafe . Pointer , data unsafe . Pointer ) {
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 ) )
2014-08-30 10:46:13 -05:00
}
2015-02-17 20:29:41 -06:00
if b == false {
C . free ( data )
} else {
2015-02-17 20:38:55 -06:00
C . freeBitmap ( data )
2014-08-16 16:37:21 -05:00
}
2015-02-17 20:29:41 -06:00
delete ( t . free , data )
2014-07-28 22:29:06 -05:00
}
2014-08-03 08:18:35 -05:00
2014-08-14 08:20:20 -05:00
// 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 {
2015-02-17 20:29:41 -06:00
//TODO C.tableAutosizeColumns(t.hwnd, t.colcount)
2014-08-14 08:20:20 -05:00
}
}
2014-08-06 09:42:26 -05:00
//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
}
2014-08-17 16:44:33 -05:00
//export tableToggled
func tableToggled ( data unsafe . Pointer , row C . int , col C . int ) {
t := ( * table ) ( data )
2014-08-17 17:06:36 -05:00
t . Lock ( )
2015-02-17 20:29:41 -06:00
defer t . Unlock ( )
2014-08-17 16:44:33 -05:00
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 ) )
}
2014-08-20 20:37:44 -05:00
//export tableSelectionChanged
func tableSelectionChanged ( data unsafe . Pointer ) {
2014-08-20 20:21:45 -05:00
t := ( * table ) ( data )
t . selected . fire ( )
}
2014-08-03 08:18:35 -05:00
const (
2014-08-13 15:49:30 -05:00
// 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...
2014-10-02 09:05:53 -05:00
tableWidth = 183
2014-08-03 08:18:35 -05:00
tableHeight = 50
)
2014-10-18 16:03:07 -05:00
func ( t * table ) xpreferredSize ( d * sizing ) ( width , height int ) {
2014-08-03 08:18:35 -05:00
return fromdlgunitsX ( tableWidth , d ) , fromdlgunitsY ( tableHeight , d )
}
2014-10-18 16:03:07 -05:00
func ( t * table ) xresize ( x int , y int , width int , height int , d * sizing ) {
t . chainresize ( x , y , width , height , d )
2014-08-14 08:20:20 -05:00
t . RLock ( )
defer t . RUnlock ( )
t . autoresize ( )
2014-08-03 08:18:35 -05:00
}