📄 mclistbox.wgt
字号:
-foreground {foreground Foreground} \
-image {image Image} \
-label {label Label} \
-position {position Position} \
-labelrelief {labelrelief Labelrelief} \
-resizable {resizable Resizable} \
-visible {visible Visible} \
-width {width Width} \
]
# and likewise for item-specific stuff.
array set itemConfigureOptions [list \
-background {background Background} \
-foreground {foreground Foreground} \
-selectbackground {selectbackground SelectBackground} \
-selectforeground {selectforeground SelectForeground} \
]
# this defines the valid widget commands. It's important to
# list them here; we use this list to validate commands and
# expand abbreviations.
set widgetCommands [list \
activate bbox cget column configure \
curselection delete get index insert itemconfigure \
label nearest scan see selection \
size xview yview
]
set columnCommands [list add cget configure delete names nearest]
set labelCommands [list bind]
######################################################################
#- this initializes the option database. Kinda gross, but it works
#- (I think).
######################################################################
set packages [package names]
# why check for the Tk package? This lets us be sourced into
# an interpreter that doesn't have Tk loaded, such as the slave
# interpreter used by pkg_mkIndex. In theory it should have no
# side effects when run
if {[lsearch -exact [package names] "Tk"] != -1} {
# compute a widget name we can use to create a temporary widget
set tmpWidget ".__tmp__"
set count 0
while {[winfo exists $tmpWidget] == 1} {
set tmpWidget ".__tmp__$count"
incr count
}
# steal options from the listbox
# we want darn near all options, so we'll go ahead and do
# them all. No harm done in adding the one or two that we
# don't use.
listbox $tmpWidget
foreach foo [$tmpWidget configure] {
if {[llength $foo] == 5} {
set option [lindex $foo 1]
set value [lindex $foo 4]
option add *Mclistbox.$option $value widgetDefault
# these options also apply to the individual columns...
if {[string compare $option "foreground"] == 0 \
|| [string compare $option "background"] == 0 \
|| [string compare $option "font"] == 0} {
option add *Mclistbox*MclistboxColumn.$option $value \
widgetDefault
}
}
}
destroy $tmpWidget
# steal some options from label widgets; we only want a subset
# so we'll use a slightly different method. No harm in *not*
# adding in the one or two that we don't use... :-)
label $tmpWidget
foreach option [list Anchor Background Font \
Foreground Height Image ] {
set values [$tmpWidget configure -[string tolower $option]]
option add *Mclistbox.label$option [lindex $values 3]
}
destroy $tmpWidget
# these are unique to us...
option add *Mclistbox.columnBorderWidth 0 widgetDefault
option add *Mclistbox.columnRelief flat widgetDefault
option add *Mclistbox.labelBorderWidth 1 widgetDefault
option add *Mclistbox.labelRelief raised widgetDefault
option add *Mclistbox.labels 1 widgetDefault
option add *Mclistbox.resizableColumns 1 widgetDefault
option add *Mclistbox.selectcommand {} widgetDefault
option add *Mclistbox.fillcolumn {} widgetDefault
# column options
option add *Mclistbox*MclistboxColumn.visible 1 widgetDefault
option add *Mclistbox*MclistboxColumn.resizable 1 widgetDefault
option add *Mclistbox*MclistboxColumn.position end widgetDefault
option add *Mclistbox*MclistboxColumn.label "" widgetDefault
option add *Mclistbox*MclistboxColumn.width 0 widgetDefault
option add *Mclistbox*MclistboxColumn.bitmap "" widgetDefault
option add *Mclistbox*MclistboxColumn.image "" widgetDefault
}
######################################################################
# define the class bindings
######################################################################
SetClassBindings
}
# ::mclistbox::mclistbox --
#
# This is the command that gets exported. It creates a new
# mclistbox widget.
#
# Arguments:
#
# w path of new widget to create
# args additional option/value pairs (eg: -background white, etc.)
#
# Results:
#
# It creates the widget and sets up all of the default bindings
#
# Returns:
#
# The name of the newly create widget
proc ::mclistbox::mclistbox {args} {
variable widgetOptions
# perform a one time initialization
if {![info exists widgetOptions]} {
__mclistbox_Setup
Init
}
# make sure we at least have a widget name
if {[llength $args] == 0} {
return -code error "wrong # args: should be \"mclistbox pathName ?options?\""
}
# ... and make sure a widget doesn't already exist by that name
if {[winfo exists [lindex $args 0]]} {
return -code error "window name \"[lindex $args 0]\" already exists"
}
# and check that all of the args are valid
foreach {name value} [lrange $args 1 end] {
Canonize [lindex $args 0] option $name
}
# build it...
set w [eval Build $args]
# set some bindings...
SetBindings $w
# and we are done!
return $w
}
# ::mclistbox::Build --
#
# This does all of the work necessary to create the basic
# mclistbox.
#
# Arguments:
#
# w widget name
# args additional option/value pairs
#
# Results:
#
# Creates a new widget with the given name. Also creates a new
# namespace patterened after the widget name, as a child namespace
# to ::mclistbox
#
# Returns:
#
# the name of the widget
proc ::mclistbox::Build {w args} {
variable widgetOptions
# create the namespace for this instance, and define a few
# variables
namespace eval ::mclistbox::$w {
variable options
variable widgets
variable misc
}
# this gives us access to the namespace variables within
# this proc
upvar ::mclistbox::${w}::widgets widgets
upvar ::mclistbox::${w}::options options
upvar ::mclistbox::${w}::misc misc
# initially we start out with no columns
set misc(columns) {}
# this is our widget -- a frame of class Mclistbox. Naturally,
# it will contain other widgets. We create it here because
# we need it to be able to set our default options.
set widgets(this) [frame $w -class Mclistbox -takefocus 1]
# this defines all of the default options. We get the
# values from the option database. Note that if an array
# value is a list of length one it is an alias to another
# option, so we just ignore it
foreach name [array names widgetOptions] {
if {[llength $widgetOptions($name)] == 1} continue
set optName [lindex $widgetOptions($name) 0]
set optClass [lindex $widgetOptions($name) 1]
set options($name) [option get $w $optName $optClass]
}
# now apply any of the options supplied on the command
# line. This may overwrite our defaults, which is OK
if {[llength $args] > 0} {
array set options $args
}
# the columns all go into a text widget since it has the
# ability to scroll.
set widgets(text) [text $w.text \
-width 0 \
-height 0 \
-padx 0 \
-pady 0 \
-wrap none \
-borderwidth 0 \
-highlightthickness 0 \
-takefocus 0 \
-cursor {} \
]
$widgets(text) configure -state disabled
# here's the tricky part (shhhh... don't tell anybody!)
# we are going to create column that completely fills
# the base frame. We will use it to control the sizing
# of the widget. The trick is, we'll pack it in the frame
# and then place the text widget over it so it is never
# seen.
set columnWidgets [NewColumn $w {__hidden__}]
set widgets(hiddenFrame) [lindex $columnWidgets 0]
set widgets(hiddenListbox) [lindex $columnWidgets 1]
set widgets(hiddenLabel) [lindex $columnWidgets 2]
# by default geometry propagation is turned off, but for this
# super-secret widget we want it turned on. The idea is, we
# resize the listbox which resizes the frame which resizes the
# whole shibang.
pack propagate $widgets(hiddenFrame) on
pack $widgets(hiddenFrame) -side top -fill both -expand y
place $widgets(text) -x 0 -y 0 -relwidth 1.0 -relheight 1.0
raise $widgets(text)
# we will later rename the frame's widget proc to be our
# own custom widget proc. We need to keep track of this
# new name, so we'll define and store it here...
set widgets(frame) ::mclistbox::${w}::$w
# this moves the original frame widget proc into our
# namespace and gives it a handy name
rename ::$w $widgets(frame)
# now, create our widget proc. Obviously (?) it goes in
# the global namespace. All mclistbox widgets will actually
# share the same widget proc to cut down on the amount of
# bloat.
proc ::$w {command args} \
"eval ::mclistbox::WidgetProc {$w} \$command \$args"
# ok, the thing exists... let's do a bit more configuration.
if {[catch "Configure $widgets(this) [array get options]" error]} {
catch {destroy $w}
}
# and be prepared to handle selections.. (this, for -exportselection
# support)
selection handle $w [list ::mclistbox::SelectionHandler $w get]
return $w
}
# ::mclistbox::SelectionHandler --
#
# handle reqests to set or retrieve the primary selection. This is
# the "guts" of the implementation of the -exportselection option.
# What a pain! Note that this command is *not* called as a result
# of the widget's "selection" command, but rather as a result of
# the global selection being set or cleared.
#
# If I read the ICCCM correctly (which is doubtful; who has time to
# read that thing thoroughly?), this should return each row as a tab
# separated list of values, and the whole as a newline separated
# list of rows.
#
# Arguments:
#
# w pathname of the widget
# type one of "own", "lose" or "get"
# offset only used if type is "get"; offset into the selection
# buffer where the returned data should begin
# length number of bytes to return
#
proc ::mclistbox::SelectionHandler {w type {offset ""} {length ""}} {
upvar ::mclistbox::${w}::options options
upvar ::mclistbox::${w}::misc misc
upvar ::mclistbox::${w}::widgets widgets
switch -exact $type {
own {
selection own \
-command [list ::mclistbox::SelectionHandler $w lose] \
-selection PRIMARY \
$w
}
lose {
if {$options(-exportselection)} {
foreach id $misc(columns) {
$widgets(listbox$id) selection clear 0 end
}
}
}
get {
set end [expr {$length + $offset - 1}]
set column [lindex $misc(columns) 0]
set curselection [$widgets(listbox$column) curselection]
# this is really, really slow (relatively speaking).
# but the only way I can think of to speed this up
# is to duplicate all the data in our hidden listbox,
# which I really don't want to do because of memory
# considerations.
set data ""
foreach index $curselection {
set rowdata [join [::mclistbox::WidgetProc-get $w $index] "\t"]
lappend data $rowdata
}
set data [join $data "\n"]
return [string range $data $offset $end]
}
}
}
# ::mclistbox::convert --
#
# public routine to convert %x, %y and %W binding substitutions.
# Given an x, y and or %W value relative to a given widget, this
# routine will convert the values to be relative to the mclistbox
# widget. For example, it could be used in a binding like this:
#
# bind .mclistbox <blah> {doSomething [::mclistbox::convert %W -x %x]}
#
# Note that this procedure is *not* exported, but is indented for
# public use. It is not exported because the name could easily
# clash with existing commands.
#
# Arguments:
#
# w a widget path; typically the actual result of a %W
# substitution in a binding. It should be either a
# mclistbox widget or one of its subwidgets
#
# args should one or more of the following arguments or
# pairs of arguments:
#
# -x <x> will convert the value <x>; typically <x> will
# be the result of a %x substitution
# -y <y> will convert the value <y>; typically <y> will
# be the result of a %y substitution
# -W (or -w) will return the name of the mclistbox widget
# which is the parent of $w
#
# Returns:
#
# a list of the requested values. For example, a single -w will
# result in a list of one items, the name of the mclistbox widget.
# Supplying "-x 10 -y 20 -W" (in any order) will return a list of
# three values: the converted x and y values, and the name of
# the mclistbox widget.
proc ::mclistbox::convert {w args} {
set result {}
if {![winfo exists $w]} {
return -code error "window \"$w\" doesn't exist"
}
while {[llength $args] > 0} {
set option [lindex $args 0]
set args [lrange $args 1 end]
switch -exact -- $option {
-x {
set value [lindex $args 0]
set args [lrange $args 1 end]
set win $w
while {[winfo class $win] != "Mclistbox"} {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -