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-02-11 15:14:15 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
type classData struct {
|
2014-02-14 11:16:27 -06:00
|
|
|
name string
|
2014-03-24 12:32:38 -05:00
|
|
|
register func(s *sysData) (newClassName string, err error)
|
2014-02-14 11:16:27 -06:00
|
|
|
style uint32
|
|
|
|
xstyle uint32
|
|
|
|
mkid bool
|
2014-02-14 14:54:56 -06:00
|
|
|
altStyle uint32
|
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
|
|
|
|
selectedIndexErr int
|
2014-02-15 13:03:46 -06:00
|
|
|
addSpaceErr int
|
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-03-24 12:32:38 -05:00
|
|
|
register: registerStdWndClass,
|
2014-02-14 11:16:27 -06:00
|
|
|
style: _WS_OVERLAPPEDWINDOW,
|
|
|
|
xstyle: 0,
|
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-02-14 11:16:27 -06:00
|
|
|
name: "BUTTON",
|
|
|
|
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-02-14 11:16:27 -06:00
|
|
|
name: "BUTTON",
|
|
|
|
style: _BS_AUTOCHECKBOX | controlstyle,
|
|
|
|
xstyle: 0 | controlxstyle,
|
|
|
|
},
|
2014-02-25 00:02:16 -06:00
|
|
|
c_combobox: &classData{
|
2014-02-14 11:16:27 -06:00
|
|
|
name: "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,
|
|
|
|
selectedIndexErr: _CB_ERR,
|
2014-02-15 13:03:46 -06:00
|
|
|
addSpaceErr: _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-02-14 14:00:59 -06:00
|
|
|
name: "EDIT",
|
|
|
|
style: _ES_AUTOHSCROLL | _WS_BORDER | controlstyle,
|
|
|
|
xstyle: 0 | controlxstyle,
|
2014-02-25 14:06:51 -06:00
|
|
|
altStyle: _ES_PASSWORD | _ES_AUTOHSCROLL | _WS_BORDER | controlstyle,
|
2014-02-14 14:00:59 -06:00
|
|
|
},
|
2014-02-25 00:02:16 -06:00
|
|
|
c_label: &classData{
|
2014-02-14 14:12:03 -06:00
|
|
|
name: "STATIC",
|
|
|
|
style: _SS_NOPREFIX | controlstyle,
|
|
|
|
xstyle: 0 | controlxstyle,
|
|
|
|
},
|
2014-02-25 00:02:16 -06:00
|
|
|
c_listbox: &classData{
|
2014-02-14 15:25:39 -06:00
|
|
|
name: "LISTBOX",
|
2014-02-14 21:10:35 -06:00
|
|
|
// TODO also _WS_HSCROLL?
|
2014-03-12 13:43:40 -05:00
|
|
|
// we don't use _LBS_STANDARD because it sorts
|
2014-04-02 09:15:04 -05:00
|
|
|
// _LBS_NOINTEGRALHEIGHT gives us exactly the size we want
|
|
|
|
style: _LBS_NOTIFY | _WS_BORDER | _LBS_NOINTEGRALHEIGHT | _WS_VSCROLL | controlstyle,
|
2014-02-14 15:25:39 -06:00
|
|
|
xstyle: 0 | controlxstyle,
|
2014-04-02 09:15:04 -05:00
|
|
|
altStyle: _LBS_EXTENDEDSEL | _LBS_NOTIFY | _LBS_NOINTEGRALHEIGHT | _WS_BORDER | _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,
|
|
|
|
selectedIndexErr: _LB_ERR,
|
2014-02-15 13:03:46 -06:00
|
|
|
addSpaceErr: _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-02-25 07:28:10 -06:00
|
|
|
name: _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{
|
|
|
|
register: registerAreaWndClass,
|
|
|
|
style: areastyle,
|
|
|
|
xstyle: areaxstyle,
|
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-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:28:58 -06:00
|
|
|
classname := ct.name
|
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
|
|
|
}
|
|
|
|
if classname == "" { // need a new window class
|
|
|
|
n, err := ct.register(s)
|
2014-02-12 20:28:58 -06:00
|
|
|
if err != nil {
|
2014-03-24 12:32:38 -05:00
|
|
|
return fmt.Errorf("error creating window class for new window/control (type %d): %v", s.ctype, err)
|
2014-02-12 20:28:58 -06:00
|
|
|
}
|
|
|
|
classname = n
|
2014-02-12 09:35:15 -06: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-02-11 17:50:33 -06:00
|
|
|
uitask <- &uimsg{
|
|
|
|
call: _createWindowEx,
|
|
|
|
p: []uintptr{
|
|
|
|
uintptr(ct.xstyle),
|
2014-02-12 20:28:58 -06:00
|
|
|
uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(classname))),
|
2014-04-01 15:47:36 -05:00
|
|
|
uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(""))), // TODO can this be NULL?
|
2014-02-14 11:16:27 -06:00
|
|
|
style,
|
2014-02-11 17:50:33 -06:00
|
|
|
uintptr(_CW_USEDEFAULT),
|
2014-02-15 12:07:46 -06:00
|
|
|
uintptr(_CW_USEDEFAULT),
|
|
|
|
uintptr(_CW_USEDEFAULT),
|
|
|
|
uintptr(_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),
|
|
|
|
uintptr(_NULL),
|
|
|
|
},
|
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
|
|
|
}
|
|
|
|
s.hwnd = _HWND(r.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-02-12 17:14:37 -06:00
|
|
|
ret := make(chan uiret)
|
|
|
|
defer close(ret)
|
|
|
|
uitask <- &uimsg{
|
|
|
|
call: _setWindowText,
|
|
|
|
p: []uintptr{
|
|
|
|
uintptr(s.hwnd),
|
|
|
|
uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(text))),
|
|
|
|
},
|
|
|
|
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-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)),
|
|
|
|
uintptr(_LPARAM(unsafe.Pointer(syscall.StringToUTF16Ptr(what)))),
|
|
|
|
},
|
|
|
|
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-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)),
|
|
|
|
uintptr(_LPARAM(unsafe.Pointer(syscall.StringToUTF16Ptr(what)))),
|
|
|
|
},
|
|
|
|
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-02-15 13:03:46 -06:00
|
|
|
if r.ret == uintptr(_LB_ERR) {
|
|
|
|
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
|
|
|
|
if r.ret == uintptr(_LB_ERR) {
|
|
|
|
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-02-15 13:03:46 -06:00
|
|
|
if r.ret == uintptr(_LB_ERR) {
|
|
|
|
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 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,
|
|
|
|
}
|
|
|
|
<-ret
|
2014-02-15 13:03:46 -06:00
|
|
|
if r.ret == uintptr(_LB_ERR) {
|
|
|
|
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 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
|
|
|
|
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{
|
|
|
|
call: _setWindowLong,
|
|
|
|
p: []uintptr{
|
|
|
|
uintptr(s.hwnd),
|
|
|
|
uintptr(_GWL_STYLE),
|
|
|
|
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{
|
|
|
|
call: _setWindowLong,
|
|
|
|
p: []uintptr{
|
|
|
|
uintptr(s.hwnd),
|
|
|
|
uintptr(_GWL_STYLE),
|
|
|
|
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-02-25 00:02:16 -06:00
|
|
|
uitask <- &uimsg{
|
|
|
|
call: _sendMessage,
|
|
|
|
p: []uintptr{
|
|
|
|
uintptr(s.hwnd),
|
|
|
|
uintptr(_PBM_SETPOS),
|
|
|
|
uintptr(_WPARAM(percent)),
|
|
|
|
uintptr(0),
|
|
|
|
},
|
|
|
|
ret: ret,
|
|
|
|
}
|
|
|
|
<-ret
|
|
|
|
}
|
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
|
|
|
}
|