#============================================================================== # Contains the implementation of the multi-entry widget. # # Structure of the module: # - Namespace initialization # - Private procedure creating the default bindings # - Public procedure creating a new mentry widget # - Private configuration procedures # - Private procedures implementing the mentry widget command # - Private callback procedures # - Private procedures used in bindings # - Private utility procedures # # Copyright (c) 1999-2023 Csaba Nemethi (E-mail: csaba.nemethi@t-online.de) #============================================================================== # # Namespace initialization # ======================== # namespace eval mentry { # # Get the windowing system ("x11", "win32", "classic", or "aqua") # variable winSys [::mwutil::windowingSystem] # # Create aliases for a few tile commands if not yet present # proc createTileAliases {} { if {[string compare [interp alias {} ::mentry::style] ""] != 0} { return "" } if {[string length [info commands ::ttk::style]] == 0} { interp alias {} ::mentry::style {} ::style if {[string compare $::tile::version "0.7"] >= 0} { interp alias {} ::mentry::styleConfig {} ::style configure } else { interp alias {} ::mentry::styleConfig {} ::style default } interp alias {} ::mentry::getThemes {} ::tile::availableThemes interp alias {} ::mentry::setTheme {} ::tile::setTheme interp alias {} ::mentry::tileqt_kdeStyleChangeNotification \ {} ::tile::theme::tileqt::kdeStyleChangeNotification interp alias {} ::mentry::tileqt_currentThemeName \ {} ::tile::theme::tileqt::currentThemeName interp alias {} ::mentry::tileqt_currentThemeColour \ {} ::tile::theme::tileqt::currentThemeColour } else { interp alias {} ::mentry::style {} ::ttk::style interp alias {} ::mentry::styleConfig {} ::ttk::style configure interp alias {} ::mentry::getThemes {} ::ttk::themes interp alias {} ::mentry::setTheme {} ::ttk::setTheme interp alias {} ::mentry::tileqt_kdeStyleChangeNotification \ {} ::ttk::theme::tileqt::kdeStyleChangeNotification interp alias {} ::mentry::tileqt_currentThemeName \ {} ::ttk::theme::tileqt::currentThemeName interp alias {} ::mentry::tileqt_currentThemeColour \ {} ::ttk::theme::tileqt::currentThemeColour } interp alias {} ::mentry::getCurrentTheme {} ::mwutil::currentTheme } variable currentTheme [::mwutil::currentTheme] if {[string length $currentTheme] != 0} { createTileAliases } variable widgetStyle "" variable colorScheme "" if {[string compare $currentTheme "tileqt"] == 0} { set widgetStyle [tileqt_currentThemeName] if {[info exists ::env(KDE_SESSION_VERSION)] && [string length $::env(KDE_SESSION_VERSION)] != 0} { set colorScheme [getKdeConfigVal "General" "ColorScheme"] } else { set colorScheme [getKdeConfigVal "KDE" "colorScheme"] } } variable newAquaSupport [expr { ($::tk_version == 8.6 && [package vcompare $::tk_patchLevel "8.6.10"] >= 0) || ($::tk_version >= 8.7 && [package vcompare $::tk_patchLevel "8.7a3"] >= 0)}] variable extendedAquaSupport \ [expr {[lsearch -exact [image types] "nsimage"] >= 0}] variable uniformWheelSupport [expr {$::tk_version >= 8.7 && [package vcompare $::tk_patchLevel "8.7a4"] >= 0}] # # The array configSpecs is used to handle configuration options. The # names of its elements are the configuration options for the Mentry widget # class. The value of an array element is either an alias name or a list # containing the database name and class as well as an indicator specifying # the widgets to which the option applies: c stands for all children # (entries and labels), e for the entries only, h for the hull, and w for # the widget itself. # # Command-Line Name {Database Name Database Class W} # ----------------------------------------------------------------------- # variable configSpecs array set configSpecs { -background {background Background e} -bg -background -body {body Body w} -borderwidth {borderWidth BorderWidth h} -bd -borderwidth -cursor {cursor Cursor c} -disabledbackground {disabledBackground DisabledBackground e} -disabledforeground {disabledForeground DisabledForeground c} -exportselection {exportSelection ExportSelection e} -font {font Font c} -foreground {foreground Foreground c} -fg -foreground -highlightbackground {highlightBackground HighlightBackground h} -highlightcolor {highlightColor HighlightColor h} -highlightthickness {highlightThickness HighlightThickness h} -insertbackground {insertBackground Foreground e} -insertborderwidth {insertBorderWidth BorderWidth e} -insertofftime {insertOffTime OffTime e} -insertontime {insertOnTime OnTime e} -insertwidth {insertWidth InsertWidth e} -invalidcommand {invalidCommand InvalidCommand e} -invcmd -invalidcommand -justify {justify Justify e} -readonlybackground {readonlyBackground ReadonlyBackground e} -relief {relief Relief h} -selectbackground {selectBackground Foreground e} -selectborderwidth {selectBorderWidth BorderWidth e} -selectforeground {selectForeground Background e} -show {show Show e} -state {state State e} -takefocus {takeFocus TakeFocus h} -textvariable {textVariable Variable e} -validate {validate Validate e} -validatecommand {validateCommand ValidateCommand e} -vcmd -validatecommand } # # Extend the elements of the array configSpecs # proc extendConfigSpecs {} { variable helpEntry variable usingTile variable configSpecs # # Append the default values of the configuration options # of an invisible entry widget to the values of the # corresponding elements of the array configSpecs # set helpEntry .__helpEntry for {set n 0} {[winfo exists $helpEntry]} {incr n} { set helpEntry .__helpEntry$n } if {$usingTile} { foreach opt {-borderwidth -bd -disabledbackground -disabledforeground -highlightbackground -highlightcolor -highlightthickness -insertbackground -insertborderwidth -insertofftime -insertontime -insertwidth -readonlybackground -relief -selectbackground -selectborderwidth -selectforeground} { unset configSpecs($opt) } # # Append theme-specific values to some # elements of the array configSpecs # variable currentTheme if {[string compare $currentTheme "aqua"] == 0} { variable newAquaSupport scan $::tcl_platform(osVersion) "%d" majorOSVersion if {$newAquaSupport && $majorOSVersion >= 18} { ;# OS X 10.14+ update idletasks ;# needed for the isdark query } } elseif {[string compare $currentTheme "tileqt"] == 0} { tileqt_kdeStyleChangeNotification } setThemeDefaults # # Append theme-independent values to some # other elements of the array configSpecs # lappend configSpecs(-takefocus) {} catch {lappend configSpecs(-cursor) [ttk::cursor text]} ttk::entry $helpEntry -takefocus 0 } else { if {$::tk_version < 8.3} { foreach opt {-invalidcommand -invcmd -validate -validatecommand -vcmd} { unset configSpecs($opt) } } if {$::tk_version < 8.4} { foreach opt {-disabledbackground -disabledforeground -readonlybackground} { unset configSpecs($opt) } } tk::entry $helpEntry -takefocus 0 } foreach configSet [$helpEntry configure] { if {[llength $configSet] != 2} { set opt [lindex $configSet 0] if {[info exists configSpecs($opt)] && [llength $configSpecs($opt)] == 3} { lappend configSpecs($opt) [lindex $configSet 3] } elseif {[string compare $opt "-width"] == 0} { lappend configSpecs(-body) [lindex $configSet 3] } } } } extendConfigSpecs variable configOpts [lsort [array names configSpecs]] # # Use a list to facilitate the handling of the command options # variable cmdOpts [list \ adjustentry attrib cget clear configure entries entrycount entrylimit \ entrypath getarray getlist getstring hasattrib isempty isfull \ labelcount labelpath labels put setentryextrawidth setentryfont \ setentrywidth unsetattrib] } # # Private procedure creating the default bindings # =============================================== # #------------------------------------------------------------------------------ # mentry::createBindings # # Creates the default bindings for the binding tags Mentry, MentryMain, # MentryKeyNav, MentryEntry, and MentryLabel. #------------------------------------------------------------------------------ proc mentry::createBindings {} { # # Define some Mentry class bindings # bind Mentry continue bind Mentry { if {[string compare [focus -lastfor %W] %W] == 0} { catch {mentry::tabToEntry [mentry::firstNormal %W]} } } bind Mentry { namespace delete ::mentry::ns%W catch {rename ::%W ""} } variable usingTile if {$usingTile} { bind Mentry { after idle [list mentry::updateLabelForegrounds %W 1] } bind Mentry { after idle [list mentry::updateLabelForegrounds %W 0] } } bind Mentry <> { if {[string compare %d "FontChanged"] == 0} { mentry::updateFonts %W } } # # Define some bindings for the binding tag MentryMain # bindtags . [linsert [bindtags .] 1 MentryMain] bind MentryMain <> { after idle mentry::handleThemeChangedEvent } variable winSys variable newAquaSupport if {$usingTile && [string compare $winSys "aqua"] == 0 && $newAquaSupport} { foreach event {<> <>} { bind MentryMain $event { if {![info exists mentry::appearanceId]} { set mentry::appearanceId \ [after 0 mentry::handleAppearanceEvent] } } } } # # Define the binding tag MentryKeyNav # mwutil::defineKeyNav Mentry # # Define some bindings for the binding tag MentryEntry # variable entryClicked 0 bind MentryEntry { set mentry::entryClicked 1 } bind MentryEntry { set mentry::entryClicked 0 } bind MentryEntry { mentry::tabToPrev %W } bind MentryEntry { mentry::tabToNext %W } bind MentryEntry { mentry::goToHome %W } bind MentryEntry { mentry::goToEnd %W } bind MentryEntry { mentry::selectToHome %W } bind MentryEntry { mentry::selectToEnd %W } bind MentryEntry { mentry::backSpace %W } bind MentryEntry { mentry::procLabelChars %W %A } if {$usingTile} { # # Define some bindings for the binding tag # MentryEntry, needed for the aqua theme # bind MentryEntry { mentry::[winfo parent [winfo parent %W]] state focus } bind MentryEntry { mentry::[winfo parent [winfo parent %W]] state !focus } } # # Define some emacs-like key bindings for the binding tag MentryEntry # bind MentryEntry { if {!$tk_strictMotif} { mentry::tabToPrev %W } } bind MentryEntry { if {!$tk_strictMotif} { mentry::tabToNext %W } } bind MentryEntry { if {!$tk_strictMotif} { mentry::goToHome %W } } bind MentryEntry { if {!$tk_strictMotif} { mentry::goToEnd %W } } bind MentryEntry { if {!$tk_strictMotif} { mentry::backSpace %W } } bind MentryEntry { if {!$tk_strictMotif} { %W delete insert end break } } bind MentryEntry { if {!$tk_strictMotif} { mentry::delToLeft %W } } bind MentryEntry { if {!$tk_strictMotif} { mentry::delToLeft %W } } # # Define some bindings for the binding tag MentryLabel # bind MentryLabel { mentry::labelButton1 %W } } # # Public procedure creating a new mentry widget # ============================================= # #------------------------------------------------------------------------------ # mentry::mentry # # Creates a new multi-entry widget whose name is specified as the first command- # line argument, and configures it according to the options and their values # given on the command line. Returns the name of the newly created widget. #------------------------------------------------------------------------------ proc mentry::mentry args { variable usingTile variable configSpecs variable configOpts if {[llength $args] == 0} { mwutil::wrongNumArgs "mentry pathName ?options?" } # # Create a hull (a frame or tile entry) of the class Mentry # set win [lindex $args 0] if {[catch { if {$usingTile} { ttk::entry $win -style Hull$win.TEntry -class Mentry } else { tk::frame $win -class Mentry -container 0 -height 0 -width 0 catch {$win configure -padx 0 -pady 0} } } result] != 0} { return -code error $result } # # Create a namespace within the current one to hold the data of the widget # namespace eval ns$win { # # The folowing array holds various data for this widget # variable data array set data { entryCount 0 labelCount 0 maxEntryIdx -1 maxLabelIdx -1 inActiveWin 1 } # # The following array is used to hold arbitrary # attributes and their values for this widget # variable attribs } # # Initialize some further components of data # upvar ::mentry::ns${win}::data data foreach opt $configOpts { set data($opt) [lindex $configSpecs($opt) 3] } if {$usingTile} { variable themeDefaults set data(themeDefaults) [array get themeDefaults] foreach opt {-disabledbackground -readonlybackground} { set data($opt) $themeDefaults($opt) } } # # Take into account that some scripts start by # destroying all children of the root window # variable helpEntry if {![winfo exists $helpEntry]} { if {$usingTile} { ttk::entry $helpEntry -takefocus 0 } else { tk::entry $helpEntry -takefocus 0 } } # # Configure the widget according to the command-line # arguments and to the available database options # if {[catch { mwutil::configureWidget $win configSpecs mentry::doConfig \ mentry::doCget [lrange $args 1 end] 1 } result] != 0} { destroy $win return -code error $result } # # Move the original widget command into the current namespace # and build a new widget procedure in the global one # rename ::$win $win interp alias {} ::$win {} mentry::mentryWidgetCmd $win return $win } # # Private configuration procedures # ================================ # #------------------------------------------------------------------------------ # mentry::doConfig # # Applies the value val of the configuration option opt to the mentry widget # win. #------------------------------------------------------------------------------ proc mentry::doConfig {win opt val} { variable usingTile variable helpEntry variable configSpecs upvar ::mentry::ns${win}::data data # # Apply the value to the widget(s) corresponding to the given option # switch [lindex $configSpecs($opt) 2] { c { # # Save the properly formatted value of val # in data($opt) and apply it to all children # $helpEntry configure $opt $val set val [$helpEntry cget $opt] set data($opt) $val foreach w [entries $win] { configEntry $w $opt $val } foreach w [labels $win] { $w configure $opt $val } # # Some options need special handling # variable themeDefaults if {[string compare $opt "-font"] == 0} { if {$usingTile} { adjustChildren $win } foreach name [array names data ?*-chars] { set index [lindex [split $name "-"] 0] foreach {chars1 chars2} $data($name) {} adjustentrySubCmd $win $index $chars1 $chars2 } foreach name [array names data ?*-width] { set index [lindex [split $name "-"] 0] setentrywidthSubCmd $win $index $data($name) } } elseif {[string compare $opt "-foreground"] == 0 && $usingTile && !$data(inActiveWin) && [string compare $val $themeDefaults($opt)] == 0} { foreach w [labels $win] { $w configure $opt $themeDefaults(-foreground,background) } } } e { if {[string compare $opt "-textvariable"] == 0 && [string length $val] != 0} { # # The text variable must be an array # global $val if {[info exists $val] && ![array exists $val]} { return -code error "variable \"$val\" isn't array" } # # For each entry child, set the -textvariable configuration # option of the entry to the corresponding array element # for {set n 0} {$n < $data(entryCount)} {incr n} { [entryPath $win $n] configure $opt ${val}($n) } set data($opt) $val } else { if {[string compare $opt "-state"] == 0} { set val [mwutil::fullOpt "state" $val \ {disabled normal readonly}] } # # Save the properly formatted value of val in # data($opt) and apply it to all entry children # $helpEntry configure $opt $val set val [$helpEntry cget $opt] set data($opt) $val if {[string compare $opt "-background"] == 0 && $usingTile} { # # Most themes support the -fieldbackground option for # the style element Entry.field. In Tk versions earlier # than 8.6.10, the aqua theme supported the -background # option instead. Some themes (like Arc, plastik, tileqt, # vista, and xpnative) don't support either of them. # variable currentTheme variable newAquaSupport if {[string compare $currentTheme "aqua"] == 0 && !$newAquaSupport} { styleConfig $win.TEntry -background $val } else { styleConfig $win.TEntry -fieldbackground $val } if {[winfo viewable $win]} { update idletasks ;# to avoid some artifacts on aqua } } else { foreach w [entries $win] { configEntry $w $opt $val } } # # Some options need special handling # if {[string compare $opt "-background"] == 0} { if {$::tk_version < 8.4} { set labelBg $val } else { switch $data(-state) { normal { set labelBg $val } disabled { set labelBg $data(-disabledbackground) } readonly { set labelBg $data(-readonlybackground) } } if {[string length $labelBg] == 0} { set labelBg $val } } foreach w [labels $win] { $w configure $opt $labelBg } # # Set also the hull's background, because # of the shadow colors of its 3-D border # if {$usingTile} { variable currentTheme variable newAquaSupport if {[string compare $currentTheme "aqua"] == 0 && !$newAquaSupport} { styleConfig Hull$win.TEntry -background $labelBg } else { styleConfig Hull$win.TEntry \ -fieldbackground $labelBg } if {[winfo viewable $win]} { update idletasks ;# to avoid some artifacts on aqua } } else { $win configure $opt $labelBg } } elseif {[regexp \ {^-(disabledbackground|readonlybackground|state)$} \ $opt] && $::tk_version >= 8.4} { switch $data(-state) { normal { set labelBg $data(-background) set labelState normal } disabled { set labelBg $data(-disabledbackground) set labelState disabled } readonly { set labelBg $data(-readonlybackground) set labelState normal } } if {[string length $labelBg] == 0} { set labelBg $data(-background) } foreach w [labels $win] { $w configure -background $labelBg -state $labelState } if {$usingTile} { $win configure -state $data(-state) } else { $win configure -background $labelBg } } } } h { # # Apply the value to the hull and save the # properly formatted value of val in data($opt) # $win configure $opt $val set data($opt) [$win cget $opt] } w { if {[string compare $opt "-body"] == 0} { createChildren $win $val } } } } #------------------------------------------------------------------------------ # mentry::doCget # # Returns the value of the configuration option opt for the mentry widget win. #------------------------------------------------------------------------------ proc mentry::doCget {win opt} { upvar ::mentry::ns${win}::data data return $data($opt) } #------------------------------------------------------------------------------ # mentry::createChildren # # For each pair given in the list body, the procedure creates an # entry of the given width and a label displaying the given text, and defines # some callbacks as well as bindings for the entry just created. #------------------------------------------------------------------------------ proc mentry::createChildren {win body} { variable winSys variable usingTile variable newAquaSupport variable themeDefaults variable configSpecs variable configOpts upvar ::mentry::ns${win}::data data # # Check the syntax of body before performing any changes # set argCount [llength $body] if {$argCount == 0} { return -code error "expected at least one entry child width" } foreach {width text} $body { if {[catch {format "%d" $width}] != 0 || $width <= 0} { return -code error "expected positive integer but got \"$width\"" } } # # Destroy any existing children of the hull # foreach w [winfo children $win] { if {[regexp {^(Frame|Entry|Label)$} [winfo class $w]]} { destroy $w } } set data(entryCount) [expr {($argCount + 1) / 2}] set data(labelCount) [expr {$argCount / 2}] set data(maxEntryIdx) [expr {$data(entryCount) - 1}] set data(maxLabelIdx) [expr {$data(labelCount) - 1}] if {$usingTile} { foreach {bd bd2 x deltaWidth} [geomParams] {} } foreach name [array names data ?*-chars] { unset data($name) } foreach name [array names data ?*-font] { unset data($name) } foreach name [array names data ?*-width] { unset data($name) } set data(-body) {} set n 0 foreach {width text} $body { # # Append the properly formatted value # of width to the list data(-body) # lappend data(-body) [format "%d" $width] # # Create an entry of the given width # within (a frame child of) the hull win # if {$usingTile} { set f [framePath $win $n] tk::frame $f -borderwidth 0 -container 0 -highlightthickness 0 \ -relief flat -takefocus 0 catch {$f configure -padx 0 -pady 0} set w $f.e ttk::entry $w -style $win.TEntry -takefocus 0 -textvariable "" \ -width $width } else { set w [entryPath $win $n] tk::entry $w -borderwidth 0 -highlightthickness 0 \ -takefocus 0 -textvariable "" -width $width } # # Apply to it the current configuration options # foreach opt $configOpts { if {[string compare $opt "-textvariable"] == 0 && [string length $data($opt)] != 0} { upvar data($opt) val $w configure $opt ${val}($n) } elseif {[regexp {[ec]} [lindex $configSpecs($opt) 2]]} { configEntry $w $opt $data($opt) } } # # Manage the entry # if {$usingTile} { set frameWidth [expr {[reqEntryWidth $w] - $deltaWidth}] set frameHeight [expr {[winfo reqheight $w] - $bd2}] $f configure -width $frameWidth -height $frameHeight place $w -x -$x -relwidth 1.0 -width $deltaWidth \ -y -$bd -relheight 1.0 -height $bd2 pack $f -side left -expand 1 -fill both -pady $bd if {$n == 0} { pack configure $f -padx [list $bd 0] } } else { pack $w -side left -expand 1 -fill both } # # Define some callbacks for the entry just created # wcb::callback $w before insert [list wcb::checkEntryLen $width] wcb::callback $w after insert \ [list mentry::condTabToNext $width $win $n] wcb::callback $w after motion \ [list mentry::condGoToNeighbor $win $n] # # Modify the list of binding tags of the entry # bindtags $w [list $w MentryEntry [winfo class $w] [winfo toplevel $w] \ MentryKeyNav all] if {$n == $data(labelCount)} { if {$usingTile} { set w $f } break } # # Append the value of text to the list data(-body) # lappend data(-body) $text # # Create a label displaying the given text within the hull win # set w [labelPath $win $n] tk::label $w -anchor center -bitmap "" -borderwidth 0 -height 0 \ -highlightthickness 0 -image "" -padx 0 -pady 0 \ -takefocus 0 -text $text -textvariable "" -width 0 \ -wraplength 0 set defVal [lindex [$w configure -underline] 3] $w configure -underline $defVal ;# -1 or "" (see TIP #577) if {$usingTile} { set padY $themeDefaults(-labelpady) } elseif {[string compare $winSys "aqua"] == 0 && $newAquaSupport} { set padY {0 0} } else { set padY {1 0} } pack $w -side left -expand 1 -fill y -pady $padY # # Apply to it the current configuration options # foreach opt $configOpts { if {[string compare [lindex $configSpecs($opt) 2] "c"] == 0 && [info exists data($opt)]} { $w configure $opt $data($opt) } } if {$::tk_version < 8.4} { $w configure -background $data(-background) } else { switch $data(-state) { normal { set labelBg $data(-background) set labelState normal } disabled { set labelBg $data(-disabledbackground) set labelState disabled } readonly { set labelBg $data(-readonlybackground) set labelState normal } } if {[string length $labelBg] == 0} { set labelBg $data(-background) } $w configure -background $labelBg -state $labelState } if {$usingTile} { $w configure -disabledforeground $themeDefaults(-disabledforeground) } # # Replace the binding tag Label with MentryLabel # in the list of binding tags of the label # bindtags $w [lreplace [bindtags $w] 1 1 MentryLabel] incr n } # # Adjust the last child's geometry # if {$usingTile} { if {$argCount == 1} { pack configure $w -padx [list $bd $bd] } elseif {[string compare [winfo class $w] "Label"] == 0} { pack configure $w -padx [list 0 [expr {$bd + 1}]] } else { pack configure $w -padx [list 0 $bd] } } elseif {$::tk_version >= 8.4 && [string compare [winfo class $w] "Label"] == 0} { pack configure $w -padx [list 0 1] } } # # Private procedures implementing the mentry widget command # ========================================================= # #------------------------------------------------------------------------------ # mentry::mentryWidgetCmd # # This procedure is invoked to process the Tcl command corresponding to a # multi-entry widget. #------------------------------------------------------------------------------ proc mentry::mentryWidgetCmd {win args} { set argCount [llength $args] if {$argCount == 0} { mwutil::wrongNumArgs "$win option ?arg arg ...?" } upvar ::mentry::ns${win}::data data variable cmdOpts set cmd [mwutil::fullOpt "option" [lindex $args 0] $cmdOpts] switch $cmd { adjustentry { if {$argCount < 3 || $argCount > 4} { mwutil::wrongNumArgs "$win $cmd index string1 ?string2?" } set n [childIndex [lindex $args 1] $data(maxEntryIdx)] set chars1 [lindex $args 2] set chars2 [expr {$argCount == 4 ? [lindex $args 3] : ""}] return [adjustentrySubCmd $win $n $chars1 $chars2] } attrib { return [mwutil::attribSubCmd $win "widget" [lrange $args 1 end]] } cget { if {$argCount != 2} { mwutil::wrongNumArgs "$win $cmd option" } # # Return the value of the specified configuration option # variable configSpecs set opt [mwutil::fullConfigOpt [lindex $args 1] configSpecs] return $data($opt) } clear { if {$argCount < 2 || $argCount > 3} { mwutil::wrongNumArgs "$win $cmd firstIndex ?lastIndex?" } set firstIdx [childIndex [lindex $args 1] $data(maxEntryIdx)] if {$argCount == 3} { set lastIdx [childIndex [lindex $args 2] $data(maxEntryIdx)] } else { set lastIdx $firstIdx } for {set n $firstIdx} {$n <= $lastIdx} {incr n} { _[entryPath $win $n] delete 0 end } return "" } configure { variable configSpecs return [mwutil::configureSubCmd $win configSpecs mentry::doConfig \ mentry::doCget [lrange $args 1 end]] } entries { if {$argCount != 1} { mwutil::wrongNumArgs "$win $cmd" } return [entries $win] } entrycount { if {$argCount != 1} { mwutil::wrongNumArgs "$win $cmd" } return $data(entryCount) } entrylimit { if {$argCount != 2} { mwutil::wrongNumArgs "$win $cmd index" } set n [childIndex [lindex $args 1] $data(maxEntryIdx)] return [lindex $data(-body) [expr {$n * 2}]] } entrypath { if {$argCount != 2} { mwutil::wrongNumArgs "$win $cmd index" } set n [childIndex [lindex $args 1] $data(maxEntryIdx)] return [entryPath $win $n] } getarray { if {$argCount != 2} { mwutil::wrongNumArgs "$win $cmd array" } # # The last argument must be an array # set varName [lindex $args 1] set _varName [list $varName] if {[uplevel info exists $_varName] && ![uplevel array exists $_varName]} { return -code error "variable \"$varName\" isn't array" } upvar $varName arr for {set n 0} {$n < $data(entryCount)} {incr n} { set arr($n) [[entryPath $win $n] get] } return "" } getlist { if {$argCount != 1} { mwutil::wrongNumArgs "$win $cmd" } set result {} foreach w [entries $win] { lappend result [$w get] } return $result } getstring { if {$argCount != 1} { mwutil::wrongNumArgs "$win $cmd" } set result "" foreach w [winfo children $win] { switch [winfo class $w] { Frame { append result [$w.e get] } Entry { append result [$w get] } Label { append result [$w cget -text] } } } return $result } hasattrib { if {$argCount != 2} { mwutil::wrongNumArgs "$win $cmd name" } return [mwutil::hasattribSubCmd $win "widget" [lindex $args 1]] } isempty { switch $argCount { 1 { foreach w [entries $win] { if {[string length [$w get]] != 0} { return 0 } } return 1 } 2 { set n [childIndex [lindex $args 1] $data(maxEntryIdx)] set w [entryPath $win $n] return [expr {[string length [$w get]] == 0}] } default { mwutil::wrongNumArgs "$win $cmd ?index?" } } } isfull { switch $argCount { 1 { for {set n 0} {$n < $data(entryCount)} {incr n} { set w [entryPath $win $n] set limit [lindex $data(-body) [expr {$n * 2}]] if {[string length [$w get]] != $limit} { return 0 } } return 1 } 2 { set n [childIndex [lindex $args 1] $data(maxEntryIdx)] set w [entryPath $win $n] set limit [lindex $data(-body) [expr {$n * 2}]] return [expr {[string length [$w get]] == $limit}] } default { mwutil::wrongNumArgs "$win $cmd ?index?" } } } labelcount { if {$argCount != 1} { mwutil::wrongNumArgs "$win $cmd" } return $data(labelCount) } labelpath { if {$argCount != 2} { mwutil::wrongNumArgs "$win $cmd index" } set n [childIndex [lindex $args 1] $data(maxLabelIdx)] return [labelPath $win $n] } labels { if {$argCount != 1} { mwutil::wrongNumArgs "$win $cmd" } return [labels $win] } put { if {$argCount < 2} { mwutil::wrongNumArgs "$win $cmd startIndex\ ?string string ...?" } set startIdx [childIndex [lindex $args 1] $data(maxEntryIdx)] return [putSubCmd $win $startIdx [lrange $args 2 end]] } setentryextrawidth { if {$argCount != 3} { mwutil::wrongNumArgs "$win $cmd index amount" } set n [childIndex [lindex $args 1] $data(maxEntryIdx)] return [setentryextrawidthSubCmd $win $n [lindex $args 2]] } setentryfont { if {$argCount != 3} { mwutil::wrongNumArgs "$win $cmd index font" } set n [childIndex [lindex $args 1] $data(maxEntryIdx)] return [setentryfontSubCmd $win $n [lindex $args 2]] } setentrywidth { if {$argCount != 3} { mwutil::wrongNumArgs "$win $cmd index characters" } set n [childIndex [lindex $args 1] $data(maxEntryIdx)] return [setentrywidthSubCmd $win $n [lindex $args 2]] } unsetattrib { if {$argCount != 2} { mwutil::wrongNumArgs "$win $cmd name" } return [mwutil::unsetattribSubCmd $win "widget" [lindex $args 1]] } } } #------------------------------------------------------------------------------ # mentry::adjustentrySubCmd # # This procedure is invoked to process the mentry adjustentry subcommand. #------------------------------------------------------------------------------ proc mentry::adjustentrySubCmd {win index chars1 chars2} { upvar ::mentry::ns${win}::data data set data($index-chars) [list $chars1 $chars2] set w [entryPath $win $index] set font [$w cget -font] # # Get the max. widths maxWidth1 and maxWidth2 # of the characters in chars1 and chars2 # set len [string length $chars1] set maxWidth1 0 for {set n 0} {$n < $len} {incr n} { set width [font measure $font -displayof $w [string index $chars1 $n]] if {$width > $maxWidth1} { set maxWidth1 $width } } set len [string length $chars2] set maxWidth2 0 for {set n 0} {$n < $len} {incr n} { set width [font measure $font -displayof $w [string index $chars2 $n]] if {$width > $maxWidth2} { set maxWidth2 $width } } set count [lindex $data(-body) [expr {$index * 2}]] # # Get the requested width in case all count characters are from chars1 # set reqWidth1 [expr {$maxWidth1 * $count}] # # Get the requested width in case count - 1 characters # are from chars1 and one character is from chars2 # set lessCount [expr {$count - 1}] set reqWidth2 [expr {$maxWidth1*$lessCount + $maxWidth2}] # # Set the requested width to the maximum of the two # set reqWidth [expr {$reqWidth1 < $reqWidth2 ? $reqWidth2 : $reqWidth1}] set zeroWidth [font measure $font -displayof $w "0"] set availWidth [expr {$zeroWidth * $count}] if {$reqWidth < $availWidth} { return "" } variable usingTile set iPadX [expr {($reqWidth - $availWidth + 1) / 2}] if {$usingTile} { pack configure [winfo parent $w] -ipadx $iPadX } else { pack configure $w -ipadx $iPadX } return "" } #------------------------------------------------------------------------------ # mentry::putSubCmd # # This procedure is invoked to process the mentry put subcommand. #------------------------------------------------------------------------------ proc mentry::putSubCmd {win startIdx strList} { upvar ::mentry::ns${win}::data data # # If the focus is currently on one of win's children then set it # temporarily to the top-level window containing win, to make sure # that the after-insert callback condTabToNext will not change it # set focus [focus -displayof $win] if {[string length $focus] != 0 && [string compare $focus "."] != 0 && ([string compare [winfo parent $focus] $win] == 0 || [string compare [winfo parent [winfo parent $focus]] $win] == 0)} { focus [winfo toplevel $win] set focusChanged 1 } else { set focusChanged 0 } # # Attempt to replace the texts of the entry children whose indices are # >= startIdx with the given strings, until either the entries or the # strings are consumed, by using the delete and insert operations; abort # the loop if one of these subcommands is canceled by some before-callback # set undo 0 set n $startIdx set oldStrings {} set oldPositions {} foreach str $strList { if {$n == $data(entryCount)} { break } set w [entryPath $win $n] lappend oldStrings [$w get] lappend oldPositions [$w index insert] $w delete 0 end if {[wcb::canceled $w delete]} { set undo 1 break } $w insert 0 $str if {[wcb::canceled $w insert]} { set undo 1 break } incr n } # # Restore the original contents of the entry children if necessary, and # in any case restore the position of the insertion cursor in each entry # set n $startIdx foreach oldStr $oldStrings oldPos $oldPositions { set w [entryPath $win $n] if {$undo} { $w delete 0 end $w insert 0 $oldStr } $w icursor $oldPos incr n } # # Reset the focus if needed and return the negation of $undo # if {$focusChanged} { focus $focus } return [expr {!$undo}] } #------------------------------------------------------------------------------ # mentry::setentryextrawidthSubCmd # # This procedure is invoked to process the mentry setentryextrawidth subcommand. #------------------------------------------------------------------------------ proc mentry::setentryextrawidthSubCmd {win index amount} { set w [entryPath $win $index] set pixels [winfo pixels $w $amount] if {$pixels < 0} { set pixels 0 } set iPadX [expr {($pixels + 1) / 2}] variable usingTile if {$usingTile} { pack configure [winfo parent $w] -ipadx $iPadX } else { pack configure $w -ipadx $iPadX } return "" } #------------------------------------------------------------------------------ # mentry::setentryfontSubCmd # # This procedure is invoked to process the mentry setentryfont subcommand. #------------------------------------------------------------------------------ proc mentry::setentryfontSubCmd {win index font} { set w [entryPath $win $index] $w configure -font $font upvar ::mentry::ns${win}::data data set data($index-font) $font if {[info exists data($index-chars)]} { foreach {chars1 chars2} $data($index-chars) {} adjustentrySubCmd $win $index $chars1 $chars2 } if {[info exists data($index-width)]} { setentrywidthSubCmd $win $index $data($index-width) } variable usingTile if {!$usingTile} { return "" } foreach {bd bd2 x deltaWidth} [geomParams] {} set frameWidth [expr {[reqEntryWidth $w] - $deltaWidth}] set frameHeight [expr {[winfo reqheight $w] - $bd2}] [winfo parent $w] configure -width $frameWidth -height $frameHeight place configure $w -x -$x -relwidth 1.0 -width $deltaWidth \ -y -$bd -relheight 1.0 -height $bd2 return "" } #------------------------------------------------------------------------------ # mentry::setentrywidthSubCmd # # This procedure is invoked to process the mentry setentrywidth subcommand. #------------------------------------------------------------------------------ proc mentry::setentrywidthSubCmd {win index width} { set w [entryPath $win $index] $w configure -width $width upvar ::mentry::ns${win}::data data set data($index-width) $width variable usingTile if {!$usingTile} { return "" } foreach {bd bd2 x deltaWidth} [geomParams] {} set frameWidth [expr {[reqEntryWidth $w] - $deltaWidth}] [winfo parent $w] configure -width $frameWidth place configure $w -x -$x -relwidth 1.0 -width $deltaWidth return "" } #------------------------------------------------------------------------------ # mentry::childIndex # # Checks the index n, rounds it to the nearest value between 0 and max, and # returns either the rounded value or an error. #------------------------------------------------------------------------------ proc mentry::childIndex {n max} { if {[string first $n "end"] == 0} { return $max } elseif {[catch {format "%d" $n} index] != 0} { return -code error \ "bad index \"$n\": must be end or a number" } elseif {$index < 0} { return 0 } elseif {$index > $max} { return $max } else { return $index } } # # Private callback procedures # =========================== # #------------------------------------------------------------------------------ # mentry::condTabToNext # # This after-insert callback checks whether the insertion cursor in the n'th # entry child of the mentry widget win is just behind the character having the # index width; if this is the case, it moves the focus to the next enabled # entry child, selects the content of that widget, and sets the insertion # cursor to its end. #------------------------------------------------------------------------------ proc mentry::condTabToNext {width win n w idx str} { if {[$w index insert] == $width && [string compare [focus -displayof $win] [entryPath $win $n]] == 0 && [string length [set next [nextNormal $win $n]]] != 0} { tabToEntry $next } } #------------------------------------------------------------------------------ # mentry::condGoToNeighbor # # This after-motion callback examines the index idx passed to the last icursor # command in the n'th entry child of the mentry widget win. If it was # negative, the procedure clears the selection in the current entry widget, # moves the focus to the previous enabled entry child, and sets the insertion # cursor to the end of that widget; if it was greater than the index of the # last character, the procedure clears the selection in the current entry # widget, moves the focus to the next enabled entry child, and sets the # insertion cursor to the beginning of that widget. #------------------------------------------------------------------------------ proc mentry::condGoToNeighbor {win n w idx} { variable entryClicked if {![regexp {^[0-9-]+$} $idx] || $entryClicked || [string compare [focus -displayof $win] [entryPath $win $n]] != 0} { return "" } if {$idx < 0 && [string length [set prev [prevNormal $win $n]]] != 0} { $w selection clear focus $prev entrySetCursor $prev end } elseif {$idx > [$w index end] && [string length [set next [nextNormal $win $n]]] != 0} { $w selection clear focus $next entrySetCursor $next 0 } } # # Private procedures used in bindings # =================================== # #------------------------------------------------------------------------------ # mentry::updateLabelForegrounds # # This procedure handles the events and by updating the # -foreground option of the labels of the mentry widget win. #------------------------------------------------------------------------------ proc mentry::updateLabelForegrounds {win inActiveWin} { # # This is an "after idle" callback; check whether the window exists # if {![array exists ::mentry::ns${win}::data]} { return "" } upvar ::mentry::ns${win}::data data variable themeDefaults set data(inActiveWin) $inActiveWin if {!$data(inActiveWin) && [string compare $data(-foreground) \ $themeDefaults(-foreground)] == 0} { set labelFg $themeDefaults(-foreground,background) } else { set labelFg $data(-foreground) } foreach w [labels $win] { $w configure -foreground $labelFg } } #------------------------------------------------------------------------------ # mentry::updateFonts # # This procedure handles the virtual event <> if the latter's # %d field equals "FontChanged". #------------------------------------------------------------------------------ proc mentry::updateFonts win { upvar ::mentry::ns${win}::data data doConfig $win -font $data(-font) foreach name [array names data ?*-font] { set index [lindex [split $name "-"] 0] setentryfontSubCmd $win $index $data($name) } } #------------------------------------------------------------------------------ # mentry::handleThemeChangedEvent # # This procedure handles the virtual event <>. #------------------------------------------------------------------------------ proc mentry::handleThemeChangedEvent {} { variable currentTheme variable widgetStyle variable colorScheme set newTheme [::mwutil::currentTheme] if {[string compare $newTheme $currentTheme] == 0} { if {[string compare $newTheme "tileqt"] == 0} { set newWidgetStyle [tileqt_currentThemeName] if {[info exists ::env(KDE_SESSION_VERSION)] && [string length $::env(KDE_SESSION_VERSION)] != 0} { set newColorScheme [getKdeConfigVal "General" "ColorScheme"] } else { set newColorScheme [getKdeConfigVal "KDE" "colorScheme"] } if {[string compare $newWidgetStyle $widgetStyle] == 0 && [string compare $newColorScheme $colorScheme] == 0} { return "" } } else { return "" } } set currentTheme $newTheme if {[string compare $newTheme "tileqt"] == 0} { set widgetStyle $newWidgetStyle set colorScheme $newColorScheme } else { set widgetStyle "" set colorScheme "" } # # Populate the array themeDefaults with # values corresponding to the new theme # setThemeDefaults event generate . <> # # Level-order traversal like in the Tk library procedue ::ttk::ThemeChanged # set lst1 {.} while {[llength $lst1] != 0} { set lst2 {} foreach w $lst1 { if {[string compare [winfo class $w] "Mentry"] == 0} { updateConfigSpecs $w } foreach child [winfo children $w] { lappend lst2 $child } } set lst1 $lst2 } } #------------------------------------------------------------------------------ # mentry::updateConfigSpecs # # Updates the theme-specific default values of some mentry configuration # options. #------------------------------------------------------------------------------ proc mentry::updateConfigSpecs win { upvar ::mentry::ns${win}::data data variable usingTile if {$usingTile} { # # Populate the array tmp with values corresponding to the old theme # array set tmp $data(themeDefaults) # # Set those configuration options whose values equal the old # theme-specific defaults to the new theme-specific ones # variable themeDefaults foreach opt {-disabledbackground -readonlybackground} { set data($opt) $themeDefaults($opt) } foreach opt {-background -foreground -font} { if {[string compare $data($opt) $tmp($opt)] == 0} { doConfig $win $opt $themeDefaults($opt) } } adjustChildren $win # # Most themes support the -fieldbackground option for the style element # Entry.field. In Tk versions earlier than 8.6.10, the aqua theme # supported the -background option instead. Some themes (like Arc, # plastik, tileqt, vista, and xpnative) don't support either of them. # variable currentTheme variable newAquaSupport foreach style [list $win.TEntry Hull$win.TEntry] { if {[string compare $currentTheme "aqua"] == 0 && !$newAquaSupport} { styleConfig $style -background $data(-background) } else { styleConfig $style -fieldbackground $data(-background) } if {[winfo viewable $win]} { update idletasks ;# to avoid some artifacts on aqua } } # # Set the foreground color of the label children # if {!$data(inActiveWin) && [string compare $data(-foreground) \ $themeDefaults(-foreground)] == 0} { set labelFg $themeDefaults(-foreground,background) } else { set labelFg $data(-foreground) } foreach w [labels $win] { $w configure -foreground $labelFg \ -disabledforeground $themeDefaults(-disabledforeground) } set data(themeDefaults) [array get themeDefaults] } } #------------------------------------------------------------------------------ # mentry::handleAppearanceEvent # # This procedure handles the virtual events <> and <>. #------------------------------------------------------------------------------ proc mentry::handleAppearanceEvent {} { variable appearanceId unset appearanceId variable currentTheme if {[string compare $currentTheme "aqua"] != 0} { return "" } # # Populate the array themeDefaults with # values corresponding to the new appearance # setThemeDefaults event generate . <> # # Level-order traversal like in the Tk library procedue ::ttk::ThemeChanged # set lst1 {.} while {[llength $lst1] != 0} { set lst2 {} foreach w $lst1 { if {[string compare [winfo class $w] "Mentry"] == 0} { updateAppearance $w } foreach child [winfo children $w] { lappend lst2 $child } } set lst1 $lst2 } } #------------------------------------------------------------------------------ # mentry::updateAppearance # # Updates the appearance of the mentry widget win according to the virtual # events <> and <>. #------------------------------------------------------------------------------ proc mentry::updateAppearance win { upvar ::mentry::ns${win}::data data # # Populate the array tmp with values # corresponding to the old appearance # array set tmp $data(themeDefaults) # # Set those configuration options whose values equal the old # theme-specific defaults to the new theme-specific ones # variable themeDefaults foreach opt {-disabledbackground -readonlybackground} { set data($opt) $themeDefaults($opt) } foreach opt {-background -foreground} { if {[string compare $data($opt) $tmp($opt)] == 0} { doConfig $win $opt $themeDefaults($opt) } } # # Set the foreground color of the label children # if {!$data(inActiveWin) && [string compare $data(-foreground) \ $themeDefaults(-foreground)] == 0} { set labelFg $themeDefaults(-foreground,background) } else { set labelFg $data(-foreground) } foreach w [labels $win] { $w configure -foreground $labelFg \ -disabledforeground $themeDefaults(-disabledforeground) } set data(themeDefaults) [array get themeDefaults] } #------------------------------------------------------------------------------ # mentry::tabToPrev # # This procedure handles events in the entry child w of a mentry # widget. If possible, it moves the focus to the previous enabled entry child, # selects the content of that widget, and sets the insertion cursor to its end; # otherwise, it moves the insertion cursor to the beginning of the current # entry and clears the selection in that widget. #------------------------------------------------------------------------------ proc mentry::tabToPrev w { parseChildPath $w win n set prev [prevNormal $win $n] if {[string length $prev] != 0} { tabToEntry $prev } else { entrySetCursor $w 0 } return -code break "" } #------------------------------------------------------------------------------ # mentry::tabToNext # # This procedure handles events in the entry child w of a # mentry widget. If possible, it moves the focus to the next enabled entry # child, selects the content of that widget, and sets the insertion cursor to # its end; otherwise, it moves the insertion cursor to the end of the current # entry and clears the selection in that widget. #------------------------------------------------------------------------------ proc mentry::tabToNext w { parseChildPath $w win n set next [nextNormal $win $n] if {[string length $next] != 0} { tabToEntry $next } else { entrySetCursor $w end } return -code break "" } #------------------------------------------------------------------------------ # mentry::goToHome # # This procedure handles events in the entry child w of a mentry widget. # It clears the selection in the current entry, moves the focus to the first # enabled entry child, and sets the insertion cursor to the beginning of that # widget. #------------------------------------------------------------------------------ proc mentry::goToHome w { parseChildPath $w win n set first [firstNormal $win] $w selection clear focus $first catch {entrySetCursor $first 0} return -code break "" } #------------------------------------------------------------------------------ # mentry::goToEnd # # This procedure handles events in the entry child w of a mentry widget. # It clears the selection in the current entry, moves the focus to the last # enabled entry child, and sets the insertion cursor to the end of that widget. #------------------------------------------------------------------------------ proc mentry::goToEnd w { parseChildPath $w win n set last [lastNormal $win] $w selection clear focus $last catch {entrySetCursor $last end} return -code break "" } #------------------------------------------------------------------------------ # mentry::selectToHome # # This procedure handles events in the entry child w of a mentry # widget. It moves the focus to the first enabled entry child, sets the # insertion cursor to the beginning of that widget, and either extends the # selection to that position. or clears the selection in w and selects the # contents of the new widget, depending upon whether the first enabled entry # child equals w. #------------------------------------------------------------------------------ proc mentry::selectToHome w { parseChildPath $w win n set first [firstNormal $win] if {[string compare $first $w] != 0} { $w selection clear focus $first catch {$first icursor end} } catch { if {[$first selection present]} { $first selection range 0 sel.last } else { $first selection range 0 insert } $first icursor 0 entryViewCursor $first } return -code break "" } #------------------------------------------------------------------------------ # mentry::selectToEnd # # This procedure handles events in the entry child w of a mentry # widget. It moves the focus to the last enabled entry child, sets the # insertion cursor to the end of that widget, and either extends the # selection to that position. or clears the selection in w and selects the # contents of the new widget, depending upon whether the last enabled entry # child equals w. #------------------------------------------------------------------------------ proc mentry::selectToEnd w { parseChildPath $w win n set last [lastNormal $win] if {[string compare $last $w] != 0} { $w selection clear focus $last catch {$last icursor 0} } catch { if {[$last selection present]} { $last selection range sel.first end } else { $last selection range insert end } $last icursor end entryViewCursor $last } return -code break "" } #------------------------------------------------------------------------------ # mentry::backSpace # # This procedure handles events in the entry child w of a mentry # widget. It deletes the selection if there is one in the entry. Otherwise, # it deletes either the character to the left of the insertion cursor in the # current entry, or the last character of the previous enabled entry child, # depending upon the position of the insertion cursor. In the second case, it # also moves the focus to the previous enabled entry child and sets the # insertion cursor to its end. #------------------------------------------------------------------------------ proc mentry::backSpace w { parseChildPath $w win n if {[$w selection present]} { $w delete sel.first sel.last } else { if {[$w index insert] == 0 && [string length [set prev [prevNormal $win $n]]] != 0} { focus $prev entrySetCursor $prev end set w $prev } set x [expr {[$w index insert] - 1}] if {$x >= 0} { $w delete $x } if {[$w index insert] <= [$w index @0]} { set range [$w xview] set left [lindex $range 0] set right [lindex $range 1] $w xview moveto [expr {$left - ($right - $left)/2.0}] } } return -code break "" } #------------------------------------------------------------------------------ # mentry::delToLeft # # This procedure handles and events in the entry # child w of a mentry widget. It deletes either all characters to the left of # the insertion cursor in the current entry, or the contents of the previous # enabled entry child, depending upon the position of the insertion cursor. In # the second case, it also clears the selection in the current entry widget and # moves the focus to the previous enabled entry child. #------------------------------------------------------------------------------ proc mentry::delToLeft w { parseChildPath $w win n if {[$w index insert] == 0 && [string length [set prev [prevNormal $win $n]]] != 0} { $w selection clear focus $prev $prev delete 0 end } else { $w delete 0 insert } return -code break "" } #------------------------------------------------------------------------------ # mentry::procLabelChars # # This procedure handles events in the entry child w of a mentry # widget. If this entry is non-empty and the character char corresponding to # the event is contained in the text displayed in the label child following the # entry (if any) then the procedure moves the focus to the next enabled entry # child, selects the content of that widget, and sets the insertion cursor to # its end. #------------------------------------------------------------------------------ proc mentry::procLabelChars {w char} { parseChildPath $w win n set label [labelPath $win $n] if {![winfo exists $label] || [string first $char [$label cget -text]] < 0} { return "" } if {[string length [$w get]] == 0} { return -code break "" } set next [nextNormal $win $n] if {[string length $next] != 0} { tabToEntry $next } return -code break "" } #------------------------------------------------------------------------------ # mentry::labelButton1 # # This procedure handles events in the label child w of a mentry # widget. It generates a event in the previous enabled entry child, # after its last character. #------------------------------------------------------------------------------ proc mentry::labelButton1 w { parseChildPath $w win n incr n set entry [prevNormal $win $n] if {[string length $entry] != 0} { set bbox [$entry bbox end] set x [expr {[lindex $bbox 0] + [lindex $bbox 2]}] event generate $entry -x $x variable entryClicked set entryClicked 0 } } #------------------------------------------------------------------------------ # mentry::parseChildPath # # Extracts the path name of the mentry widget as well as the child's index from # the path name w of a child of a mentry widget. #------------------------------------------------------------------------------ proc mentry::parseChildPath {w winName indexName} { upvar $winName win $indexName index return [regexp {^(.+)\.[fel]([0-9]+)(\.e)?$} $w dummy win index] } # # Private utility procedures # ========================== # #------------------------------------------------------------------------------ # mentry::framePath # # Returns the path name of the n'th frame child of the tile-based mentry widget # win. #------------------------------------------------------------------------------ proc mentry::framePath {win n} { return $win.f$n } #------------------------------------------------------------------------------ # mentry::entryPath # # Returns the path name of the n'th entry (grand)child of the mentry widget win. #------------------------------------------------------------------------------ proc mentry::entryPath {win n} { variable usingTile if {$usingTile} { return $win.f$n.e } else { return $win.e$n } } #------------------------------------------------------------------------------ # mentry::labelPath # # Returns the path name of the n'th label child of the mentry widget win. #------------------------------------------------------------------------------ proc mentry::labelPath {win n} { return $win.l$n } #------------------------------------------------------------------------------ # mentry::entries # # Returns a list containing the path names of the entry children of the widget # win. #------------------------------------------------------------------------------ proc mentry::entries win { set lst {} foreach w [winfo children $win] { set class [winfo class $w] if {[string compare $class "Entry"] == 0} { lappend lst $w } elseif {[string compare $class "Frame"] == 0} { lappend lst $w.e } } return $lst } #------------------------------------------------------------------------------ # mentry::labels # # Returns a list containing the path names of the label children of the widget # win. #------------------------------------------------------------------------------ proc mentry::labels win { set lst {} foreach w [winfo children $win] { if {[string compare [winfo class $w] "Label"] == 0} { lappend lst $w } } return $lst } #------------------------------------------------------------------------------ # mentry::prevNormal # # Returns the path name of the rightmost enabled entry child to the left of the # n'th entry of the mentry widget win. #------------------------------------------------------------------------------ proc mentry::prevNormal {win n} { for {incr n -1} {$n >= 0} {incr n -1} { set w [entryPath $win $n] if {[string compare [$w cget -state] "normal"] == 0} { return $w } } return "" } #------------------------------------------------------------------------------ # mentry::nextNormal # # Returns the path name of the leftmost enabled entry child to the right of the # n'th entry of the mentry widget win. #------------------------------------------------------------------------------ proc mentry::nextNormal {win n} { upvar ::mentry::ns${win}::data data for {incr n} {$n < $data(entryCount)} {incr n} { set w [entryPath $win $n] if {[string compare [$w cget -state] "normal"] == 0} { return $w } } return "" } #------------------------------------------------------------------------------ # mentry::firstNormal # # Returns the path name of the first enabled entry child of the mentry widget # win. #------------------------------------------------------------------------------ proc mentry::firstNormal win { return [nextNormal $win -1] } #------------------------------------------------------------------------------ # mentry::lastNormal # # Returns the path name of the last enabled entry child of the mentry widget # win. #------------------------------------------------------------------------------ proc mentry::lastNormal win { upvar ::mentry::ns${win}::data data return [prevNormal $win $data(entryCount)] } #------------------------------------------------------------------------------ # mentry::adjustChildren # # Adjusts the geometry of the children of the tile-based mentry widget win. #------------------------------------------------------------------------------ proc mentry::adjustChildren win { set childList [winfo children $win] set childCount [llength $childList] if {$childCount == 0} { return "" } foreach {bd bd2 x deltaWidth} [geomParams] {} pack configure [framePath $win 0] -padx [list $bd 0] variable themeDefaults foreach w $childList { if {[string compare [winfo class $w] "Frame"] == 0} { set frameWidth [expr {[reqEntryWidth $w.e] - $deltaWidth}] set frameHeight [expr {[winfo reqheight $w.e] - $bd2}] $w configure -width $frameWidth -height $frameHeight place configure $w.e -x -$x -relwidth 1.0 -width $deltaWidth \ -y -$bd -relheight 1.0 -height $bd2 pack configure $w -pady $bd } else { pack configure $w -pady $themeDefaults(-labelpady) } } if {$childCount == 1} { pack configure $w -padx [list $bd $bd] } elseif {[string compare [winfo class $w] "Label"] == 0} { pack configure $w -padx [list 0 [expr {$bd + 1}]] } else { pack configure $w -padx [list 0 $bd] } } #------------------------------------------------------------------------------ # mentry::tabToEntry # # Moves the focus to the specified entry widget, selects its contents, and sets # the insertion cursor to its end. #------------------------------------------------------------------------------ proc mentry::tabToEntry w { focus $w $w selection range 0 end $w icursor end } #------------------------------------------------------------------------------ # mentry::entrySetCursor # # Moves the insertion cursor to the specified position in the given entry # widget, clears the selection, and makes sure that the insertion cursor is # visible. #------------------------------------------------------------------------------ proc mentry::entrySetCursor {w pos} { $w icursor $pos $w selection clear entryViewCursor $w } #------------------------------------------------------------------------------ # mentry::entryViewCursor # # Makes sure that the insertion cursor in the specified entry is visible by # adjusting the view if necessary. #------------------------------------------------------------------------------ proc mentry::entryViewCursor w { set c [$w index insert] if {$c < [$w index @0] || $c > [$w index @[winfo width $w]]} { $w xview $c } } #------------------------------------------------------------------------------ # mentry::configEntry # # This procedure configures the entry widget w according to the options and # their values given in args. #------------------------------------------------------------------------------ proc mentry::configEntry {w args} { foreach {opt val} $args { switch -- $opt { -background { if {[string compare [winfo class $w] "TEntry"] != 0} { $w configure $opt $val } } -foreground { if {[string compare [winfo class $w] "TEntry"] == 0} { variable themeDefaults if {[string compare [winfo rgb $w $val] [winfo rgb $w \ $themeDefaults(-foreground)]] == 0} { set val "" ;# for automatic adaptation to the states } $w instate !disabled { $w configure $opt $val } } else { $w configure $opt $val } } -state { $w configure $opt $val if {[string compare [winfo class $w] "TEntry"] == 0} { variable themeDefaults if {[string compare $val "disabled"] == 0} { # # Set the entry's foreground color to the theme- # specific one (needed for current tile versions) # $w configure -foreground "" } else { # # Restore the entry's foreground color # (needed for current tile versions) # if {[parseChildPath $w win n]} { upvar ::mentry::ns${win}::data data configEntry $w -foreground $data(-foreground) } } } } default { $w configure $opt $val } } } } #------------------------------------------------------------------------------ # mentry::reqEntryWidth # # Returns the requested width in pixels of the tile entry widget w. #------------------------------------------------------------------------------ proc mentry::reqEntryWidth w { variable currentTheme variable isAwTheme if {[string match "*clearlooks" $currentTheme] && $isAwTheme} { # # If the tile entry was created with -width 1 then in the awthemes # "clearlooks" and "awclearlooks" themes its width will silently be # changed to 2. For this reason, compute the widget's requested # width based on its font rather than returning [winfo reqwidth $w]. # set zeroWidth [font measure [$w cget -font] -displayof $w "0"] return [expr {[$w cget -width] * $zeroWidth + 8}] } elseif {[string compare $currentTheme "vista"] == 0} { # # If the tile entry was created with -width 1 or -width 2 then # in the "vista" theme its width will silently be changed to 3. # For this reason, compute the widget's requested width based # on its font rather than returning [winfo reqwidth $w]. # set zeroWidth [font measure [$w cget -font] -displayof $w "0"] return [expr {[$w cget -width] * $zeroWidth + 6}] } else { return [winfo reqwidth $w] } } #------------------------------------------------------------------------------ # mentry::geomParams # # Returns a few parameters needed for geometry management. #------------------------------------------------------------------------------ proc mentry::geomParams {} { variable themeDefaults set bd $themeDefaults(-borderwidth) set bd2 [expr {2*$bd}] set x $bd set deltaWidth $bd2 variable currentTheme switch -- $currentTheme { aqua { variable newAquaSupport if {$newAquaSupport} { incr x 2 incr deltaWidth 4 } } Arc - arc - awarc { incr x 2 incr deltaWidth 4 } awdark - awlight { incr x 4 incr deltaWidth 8 } black - awblack - awtemplate { incr x 2 incr deltaWidth 4 } Breeze - breeze - awbreeze - awbreezedark { incr x 3 incr deltaWidth 6 } clearlooks - awclearlooks - winxpblue - awwinxpblue { variable isAwTheme if {$isAwTheme} { incr x incr deltaWidth 2 } } srivlg { incr x incr deltaWidth 2 } } return [list $bd $bd2 $x $deltaWidth] }