// 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) }