// Copyright 2010 The Walk Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // +build windows package walk import ( "bytes" "math" "math/big" "strconv" "strings" "syscall" "time" "github.com/lxn/win" ) var ( decimalSepB byte decimalSepUint16 uint16 decimalSepS string groupSepB byte groupSepUint16 uint16 groupSepS string ) func init() { AppendToWalkInit(func() { var buf [4]uint16 win.GetLocaleInfo(win.LOCALE_USER_DEFAULT, win.LOCALE_SDECIMAL, &buf[0], int32(len(buf))) decimalSepB = byte(buf[0]) decimalSepS = syscall.UTF16ToString(buf[0:1]) decimalSepUint16 = buf[0] win.GetLocaleInfo(win.LOCALE_USER_DEFAULT, win.LOCALE_STHOUSAND, &buf[0], int32(len(buf))) groupSepB = byte(buf[0]) groupSepS = syscall.UTF16ToString(buf[0:1]) groupSepUint16 = buf[0] }) } func maxi(a, b int) int { if a > b { return a } return b } func mini(a, b int) int { if a < b { return a } return b } func boolToInt(value bool) int { if value { return 1 } return 0 } func uint16IndexUint16(s []uint16, v uint16) int { for i, u := range s { if u == v { return i } } return -1 } func uint16ContainsUint16(s []uint16, v uint16) bool { return uint16IndexUint16(s, v) != -1 } func uint16CountUint16(s []uint16, v uint16) int { var count int for _, u := range s { if u == v { count++ } } return count } func uint16RemoveUint16(s []uint16, v uint16) []uint16 { count := uint16CountUint16(s, v) if count == 0 { return s } ret := make([]uint16, 0, len(s)-count) for _, u := range s { if u != v { ret = append(ret, u) } } return ret } func assertFloat64Or(value interface{}, defaultValue float64) float64 { if f, ok := value.(float64); ok { return f } return defaultValue } func assertIntOr(value interface{}, defaultValue int) int { if n, ok := value.(int); ok { return n } return defaultValue } func assertStringOr(value interface{}, defaultValue string) string { if s, ok := value.(string); ok { return s } return defaultValue } func assertTimeOr(value interface{}, defaultValue time.Time) time.Time { if t, ok := value.(time.Time); ok { return t } return defaultValue } func ParseFloat(s string) (float64, error) { s = strings.TrimSpace(s) t := FormatFloatGrouped(1000, 2) replaceSep := func(new string, index func(string, func(rune) bool) int) { i := index(t, func(r rune) bool { return r < '0' || r > '9' }) var sep string if i > -1 { sep = string(t[i]) } if sep != "" { s = strings.Replace(s, string(sep), new, -1) } } replaceSep("", strings.IndexFunc) replaceSep(".", strings.LastIndexFunc) return strconv.ParseFloat(s, 64) } func FormatFloat(f float64, prec int) string { return formatFloatString(strconv.FormatFloat(f, 'f', prec, 64), prec, false) } func FormatFloatGrouped(f float64, prec int) string { return formatFloatString(strconv.FormatFloat(f, 'f', maxi(1, prec), 64), prec, true) } func formatBigRat(r *big.Rat, prec int) string { return formatFloatString(r.FloatString(prec), prec, false) } func formatBigRatGrouped(r *big.Rat, prec int) string { return formatFloatString(r.FloatString(prec), prec, true) } func formatFloatString(s string, prec int, grouped bool) string { switch s { case "NaN", "-Inf", "+Inf": return s } s = strings.Replace(s, ".", decimalSepS, 1) if !grouped { return s } b := new(bytes.Buffer) var firstDigit int if len(s) > 0 && s[0] == '-' { firstDigit = 1 b.WriteByte('-') s = s[1:] } intLen := len(s) - maxi(1, prec) - 1 n := intLen % 3 if n != 0 { b.WriteString(s[:n]) } for i := n; i < intLen; i += 3 { if b.Len() > firstDigit { b.WriteByte(groupSepB) } b.WriteString(s[i : i+3]) } b.WriteString(s[intLen:]) s = b.String() if prec == 0 { s = s[:len(s)-2] } return s } func applyEnabledToDescendants(window Window, enabled bool) { wb := window.AsWindowBase() wb.applyEnabled(enabled) walkDescendants(window, func(w Window) bool { if w.Handle() == wb.hWnd { return true } if enabled && !w.AsWindowBase().enabled { return false } w.(applyEnableder).applyEnabled(enabled) return true }) } var seenInApplyFontToDescendantsDuringDPIChange map[*WindowBase]bool func applyFontToDescendants(window Window, font *Font) { wb := window.AsWindowBase() wb.applyFont(font) walkDescendants(window, func(w Window) bool { if w.Handle() == wb.hWnd { return true } if w.AsWindowBase().font != nil { return false } if seenInApplyFontToDescendantsDuringDPIChange != nil { wb := w.AsWindowBase() if seenInApplyFontToDescendantsDuringDPIChange[wb] { return true } seenInApplyFontToDescendantsDuringDPIChange[wb] = true } w.(applyFonter).applyFont(font) return true }) } func applySysColorsToDescendants(window Window) { wb := window.AsWindowBase() wb.ApplySysColors() walkDescendants(window, func(w Window) bool { if w.Handle() == wb.hWnd { return true } w.(ApplySysColorser).ApplySysColors() return true }) } var seenInApplyDPIToDescendantsDuringDPIChange map[*WindowBase]bool func applyDPIToDescendants(window Window, dpi int) { wb := window.AsWindowBase() wb.ApplyDPI(dpi) walkDescendants(window, func(w Window) bool { if w.Handle() == wb.hWnd { return true } if seenInApplyDPIToDescendantsDuringDPIChange != nil { wb := w.AsWindowBase() if seenInApplyDPIToDescendantsDuringDPIChange[wb] { return true } seenInApplyDPIToDescendantsDuringDPIChange[wb] = true } w.(ApplyDPIer).ApplyDPI(dpi) return true }) } func walkDescendants(window Window, f func(w Window) bool) { window = window.AsWindowBase().window if window == nil || !f(window) { return } var children []*WidgetBase switch w := window.(type) { case *NumberEdit: if w.edit != nil { children = append(children, w.edit.AsWidgetBase()) } case *TabWidget: for _, p := range w.Pages().items { children = append(children, p.AsWidgetBase()) } case Container: if c := w.Children(); c != nil { children = c.items } else { children = nil } } for _, wb := range children { walkDescendants(wb.window.(Widget), f) } } func less(a, b interface{}, order SortOrder) bool { if _, ok := a.(error); ok { _, bIsErr := b.(error) return order == SortAscending == !bIsErr } if _, ok := b.(error); ok { return order == SortDescending } if a == nil { return order == SortAscending == (b != nil) } if b == nil { return order == SortDescending } switch av := a.(type) { case string: if bv, ok := b.(string); ok { if order == SortAscending { return av < bv } else { return bv < av } } case int: if bv, ok := b.(int); ok { if order == SortAscending { return av < bv } else { return bv < av } } case float64: if bv, ok := b.(float64); ok { if order == SortAscending { return av < bv } else { return bv < av } } case float32: if bv, ok := b.(float32); ok { if order == SortAscending { return av < bv } else { return bv < av } } case int64: if bv, ok := b.(int64); ok { if order == SortAscending { return av < bv } else { return bv < av } } case int32: if bv, ok := b.(int32); ok { if order == SortAscending { return av < bv } else { return bv < av } } case int16: if bv, ok := b.(int16); ok { if order == SortAscending { return av < bv } else { return bv < av } } case int8: if bv, ok := b.(int8); ok { if order == SortAscending { return av < bv } else { return bv < av } } case uint: if bv, ok := b.(uint); ok { if order == SortAscending { return av < bv } else { return bv < av } } case uint64: if bv, ok := b.(uint64); ok { if order == SortAscending { return av < bv } else { return bv < av } } case uint32: if bv, ok := b.(uint32); ok { if order == SortAscending { return av < bv } else { return bv < av } } case uint16: if bv, ok := b.(uint16); ok { if order == SortAscending { return av < bv } else { return bv < av } } case uint8: if bv, ok := b.(uint8); ok { if order == SortAscending { return av < bv } else { return bv < av } } case time.Time: if bv, ok := b.(time.Time); ok { if order == SortAscending { return av.Before(bv) } else { return bv.Before(av) } } case bool: if bv, ok := b.(bool); ok { if order == SortAscending { return !av && bv } else { return !bv && av } } } return false } func dpiForHDC(hdc win.HDC) int { if hwnd := win.WindowFromDC(hdc); hwnd != 0 { return int(win.GetDpiForWindow(hwnd)) } return int(win.GetDeviceCaps(hdc, win.LOGPIXELSX)) } // IntFrom96DPI converts from 1/96" units to native pixels. func IntFrom96DPI(value, dpi int) int { return scaleInt(value, float64(dpi)/96.0) } // IntTo96DPI converts from native pixels to 1/96" units. func IntTo96DPI(value, dpi int) int { return scaleInt(value, 96.0/float64(dpi)) } func scaleInt(value int, scale float64) int { return int(math.Round(float64(value) * scale)) } // MarginsFrom96DPI converts from 1/96" units to native pixels. func MarginsFrom96DPI(value Margins, dpi int) Margins { return scaleMargins(value, float64(dpi)/96.0) } // MarginsTo96DPI converts from native pixels to 1/96" units. func MarginsTo96DPI(value Margins, dpi int) Margins { return scaleMargins(value, 96.0/float64(dpi)) } func scaleMargins(value Margins, scale float64) Margins { return Margins{ HNear: scaleInt(value.HNear, scale), VNear: scaleInt(value.VNear, scale), HFar: scaleInt(value.HFar, scale), VFar: scaleInt(value.VFar, scale), } } // PointFrom96DPI converts from 1/96" units to native pixels. func PointFrom96DPI(value Point, dpi int) Point { return scalePoint(value, float64(dpi)/96.0) } // PointTo96DPI converts from native pixels to 1/96" units. func PointTo96DPI(value Point, dpi int) Point { return scalePoint(value, 96.0/float64(dpi)) } func scalePoint(value Point, scale float64) Point { return Point{ X: scaleInt(value.X, scale), Y: scaleInt(value.Y, scale), } } // RectangleFrom96DPI converts from 1/96" units to native pixels. func RectangleFrom96DPI(value Rectangle, dpi int) Rectangle { return scaleRectangle(value, float64(dpi)/96.0) } // RectangleTo96DPI converts from native pixels to 1/96" units. func RectangleTo96DPI(value Rectangle, dpi int) Rectangle { return scaleRectangle(value, 96.0/float64(dpi)) } func scaleRectangle(value Rectangle, scale float64) Rectangle { return Rectangle{ X: scaleInt(value.X, scale), Y: scaleInt(value.Y, scale), Width: scaleInt(value.Width, scale), Height: scaleInt(value.Height, scale), } } // SizeFrom96DPI converts from 1/96" units to native pixels. func SizeFrom96DPI(value Size, dpi int) Size { return scaleSize(value, float64(dpi)/96.0) } // SizeTo96DPI converts from native pixels to 1/96" units. func SizeTo96DPI(value Size, dpi int) Size { return scaleSize(value, 96.0/float64(dpi)) } func scaleSize(value Size, scale float64) Size { return Size{ Width: scaleInt(value.Width, scale), Height: scaleInt(value.Height, scale), } }