andlabs-ui/comctl_windows.go

182 lines
5.0 KiB
Go
Raw Normal View History

// 25 february 2014
package ui
import (
"fmt"
"syscall"
"unsafe"
"io/ioutil"
)
// pretty much every constant here except _WM_USER is from commctrl.h
2014-03-12 15:58:33 -05:00
// TODO for all: filter out constants not available in Windows XP
var (
// TODO deinitialize at program end?
comctlManifestCookie uintptr
)
// InitCommonControlsEx constants.
const (
_ICC_LISTVIEW_CLASSES = 0x00000001
_ICC_TREEVIEW_CLASSES = 0x00000002
_ICC_BAR_CLASSES = 0x00000004
_ICC_TAB_CLASSES = 0x00000008
_ICC_UPDOWN_CLASS = 0x00000010
_ICC_PROGRESS_CLASS = 0x00000020
_ICC_HOTKEY_CLASS = 0x00000040
_ICC_ANIMATE_CLASS = 0x00000080
_ICC_WIN95_CLASSES = 0x000000FF
_ICC_DATE_CLASSES = 0x00000100
_ICC_USEREX_CLASSES = 0x00000200
_ICC_COOL_CLASSES = 0x00000400
_ICC_INTERNET_CLASSES = 0x00000800
_ICC_PAGESCROLLER_CLASS = 0x00001000
_ICC_NATIVEFNTCTL_CLASS = 0x00002000
_ICC_STANDARD_CLASSES = 0x00004000
_ICC_LINK_CLASS = 0x00008000
)
var (
_activateActCtx = kernel32.NewProc("ActivateActCtx")
_createActCtx = kernel32.NewProc("CreateActCtxW")
)
/*
Windows requires a manifest file to enable Common Controls version 6.
The only way to not require an external manifest is to synthesize the manifest ourselves.
We can use the activation context API to load it at runtime.
References:
- http://stackoverflow.com/questions/4308503/how-to-enable-visual-styles-without-a-manifest
- http://support.microsoft.com/kb/830033
*/
func forceCommonControls6() (err error) {
var (
// from winbase.h; var because Go won't let me convert this constant
_INVALID_HANDLE_VALUE = -1
)
manifestfile, err := ioutil.TempFile("", "gouicomctl32v6manifest")
if err != nil {
return fmt.Errorf("error creating synthesized manifest file: %v", err)
}
_, err = manifestfile.Write(manifest)
if err != nil {
return fmt.Errorf("error writing synthesized manifest file: %v", err)
}
filename := manifestfile.Name()
// we now have to close the file, otherwise ActivateActCtx() will complain that it's in use by another program
// if ioutil.TempFile() ever changes so that the file is deleted when it is closed, this will need to change
manifestfile.Close()
var actctx struct {
cbSize uint32
dwFlags uint32
lpSource *uint16
wProcessorArchitecture uint16
wLangId uint16 // originally LANGID
lpAssemblyDirectory uintptr // originally LPCWSTR
lpResourceName uintptr // originally LPCWSTR
lpApplicationName uintptr // originally LPCWSTR
hModule _HANDLE // originally HMODULE
}
actctx.cbSize = uint32(unsafe.Sizeof(actctx))
// TODO set ACTCTX_FLAG_SET_PROCESS_DEFAULT? I can't find a reference to figure out what this means
actctx.lpSource = syscall.StringToUTF16Ptr(filename)
r1, _, err := _createActCtx.Call(uintptr(unsafe.Pointer(&actctx)))
if r1 == uintptr(_INVALID_HANDLE_VALUE) { // failure
return fmt.Errorf("error creating activation context for synthesized manifest file: %v", err)
}
r1, _, err = _activateActCtx.Call(
r1,
uintptr(unsafe.Pointer(&comctlManifestCookie)))
if r1 == uintptr(_FALSE) { // failure
return fmt.Errorf("error activating activation context for synthesized manifest file: %v", err)
}
return nil
}
func initCommonControls() (err error) {
var icc struct {
dwSize uint32
dwICC uint32
}
err = forceCommonControls6()
if err != nil {
return fmt.Errorf("error forcing Common Controls version 6 (or newer): %v", err)
}
icc.dwSize = uint32(unsafe.Sizeof(icc))
icc.dwICC = _ICC_PROGRESS_CLASS
comctl32 = syscall.NewLazyDLL("comctl32.dll")
r1, _, err := comctl32.NewProc("InitCommonControlsEx").Call(uintptr(unsafe.Pointer(&icc)))
if r1 == _FALSE { // failure
// TODO does it set GetLastError()?
return fmt.Errorf("error initializing Common Controls (comctl32.dll): %v", err)
}
return nil
}
// Common Controls class names.
const (
_PROGRESS_CLASS = "msctls_progress32"
)
// Shared Common Controls styles.
const (
_WM_USER = 0x0400
_CCM_FIRST = 0x2000
_CCM_SETBKCOLOR = (_CCM_FIRST + 1)
)
// Progress Bar styles.
const (
_PBS_SMOOTH = 0x01
_PBS_VERTICAL = 0x04
_PBS_MARQUEE = 0x08
)
// Progress Bar messages.
const (
_PBM_SETRANGE = (_WM_USER + 1)
_PBM_SETPOS = (_WM_USER + 2)
_PBM_DELTAPOS = (_WM_USER + 3)
_PBM_SETSTEP = (_WM_USER + 4)
_PBM_STEPIT = (_WM_USER + 5)
_PBM_SETRANGE32 = (_WM_USER + 6)
_PBM_GETRANGE = (_WM_USER + 7)
_PBM_GETPOS = (_WM_USER + 8)
_PBM_SETBARCOLOR = (_WM_USER + 9)
_PBM_SETBKCOLOR = _CCM_SETBKCOLOR
_PBM_SETMARQUEE = (_WM_USER + 10)
)
var manifest = []byte(`<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity
version="1.0.0.0"
processorArchitecture="*"
name="CompanyName.ProductName.YourApplication"
type="win32"
/>
<description>Your application description here.</description>
<dependency>
<dependentAssembly>
<assemblyIdentity
type="win32"
name="Microsoft.Windows.Common-Controls"
version="6.0.0.0"
processorArchitecture="*"
publicKeyToken="6595b64144ccf1df"
language="*"
/>
</dependentAssembly>
</dependency>
</assembly>
`)