2014-02-11 15:14:15 -06:00
// 11 february 2014
2014-03-12 20:55:45 -05:00
2014-02-19 10:41:10 -06:00
package ui
2014-02-11 15:14:15 -06:00
import (
2014-02-11 17:50:33 -06:00
"fmt"
2014-02-11 15:14:15 -06:00
"syscall"
"unsafe"
2014-02-12 09:35:15 -06:00
"sync"
2014-02-11 15:14:15 -06:00
)
type sysData struct {
cSysData
2014-02-11 17:50:33 -06:00
hwnd _HWND
2014-02-12 20:08:10 -06:00
children map [ _HMENU ] * sysData
nextChildID _HMENU
childrenLock sync . Mutex
2014-03-12 17:53:57 -05:00
isMarquee bool // for sysData.setProgress()
2014-03-29 17:51:22 -05:00
// unlike with GTK+ and Mac OS X, we're responsible for sizing Area properly ourselves
areawidth int
areaheight int
2014-05-23 16:01:56 -05:00
clickCounter clickCounter
2014-06-05 22:27:17 -05:00
lastfocus _HWND
2014-02-11 15:14:15 -06:00
}
type classData struct {
2014-06-03 02:19:19 -05:00
name * uint16
2014-02-14 11:16:27 -06:00
style uint32
xstyle uint32
2014-02-14 14:54:56 -06:00
altStyle uint32
2014-05-30 16:59:29 -05:00
storeSysData bool
2014-04-01 20:24:20 -05:00
doNotLoadFont bool
2014-02-14 11:16:27 -06:00
appendMsg uintptr
2014-02-15 11:45:17 -06:00
insertBeforeMsg uintptr
2014-02-14 11:16:27 -06:00
deleteMsg uintptr
2014-02-15 11:32:12 -06:00
selectedIndexMsg uintptr
2014-05-25 10:31:57 -05:00
selectedIndexErr uintptr
addSpaceErr uintptr
2014-03-08 15:58:18 -06:00
lenMsg uintptr
2014-02-11 15:14:15 -06:00
}
2014-02-12 10:29:20 -06:00
const controlstyle = _WS_CHILD | _WS_VISIBLE | _WS_TABSTOP
const controlxstyle = 0
2014-02-11 15:14:15 -06:00
var classTypes = [ nctypes ] * classData {
2014-02-25 00:02:16 -06:00
c_window : & classData {
2014-06-03 09:52:53 -05:00
name : stdWndClass ,
2014-02-14 11:16:27 -06:00
style : _WS_OVERLAPPEDWINDOW ,
xstyle : 0 ,
2014-05-30 16:59:29 -05:00
storeSysData : true ,
2014-04-01 20:24:20 -05:00
doNotLoadFont : true ,
2014-02-11 15:14:15 -06:00
} ,
2014-02-25 00:02:16 -06:00
c_button : & classData {
2014-06-03 02:19:19 -05:00
name : toUTF16 ( "BUTTON" ) ,
2014-02-14 11:16:27 -06:00
style : _BS_PUSHBUTTON | controlstyle ,
xstyle : 0 | controlxstyle ,
2014-02-12 10:29:20 -06:00
} ,
2014-02-25 00:02:16 -06:00
c_checkbox : & classData {
2014-06-03 02:19:19 -05:00
name : toUTF16 ( "BUTTON" ) ,
2014-06-05 17:28:40 -05:00
// don't use BS_AUTOCHECKBOX because http://blogs.msdn.com/b/oldnewthing/archive/2014/05/22/10527522.aspx
style : _BS_CHECKBOX | controlstyle ,
2014-02-14 11:16:27 -06:00
xstyle : 0 | controlxstyle ,
} ,
2014-02-25 00:02:16 -06:00
c_combobox : & classData {
2014-06-03 02:19:19 -05:00
name : toUTF16 ( "COMBOBOX" ) ,
2014-02-15 16:55:15 -06:00
style : _CBS_DROPDOWNLIST | _WS_VSCROLL | controlstyle ,
2014-02-14 11:16:27 -06:00
xstyle : 0 | controlxstyle ,
2014-02-15 16:55:15 -06:00
altStyle : _CBS_DROPDOWN | _CBS_AUTOHSCROLL | _WS_VSCROLL | controlstyle ,
2014-02-14 11:16:27 -06:00
appendMsg : _CB_ADDSTRING ,
2014-02-15 11:06:29 -06:00
insertBeforeMsg : _CB_INSERTSTRING ,
2014-02-14 11:16:27 -06:00
deleteMsg : _CB_DELETESTRING ,
2014-02-15 11:32:12 -06:00
selectedIndexMsg : _CB_GETCURSEL ,
2014-05-25 10:31:57 -05:00
selectedIndexErr : negConst ( _CB_ERR ) ,
addSpaceErr : negConst ( _CB_ERRSPACE ) ,
2014-03-08 15:58:18 -06:00
lenMsg : _CB_GETCOUNT ,
2014-02-13 11:26:43 -06:00
} ,
2014-02-25 00:02:16 -06:00
c_lineedit : & classData {
2014-06-03 02:19:19 -05:00
name : toUTF16 ( "EDIT" ) ,
2014-04-02 18:57:49 -05:00
// WS_EX_CLIENTEDGE without WS_BORDER will apply visual styles
// thanks to MindChild in irc.efnet.net/#winprog
style : _ES_AUTOHSCROLL | controlstyle ,
xstyle : _WS_EX_CLIENTEDGE | controlxstyle ,
altStyle : _ES_PASSWORD | _ES_AUTOHSCROLL | controlstyle ,
2014-02-14 14:00:59 -06:00
} ,
2014-02-25 00:02:16 -06:00
c_label : & classData {
2014-06-03 02:19:19 -05:00
name : toUTF16 ( "STATIC" ) ,
2014-04-28 12:06:24 -05:00
// SS_NOPREFIX avoids accelerator translation; SS_LEFTNOWORDWRAP clips text past the end
// TODO find out if the default behavior is not to ellipsize
2014-04-13 17:05:07 -05:00
style : _SS_NOPREFIX | _SS_LEFTNOWORDWRAP | controlstyle ,
2014-02-14 14:12:03 -06:00
xstyle : 0 | controlxstyle ,
} ,
2014-02-25 00:02:16 -06:00
c_listbox : & classData {
2014-06-03 02:19:19 -05:00
name : toUTF16 ( "LISTBOX" ) ,
2014-04-28 12:06:24 -05:00
// we don't use LBS_STANDARD because it sorts (and has WS_BORDER; see above)
// LBS_NOINTEGRALHEIGHT gives us exactly the size we want
// LBS_MULTISEL sounds like it does what we want but it actually doesn't; instead, it toggles item selection regardless of modifier state, which doesn't work like anything else (see http://msdn.microsoft.com/en-us/library/windows/desktop/bb775149%28v=vs.85%29.aspx and http://msdn.microsoft.com/en-us/library/windows/desktop/aa511485.aspx)
2014-04-13 12:00:07 -05:00
style : _LBS_NOTIFY | _LBS_NOINTEGRALHEIGHT | _WS_VSCROLL | controlstyle ,
2014-04-02 18:57:49 -05:00
xstyle : _WS_EX_CLIENTEDGE | controlxstyle ,
2014-04-13 12:00:07 -05:00
altStyle : _LBS_EXTENDEDSEL | _LBS_NOTIFY | _LBS_NOINTEGRALHEIGHT | _WS_VSCROLL | controlstyle ,
2014-02-14 15:25:39 -06:00
appendMsg : _LB_ADDSTRING ,
2014-02-15 11:06:29 -06:00
insertBeforeMsg : _LB_INSERTSTRING ,
2014-02-14 15:25:39 -06:00
deleteMsg : _LB_DELETESTRING ,
2014-02-15 11:32:12 -06:00
selectedIndexMsg : _LB_GETCURSEL ,
2014-05-25 10:31:57 -05:00
selectedIndexErr : negConst ( _LB_ERR ) ,
addSpaceErr : negConst ( _LB_ERRSPACE ) ,
2014-03-08 15:58:18 -06:00
lenMsg : _LB_GETCOUNT ,
2014-02-14 15:25:39 -06:00
} ,
2014-02-25 00:02:16 -06:00
c_progressbar : & classData {
2014-06-03 02:19:19 -05:00
name : toUTF16 ( x_PROGRESS_CLASS ) ,
2014-02-25 00:02:16 -06:00
style : _PBS_SMOOTH | controlstyle ,
xstyle : 0 | controlxstyle ,
2014-04-01 20:24:20 -05:00
doNotLoadFont : true ,
2014-02-25 00:02:16 -06:00
} ,
2014-03-24 12:32:38 -05:00
c_area : & classData {
2014-06-03 10:04:31 -05:00
name : areaWndClass ,
2014-03-24 12:32:38 -05:00
style : areastyle ,
xstyle : areaxstyle ,
2014-05-30 21:14:55 -05:00
storeSysData : true ,
2014-04-01 20:24:20 -05:00
doNotLoadFont : true ,
2014-03-24 12:32:38 -05:00
} ,
2014-02-11 15:14:15 -06:00
}
2014-02-12 20:33:24 -06:00
func ( s * sysData ) addChild ( child * sysData ) _HMENU {
2014-02-12 20:08:10 -06:00
s . childrenLock . Lock ( )
defer s . childrenLock . Unlock ( )
s . nextChildID ++ // start at 1
2014-02-12 20:33:24 -06:00
if s . children == nil {
s . children = map [ _HMENU ] * sysData { }
}
2014-02-12 20:08:10 -06:00
s . children [ s . nextChildID ] = child
return s . nextChildID
}
2014-02-12 09:35:15 -06:00
2014-02-12 20:08:10 -06:00
func ( s * sysData ) delChild ( id _HMENU ) {
s . childrenLock . Lock ( )
defer s . childrenLock . Unlock ( )
delete ( s . children , id )
2014-02-12 09:35:15 -06:00
}
2014-06-03 02:19:19 -05:00
var (
_blankString = toUTF16 ( "" )
blankString = utf16ToArg ( _blankString )
)
2014-04-01 15:47:36 -05:00
func ( s * sysData ) make ( window * sysData ) ( err error ) {
2014-02-11 17:50:33 -06:00
ret := make ( chan uiret )
defer close ( ret )
ct := classTypes [ s . ctype ]
2014-02-12 20:08:10 -06:00
cid := _HMENU ( 0 )
2014-02-12 10:29:20 -06:00
pwin := uintptr ( _NULL )
2014-02-12 20:08:10 -06:00
if window != nil { // this is a child control
cid = window . addChild ( s )
pwin = uintptr ( window . hwnd )
2014-03-24 12:32:38 -05:00
}
2014-02-14 11:16:27 -06:00
style := uintptr ( ct . style )
2014-02-14 14:54:56 -06:00
if s . alternate {
style = uintptr ( ct . altStyle )
2014-02-14 11:16:27 -06:00
}
2014-05-30 16:59:29 -05:00
lpParam := uintptr ( _NULL )
if ct . storeSysData {
lpParam = uintptr ( unsafe . Pointer ( s ) )
}
2014-02-11 17:50:33 -06:00
uitask <- & uimsg {
call : _createWindowEx ,
p : [ ] uintptr {
uintptr ( ct . xstyle ) ,
2014-06-03 02:19:19 -05:00
utf16ToArg ( ct . name ) ,
blankString , // we set the window text later
2014-02-14 11:16:27 -06:00
style ,
2014-05-25 11:31:38 -05:00
negConst ( _CW_USEDEFAULT ) ,
negConst ( _CW_USEDEFAULT ) ,
negConst ( _CW_USEDEFAULT ) ,
negConst ( _CW_USEDEFAULT ) ,
2014-02-12 10:29:20 -06:00
pwin ,
2014-02-12 20:08:10 -06:00
uintptr ( cid ) ,
2014-02-11 17:50:33 -06:00
uintptr ( hInstance ) ,
2014-05-30 16:59:29 -05:00
lpParam ,
2014-02-11 17:50:33 -06:00
} ,
2014-02-11 18:09:10 -06:00
ret : ret ,
2014-02-11 17:50:33 -06:00
}
r := <- ret
2014-02-11 18:34:28 -06:00
if r . ret == 0 { // failure
2014-02-12 20:08:10 -06:00
if window != nil {
window . delChild ( cid )
}
2014-02-15 12:16:17 -06:00
return fmt . Errorf ( "error actually creating window/control: %v" , r . err )
2014-02-11 17:50:33 -06:00
}
2014-05-30 22:28:41 -05:00
if ! ct . storeSysData { // regular control; store s.hwnd ourselves
s . hwnd = _HWND ( r . ret )
} else if s . hwnd != _HWND ( r . ret ) { // we store sysData in storeSysData(); sanity check
panic ( fmt . Errorf ( "hwnd mismatch creating window/control: storeSysData() stored 0x%X but CreateWindowEx() returned 0x%X" , s . hwnd , ret ) )
}
2014-04-01 20:24:20 -05:00
if ! ct . doNotLoadFont {
2014-02-24 13:49:46 -06:00
uitask <- & uimsg {
call : _sendMessage ,
p : [ ] uintptr {
uintptr ( s . hwnd ) ,
uintptr ( _WM_SETFONT ) ,
2014-04-01 20:24:20 -05:00
uintptr ( _WPARAM ( controlFont ) ) ,
2014-02-24 13:49:46 -06:00
uintptr ( _LPARAM ( _TRUE ) ) ,
} ,
ret : ret ,
}
<- ret
}
2014-02-11 17:50:33 -06:00
return nil
2014-02-11 15:14:15 -06:00
}
2014-02-11 17:50:33 -06:00
var (
_updateWindow = user32 . NewProc ( "UpdateWindow" )
)
// if the object is a window, we need to do the following the first time
// ShowWindow(hwnd, nCmdShow);
// UpdateWindow(hwnd);
2014-03-09 20:40:14 -05:00
func ( s * sysData ) firstShow ( ) error {
ret := make ( chan uiret )
defer close ( ret )
uitask <- & uimsg {
call : _showWindow ,
p : [ ] uintptr {
uintptr ( s . hwnd ) ,
uintptr ( nCmdShow ) ,
} ,
ret : ret ,
}
<- ret
uitask <- & uimsg {
call : _updateWindow ,
p : [ ] uintptr { uintptr ( s . hwnd ) } ,
ret : ret ,
2014-02-11 17:50:33 -06:00
}
2014-03-09 20:40:14 -05:00
r := <- ret
if r . ret == 0 { // failure
return fmt . Errorf ( "error updating window for the first time: %v" , r . err )
2014-02-11 17:50:33 -06:00
}
2014-03-09 20:40:14 -05:00
return nil
}
2014-03-09 20:56:17 -05:00
func ( s * sysData ) show ( ) {
2014-02-11 15:14:15 -06:00
ret := make ( chan uiret )
defer close ( ret )
uitask <- & uimsg {
2014-02-11 15:23:38 -06:00
call : _showWindow ,
2014-03-09 20:40:14 -05:00
p : [ ] uintptr {
uintptr ( s . hwnd ) ,
uintptr ( _SW_SHOW ) ,
} ,
2014-02-11 15:14:15 -06:00
ret : ret ,
}
2014-02-11 19:23:49 -06:00
<- ret
2014-02-11 15:14:15 -06:00
}
2014-03-09 20:56:17 -05:00
func ( s * sysData ) hide ( ) {
2014-02-11 15:14:15 -06:00
ret := make ( chan uiret )
defer close ( ret )
uitask <- & uimsg {
call : _showWindow ,
2014-03-09 20:56:17 -05:00
p : [ ] uintptr {
uintptr ( s . hwnd ) ,
uintptr ( _SW_HIDE ) ,
} ,
2014-02-11 15:14:15 -06:00
ret : ret ,
}
2014-02-11 19:23:49 -06:00
<- ret
2014-02-11 15:14:15 -06:00
}
2014-02-12 17:14:37 -06:00
2014-03-10 09:39:08 -05:00
func ( s * sysData ) setText ( text string ) {
2014-06-03 02:19:19 -05:00
ptext := toUTF16 ( text )
2014-02-12 17:14:37 -06:00
ret := make ( chan uiret )
defer close ( ret )
uitask <- & uimsg {
call : _setWindowText ,
p : [ ] uintptr {
uintptr ( s . hwnd ) ,
2014-06-03 02:19:19 -05:00
utf16ToArg ( ptext ) ,
2014-02-12 17:14:37 -06:00
} ,
ret : ret ,
}
r := <- ret
if r . ret == 0 { // failure
2014-03-10 10:06:07 -05:00
panic ( fmt . Errorf ( "error setting window/control text: %v" , r . err ) )
2014-02-12 17:14:37 -06:00
}
}
2014-02-13 04:28:26 -06:00
2014-03-03 16:44:03 -06:00
func ( s * sysData ) setRect ( x int , y int , width int , height int , winheight int ) error {
2014-02-17 00:40:53 -06:00
r1 , _ , err := _moveWindow . Call (
uintptr ( s . hwnd ) ,
uintptr ( x ) ,
uintptr ( y ) ,
uintptr ( width ) ,
uintptr ( height ) ,
uintptr ( _TRUE ) )
if r1 == 0 { // failure
return fmt . Errorf ( "error setting window/control rect: %v" , err )
2014-02-13 04:28:26 -06:00
}
return nil
}
2014-02-13 14:14:10 -06:00
2014-02-15 12:36:24 -06:00
func ( s * sysData ) isChecked ( ) bool {
2014-02-13 14:14:10 -06:00
ret := make ( chan uiret )
defer close ( ret )
uitask <- & uimsg {
call : _sendMessage ,
p : [ ] uintptr {
uintptr ( s . hwnd ) ,
uintptr ( _BM_GETCHECK ) ,
uintptr ( 0 ) ,
uintptr ( 0 ) ,
} ,
ret : ret ,
}
r := <- ret
2014-02-15 12:36:24 -06:00
return r . ret == _BST_CHECKED
2014-02-13 14:14:10 -06:00
}
2014-02-14 11:16:27 -06:00
2014-02-15 12:36:24 -06:00
func ( s * sysData ) text ( ) ( str string ) {
2014-02-14 11:16:27 -06:00
var tc [ ] uint16
ret := make ( chan uiret )
defer close ( ret )
uitask <- & uimsg {
call : _sendMessage ,
p : [ ] uintptr {
uintptr ( s . hwnd ) ,
uintptr ( _WM_GETTEXTLENGTH ) ,
uintptr ( 0 ) ,
uintptr ( 0 ) ,
} ,
ret : ret ,
}
r := <- ret
length := r . ret + 1 // terminating null
tc = make ( [ ] uint16 , length )
uitask <- & uimsg {
call : _sendMessage ,
p : [ ] uintptr {
uintptr ( s . hwnd ) ,
uintptr ( _WM_GETTEXT ) ,
uintptr ( _WPARAM ( length ) ) ,
uintptr ( _LPARAM ( unsafe . Pointer ( & tc [ 0 ] ) ) ) ,
} ,
ret : ret ,
}
<- ret
2014-02-15 12:36:24 -06:00
return syscall . UTF16ToString ( tc )
2014-02-14 11:16:27 -06:00
}
2014-03-09 15:02:17 -05:00
func ( s * sysData ) append ( what string ) {
2014-06-03 02:19:19 -05:00
pwhat := toUTF16 ( what )
2014-02-14 11:16:27 -06:00
ret := make ( chan uiret )
defer close ( ret )
uitask <- & uimsg {
call : _sendMessage ,
p : [ ] uintptr {
uintptr ( s . hwnd ) ,
uintptr ( classTypes [ s . ctype ] . appendMsg ) ,
uintptr ( _WPARAM ( 0 ) ) ,
2014-06-03 02:19:19 -05:00
utf16ToLPARAM ( pwhat ) ,
2014-02-14 11:16:27 -06:00
} ,
ret : ret ,
}
2014-02-15 13:03:46 -06:00
r := <- ret
if r . ret == uintptr ( classTypes [ s . ctype ] . addSpaceErr ) {
2014-03-09 15:02:17 -05:00
panic ( fmt . Errorf ( "out of space adding item to combobox/listbox (last error: %v)" , r . err ) )
2014-02-15 13:03:46 -06:00
} else if r . ret == uintptr ( classTypes [ s . ctype ] . selectedIndexErr ) {
2014-03-09 15:02:17 -05:00
panic ( fmt . Errorf ( "failed to add item to combobox/listbox (last error: %v)" , r . err ) )
2014-02-15 13:03:46 -06:00
}
2014-02-14 11:16:27 -06:00
}
2014-02-15 11:06:29 -06:00
2014-03-09 15:02:17 -05:00
func ( s * sysData ) insertBefore ( what string , index int ) {
2014-06-03 02:19:19 -05:00
pwhat := toUTF16 ( what )
2014-02-15 11:06:29 -06:00
ret := make ( chan uiret )
defer close ( ret )
uitask <- & uimsg {
call : _sendMessage ,
p : [ ] uintptr {
uintptr ( s . hwnd ) ,
uintptr ( classTypes [ s . ctype ] . insertBeforeMsg ) ,
uintptr ( _WPARAM ( index ) ) ,
2014-06-03 02:19:19 -05:00
utf16ToLPARAM ( pwhat ) ,
2014-02-15 11:06:29 -06:00
} ,
ret : ret ,
}
2014-02-15 13:03:46 -06:00
r := <- ret
if r . ret == uintptr ( classTypes [ s . ctype ] . addSpaceErr ) {
2014-03-09 15:02:17 -05:00
panic ( fmt . Errorf ( "out of space adding item to combobox/listbox (last error: %v)" , r . err ) )
2014-02-15 13:03:46 -06:00
} else if r . ret == uintptr ( classTypes [ s . ctype ] . selectedIndexErr ) {
2014-03-09 15:02:17 -05:00
panic ( fmt . Errorf ( "failed to add item to combobox/listbox (last error: %v)" , r . err ) )
2014-02-15 13:03:46 -06:00
}
2014-02-15 11:06:29 -06:00
}
2014-02-15 11:32:12 -06:00
2014-02-15 13:03:46 -06:00
func ( s * sysData ) selectedIndex ( ) int {
2014-02-15 11:32:12 -06:00
ret := make ( chan uiret )
defer close ( ret )
uitask <- & uimsg {
call : _sendMessage ,
p : [ ] uintptr {
uintptr ( s . hwnd ) ,
uintptr ( classTypes [ s . ctype ] . selectedIndexMsg ) ,
uintptr ( _WPARAM ( 0 ) ) ,
uintptr ( _LPARAM ( 0 ) ) ,
} ,
ret : ret ,
}
r := <- ret
2014-02-15 17:27:34 -06:00
if r . ret == uintptr ( classTypes [ s . ctype ] . selectedIndexErr ) { // no selection or manually entered text (apparently, for the latter)
2014-02-15 13:03:46 -06:00
return - 1
2014-02-15 11:32:12 -06:00
}
2014-02-15 13:03:46 -06:00
return int ( r . ret )
2014-02-15 11:32:12 -06:00
}
2014-02-15 13:03:46 -06:00
func ( s * sysData ) selectedIndices ( ) [ ] int {
2014-02-15 11:32:12 -06:00
if ! s . alternate { // single-selection list box; use single-selection method
2014-02-15 13:05:10 -06:00
index := s . selectedIndex ( )
if index == - 1 {
return nil
}
return [ ] int { index }
2014-02-15 11:32:12 -06:00
}
ret := make ( chan uiret )
defer close ( ret )
uitask <- & uimsg {
call : _sendMessage ,
p : [ ] uintptr {
uintptr ( s . hwnd ) ,
uintptr ( _LB_GETSELCOUNT ) ,
uintptr ( 0 ) ,
uintptr ( 0 ) ,
} ,
2014-02-15 11:45:17 -06:00
ret : ret ,
2014-02-15 11:32:12 -06:00
}
r := <- ret
2014-05-25 10:31:57 -05:00
if r . ret == negConst ( _LB_ERR ) {
2014-02-15 13:03:46 -06:00
panic ( "UI library internal error: LB_ERR from LB_GETSELCOUNT in what we know is a multi-selection listbox" )
}
2014-02-15 17:36:14 -06:00
if r . ret == 0 { // nothing selected
return nil
}
2014-02-15 11:32:12 -06:00
indices := make ( [ ] int , r . ret )
2014-02-15 11:45:17 -06:00
uitask <- & uimsg {
2014-02-15 11:32:12 -06:00
call : _sendMessage ,
p : [ ] uintptr {
uintptr ( s . hwnd ) ,
uintptr ( _LB_GETSELITEMS ) ,
uintptr ( _WPARAM ( r . ret ) ) ,
uintptr ( _LPARAM ( unsafe . Pointer ( & indices [ 0 ] ) ) ) ,
} ,
ret : ret ,
}
2014-02-15 13:03:46 -06:00
r = <- ret
2014-05-25 10:31:57 -05:00
if r . ret == negConst ( _LB_ERR ) {
2014-02-15 13:03:46 -06:00
panic ( "UI library internal error: LB_ERR from LB_GETSELITEMS in what we know is a multi-selection listbox" )
}
return indices
2014-02-15 11:32:12 -06:00
}
2014-02-15 13:03:46 -06:00
func ( s * sysData ) selectedTexts ( ) [ ] string {
indices := s . selectedIndices ( )
2014-02-15 11:32:12 -06:00
ret := make ( chan uiret )
defer close ( ret )
strings := make ( [ ] string , len ( indices ) )
for i , v := range indices {
uitask <- & uimsg {
call : _sendMessage ,
p : [ ] uintptr {
uintptr ( s . hwnd ) ,
uintptr ( _LB_GETTEXTLEN ) ,
uintptr ( _WPARAM ( v ) ) ,
uintptr ( 0 ) ,
} ,
ret : ret ,
}
r := <- ret
2014-05-25 10:31:57 -05:00
if r . ret == negConst ( _LB_ERR ) {
2014-04-13 01:57:35 -05:00
panic ( "UI library internal error: LB_ERR from LB_GETTEXTLEN in what we know is a valid listbox index (came from LB_GETSELITEMS)" )
2014-02-15 13:03:46 -06:00
}
2014-02-15 11:32:12 -06:00
str := make ( [ ] uint16 , r . ret )
uitask <- & uimsg {
call : _sendMessage ,
p : [ ] uintptr {
uintptr ( s . hwnd ) ,
uintptr ( _LB_GETTEXT ) ,
uintptr ( _WPARAM ( v ) ) ,
uintptr ( _LPARAM ( unsafe . Pointer ( & str [ 0 ] ) ) ) ,
} ,
ret : ret ,
}
2014-04-13 01:57:35 -05:00
r = <- ret
2014-05-25 10:31:57 -05:00
if r . ret == negConst ( _LB_ERR ) {
2014-04-13 01:57:35 -05:00
panic ( "UI library internal error: LB_ERR from LB_GETTEXT in what we know is a valid listbox index (came from LB_GETSELITEMS)" )
2014-02-15 13:03:46 -06:00
}
2014-02-15 11:32:12 -06:00
strings [ i ] = syscall . UTF16ToString ( str )
}
2014-02-15 13:03:46 -06:00
return strings
2014-02-15 11:32:12 -06:00
}
2014-02-15 12:02:10 -06:00
func ( s * sysData ) setWindowSize ( width int , height int ) error {
var rect _RECT
ret := make ( chan uiret )
defer close ( ret )
uitask <- & uimsg {
call : _getClientRect ,
p : [ ] uintptr {
uintptr ( s . hwnd ) ,
uintptr ( unsafe . Pointer ( & rect ) ) ,
} ,
ret : ret ,
}
r := <- ret
if r . ret == 0 {
return fmt . Errorf ( "error getting upper-left of window for resize: %v" , r . err )
}
2014-03-03 16:44:03 -06:00
// 0 because (0,0) is top-left so no winheight
2014-05-11 10:11:00 -05:00
err := s . setRect ( int ( rect . left ) , int ( rect . top ) , width , height , 0 )
2014-02-15 12:02:10 -06:00
if err != nil {
return fmt . Errorf ( "error actually resizing window: %v" , err )
}
return nil
}
2014-02-15 17:14:43 -06:00
2014-03-11 12:50:02 -05:00
func ( s * sysData ) delete ( index int ) {
2014-02-15 17:14:43 -06:00
ret := make ( chan uiret )
defer close ( ret )
uitask <- & uimsg {
call : _sendMessage ,
p : [ ] uintptr {
uintptr ( s . hwnd ) ,
uintptr ( classTypes [ s . ctype ] . deleteMsg ) ,
uintptr ( _WPARAM ( index ) ) ,
uintptr ( 0 ) ,
} ,
ret : ret ,
}
r := <- ret
if r . ret == uintptr ( classTypes [ s . ctype ] . selectedIndexErr ) {
2014-03-11 12:50:02 -05:00
panic ( fmt . Errorf ( "failed to delete item from combobox/listbox (last error: %v)" , r . err ) )
2014-02-15 17:14:43 -06:00
}
}
2014-02-25 00:02:16 -06:00
2014-03-12 17:53:57 -05:00
func ( s * sysData ) setIndeterminate ( ) {
ret := make ( chan uiret )
defer close ( ret )
uitask <- & uimsg {
2014-06-07 13:50:05 -05:00
call : _setWindowLongPtr ,
2014-03-12 17:53:57 -05:00
p : [ ] uintptr {
uintptr ( s . hwnd ) ,
2014-05-25 10:31:57 -05:00
negConst ( _GWL_STYLE ) ,
2014-03-12 17:53:57 -05:00
uintptr ( classTypes [ s . ctype ] . style | _PBS_MARQUEE ) ,
} ,
ret : ret ,
}
r := <- ret
if r . ret == 0 {
panic ( fmt . Errorf ( "error setting progress bar style to enter indeterminate mode: %v" , r . err ) )
}
uitask <- & uimsg {
call : _sendMessage ,
p : [ ] uintptr {
uintptr ( s . hwnd ) ,
uintptr ( _PBM_SETMARQUEE ) ,
uintptr ( _WPARAM ( _TRUE ) ) ,
uintptr ( 0 ) ,
} ,
ret : ret ,
}
<- ret
s . isMarquee = true
}
2014-02-25 00:02:16 -06:00
func ( s * sysData ) setProgress ( percent int ) {
2014-03-12 17:53:57 -05:00
if percent == - 1 {
s . setIndeterminate ( )
return
}
2014-02-25 00:02:16 -06:00
ret := make ( chan uiret )
defer close ( ret )
2014-03-12 17:53:57 -05:00
if s . isMarquee {
// turn off marquee before switching back
uitask <- & uimsg {
call : _sendMessage ,
p : [ ] uintptr {
uintptr ( s . hwnd ) ,
uintptr ( _PBM_SETMARQUEE ) ,
uintptr ( _WPARAM ( _FALSE ) ) ,
uintptr ( 0 ) ,
} ,
ret : ret ,
}
<- ret
uitask <- & uimsg {
2014-06-07 13:50:05 -05:00
call : _setWindowLongPtr ,
2014-03-12 17:53:57 -05:00
p : [ ] uintptr {
uintptr ( s . hwnd ) ,
2014-05-25 10:31:57 -05:00
negConst ( _GWL_STYLE ) ,
2014-03-12 17:53:57 -05:00
uintptr ( classTypes [ s . ctype ] . style ) ,
} ,
ret : ret ,
}
r := <- ret
if r . ret == 0 {
panic ( fmt . Errorf ( "error setting progress bar style to leave indeterminate mode (percent %d): %v" , percent , r . err ) )
}
s . isMarquee = false
}
2014-06-07 22:27:38 -05:00
send := func ( msg uintptr , n int , l _LPARAM ) {
uitask <- & uimsg {
call : _sendMessage ,
p : [ ] uintptr {
uintptr ( s . hwnd ) ,
msg ,
uintptr ( _WPARAM ( n ) ) ,
uintptr ( l ) ,
} ,
ret : ret ,
}
<- ret
}
// Windows 7 has a non-disableable slowly-animating progress bar increment
// there isn't one for decrement, so we'll work around by going one higher and then lower again
// for the case where percent == 100, we need to increase the range temporarily
// this kind of thing is why I want to move away from uimsg and toward having uitask take func()s like on the other platforms
// sources: http://social.msdn.microsoft.com/Forums/en-US/61350dc7-6584-4c4e-91b0-69d642c03dae/progressbar-disable-smooth-animation http://stackoverflow.com/questions/2217688/windows-7-aero-theme-progress-bar-bug http://discuss.joelonsoftware.com/default.asp?dotnet.12.600456.2 http://stackoverflow.com/questions/22469876/progressbar-lag-when-setting-position-with-pbm-setpos http://stackoverflow.com/questions/6128287/tprogressbar-never-fills-up-all-the-way-seems-to-be-updating-too-fast
if percent == 100 {
send ( _PBM_SETRANGE32 , 0 , 101 )
}
send ( _PBM_SETPOS , percent + 1 , 0 )
send ( _PBM_SETPOS , percent , 0 )
if percent == 100 {
send ( _PBM_SETRANGE32 , 0 , 100 )
2014-02-25 00:02:16 -06:00
}
}
2014-03-08 15:58:18 -06:00
func ( s * sysData ) len ( ) int {
ret := make ( chan uiret )
defer close ( ret )
uitask <- & uimsg {
call : _sendMessage ,
p : [ ] uintptr {
uintptr ( s . hwnd ) ,
uintptr ( classTypes [ s . ctype ] . lenMsg ) ,
uintptr ( _WPARAM ( 0 ) ) ,
uintptr ( _LPARAM ( 0 ) ) ,
} ,
ret : ret ,
}
r := <- ret
if r . ret == uintptr ( classTypes [ s . ctype ] . selectedIndexErr ) {
panic ( fmt . Errorf ( "unexpected error return from sysData.len(); GetLastError() says %v" , r . err ) )
}
return int ( r . ret )
}
2014-03-24 12:32:38 -05:00
func ( s * sysData ) setAreaSize ( width int , height int ) {
2014-03-29 17:51:22 -05:00
ret := make ( chan uiret )
defer close ( ret )
uitask <- & uimsg {
call : _sendMessage ,
p : [ ] uintptr {
uintptr ( s . hwnd ) ,
uintptr ( msgSetAreaSize ) ,
uintptr ( width ) , // WPARAM is UINT_PTR on Windows XP and newer at least, so we're good with this
uintptr ( height ) ,
} ,
ret : ret ,
}
<- ret
2014-03-24 12:32:38 -05:00
}