487 lines
9.7 KiB
Go
487 lines
9.7 KiB
Go
|
// Copyright 2013 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 (
|
||
|
"syscall"
|
||
|
"unsafe"
|
||
|
|
||
|
"github.com/lxn/win"
|
||
|
)
|
||
|
|
||
|
// TableViewColumn represents a column in a TableView.
|
||
|
type TableViewColumn struct {
|
||
|
tv *TableView
|
||
|
name string
|
||
|
dataMember string
|
||
|
alignment Alignment1D
|
||
|
format string
|
||
|
precision int
|
||
|
title string
|
||
|
titleOverride string
|
||
|
width int
|
||
|
lessFunc func(i, j int) bool
|
||
|
formatFunc func(value interface{}) string
|
||
|
visible bool
|
||
|
frozen bool
|
||
|
}
|
||
|
|
||
|
// NewTableViewColumn returns a new TableViewColumn.
|
||
|
func NewTableViewColumn() *TableViewColumn {
|
||
|
return &TableViewColumn{
|
||
|
format: "%v",
|
||
|
visible: true,
|
||
|
width: 50,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Alignment returns the alignment of the TableViewColumn.
|
||
|
func (tvc *TableViewColumn) Alignment() Alignment1D {
|
||
|
return tvc.alignment
|
||
|
}
|
||
|
|
||
|
// SetAlignment sets the alignment of the TableViewColumn.
|
||
|
func (tvc *TableViewColumn) SetAlignment(alignment Alignment1D) (err error) {
|
||
|
if alignment == AlignDefault {
|
||
|
alignment = AlignNear
|
||
|
}
|
||
|
|
||
|
if alignment == tvc.alignment {
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
old := tvc.alignment
|
||
|
defer func() {
|
||
|
if err != nil {
|
||
|
tvc.alignment = old
|
||
|
}
|
||
|
}()
|
||
|
|
||
|
tvc.alignment = alignment
|
||
|
|
||
|
return tvc.update()
|
||
|
}
|
||
|
|
||
|
// DataMember returns the data member this TableViewColumn is bound against.
|
||
|
func (tvc *TableViewColumn) DataMember() string {
|
||
|
return tvc.dataMember
|
||
|
}
|
||
|
|
||
|
// DataMemberEffective returns the effective data member this TableViewColumn is
|
||
|
// bound against.
|
||
|
func (tvc *TableViewColumn) DataMemberEffective() string {
|
||
|
if tvc.dataMember != "" {
|
||
|
return tvc.dataMember
|
||
|
}
|
||
|
|
||
|
return tvc.name
|
||
|
}
|
||
|
|
||
|
// SetDataMember sets the data member this TableViewColumn is bound against.
|
||
|
func (tvc *TableViewColumn) SetDataMember(dataMember string) {
|
||
|
tvc.dataMember = dataMember
|
||
|
}
|
||
|
|
||
|
// Format returns the format string for converting a value into a string.
|
||
|
func (tvc *TableViewColumn) Format() string {
|
||
|
return tvc.format
|
||
|
}
|
||
|
|
||
|
// SetFormat sets the format string for converting a value into a string.
|
||
|
func (tvc *TableViewColumn) SetFormat(format string) (err error) {
|
||
|
if format == tvc.format {
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
old := tvc.format
|
||
|
defer func() {
|
||
|
if err != nil {
|
||
|
tvc.format = old
|
||
|
}
|
||
|
}()
|
||
|
|
||
|
tvc.format = format
|
||
|
|
||
|
if tvc.tv == nil {
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
return tvc.tv.Invalidate()
|
||
|
}
|
||
|
|
||
|
// Name returns the name of this TableViewColumn.
|
||
|
func (tvc *TableViewColumn) Name() string {
|
||
|
return tvc.name
|
||
|
}
|
||
|
|
||
|
// SetName sets the name of this TableViewColumn.
|
||
|
func (tvc *TableViewColumn) SetName(name string) {
|
||
|
tvc.name = name
|
||
|
}
|
||
|
|
||
|
// Precision returns the number of decimal places for formatting float32,
|
||
|
// float64 or big.Rat values.
|
||
|
func (tvc *TableViewColumn) Precision() int {
|
||
|
return tvc.precision
|
||
|
}
|
||
|
|
||
|
// SetPrecision sets the number of decimal places for formatting float32,
|
||
|
// float64 or big.Rat values.
|
||
|
func (tvc *TableViewColumn) SetPrecision(precision int) (err error) {
|
||
|
if precision == tvc.precision {
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
old := tvc.precision
|
||
|
defer func() {
|
||
|
if err != nil {
|
||
|
tvc.precision = old
|
||
|
}
|
||
|
}()
|
||
|
|
||
|
tvc.precision = precision
|
||
|
|
||
|
if tvc.tv == nil {
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
return tvc.tv.Invalidate()
|
||
|
}
|
||
|
|
||
|
// Title returns the (default) text to display in the column header.
|
||
|
func (tvc *TableViewColumn) Title() string {
|
||
|
return tvc.title
|
||
|
}
|
||
|
|
||
|
// SetTitle sets the (default) text to display in the column header.
|
||
|
func (tvc *TableViewColumn) SetTitle(title string) (err error) {
|
||
|
if title == tvc.title {
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
old := tvc.title
|
||
|
defer func() {
|
||
|
if err != nil {
|
||
|
tvc.title = old
|
||
|
}
|
||
|
}()
|
||
|
|
||
|
tvc.title = title
|
||
|
|
||
|
return tvc.update()
|
||
|
}
|
||
|
|
||
|
// TitleOverride returns the (overridden by user) text to display in the column
|
||
|
// header.
|
||
|
func (tvc *TableViewColumn) TitleOverride() string {
|
||
|
return tvc.titleOverride
|
||
|
}
|
||
|
|
||
|
// SetTitleOverride sets the (overridden by user) text to display in the column
|
||
|
// header.
|
||
|
func (tvc *TableViewColumn) SetTitleOverride(titleOverride string) (err error) {
|
||
|
if titleOverride == tvc.titleOverride {
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
old := tvc.titleOverride
|
||
|
defer func() {
|
||
|
if err != nil {
|
||
|
tvc.titleOverride = old
|
||
|
}
|
||
|
}()
|
||
|
|
||
|
tvc.titleOverride = titleOverride
|
||
|
|
||
|
return tvc.update()
|
||
|
}
|
||
|
|
||
|
// TitleEffective returns the effective text to display in the column header.
|
||
|
func (tvc *TableViewColumn) TitleEffective() string {
|
||
|
if tvc.titleOverride != "" {
|
||
|
return tvc.titleOverride
|
||
|
}
|
||
|
|
||
|
if tvc.title != "" {
|
||
|
return tvc.title
|
||
|
}
|
||
|
|
||
|
return tvc.DataMemberEffective()
|
||
|
}
|
||
|
|
||
|
// Visible returns if the column is visible.
|
||
|
func (tvc *TableViewColumn) Visible() bool {
|
||
|
return tvc.visible
|
||
|
}
|
||
|
|
||
|
// SetVisible sets if the column is visible.
|
||
|
func (tvc *TableViewColumn) SetVisible(visible bool) (err error) {
|
||
|
if visible == tvc.visible {
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
old := tvc.visible
|
||
|
defer func() {
|
||
|
if err != nil {
|
||
|
tvc.visible = old
|
||
|
}
|
||
|
}()
|
||
|
|
||
|
tvc.visible = visible
|
||
|
|
||
|
if tvc.tv == nil {
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
if visible {
|
||
|
return tvc.create()
|
||
|
}
|
||
|
|
||
|
return tvc.destroy()
|
||
|
}
|
||
|
|
||
|
// Frozen returns if the column is frozen.
|
||
|
func (tvc *TableViewColumn) Frozen() bool {
|
||
|
return tvc.frozen
|
||
|
}
|
||
|
|
||
|
// SetFrozen sets if the column is frozen.
|
||
|
func (tvc *TableViewColumn) SetFrozen(frozen bool) (err error) {
|
||
|
if frozen == tvc.frozen {
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
var checkBoxes bool
|
||
|
if tvc.tv != nil {
|
||
|
checkBoxes = tvc.tv.CheckBoxes()
|
||
|
}
|
||
|
|
||
|
old := tvc.frozen
|
||
|
defer func() {
|
||
|
if err != nil {
|
||
|
tvc.frozen = old
|
||
|
|
||
|
if tvc.tv != nil {
|
||
|
tvc.create()
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if tvc.tv != nil {
|
||
|
tvc.tv.hasFrozenColumn = tvc.tv.visibleFrozenColumnCount() > 0
|
||
|
tvc.tv.SetCheckBoxes(checkBoxes)
|
||
|
tvc.tv.applyImageList()
|
||
|
}
|
||
|
}()
|
||
|
|
||
|
if tvc.tv != nil {
|
||
|
if err = tvc.destroy(); err != nil {
|
||
|
return
|
||
|
}
|
||
|
}
|
||
|
|
||
|
tvc.frozen = frozen
|
||
|
|
||
|
if tvc.tv != nil {
|
||
|
return tvc.create()
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// Width returns the width of the column in pixels.
|
||
|
func (tvc *TableViewColumn) Width() int {
|
||
|
if tvc.tv == nil || !tvc.visible {
|
||
|
return tvc.width
|
||
|
}
|
||
|
|
||
|
// We call win.SendMessage instead of tvc.sendMessage here, because some
|
||
|
// call inside the latter interferes with scrolling via scroll bar button
|
||
|
// when *TableViewColumn.Width is called from *TableView.StretchLastColumn.
|
||
|
var hwnd win.HWND
|
||
|
if tvc.frozen {
|
||
|
hwnd = tvc.tv.hwndFrozenLV
|
||
|
} else {
|
||
|
hwnd = tvc.tv.hwndNormalLV
|
||
|
}
|
||
|
|
||
|
return tvc.tv.IntTo96DPI(int(win.SendMessage(hwnd, win.LVM_GETCOLUMNWIDTH, uintptr(tvc.indexInListView()), 0)))
|
||
|
}
|
||
|
|
||
|
// SetWidth sets the width of the column in pixels.
|
||
|
func (tvc *TableViewColumn) SetWidth(width int) (err error) {
|
||
|
if width == tvc.width {
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
old := tvc.width
|
||
|
defer func() {
|
||
|
if err != nil {
|
||
|
tvc.width = old
|
||
|
}
|
||
|
}()
|
||
|
|
||
|
tvc.width = width
|
||
|
|
||
|
return tvc.update()
|
||
|
}
|
||
|
|
||
|
// LessFunc returns the less func of this TableViewColumn.
|
||
|
//
|
||
|
// This function is used to provide custom sorting for models based on ReflectTableModel only.
|
||
|
func (tvc *TableViewColumn) LessFunc() func(i, j int) bool {
|
||
|
return tvc.lessFunc
|
||
|
}
|
||
|
|
||
|
// SetLessFunc sets the less func of this TableViewColumn.
|
||
|
//
|
||
|
// This function is used to provide custom sorting for models based on ReflectTableModel only.
|
||
|
func (tvc *TableViewColumn) SetLessFunc(lessFunc func(i, j int) bool) {
|
||
|
tvc.lessFunc = lessFunc
|
||
|
}
|
||
|
|
||
|
// FormatFunc returns the custom format func of this TableViewColumn.
|
||
|
func (tvc *TableViewColumn) FormatFunc() func(value interface{}) string {
|
||
|
return tvc.formatFunc
|
||
|
}
|
||
|
|
||
|
// FormatFunc sets the custom format func of this TableViewColumn.
|
||
|
func (tvc *TableViewColumn) SetFormatFunc(formatFunc func(value interface{}) string) {
|
||
|
tvc.formatFunc = formatFunc
|
||
|
}
|
||
|
|
||
|
func (tvc *TableViewColumn) indexInListView() int32 {
|
||
|
if tvc.tv == nil {
|
||
|
return -1
|
||
|
}
|
||
|
|
||
|
var idx int32
|
||
|
|
||
|
for _, c := range tvc.tv.columns.items {
|
||
|
if c.frozen != tvc.frozen {
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
if c == tvc {
|
||
|
break
|
||
|
}
|
||
|
|
||
|
if c.visible {
|
||
|
idx++
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return idx
|
||
|
}
|
||
|
|
||
|
func (tvc *TableViewColumn) create() error {
|
||
|
var lvc win.LVCOLUMN
|
||
|
|
||
|
index := tvc.indexInListView()
|
||
|
|
||
|
dpi := tvc.tv.DPI()
|
||
|
lvc.Mask = win.LVCF_FMT | win.LVCF_WIDTH | win.LVCF_TEXT | win.LVCF_SUBITEM
|
||
|
lvc.ISubItem = index
|
||
|
lvc.PszText = syscall.StringToUTF16Ptr(tvc.TitleEffective())
|
||
|
if tvc.width > 0 {
|
||
|
lvc.Cx = int32(IntFrom96DPI(tvc.width, dpi))
|
||
|
} else {
|
||
|
lvc.Cx = int32(IntFrom96DPI(100, dpi))
|
||
|
}
|
||
|
|
||
|
switch tvc.alignment {
|
||
|
case AlignCenter:
|
||
|
lvc.Fmt = 2
|
||
|
|
||
|
case AlignFar:
|
||
|
lvc.Fmt = 1
|
||
|
}
|
||
|
|
||
|
if -1 == int(tvc.sendMessage(win.LVM_INSERTCOLUMN, uintptr(index), uintptr(unsafe.Pointer(&lvc)))) {
|
||
|
return newError("LVM_INSERTCOLUMN")
|
||
|
}
|
||
|
|
||
|
tvc.tv.updateLVSizes()
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func (tvc *TableViewColumn) destroy() error {
|
||
|
width := tvc.Width()
|
||
|
|
||
|
if win.FALSE == tvc.sendMessage(win.LVM_DELETECOLUMN, uintptr(tvc.indexInListView()), 0) {
|
||
|
return newError("LVM_DELETECOLUMN")
|
||
|
}
|
||
|
|
||
|
tvc.width = width
|
||
|
|
||
|
tvc.tv.updateLVSizes()
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func (tvc *TableViewColumn) update() error {
|
||
|
if tvc.tv == nil || !tvc.visible {
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
lvc := tvc.getLVCOLUMN()
|
||
|
|
||
|
if win.FALSE == tvc.sendMessage(win.LVM_SETCOLUMN, uintptr(tvc.indexInListView()), uintptr(unsafe.Pointer(lvc))) {
|
||
|
return newError("LVM_SETCOLUMN")
|
||
|
}
|
||
|
|
||
|
tvc.tv.updateLVSizes()
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func (tvc *TableViewColumn) getLVCOLUMN() *win.LVCOLUMN {
|
||
|
var lvc win.LVCOLUMN
|
||
|
|
||
|
dpi := 96
|
||
|
if tvc.tv != nil {
|
||
|
dpi = tvc.tv.DPI()
|
||
|
} else {
|
||
|
dpi = screenDPI()
|
||
|
}
|
||
|
width := IntFrom96DPI(tvc.width, dpi)
|
||
|
|
||
|
lvc.Mask = win.LVCF_FMT | win.LVCF_WIDTH | win.LVCF_TEXT | win.LVCF_SUBITEM
|
||
|
lvc.ISubItem = int32(tvc.indexInListView())
|
||
|
lvc.PszText = syscall.StringToUTF16Ptr(tvc.TitleEffective())
|
||
|
lvc.Cx = int32(width)
|
||
|
|
||
|
switch tvc.alignment {
|
||
|
case AlignCenter:
|
||
|
lvc.Fmt = 2
|
||
|
|
||
|
case AlignFar:
|
||
|
lvc.Fmt = 1
|
||
|
}
|
||
|
|
||
|
return &lvc
|
||
|
}
|
||
|
|
||
|
func (tvc *TableViewColumn) sendMessage(msg uint32, wp, lp uintptr) uintptr {
|
||
|
if tvc.tv == nil {
|
||
|
return 0
|
||
|
}
|
||
|
|
||
|
tvc.tv.hasFrozenColumn = tvc.tv.visibleFrozenColumnCount() > 0
|
||
|
tvc.tv.SetCheckBoxes(tvc.tv.CheckBoxes())
|
||
|
tvc.tv.applyImageList()
|
||
|
|
||
|
var hwnd win.HWND
|
||
|
if tvc.frozen {
|
||
|
hwnd = tvc.tv.hwndFrozenLV
|
||
|
} else {
|
||
|
hwnd = tvc.tv.hwndNormalLV
|
||
|
}
|
||
|
|
||
|
return win.SendMessage(hwnd, msg, wp, lp)
|
||
|
}
|