cooltip

ET 2023-12-27 cooltip 1.6

This is an upgrade to tooltip 1.6 found in the Tklib. It supports tips with 7 configurable color text tags and 3 styles. Another 9 extra tags can be configured by program control for advanced use. A separate GUI editor is included to build cool tips with style. Unicode up to \uFFFF is supported.

It is upwards compatible with existing tooltip code, as it uses the same namespace, tooltip:: and the same commands, including the configuration options from the latest tooltip version 1.6. The Tooltip 1.6 options for -index, -items, -tag, and -tab can be added as specified in the tooltip 1.6 manual.

Screen Shot


Quick test drive:
  1. Expand the first discussion below, scroll to the code block and click the copy button in the upper right corner.
  2. Create a file cooltip-1.6.tm and paste in the code and save the file in a new directory.
  3. Repeat for the second discussion, name the file cool.tcl
  4. cd to the directory containing the 2 files, and run: wish cool.tcl
  5. You should see 2 windows, the cooltip GUI and a console. Hover the mouse over the various buttons in the cooltip GUI window.
  6. Read ahead for more information or click exit in the console or close the GUI window.

Cooltip is bundled with a GUI tip builder to build more stylish tips. The tip builder also demonstrates the kinds of tips that can be built using the tool. The cooltip 1.6 package is assumed to be a Tcl module and should therefore be placed in a file named:

cooltip-1.6.tm

The GUI builder assumes that it and the cooltip module are in the same directory and is run with a wish file.tcl command. It uses [info script] to know where it and the cooltip module reside. It can have any name, but must be able to find the cooltip module.

Cooltip works with 2 types of tips, but uses the same command. The options are those from tooltip 1.6 used to designate such things as menu index, text widget tags, listbox items, and ttk::notebook tabs.

tooltip::tooltip <widget> ?options?  <tip>

If <tip> is a regular text string, then it should work the same as with tooltip 1.6. One builds a styled tip using the GUI builder, and when ready for use clicks the copy button. This will create the tip text as a text widget dump assigned to a variable with a

set tooltipN {##<widget dump>} 

statement and places it onto the clipboard. Then <tip> would be $tooltipN rather than a litteral text string. N can be any text. A styled tip text is prefixed with ## to differentiate it from a non-styled tip.

This can then be pasted from the clipboard into the user program, possibly replacing an earlier version. To re-edit one copies this set statement and pastes it back into the GUI builder. This eliminates any need for intermediate files. However, the GUI is also able to save to and load from a pair of fixed filenames (in the [pwd] directory) for quick backups of text and configuration settings.

If the user does any advanced configuration of the text tags, these are saved in the 2nd file of the pair as a single tcl source-able statement; this can be simply added to the user program if they were changed in the GUI console. The Config settings apply to all tips, so the config is not saved with each individual tip.

All unicode chars inserted by the builder, are reverted back to their \uXXXX escapes before copied to the clipboard for pasting into the program.

When pasted back into the GUI builder it will parse the set statement extracting the text dump re-creating the tip in its text window. The GUI builder uses the Tk text widget, and so supports all the standard shortcuts. It also handles undo (control-z) but only the text, not the attributes. On windows, redo is control-y and shift-control-z on linux. In addition, a control-wheel will increase or decrease the config value for the font.

Note: the GUI program's copy/paste buttons (on the menubar) are not raw copy/paste, but specialized to work only on the set statements exclusively. Regular copy/paste is supported by the text widget shortcuts (control-c/control-v).

ET 1/13/2024 - recent changes - mirror changes in the tooltip widget config (when entering config changes via the console window) into the editor widget and added the control-mousewheel for font size zooming. Load and Save should also remember any config changes too. New is the configure option with a dialog to configure most attributes of each color 1/14/2024. Reworked the clear command, added awdark if available 1/15. And fixed console on linux code for 9.0. Font chooser in configure. Bug fix, performance fix. 1/18, border dialog 1/27


The user program would either find the cooltip module in a known module directory with a simple package require cooltip, or similar to auto_path, use the module command tcl::tm::path add <path> to let the package command find the module in the <path> folder specified.

A user program could also just source the module or even simply embed it into the program (it's relatively small at ~800 lines). The GUI builder is not needed at runtime in the target user program.


Below in the discussion is the cooltip module, which as mentioned, should be named cooltip-1.6.tm and placed in either a known module directory, or in the same directory with the GUI builder that follows.

In most cases, the built in 7 colors and 3 styles with a single font should be sufficient for most tool tips. However, using the config option one can create very elaborate tips using multiple fonts, colors, and other attributes. For example, superscripts can be created using a smaller font with the -offset option.

Cooltip also includes 2 config options, -extraw and -extrah to increase the width and height of all tooltips and defaults to 0. In addition to the 7 colors, which can be configured if desired (e.g. to change the actual color) there are 9 extra tags grey51..grey59 which are available for full configuration using any option for a text widget tag. These are not used by the GUI program so changing them won't interfere with its own tips. When text is changed to one of those colors, it will have a grey background and when hovering over with the mouse pointer, a tooltip will identify the color assigned.

When using a tooltip::tooltip config with no options, it will output all the current values. The color options can also include other attributes, such as -font as well as modifications to the color itself (e.g. note how -grey is actually set to grey80). Each option value is a list of text widget tag options. To remove a tag option, set to {}.

The GUI builder also includes an enhanced console for windows and linux. When it starts up, it pre-loads the history with a few sample configuration commands that can be retrieved and edited using the up arrow key. This provides a way to test config's of say the 9 grey5n tags with a quick feedback using the GUI's ->test widget.

In the below sample commands entered into the GUI's console, one can see all the config settings and change of grey51's font and relief settings. One could then select text and assign it to color grey51 which would also modify the other attributes.

% tooltip::tooltip config
-foreground black -background lightyellow -font {FixedSys 10} -red {-foreground red} -green {-foreground green} -blue {-foreground lightblue}
 -orange {-foreground orange} -violet {-foreground violet} -black {-foreground black} -grey {-foreground grey80} -grey51 {-background grey90 
-foreground black -selectbackground lightblue} -grey52 {-background grey90 -foreground black -selectbackground lightblue} -grey53 {-background 
grey90 -foreground black -selectbackground lightblue} -grey54 {-background grey90 -foreground black -selectbackground lightblue} -grey55
 {-background grey90 -foreground black -selectbackground lightblue} -grey56 {-background grey90 -foreground black -selectbackground lightblue}
 -grey57 {-background grey90 -foreground black -selectbackground lightblue} -grey58 {-background grey90 -foreground black -selectbackground 
lightblue} -grey59 {-background grey90 -foreground black -selectbackground lightblue} -extraw 0 -extrah 0

% tooltip::tooltip config -grey51
-background grey90 -foreground black -selectbackground lightblue

% tooltip::tooltip config -grey51 {-font {courier 12} -relief ridge -borderwidth 4}
% tooltip::tooltip config -grey51
-background grey90 -borderwidth 4 -font {courier 12} -foreground black -relief ridge -selectbackground lightblue

Note that with multiple fonts one will likely need to pad lines on the right with spaces so the geometry is accurate. Similarly, one may need extra blank lines at the bottom. The -extraw and -extrah are global options and effect all tips and could be used as well.

# #
# cooltip-1.6.tm
#
#
# tooltips with colors
#
# Copyright (c) 1996-2007 Jeffrey Hobbs
#
# See the file "license.terms" for information on usage and redistribution
# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
#
# RCS: @(#) $Id: tooltip.tcl,v 1.16 2008/12/01 23:37:16 hobbs Exp $
#
# Initiated: 28 October 1996
# As modified by ET 25 dec 2023

package require Tk 8.5-
package require msgcat

#------------------------------------------------------------------------
# PROCEDURE
#   tooltip::tooltip
#
# DESCRIPTION
#   Implements a tooltip (balloon help) system
#
# ARGUMENTS
#   tooltip <option> ?arg?
#
# clear ?pattern?
#   Stops the specified widgets (defaults to all) from showing tooltips.
#
# configure ?opt ?val opt val ...??    if configureall returns even {} items on introspection
#       Configure foreground, background and font.
#       and 7+9 color replacements + wrap, extrah, extraw
#
# delay ?millisecs?
#   Query or set the delay.  The delay is in milliseconds and must
#   be at least 50.  Returns the delay.
#
# fade ?boolean?
#   Enables or disables fading of the tooltip.
#
# disable OR off
#   Disables all tooltips.
#
# enable OR on
#   Enables tooltips for defined widgets.
#
# <widget> ?-index index? ?-item(s) items? ?-tab tabId" ?-tag tag? ?message?
#   * If -index is specified, then <widget> is assumed to be a menu and
#     index represents what index into the menu (either the numerical index
#     or the label) to associate the tooltip message with.
#     Tooltips do not appear for disabled menu items.
#   * If -item(s) is specified, then <widget> is assumed to be a listbox,
#     ttk::treeview or canvas and items specifies one or more items.
#   * If -tab is specified, then <widget> is assumed to be a ttk::notebook
#     and tabId specifies a tab identifier.
#   * If -tag is specified, then <widget> is assumed to be a text and tag
#     specifies a tag name.
#   If message is {}, then the tooltip for that widget is removed.
#   The widget must exist prior to calling tooltip.  The current
#   tooltip message for <widget> is returned, if any.
#
# RETURNS: varies (see methods above)
#
# NAMESPACE & STATE
#   The namespace tooltip is used.
#   Control toplevel name via ::tooltip::wname.
#
# EXAMPLE USAGE:
#   tooltip .button "A Button"
#   tooltip .menu -index 5 "Loads a file" - index must be a number
# tooltip::G(alwayson) is a list of regular widgets (i.e. ones with no -index, etc.) that are always on
#   These apply only to buttons, checkboxes, entry, etc.
#------------------------------------------------------------------------

namespace eval ::tooltip {
    namespace export -clear tooltip
    variable tooltip
    variable G

    if {![info exists G]} {
        # config_done is set whenever the configure changes anything, so we can trace it in the gui program
        array set G {
            config_done 1
            enabled     1
            fade        1
            extraw       0
            extrah       0
            background lightyellow
            font        {FixedSys 10}
            FADESTEP    0.2
            FADEID      {}
            DELAY       500
            AFTERID     {}
            alwayson    {}
            LAST        -1
            TOPLEVEL    .__tooltip__
        }
        if {[tk windowingsystem] eq "x11"} {
            set G(fade) 0 ; # don't fade by default on X11
        }
    }

    # functional options
    option add *Tooltip.Label.highlightThickness 0
    option add *Tooltip.Label.relief             solid
    option add *Tooltip.Label.borderWidth        1
    option add *Tooltip.Label.padX               5
    option add *Tooltip.Label.padY               5
    # configurable options
    option add *Tooltip.Label.background         lightyellow
    option add *Tooltip.Label.foreground         black
    option add *Tooltip.Label.font               $G(font)   ;# was TkTooltipFont

    # The extra ::hide call in <Enter> is necessary to catch moving to
    # child widgets where the <Leave> event won't be generated
    bind Tooltip <Enter> [namespace code {
    #tooltip::hide
    variable tooltip
    variable G
    set G(LAST) -1
    if {($G(enabled) || ("%W" in $G(alwayson))) && [info exists tooltip(%W)]} {
        set G(AFTERID) \
        [after $G(DELAY) [namespace code [list show %W $tooltip(%W) cursor]]]
    }
    }]

    bind Menu <<MenuSelect>>    [namespace code { menuMotion %W }]
    bind Tooltip <Leave>    [namespace code [list hide 1]] ; # fade ok
    bind Tooltip <Any-KeyPress> [namespace code hide]
    bind Tooltip <Any-Button>   [namespace code hide]
}

proc ::tooltip::tooltip {w args} {
    variable tooltip
    variable G
    switch -glob -- $w {
    clear   {
        if {[llength $args]==0} { set args .* }
        clear [lindex $args 0]
    }
    delay   {
        if {[llength $args]} {
        set millisecs [lindex $args 0]
        if {![string is integer -strict $millisecs] || $millisecs<50} {
            return -code error "tooltip delay must be an integer\
                greater than or equal to 50 (delay is in millisecs)"
        }
        return [set G(DELAY) $millisecs]
        } else {
        return $G(DELAY)
        }
    }
    fade    {
        if {[llength $args]} {
        set G(fade) [string is true -strict [lindex $args 0]]
        }
        return $G(fade)
    }
    off - disable   {
        set G(enabled) 0
        hide
    }
    on - enable {
        set G(enabled) 1
    }
    con* {
            return [::tooltip::configure $w {*}$args]
    }
    default {
        set i $w
        if {[llength $args]} {
        set i [uplevel 1 [namespace code [list register $w {*}$args]]]
        }
        set b $G(TOPLEVEL)
        if {[info exists tooltip($i)]} { return $tooltip($i) }
    }
    }
}

proc ::tooltip::register {w args} {
    variable tooltip
    set key [lindex $args 0]
    while {[string match -* $key]} {
    switch -- $key {
        -- {
            set args [lreplace $args 0 0]
            set key [lindex $args 0]
            break
        }
        -index {
        if {[catch {$w entrycget 1 -label}]} {
            return -code error "widget \"$w\" does not seem to be a\
                menu, which is required for the -index switch"
        }
        set index [lindex $args 1]
        set args [lreplace $args 0 1]
        }
        -item - -items {
                if {[winfo class $w] in {Listbox Treeview}} {
                    set items [lindex $args 1]
                } else {
                    set namedItem [lindex $args 1]
                    if {[catch {$w find withtag $namedItem} items]} {
                        return -code error "widget \"$w\" is not a canvas, or\
                item \"$namedItem\" does not exist in the canvas"
                    }
                }
        set args [lreplace $args 0 1]
        }
        -tab {
        if {[winfo class $w] ne "TNotebook"} {
            return -code error "widget \"$w\" is not a ttk::notebook\
            widget"
        }
        set tabId [lindex $args 1]
        if {[catch {$w index $tabId} tabIndex]} {
            return -code error $tabIndex
        } elseif {$tabIndex < 0 || $tabIndex >= [$w index end]} {
            return -code error "tab index $tabId out of bounds"
        }
        set tabWin [lindex [$w tabs] $tabIndex]
        set args [lreplace $args 0 1]
        }
            -tag {
                set tag [lindex $args 1]
                set r [catch {lsearch -exact [$w tag names] $tag} ndx]
                if {$r || $ndx == -1} {
                    return -code error "widget \"$w\" is not a text widget or\
                        \"$tag\" is not a text tag"
                }
                set args [lreplace $args 0 1]
            }
        default {
        return -code error "unknown option \"$key\":\
            should be -index, -item(s), -tab, -tag or --"
        }
    }
    set key [lindex $args 0]
    }
    if {[llength $args] != 1} {
    return -code error "wrong # args: should be \"tooltip widget\
        ?-index index? ?-item(s) items? ?-tab tabId? ?-tag tag? ?--?\
        message\""
    }
    if {$key eq ""} {
    clear $w
    } else {
    if {![winfo exists $w]} {
        return -code error "bad window path name \"$w\""
    }
    if {[info exists index]} {
        set tooltip($w,$index) $key
        return $w,$index
    } elseif {[info exists items]} {
        foreach item $items {
        set tooltip($w,$item) $key
        set class [winfo class $w]
        if { $class eq "Listbox" || $class eq "Treeview"} {
            enableListbox $w $item
        } else {
            enableCanvas $w $item
        }
        }
        # Only need to return the first item for the purposes of
        # how this is called
        return $w,[lindex $items 0]
    } elseif {[info exists tabWin]} {
        set tooltip($w,$tabWin) $key
        enableNotebook $w $tabWin
        return $w,$tabWin
        } elseif {[info exists tag]} {
            set tooltip($w,t_$tag) $key
            enableTag $w $tag
            return $w,$tag
    } else {
        set tooltip($w) $key
        # Note: Add the necessary bindings only once.
        set tags [bindtags $w]
        if {[lsearch -exact $tags "Tooltip"] == -1} {
        bindtags $w [linsert $tags end "Tooltip"]
        }
        return $w
    }
    }
}

proc ::tooltip::createToplevel {} {
    variable G
    variable labelOpts
    set b $G(TOPLEVEL)

    toplevel $b -class Tooltip -borderwidth 0
    if {[tk windowingsystem] eq "aqua"} {
        ::tk::unsupported::MacWindowStyle style $b help none
    } else {
        wm overrideredirect $b 1
    }
    catch {wm attributes $b -topmost 1}
    # avoid the blink issue with 1 to <1 alpha on Windows
    catch {wm attributes $b -alpha 0.99}
    wm positionfrom $b program
    wm withdraw $b
    label $b.label {*}[expr {[info exists labelOpts] ? $labelOpts : ""}]
#                           cputs green "label create [linsert $labelOpts 0 label $b.label]\n"
                            text $b.text -background yellow -relief raised -bd 2 -wrap none -font $G(font)
                            pack $b.text  -ipadx 1 -expand 1 -fill both ;#create our text widget instead of the label, leave label alone though, just don't pack it
                            set_tags $b.text ;# setup our style and color tags
#               pack $b.text $b.label  -ipadx 1
}

proc ::tooltip::configure {config args} { ;# config is the entire word, if it's configureall, it's special returns even {} items
    set len [llength $args]
    if {$len >= 2 && ($len % 2) != 0} {
        return -level 2 -code error "wrong # args. Should be\
                \"tooltip configure ?opt ?val opt val ...??\""
    }
    
    variable G
    set b $G(TOPLEVEL)
    if {![winfo exists $b]} {
        createToplevel
    }
    set opts1 {-foreground -background -font}
    set opts2 {-red -green -blue -orange -violet -black -grey -grey51 -grey52 -grey53 -grey54 -grey55 -grey56 -grey57 -grey58 -grey59 -extraw -extrah -wrap}
    if { $len == 0 } {
        set keys {}
        foreach item $opts1 {
            lappend keys $item [::tooltip::configure $config $item] 
        }
        foreach item $opts2 {
            lappend keys $item [::tooltip::configure $config $item] 
        }
        return $keys
    }
    
    foreach {key value} $args {
        if       { $key eq "-fg" } {
            set key -foreground
        } elseif { $key eq "-bg"  } {
            set key -background
        }
        if       { $key in $opts1 } { ;# compatibility
            set keys {}
            foreach opt {-foreground -background -font} {
                set val [$b.label configure $opt]
                set opts($opt) [lindex $val 4]
                set defs($opt) [lindex $val 1]
                lappend keys $opt
            }
            if { $len == 1  } {
                
                set key [lindex $args 0]
                if       { $key eq "-fg" } {
                    set key -foreground
                } elseif { $key eq "-bg"  } {
                    set key -background
                }
                if {$key ni $keys} {
                    return -level 2 -code error "unknown option \"$key\""
                } else {
                    return $opts($key)
                }
            } else {
                # allow -fg and -bg as aliases
                lappend keys -fg -bg
                set defs(-fg) $defs(-foreground)
                set defs(-bg) $defs(-background)
                
                if {$key ni $keys} {
                    return -level 2 -code error "unknown option \"$key\""
                }
                
                if [catch {
                    $b.label configure $key $value
                    option add *Tooltip.Label.$defs($key) $value
#                   puts "option add *Tooltip.Label.$defs($key) $value"
                    if { $key eq "-font" } {
                         $b.text tag configure label -font $value
                         set G(font) $value
                         set_tags $b.text no
                         $b.text configure -font $value
                    }
                    if { $key eq "-background" } {
                         set G(background) $value
                    }
                } err] {
                    return -level 2 -code error $err
                }
                    
            }
            
        } elseif { $key in $opts2 }  {
#           puts  "in opt2 keyx= |$key| opts2= |$opts2| "
            if       { $len == 1 } {
                if { $key eq "-extraw" } {
                    return $G(extraw) 
                } elseif { $key eq "-wrap" } {
                    return [$G(TOPLEVEL).text cget -wrap]
                } elseif { $key eq "-extrah" } {
                    return $G(extrah) 
                }
                set cfig [$b.text tag configure [string range $key 1 end]]
                set cfig2 [list]
                foreach item $cfig {
                    if { [lindex $item end] ne "" || $config eq "configureall"} {
                        lappend cfig2 [lindex $item 0] [lindex $item end]
                    }   
                }
                return $cfig2
            } else {
                if { $key eq "-extraw" } {
                    set G(extraw) $value
                } elseif { $key eq "-extrah" } {
                    set G(extrah) $value
                } elseif { $key eq "-wrap" } {
                     $G(TOPLEVEL).text config -wrap $value
                    
                } else {
                    $b.text  tag configure [string range $key 1 end]   {*}$value
                }
            }
        } else {
            return -level 2 -code error "unknown option $key"
        }
    }
    incr G(config_done) 1 ;# write to this so config change can be traced
    return
    
    
}


proc ::tooltip::clear {{pattern .*}} {
    variable tooltip
    # cache the current widget at pointer
    set ptrw [winfo containing {*}[winfo pointerxy .]]
    foreach w [array names tooltip $pattern] {
    unset tooltip($w)
    if {[winfo exists $w]} {
        set tags [bindtags $w]
        if {[set i [lsearch -exact $tags "Tooltip"]] != -1} {
        bindtags $w [lreplace $tags $i $i]
        }
        ## We don't remove TooltipMenu because there
        ## might be other indices that use it

        # Withdraw the tooltip if we clear the current contained item
        if {$ptrw eq $w} { hide }
    }
    }
}

proc ::tooltip::show {w msg {i {}}} {
    if {![winfo exists $w]} { return }

    # Use string match to allow that the help will be shown when
    # the pointer is in any descendant of the desired widget
    if {([winfo class $w] ne "Menu")
    && ![string match $w* [winfo containing {*}[winfo pointerxy $w]]]} {
    return
    }

    variable G

    after cancel $G(FADEID)
    set b $G(TOPLEVEL)
    if {![winfo exists $b]} {
        createToplevel
    }
    # Use late-binding msgcat (lazy translation) to support programs
    # that allow on-the-fly l10n changes
    $b.label configure -text [::msgcat::mc $msg] -justify left
                                            if { [string range $msg 0 1] eq "##" } {
                                                set msg [string range $msg 2 end]
#                                               puts "\n---\nfound a complex one\n$msg\n---"
                                                $b.text delete 1.0 end
                                                tooltip::restore $b.text $msg
                                                lassign [w-h $b.text] wid ht
#                                               puts "Wid= |$wid| Ht= |$ht| complex one"
                                                $b.text config -width [expr {   $wid+$G(extraw)    }] -height [expr {   $ht-2+$G(extrah)   }] -background $G(background)
                                            } else {
                                                wm geom $b {}
                                                $b.text delete 1.0 end
                                                $b.text insert 1.0 $msg label
                                                lassign [w-h-l $msg] wid ht
#                                               puts "wid= |$wid| ht= |$ht| "
                                                set bg [$b.label cget -background]
                                                set fg [$b.label cget -foreground]
                                                set ft [$b.label cget -font]
#                                                $b.text config -width [expr {   $wid+$G(extraw)    }] -height [expr {   $ht-1+$G(extrah)   }] -background $bg -foreground $fg -font $ft
                                                $b.text config -width [expr {   $wid    }] -height [expr {   $ht-1  }] -background $bg -foreground $fg -font $ft
                                            }
    update idletasks
    set screenw [winfo screenwidth $w]
    set screenh [winfo screenheight $w]
    set reqw [winfo reqwidth $b]
    set reqh [winfo reqheight $b]
    # When adjusting for being on the screen boundary, check that we are
    # near the "edge" already, as Tk handles multiple monitors oddly
    if {$i eq "cursor"} {
        set py [winfo pointery $w]
    set y [expr {$py + 20}]
# this is a wrong calculation?
#   if {($y < $screenh) && ($y+$reqh) > $screenh} {}
    if { ($y + $reqh) > $screenh } {
        set y [expr {$py - $reqh - 5}]
    }
    } elseif {$i ne ""} {
        # menu entry
    set y [expr {[winfo rooty $w]+[winfo vrooty $w]+[$w yposition $i]+25}]
    if {($y < $screenh) && ($y+$reqh) > $screenh} {
        # show above if we would be offscreen
        set y [expr {[winfo rooty $w]+[$w yposition $i]-$reqh-5}]
    }
    } else {
    set y [expr {[winfo rooty $w]+[winfo vrooty $w]+[winfo height $w]+5}]
    if {($y < $screenh) && ($y+$reqh) > $screenh} {
        # show above if we would be offscreen
        set y [expr {[winfo rooty $w]-$reqh-5}]
    }
    }
    if {$i eq "cursor"} {
    set x [winfo pointerx $w]
    } else {
    set x [expr {[winfo rootx $w]+[winfo vrootx $w]+
             ([winfo width $w]-$reqw)/2}]
    }
    # only readjust when we would appear right on the screen edge
    if {$x<0 && ($x+$reqw)>0} {
    set x 0
    } elseif {($x < $screenw) && ($x+$reqw) > $screenw} {
    set x [expr {$screenw-$reqw}]
    }
    if {[tk windowingsystem] eq "aqua"} {
    set focus [focus]
    }
    # avoid the blink issue with 1 to <1 alpha on Windows, watch half-fading
    catch {wm attributes $b -alpha 0.99}
    wm geometry $b +$x+$y
    wm deiconify $b
    raise $b
    if {[tk windowingsystem] eq "aqua" && $focus ne ""} {
    # Aqua's help window steals focus on display
    after idle [list focus -force $focus]
    }
}
proc ::tooltip::w-h {w} {
    set max 0
    for {set item 0} {$item < 100} {incr item} {
        set ind [$w index ${item}.end]  
        set wid [lindex [split $ind  .] 1]
        if { $wid > $max } {
            set max $wid
        }
    }
    set h [lindex [split [$w index end] .] 0 ]
    return [list $max $h]
}
proc ::tooltip::w-h-l {l} {
    set max 0
    set lines [split $l \n]
    foreach line $lines {
        set wid [string length $line]
        if { $wid > $max } {
            set max $wid
        }
    }
    return [list $max [llength $lines]]
}

proc ::tooltip::menuMotion {w} {
    variable G

    if {$G(enabled)} {
    variable tooltip

        # Menu events come from a funny path, map to the real path.
        set m [string map {"#" "."} [winfo name $w]]
    set cur [$w index active]

    # The next two lines (all uses of LAST) are necessary until the
    # <<MenuSelect>> event is properly coded for Unix/(Windows)?
#   puts "cur= |$cur| G(LAST)= |$G(LAST)| returning = [expr {  $cur == $G(LAST)    }]" ;# this is broken on 9.0b1 (if only 1 item)
#    if {$cur == $G(LAST)} return ;# I don't grok this, but it breaks the code in 9.0b1, and w/o it, it works in 8.6 and 9.0 ughhhh
    set G(LAST) $cur
    # a little inlining - this is :hide
    after cancel $G(AFTERID)

    catch {wm withdraw $G(TOPLEVEL)}
#               puts "[info exists tooltip($m,$cur)]  $m,$cur"
                if { ! [info exists tooltip($m,$cur)]} {
                    if { [info exists tooltip($w,$cur)] } {
                        set m $w
                    }
                    
                }
#               puts "[info exists tooltip($m,$cur)]  $m,$cur  [catch {$w entrycget $cur -label} cur] [info exists tooltip($m,$cur)]"
    if {[info exists tooltip($m,$cur)] || \
        (![catch {$w entrycget $cur -label} cur] && \
        [info exists tooltip($m,$cur)])} {
        set G(AFTERID) [after $G(DELAY) \
            [namespace code [list show $w $tooltip($m,$cur) cursor]]] 
    }
    }
}

proc ::tooltip::hide {{fadeOk 0}} {
    variable G

    after cancel $G(AFTERID)
    after cancel $G(FADEID)
    if {$fadeOk && $G(fade)} {
    fade $G(TOPLEVEL) $G(FADESTEP)
    } else {
    catch {wm withdraw $G(TOPLEVEL)}
    }
}

proc ::tooltip::fade {w step} {
    if {[catch {wm attributes $w -alpha} alpha] || $alpha <= 0.0} {
        catch { wm withdraw $w }
        catch { wm attributes $w -alpha 0.99 }
    } else {
    variable G
        wm attributes $w -alpha [expr {$alpha-$step}]
        set G(FADEID) [after 50 [namespace code [list fade $w $step]]]
    }
}

proc ::tooltip::wname {{w {}}} {
    variable G
    if {[llength [info level 0]] > 1} {
    # $w specified
    if {$w ne $G(TOPLEVEL)} {
        hide
        destroy $G(TOPLEVEL)
        set G(TOPLEVEL) $w
    }
    }
    return $G(TOPLEVEL)
}

proc ::tooltip::listitemTip {w x y} {
    variable tooltip
    variable G

    set G(LAST) -1
    if {[winfo class $w] eq "Listbox"} {
    set item [$w index @$x,$y]
    } else {
    set item [$w identify item $x $y]
    }
    if {$G(enabled) && [info exists tooltip($w,$item)]} {
    set G(AFTERID) [after $G(DELAY) \
        [namespace code [list show $w $tooltip($w,$item) cursor]]]
    }
}

# Handle the lack of <Enter>/<Leave> between listbox/treeview items using
# <Motion>
proc ::tooltip::listitemMotion {w x y} {
    variable tooltip
    variable G
    if {$G(enabled)} {
    if {[winfo class $w] eq "Listbox"} {
        set item [$w index @$x,$y]
    } else {
        set item {}
        set region [$w identify region $x $y]
        if {$region  eq "tree" || $region eq "cell"} {
        set item [$w identify item $x $y]
        }
    }
        if {$item ne $G(LAST)} {
            set G(LAST) $item
            after cancel $G(AFTERID)
            catch {wm withdraw $G(TOPLEVEL)}
            if {[info exists tooltip($w,$item)]} {
                set G(AFTERID) [after $G(DELAY) \
                   [namespace code [list show $w $tooltip($w,$item) cursor]]]
            }
        }
    }
}

# Initialize tooltip events for listbox/treeview widgets
proc ::tooltip::enableListbox {w args} {
    if {[string match *listitemTip* [bind $w <Enter>]]} { return }
    bind $w <Enter> +[namespace code [list listitemTip %W %x %y]]
    bind $w <Motion> +[namespace code [list listitemMotion %W %x %y]]
    bind $w <Leave> +[namespace code [list hide 1]] ; # fade ok
    bind $w <Any-KeyPress> +[namespace code hide]
    bind $w <Any-Button> +[namespace code hide]
}

proc ::tooltip::canvasitemTip {w args} {
    variable tooltip
    variable G

    set G(LAST) -1
    set item [$w find withtag current]
    if {$G(enabled) && [info exists tooltip($w,$item)]} {
    set G(AFTERID) [after $G(DELAY) \
        [namespace code [list show $w $tooltip($w,$item) cursor]]]
    }
}

proc ::tooltip::enableCanvas {w args} {
    if {[string match *canvasitemTip* [$w bind all <Enter>]]} { return }
    $w bind all <Enter> +[namespace code [list canvasitemTip $w]]
    $w bind all <Leave> +[namespace code [list hide 1]] ; # fade ok
    $w bind all <Any-KeyPress> +[namespace code hide]
    $w bind all <Any-Button> +[namespace code hide]
}

proc ::tooltip::notebooktabTip {w x y} {
    variable tooltip
    variable G

    set G(LAST) -1
    set tabIndex [$w index @$x,$y]
    set tabWin [lindex [$w tabs] $tabIndex]
    if {$G(enabled) && [info exists tooltip($w,$tabWin)]} {
    set G(AFTERID) [after $G(DELAY) \
        [namespace code [list show $w $tooltip($w,$tabWin) cursor]]]
    }
}

# Handle the lack of <Enter>/<Leave> between ttk::notebook items using <Motion>
proc ::tooltip::notebooktabMotion {w x y} {
    variable tooltip
    variable G
    if {$G(enabled)} {
    set tabIndex [$w index @$x,$y]
    set tabWin [lindex [$w tabs] $tabIndex]
        if {$tabWin ne $G(LAST)} {
            set G(LAST) $tabWin
            after cancel $G(AFTERID)
            catch {wm withdraw $G(TOPLEVEL)}
            if {[info exists tooltip($w,$tabWin)]} {
                set G(AFTERID) [after $G(DELAY) \
                   [namespace code [list show $w $tooltip($w,$tabWin) cursor]]]
            }
        }
    }
}

# Initialize tooltip events for ttk::notebook widgets
proc ::tooltip::enableNotebook {w args} {
    if {[string match *notebooktabTip* [bind $w <Enter>]]} { return }
    bind $w <Enter> +[namespace code [list notebooktabTip %W %x %y]]
    bind $w <Motion> +[namespace code [list notebooktabMotion %W %x %y]]
    bind $w <Leave> +[namespace code [list hide 1]] ; # fade ok
    bind $w <Any-KeyPress> +[namespace code hide]
    bind $w <Any-Button> +[namespace code hide]
}

proc ::tooltip::tagTip {w tag} {
    variable tooltip
    variable G
    set G(LAST) -1
    if {$G(enabled) && [info exists tooltip($w,t_$tag)]} {
        if {[info exists G(AFTERID)]} { after cancel $G(AFTERID) }
        set G(AFTERID) [after $G(DELAY) \
            [namespace code [list show $w $tooltip($w,t_$tag) cursor]]]
    }
}

proc ::tooltip::enableTag {w tag} {
    if {[string match *tagTip* [$w tag bind $tag]]} { return }
    $w tag bind $tag <Enter> +[namespace code [list tagTip $w $tag]]
    $w tag bind $tag <Leave> +[namespace code [list hide 1]] ; # fade ok
    $w tag bind $tag <Any-KeyPress> +[namespace code hide]
    $w tag bind $tag <Any-Button> +[namespace code hide]
}
proc ::tooltip::set_tags {w {colors yes}} {

    variable G
    set font $G(font)
    $w tag configure bolditalicunder        -font "$font bold italic underline"
    $w tag configure boldunder              -font "$font bold underline"
    $w tag configure italicunder            -font "$font italic underline"
    $w tag configure bolditalic             -font "$font bold italic"
    $w tag configure bold                   -font "$font bold"
    $w tag configure italic                 -font "$font italic"
    $w tag configure under                  -font "$font underline"
    $w tag configure normal                 -font "$font"
    $w tag configure label                  -font "$font"
    
    if { $colors } {
        $w  tag configure red                   -foreground red
        $w  tag configure green                 -foreground green
        $w  tag configure blue                  -foreground lightblue
        $w  tag configure orange                -foreground orange
        $w  tag configure violet                -foreground violet
        $w  tag configure grey                  -foreground grey80
        $w  tag configure black                 -foreground black 
        foreach gr {1 2 3 4 5 6 7 8 9} {
            $w  tag configure grey5$gr           -foreground black -background grey90 -selectbackground lightblue
        }
    }
   
    
    
    $w tag add normal insert
}

proc ::tooltip::restore {w savex} {
    set save [subst -nocommands -novariables $savex]
    # create items, restoring their attributes
    foreach {key value index} $save \
            {
        switch $key \
                {
            exec    { eval [string replace $value 0 [string first " " $value]-1 $w] }
            image   { $w image create $index -name $value }
            text    { $w insert $index $value }
            mark    {
                switch $value {
                    current { set currentIndex $index }
                    insert { set insertIndex $index}
                    default { $w mark set $value $index }
                }
            }
            tagon   { set tag($value) $index }
            tagoff  { $w tag add $value $tag($value) $index }
            window  { $w window create $index -window $value }
        }
    }
    $w mark set current $currentIndex
    $w mark set insert $insertIndex
}

package provide tooltip 1.6 ;# - not needed for cooltip, but keeps this from causing conflicts if user tries a package require tooltip also


Below in the discussion is the cooltip gui builder program. Copy it to a file in the same directory as cooltip-1.6.tm and run with wish. If the module is in one of the known directories, the module system will find it and this same directory requirement would not be needed.

# The cooltip gui editor from Arjen's little wp, by ET 12/25/2023
#
# wordp.tcl --
#     A very basic word processor original by Arjen
#

# mainWindow --
#     Set up the window with some buttons
#
lappend auto_path [pwd]
set wtitle [wm title .]
wm title .  "$wtitle tooltip"

set ::cooltip [file dirname [info script]] 
tcl::tm::path add [file dirname [info script]] 
set ::ver [package require cooltip 1.6]


set ::myfont $::tooltip::G(font)
set ::nowcolor black


# add your own picks here

set picks   {\u261B \u2611 \u2190   \u2191  \u2192  \u2193  \u2194  
            \u2195  \u2196  \u2197  \u2198  
            \u2726  \u2727  \u2729  \u272A  \u2022 \u00A9 \u2023 "\u231A \u231B"
            \u277D \u25B2   "\u007B \uFFFC \u007D  \u3010 \uFFFC \u3011" 
            "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"
            "-setup choices-" "-in variable-" "-picks-"
} ;# set some favorites here, the 10 char line makes the tearoff wider as well


#0x2190 LEFTWARDS ARROW 
#0x2191 UPWARDS ARROW   
#0x2192 RIGHTWARDS ARROW    
#0x2193 DOWNWARDS ARROW 
#0x2194 LEFT RIGHT ARROW    
#0x2195 UP DOWN ARROW   
#0x2196 NORTH WEST ARROW    
#0x2197 NORTH EAST ARROW    
#0x2198 SOUTH EAST ARROW    

#0x2726 BLACK FOUR POINTED STAR
#0x2727 WHITE FOUR POINTED STAR
#0x2729 STRESS OUTLINED WHITE STAR  
#0x272A CIRCLED WHITE STAR  

#0x277D DINGBAT NEGATIVE CIRCLED DIGIT EIGHT
#0x25B2 BLACK UP-POINTING TRIANGLE

namespace eval popup {
    set VERSION 0.1
}



set tears {} ;# list of tearoff menus, so we can gang move them
set lastgeom {}
proc mainWindow {} {
    catch {console show}
    global bold
    global italic
    global under
    global red
    global tootip_varname tipsonoff
    global colorlab
    global picks
    global texttag
    
    set ::atoz "--"
    set ::clipboard {}
    dosetsave ""
    
    puts "Dir= |$::cooltip| ver=$::ver saveto= |$::savefile| font= |$::myfont| "
   
    set ::themex 0
    set ::darkonoff 0
    set ::curtheme [ttk::style theme use]
    puts "::argv= |$::argv| curtheme= |$::curtheme| "
# on linux, menus will be made darkmode by awthemes using option database, not ttk styles, simply by doing the package require awdark
# so we can only suppress it by not doing the package require, hence the user command line option
# On windows, this is suppressed for some reason, comment said it had bad side effects so not done   
    if { "-nodark" ni $::argv } { ;
        if [catch {
            package require awdark 
            set ::themex 1
            ttk::style theme use awdark
            set ::darkonoff 1
        } err_code] {
            puts stderr $err_code 
        }
    }
    set tootip_varname "tooltip"
    #
    # Create the toolbar
    #
    global t
    set t [frame .toolbar]

    text   .text -font $::myfont -width 100 -height 50 -wrap none -undo true -insertbackground red -background grey85 ;# need to see the cursor, red seems to work

    ttk::button      $t.load   -text "Load"             -style Toolbutton -command [list loadf .text $t.testbut]
    ttk::button      $t.save   -text "Save"             -style Toolbutton -command {storeText 1}
    ttk::combobox    $t.atoz   -text "A-Z"              -style Toolbutton -width 2 -height 27 -textvariable ::atoz -values {"--" A B C D E F G H I J K L M N O P Q R S T U V W X Y Z}   
    bind $t.atoz <<ComboboxSelected>> [list doatoz] 
    $t.atoz set "--"
   
    ttk::button      $t.next   -text "+"                -style Toolbutton -command {loadnextone}
    ttk::button      $t.copy   -text "Copy"             -style Toolbutton -command {storeText 0}
    ttk::button      $t.paste  -text "Paste"            -style Toolbutton -command [list clip .text $t.testbut]
    ttk::button      $t.clear  -text "Clear"            -style Toolbutton 
    ttk::checkbutton $t.bold   -text "Bold"             -style Toolbutton -variable bold   -command {toggleFont}
    ttk::checkbutton $t.italic -text "Italic"           -style Toolbutton -variable italic -command {toggleFont}
    ttk::checkbutton $t.under  -text "Underline"        -style Toolbutton -variable under  -command {toggleFont}
    ttk::label       $t.pick   -text "Pick"             -style Toolbutton 
    ttk::label       $t.color  -text "Color"            -style Toolbutton -background black -foreground white
    ttk::button      $t.test   -text "Notes"            -style Toolbutton -command  [list testit $t.testbut]
    ttk::button      $t.testbut -text "\u2192Test"      -style Toolbutton -command  [list testit $t.testbut]
    ttk::checkbutton $t.tip    -text "Tips On"          -variable tipsonoff -command {onoff}

    set tooltip::G(alwayson) [list $t.testbut]

    ttk::separator .sep
    ttk::separator $t.sep1 -orient vertical
    ttk::separator $t.sep2 -orient vertical
    ttk::separator $t.sep3 -orient vertical
    ttk::separator $t.sep4 -orient vertical
    ttk::separator $t.sep5 -orient vertical
   
    set colorlab $t.color ;# so we can change it to the current color
 
    bind $t.testbut <Enter> [list testit $t.testbut]
    bind $t.clear <1>         {clear text}
    bind $t.clear <Control-1> {clear all}
    bind $t.clear <Shift-1>   {clear config}
    
# our 2 tearoff menus 
    set ::fnt {ariel 10}
    set ::popup::menu(color) {
        {radiobutton  -label "red         "  -value red       -variable nowcolor  -background "PaleVioletRed1"  -foreground black -font $::fnt  -command colorset}
        {radiobutton  -label "green       "  -value green     -variable nowcolor  -background "lightgreen"      -foreground black -font $::fnt  -command colorset}
        {radiobutton  -label "blue        "  -value blue      -variable nowcolor  -background "lightblue"       -foreground black -font $::fnt  -command colorset}
        {radiobutton  -label "orange      "  -value orange    -variable nowcolor  -background "orange"          -foreground black -font $::fnt  -command colorset}
        {radiobutton  -label "violet      "  -value violet    -variable nowcolor  -background "violet"          -foreground black -font $::fnt  -command colorset}
        {radiobutton  -label "grey        "  -value grey      -variable nowcolor  -background "grey80"          -foreground white -font $::fnt  -command colorset}
        {radiobutton  -label "black       "  -value black     -variable nowcolor  -background "black"           -foreground white -font $::fnt  -command colorset -selectcolor white}
        {separator}
        {separator}
        {radiobutton  -label "grey51      "  -value grey51    -variable nowcolor  -background "grey80"          -foreground white -font $::fnt  -command colorset -selectcolor white}
        {radiobutton  -label "grey52      "  -value grey52    -variable nowcolor  -background "grey70"          -foreground white -font $::fnt  -command colorset -selectcolor white}
        {radiobutton  -label "grey53      "  -value grey53    -variable nowcolor  -background "grey80"          -foreground white -font $::fnt  -command colorset -selectcolor white}
        {radiobutton  -label "grey54      "  -value grey54    -variable nowcolor  -background "grey70"          -foreground white -font $::fnt  -command colorset -selectcolor white}
        {radiobutton  -label "grey55      "  -value grey55    -variable nowcolor  -background "grey80"          -foreground white -font $::fnt  -command colorset -selectcolor white}
        {radiobutton  -label "grey56      "  -value grey56    -variable nowcolor  -background "grey70"          -foreground white -font $::fnt  -command colorset -selectcolor white}
        {radiobutton  -label "grey57      "  -value grey57    -variable nowcolor  -background "grey80"          -foreground white -font $::fnt  -command colorset -selectcolor white}
        {radiobutton  -label "grey58      "  -value grey58    -variable nowcolor  -background "grey70"          -foreground white -font $::fnt  -command colorset -selectcolor white}
        {radiobutton  -label "grey59      "  -value grey59    -variable nowcolor  -background "grey80"          -foreground white -font $::fnt  -command colorset -selectcolor white}
        {separator}
        {separator}
        {checkbutton  -label "DarkMode    "  -variable darkonoff -onvalue 1 -offvalue 0 -command {dodark}       -font $::fnt}
        {checkbutton  -label "Wrapping    "  -variable wraponoff -onvalue 1 -offvalue 0 -command {dowrapping}   -font $::fnt}
        {separator}
        {command      -label "Configure   " -command {con_dlog $::nowcolor}  -font $::fnt}
        {command      -label "Change Folder" -command {newpwd}  -font $::fnt}
        {separator}
        {command      -label "Create Catalog" -command {createcatalog}  -font $::fnt}
        {command      -label "Expand Catalog" -command {expandcatalog}  -font $::fnt}
        {cascade -label "Borders" -menu .borders -font $::fnt }
        {separator}
        {command      -label "Console"   -command {console show}          -font $::fnt}
        {separator}
        {separator}
        {command      -label "Exit"      -command exit  -foreground red   -font $::fnt} 
    }
    set ::popup::menu(borders) {
        {command -label "Configure Borders ..."  -command {main_dlog} -font $::fnt  }
        {command -label "Output Border Code   " -command {dump_main_config} -font $::fnt  }
        {nop command -label "Add Borders3 "     -command {do_addborders 3} -font $::fnt  }
    }
    set ::wraponoff 0
    set ::popup::menu(picks) {}
    foreach pick $picks {
        append ::popup::menu(picks) \n \{ [list command -label $pick -font {arial 12} -command "pick1 $pick"] \} 
    }
    
    ::popup::create color 1 {-tearoffcommand {tearoffcmd 0 0 } }
    ::popup::create picks 1 {-tearoffcommand {tearoffcmd 140 0} }
    ::popup::create borders 1 {-tearoffcommand {tearoffcmd -37 -50} }
    
    bind $t.color  <Button-1> {::popup::show %W color} 
    bind $t.pick   <Button-1> {::popup::show %W picks} 

# ok, now grid them or pack em, I prefer packing

    set widgets [list  $t.load  $t.save $t.atoz $t.next $t.clear $t.sep1  $t.copy $t.paste $t.sep2 $t.test $t.testbut  $t.sep3 $t.bold $t.italic $t.under $t.sep4  $t.color $t.pick $t.sep5 $t.tip]
    if { 0 } {
        grid {*}$widgets  -padx 2 -sticky ns
    
        grid   $t    -sticky w
        grid   .sep  -sticky we
        grid   .text
    } else {
        pack  {*}$widgets  -side left -fill both -expand true -anchor w
    
        pack   $t  .sep  .text -fill x -side top
    }

#
# Tags for the various fonts
#
    tooltip::set_tags .text
    .text tag add normal insert


    set texttag normal
    set italic  0
    set bold    0
    set under   0
    set red     0
    set tipsonoff 1
    clear text;# if we don't do this, the first load is messed up
    colorset
    focus -force .text



set tooltip_bold {##mark current 1.0 tagon normal 1.0 tagon black 1.0 text Styles 1.0 tagoff black 1.6 text { } 1.6 tagoff normal 1.7 tagon red 1.7 tagon bold 1.7 text \u2196 1.7 tagoff bold 1.8 tagoff red 1.8 tagon normal 1.8 text { } 1.8 tagon black 1.9 text {and colors} 1.9 tagoff black 1.19 text { } 1.19 tagoff normal 1.20 tagon red 1.20 tagon bold 1.20 text \u2197 1.20 tagoff bold 1.21 tagoff red 1.21 tagon normal 1.21 text { } 1.21 tagon black 1.22 text {are sticky. Once they are
} 1.22 text {set, text typed or pasted will use that style. You can
} 2.0 text {use all 3 together with any of the colors. So be
} 3.0 text {         \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
} 4.0 tagoff black 5.0 text { } 5.0 tagon black 5.1 text \u2726 5.1 tagoff black 5.2 text {             } 5.2 tagon black 5.15 text {   } 5.15 tagoff black 5.18 text { } 5.18 tagoff normal 5.19 tagon bolditalicunder 5.19 tagon red 5.19 text {B } 5.19 tagoff red 5.21 tagon orange 5.21 text O 5.21 tagoff orange 5.22 tagon red 5.22 text { } 5.22 tagoff red 5.23 tagon green 5.23 text L 5.23 tagoff green 5.24 tagon red 5.24 text { } 5.24 tagoff red 5.25 tagon green 5.25 text D 5.25 tagoff bolditalicunder 5.26 tagoff green 5.26 tagon black 5.26 tagon normal 5.26 text {                  \u2726} 5.26 tagoff normal 5.45 tagon under 5.45 text {
} 5.45 tagoff under 6.0 tagon normal 6.0 text {         \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
} 6.0 text {To modify an attribute of existing text, select the 
} 7.0 text {text and then set the attribues/colors} 8.0 mark tk::anchor1 8.38 mark insert 8.38 text { you want.
} 8.38 tagoff normal 9.0 tagoff black 9.0}


set tooltip_copy {##mark current 1.0 tagon normal 1.0 tagon black 1.0 text The 1.0 tagoff black 1.3 text { } 1.3 tagon red 1.4 text Copy 1.4 tagoff red 1.8 text { } 1.8 tagon black 1.9 text {command copies} 1.9 mark tk::anchor1 1.23 mark insert 1.23 text { all the text in the 
} 1.23 text {window below } 2.0 tagoff normal 2.13 tagoff black 2.13 tagon violet 2.13 tagon bold 2.13 text \u2193 2.13 tagoff bold 2.14 tagoff violet 2.14 tagon normal 2.14 tagon black 2.14 text { into the clipboard with all its 
} 2.14 text {attributes formatted as a tcl} 3.0 tagoff black 3.29 text { } 3.29 tagon red 3.30 text set 3.30 tagoff red 3.33 text { } 3.33 tagon black 3.34 text {command.
} 3.34 text {
} 4.0 text {It can then be pasted into the user program where
} 5.0 text {it can be used in a tooltip command. The variable
} 6.0 text {defaults to tooltip. The user should change that,
} 7.0 text {for example, } 8.0 tagoff black 8.13 tagon red 8.13 text tooltip3 8.13 tagoff red 8.21 tagon black 8.21 text {. To use it, one would
} 8.21 text {write the followng tcl code:
} 9.0 text {
} 10.0 text {    tooltip::tooltip } 11.0 tagoff black 11.21 tagon orange 11.21 text <widget3> 11.21 tagoff orange 11.30 tagon black 11.30 text { } 11.30 tagoff black 11.31 tagon red 11.31 text {$tooltip3} 11.31 tagoff red 11.40 tagon black 11.40 text {
} 11.40 text {
} 12.0 tagoff black 13.0 tagon red 13.0 text Copy 13.0 tagoff red 13.4 tagon black 13.4 text { writes a formatted pretty print \u261b stdout 
} 13.4 text {or \u261b console of the text widget dump command.} 14.0 tagoff black 14.45 text {
} 14.45 text {
} 15.0 tagon red 16.0 text Copy 16.0 tagoff red 16.4 text { } 16.4 tagon black 16.5 text {ignores any currently} 16.5 tagoff black 16.26 text { } 16.26 tagoff normal 16.27 tagon violet 16.27 tagon bold 16.27 text selected 16.27 tagoff bold 16.35 tagoff violet 16.35 tagon normal 16.35 text { } 16.35 tagon black 16.36 text {text, as it
} 16.36 text {always uploads the entire window to the clipboard.
} 17.0 tagoff normal 18.0 tagoff black 18.0}

set tooltip_paste {##mark current 1.0 tagon normal 1.0 tagon black 1.0 text {Select and copy your code from} 1.0 tagoff black 1.30 text { } 1.30 tagon red 1.31 text {set tooltipN {...}} 1.31 tagoff red 1.49 text { } 1.49 tagon black 1.50 text {in your
} 1.50 text {text editor and use the} 2.0 tagoff black 2.23 text { } 2.23 tagon red 2.24 text Paste 2.24 tagoff red 2.29 text { } 2.29 tagon black 2.30 text {command to reload it here for
} 2.30 text {editing. After it's edited, use the } 3.0 tagoff black 3.36 tagon red 3.36 text Copy 3.36 tagoff red 3.40 tagon black 3.40 text { command to
} 3.40 text {place the new contents into the clipboard for pasting
} 4.0 text {back into your source program.
} 5.0 text {
} 6.0 text The 7.0 tagoff black 7.3 text { } 7.3 tagon red 7.4 text Copy 7.4 tagoff red 7.8 text { } 7.8 tagon black 7.9 text {command will use the variable name from the
} 7.9 text {last paste operation or use the variable name} 8.0 tagoff black 8.45 text { } 8.45 tagon red 8.46 text tooltip. 8.46 tagoff red 8.54 text {
} 8.54 text {
} 9.0 tagon black 10.0 text {Be sure to select the entire statement. If you don't
} 10.0 text {begin with } 11.0 tagoff black 11.11 tagon violet 11.11 text set 11.11 tagoff violet 11.14 tagon black 11.14 text { an error will be posted
} 11.14 tagoff normal 12.0 tagoff black 12.0 mark tk::anchor1 12.0 mark insert 12.0}

set tooltip_test {##tagon normal 1.0 tagon black 1.0 text \u2500\u2500\u2500\u2500 1.0 tagoff black 1.4 tagon red 1.4 text {  } 1.4 tagoff red 1.6 tagon black 1.6 text { } 1.6 tagoff normal 1.7 tagon bold 1.7 text {Notes on the \u2192Test button} 1.7 tagoff bold 1.32 tagoff black 1.32 tagon normal 1.32 tagon red 1.32 text { } 1.32 tagoff red 1.33 tagon black 1.33 text {\u2500\u2500\u2500\u2500\u2500
} 1.33 text {
} 2.0 text {\u2727 This will render with tooltip config settings} 3.0 tagoff black 3.47 tagon orange 3.47 text { } 3.47 tagoff orange 3.48 tagon black 3.48 text {as noted.
} 3.48 text {
} 4.0 mark current 5.0 text {\u2727 If the last line's not showing, you need
} 5.0 text {   a newline after the last line.
} 6.0 text {
} 7.0 text {\u2727 If it's cutting off on the right side, add some
} 8.0 text {   spaces at the end of the longest line.
} 9.0 text {   This can happen with unicode.
} 10.0 text {
} 11.0 text {\u2727 Click this button to force an update in the test    
} 12.0 text {   button.} 13.0 tagoff black 13.10 tagon orange 13.10 text {
} 13.10 text {
} 14.0 tagoff orange 15.0 tagon black 15.0 text {\u2727 Cannot use braces, use unicode instead\u3010  \u3011
} 15.0 text {
} 16.0 text {tooltip::tooltip configure ?-option value?} 17.0 tagoff black 17.42 tagon red 17.42 text { } 17.42 tagoff red 17.43 tagon orange 17.43 text {\u261b defaults} 17.43 tagoff orange 17.53 tagon red 17.53 text {
} 17.53 tagoff red 18.0 tagon black 18.0 text {
} 18.0 text {-fg -foreground } 19.0 tagoff black 19.16 tagon orange 19.16 text black 19.16 tagoff orange 19.21 tagon red 19.21 text {        } 19.21 tagoff red 19.29 tagon black 19.29 text {
} 19.29 text {-bg -background } 20.0 tagoff black 20.16 tagon orange 20.16 text lightyellow 20.16 tagoff orange 20.27 tagon red 20.27 text {    } 20.27 tagoff red 20.31 tagon black 20.31 text {
} 20.31 text -font\u3010 21.0 tagoff black 21.6 tagon orange 21.6 text {FixedSys 10} 21.6 tagoff orange 21.17 tagon black 21.17 text {\u3011
} 21.17 text {
} 22.0 text {The above affect both regular and styled tips. Can also
} 23.0 text {replace the 7 colors, e.g. } 24.0 tagoff black 24.27 tagon red 24.27 text -red 24.27 tagoff red 24.31 tagon black 24.31 text { } 24.31 tagoff black 24.32 tagon red 24.32 text purple 24.32 tagoff red 24.38 tagon black 24.38 mark tk::anchor1 24.38 mark insert 24.38 text {
} 24.38 text {
} 25.0 text {-black } 26.0 tagoff black 26.7 tagon grey 26.7 text -grey 26.7 tagoff grey 26.12 tagon black 26.12 text { } 26.12 tagoff black 26.13 tagon violet 26.13 text -violet 26.13 tagoff violet 26.20 tagon black 26.20 text { } 26.20 tagoff black 26.21 tagon orange 26.21 text -orange 26.21 tagoff orange 26.28 tagon black 26.28 text { } 26.28 tagoff black 26.29 tagon blue 26.29 text -blue 26.29 tagoff blue 26.34 tagon black 26.34 text { } 26.34 tagoff black 26.35 tagon green 26.35 text -green 26.35 tagoff green 26.41 tagon black 26.41 text { } 26.41 tagoff black 26.42 tagon red 26.42 text -red 26.42 tagoff red 26.46 tagon black 26.46 text {
} 26.46 text {
} 27.0 tagoff normal 28.0 tagoff black 28.0}

# styled tips

    tooltip::tooltip $t.bold   $tooltip_bold 
    tooltip::tooltip $t.copy   $tooltip_copy
    tooltip::tooltip $t.paste  $tooltip_paste
    tooltip::tooltip $t.test   $tooltip_test
# regular tips
    atoztips
    tooltip::tooltip $t.tip   "Turns on/off cooltip tips. The test\nbutton \u2192Test is always on\n"
    tooltip::tooltip $t.next  "Load next one in directory"
    tooltip::tooltip $t.atoz  "Save file name extra A-Z, or -none-"
    tooltip::tooltip $t.pick   "A menu of favorites, including unicode\nedit to add or replace with your own." 
    tooltip::tooltip $t.color  "A menu of colors with a tearoff.\nThe tearoff menu also has tooltips\nexplaining the menu items.\n" 
    tooltip::tooltip $t.clear  "Clear text in edit window\nShift-click Clear Configurations\nControl-click Clear Both\n" 
    foreach tag {51 52 53 54 55 56 57 58 59} {
        tooltip::tooltip .text -tag grey$tag "grey$tag"
    }
#   namespace import tooltip::tooltip
    history add "::tooltip::tooltip config -grey51 {-font {courier 12} -relief ridge -borderwidth 4}" 
    history add "::tooltip::tooltip config -grey51" 
    history add "::tooltip::tooltip config -font {courier 14}" 
    history add "::tooltip::tooltip config -background grey80" 
    history add "::tooltip::tooltip config"
    history add "$::tooltip::G(TOPLEVEL).text "
    history add "::tooltip::tooltip delay 50"
    history add "main_dlog" 
#    history add "ttk::themes"
#    history add "package require "
#    history add ".con config -bg black"
#    history add "ttk::style theme use default ;#smaller"
    puts [history info]
    puts "\nTo access the above pre-loaded history, use up-arrow\nthis version: 1/22/2024\nWhat's new? control-mousewheel zoom; text configure dialog with more attributes"
    puts "awdark if available and more ttk widgets\; experimental wrap mode; to suppress awdark\nby command line: wish [file tail [info script]] -nodark; new current directory chooser dialog"
    puts "Load/Save can have a-z with a menu and a load next; New cataloging, so one can put a series of\ntips into a single file or on the wiki; configure functionally ordered rather than alphabetical"
    puts "Fancy borders using tabs, such as in the screen shot on the wiki; will soon upload some configurations"
    console eval {wm geom . 109x24+10+10}  
    wm geom . 1004x517+181+209
    trace add variable ::tooltip::G(font)        write {catch {.text configure -font $::tooltip::G(font) ; ::tooltip::set_tags .text} ;#}
    trace add variable ::tooltip::G(config_done) write {catch {config2text; #puts "config change: $::tooltip::G(config_done) "} ;# }
    trace add variable ::nowcolor write {catch {colchange; #puts "color change: $::nowcolor "} ;# }
    bind .text <Control-MouseWheel> {mwheel %D} ;# windows
    bind .text <Control-4> {mwheel 1} ;# linux
    bind .text <Control-5> {mwheel -1}
    bind . <Configure> {gangmove}
    set ::lastfound -1
}
proc gangmove {} {
        if { $::lastgeom eq "" } {
            set ::lastgeom [wm geom .]
            return
        }
        if { ([incr ::movecount] % 4)  != 0} {
            return
        }
        set geom $::lastgeom
        regexp {^([0-9]+)x([0-9]+)([+-])([+-]?[0-9]+)([+-])([+-]?[0-9]+)} $geom -> dx dy xs xpos ys ypos
        if { [llength $::tears] > 0 } {
#           update
            set geomnow  [wm geom .] ;# get the leader's current position
            regexp {^([0-9]+)x([0-9]+)([+-])([+-]?[0-9]+)([+-])([+-]?[0-9]+)} $geomnow -> dx dy xs xx ys yy ;# only need the xx and yy
        } else {
            set ::lastgeom [wm geom .]
            return
        }
        if { $xpos == $xx && $ypos == $yy } {
        } else {
            set dx   [expr {  $xx - $xpos   }]
            set dy   [expr {  $yy - $ypos   }]  
            set ::lastgeom $geomnow
            foreach w $::tears {
                if { ![winfo exists $w] } {
                    continue
                }
                set geom [wm geom $w]
                regexp {^([0-9]+)x([0-9]+)([+-])([+-]?[0-9]+)([+-])([+-]?[0-9]+)} $geom -> sxx syy xs xpos ys ypos
                set newx [expr {    $xpos + $dx   }]
                set newy [expr {    $ypos + $dy   }]
                set newgeom ${sxx}x${syy}+$newx+$newy
                wm geom $w $newgeom
#                   wait 1000
                update
                update idletasks
            }
        }
        return
}
proc loadnextone {{getletters 0}} {
    set files [glob -nocomplain save*2.tcl]
    if { [llength $files] <= 0 } {
        puts "no files found"
        return {}
    }
#   set f $::atoz
    
    
    set letters {}
    foreach file $files {
        if {  [regexp -nocase -linestop -lineanchor {save([a-zA-Z])?2.tcl} $file -> firstone]   } {
            lappend letters $firstone
        }
    }
    set letters [lsort $letters]
    if { $getletters } {
        return $letters ;# just want the letters, don't skip
    }
    
    puts "letters= |$letters| "
    incr ::lastfound
    if { $::lastfound >= [llength $files]} {
        set ::lastfound 0
    }
    set f [lindex $letters $::lastfound]
    if { $f eq "" } {
        set f "--"
    }
    $::t.atoz set [string toupper $f ]
    doatoz [string tolower $f ]
    loadf .text $::t.testbut
}
proc getfile {file {nonl 1}} {
    set io  [open $file r]
    if { $nonl } {
        set data [read -nonewline $io]
    } else {
        set data [read  $io]
    }
    close $io
    return $data
}

proc createcatalog {args} {
    set letters [loadnextone 1]
    if { [llength $letters] <= 0 } {
        return 0
    }
    set out  {}
    set out2 {}
    foreach letter $letters {
        set letter [string tolower $letter]
        lappend out [list $letter [getfile save${letter}.tcl]]
        lappend out2 [list $letter [getfile save${letter}2.tcl]]
    }
    
    set io [open catalog.tcl w]
    foreach item $out { ;# write out all the text data with delimiter markers
        lassign $item letter data
        puts $io "# Begin text \{${letter}\}"
        puts $io "set tooltip_${letter} \{##\\"
        puts $io $data
        puts $io "\}\n# End text \{${letter}\}" 
        
    }

    set letter [lindex $letters 0]
    set letter [string tolower $letter]
    set last [lrange $letters 1 end]
    set first [getfile save${letter}2.tcl] ;# compare all to the first one
    set same 1
    foreach letter $last {
        set letter [string tolower $letter]
        if { [getfile save${letter}2.tcl] ne $first } { ;# if all configs the same, output only once
            puts "Not the same at $letter"
            set same 0
            break
        }
    }
#   puts "same= |$same| " 
    if { $same } {
        foreach item $out2 { ;# still need to process as a list, but just done the first one
            lassign $item letter data
            puts $io "# Begin config ${letters}"    
            puts $io $data
            puts $io "# End config ${letters}"
            break   
        }
    } else {
        foreach item $out2 { ;# do all of them since not all the same
            lassign $item letter data
            puts $io "# Begin config \{${letter}\}" 
            puts $io $data
            puts $io "# End config \{${letter}\}"   
        }
    }
    close $io
    set comment ""  
    if { $same && [llength $letters] > 1} {
        set comment "  - All with the same config" 
    }
    puts "created catalog with [llength $letters] item(s) = $letters $comment"
    return [llength $letters]
}
proc expandcatalog {} {
    if [catch {
        set data [getfile catalog.tcl]
        puts "ok"
    } err_code] {
        puts stderr $err_code
        return 
    }
    set lines [split $data \n]
    set n -1
    set toc [list]
    foreach line $lines {
        incr n
        if       { [string range $line 0 11] eq "# Begin text" } {
#                                                                   puts "t n= |[expr {   $n +1  }]| line= |$line| "
            lappend toc [list b t [expr {   $n+1   }] [lrange $line 3 end]]
        } elseif { [string range $line 0 13] eq "# Begin config" } {
#                                                                   puts "c n= |[expr {   $n +1  }]| line= |$line| "
            lappend toc [list b c [expr {   $n+1   }] [lrange $line 3 end]]
        } elseif { [string range $line 0  9] eq "# End text"  } {
#                                                                   puts "c n= |[expr {   $n +1  }]| line= |$line| "
            lappend toc [list e t [expr {   $n-1   }] [lrange $line 3 end]]
        } elseif { [string range $line 0 11] eq "# End config"  } {
#                                                                   puts "c n= |[expr {   $n +1  }]| line= |$line| "
            lappend toc [list e c [expr {   $n-1   }] [lrange $line 3 end]]
        } else {
#           dothis
        }   
    }
    puts ""
    foreach {odd even} $toc { ;# check pass
        lassign $odd  check1 kind1 n1 letters1  
        lassign $even check2 kind2 n2 letters2
        incr n1; incr n2 ;# only for testing not when done
        if { $check1 ne "b" || $check2 ne "e" || $kind1 ne $kind2} {
            puts "Bad catalog for lines $n1 to $n2"
            puts "n1= |$n1| n2= |$n2| |$check1 $check2 $kind1 $kind2| letters1= |$letters1| "
            return  
        }
        
#       puts "n1= |$n1| n2= |$n2| |$check1 $check2 $kind1 $kind2| letters1= |$letters1| "   
    }
    update
    puts stderr "Expand Catalog.tcl in directory [pwd]"
    foreach {odd even} $toc { ;# do it pass
        lassign $odd  check1 kind1 n1 letters1  
        lassign $even check2 kind2 n2 letters2
        if { $kind1 eq "t" } {
            lassign $letters1 letter
            set letter [string tolower $letter]
            set file "save$letter.tcl"
            write2file $lines [expr {   $n1+1   }] [expr {   $n2-1   }] $file
        } else {
            foreach letter $letters1 {
                set letter [string tolower $letter]
                set file "save${letter}2.tcl"
                write2file $lines $n1 $n2  $file
            }   
        }       
    }
}
proc write2file {lines from to file} {
    puts "Lines [format %5s  [expr {   $from+1   }] ]    ..  [format %5s [expr {   $to+1   }] ]  $file"
    set io  [open $file w]
    for {set m $from} {$m <= $to} {incr m} {
        puts $io [lindex $lines $m]     
    }
    close $io
        
}


proc dosetsave {arg} {
    set arg [string tolower $arg]
    set ::savefile [file join [pwd] "save${arg}.tcl"] ;# save writes here
    set ::savefile2 [file join [pwd] "save${arg}2.tcl"] ;# save writes here
}
proc doatoz {args} {
    if { $::atoz eq "--" } {
        dosetsave ""
    } else {
        dosetsave $::atoz   
    }
    atoztips    
    puts "Save file name: $::savefile"
}
proc atoztips {} {
    global t
    tooltip::tooltip $t.save  "Quick save text to $::savefile\nSave the config to $::savefile2\n"
    tooltip::tooltip $t.load   "The load/save commands use 2 fixed file names\n    $::savefile  is the text   dump\n    $::savefile2 is the config dump\nBoth are saved into the current directory\n\nNote copy/paste does not save the config settings\nUse save then include [file tail $::savefile2 ] contents in the user\nprogram if you have configured color options\n"

}
proc mwheel {direction} {
    lassign $::tooltip::G(font) name size
    if { [string is integer -strict $size] } { ;# must be an int, like courier 12
        if { $direction > 0 } {
            incr size
        } else {
            incr size -1    
        }
        if { $size > 30 || $size < 6 } {
            return
        }
        tooltip::tooltip configure -font [list $name $size]
    }
}
proc dowrapping {args} {
    if { $::wraponoff } {
        tooltip::tooltip config -wrap word
    } else {
        tooltip::tooltip config -wrap none  
    }
    
}
proc dodark {args} {
#puts "::themex= |$::themex| ::darkonoff= |$::darkonoff| "
    set con 0
    if { [info command .con] eq ".con" } {
        set con 1 ;# there's a dialog open
    }
    
    if { $::themex } { ;# cannot switch themes if no dark was available
        destroy .con
        if { $::darkonoff } {
#           puts "go to dark"
            ttk::style theme use awdark
        } else {
#           puts "go to light $::curtheme"  
            ttk::style theme use $::curtheme
        }
    } else {
        set ::darkonoff 0
        puts stderr "Cannot go to dark mode"
    }
    if { $con } {
        con_dlog $::nowcolor
    }
    
}
proc newpwd {args} {

    set dir [tk_chooseDirectory -initialdir [pwd] -mustexist yes -title "Choose a directory"]

    if {$dir eq ""} {
       puts stderr "No new directory chosen"
    } else {
       puts stderr "New directory is: $dir" ; update
       cd $dir
       doatoz $::atoz
       atoztips 
       set ::lastfound -1

    }
}

proc tearoffcmd {xx yy menu thetear } { ;# so tearoffs don't open at 0,0 - this is really ugly code I found somewhere !
#       puts "menu= |$menu| thetear= |$thetear| "
        if { $menu eq ".color" } {
            tooltip::tooltip $thetear -index  1 "These 7 colors can be configured as desired,\nbut if changed, will also affect this editors tips.\n\nClicking a color sets a selection to that color\neven if this is the current color unlike\nstyles where clicking one that is set will\nunset that style of the selection. Can click a style \ntwice to set a selection to the current setting.\n\nStyles are on/off, Colors are mutually exclusive \nradio buttons.\n"            
            tooltip::tooltip $thetear -index 10 "The 9 greys 51-59 are available for advanced\ntag usage. This editor doesn't use them\n\nNote: Settings such as tabs only apply if the first\ntext on a line is tagged with its color\n"           
            tooltip::tooltip $thetear -index 22 "Experimental, not working, just turns on the option\n"         
            tooltip::tooltip $thetear -index 24 "Opens a configure dialog for all the colors. Can \nchoose a different color while open.\n\nClick Apply before changing or changes will not be\nsaved except for checkboxes, listboxes, fonts, and\ncolors selected by the color picker.\n \nTo enter a named color type in the box and type Enter.\n\nTo revert to defaults clear an item and type Enter.\n"           
            tooltip::tooltip $thetear -index 25 "Select a directory of save files that\nall load, save, and catalog operations\nwill work in.\n"
            tooltip::tooltip $thetear -index 27 "Create Catalog creates a single file\ncatalog.tcl with all the save files\n"
            tooltip::tooltip $thetear -index 28 "Expand Catalog that recreates all the save\nfiles from a single catalog.tcl\n"
        }
        set geom [wm geom .]
        set re {^([0-9]+)x([0-9]+)([+-])([+-]?[0-9]+)([+-])([+-]?[0-9]+)}
        regexp $re $geom -> dx0 dy0 xs xpos ys ypos
        set geomt [wm geom $thetear]
        regexp $re $geomt -> dx dy xs xpost ys ypost
        set newx [expr {   $xpos + $dx0 +5 + $xx }] ;# place to the right of main window
        set newy [expr {   $ypos - 1 +$yy  }]
        wm geom $thetear ${dx}x$dy+${newx}+${newy}
        if { $thetear ni $::tears } {
            lappend ::tears $thetear
        }
}
 
 
proc ::popup::create {m {tear 0} {tearoffcmd {}}} {
    #----------------
    # create menu (m) with from list of supplied items (a)
    #---------------
    
    set c $m
    set m ".[string tolower $m]"
    
    # destroy any pre-exising menu with the same name
    destroy $m
    
    # create new menus
    menu $m -tearoff $tear {*}$tearoffcmd
        
    foreach i $::popup::menu($c) {
        #~~~puts "popup create $c $m $i"
        if { [lindex $i 0] != "nop" } {
            eval $m add $i
        }
    }
}

proc ::popup::show {w m} {
    #---------------
    # display the popup menu adjacent to the current pointer location
    #---------------
    
    set m ".[string tolower $m]"
    
    foreach {x y} [winfo pointerxy $w] {}
    
    set ::active(tag) $m
    tk_popup $m $x $y
    return
}
proc timems {args} {
    set result [uplevel 1 time $args]
    set number [format %.3f [expr {( [lindex $result 0] / 1000. )}]]
    set number [regsub -all {\d(?=(\d{3})+($|\.))} $number {\0,}]
    return "[format %12s $number ] milliseconds [lrange $result 2 end]"
}

proc testit {w} {
    set tip [storeText 2]
#   puts "w= |$w| tip= |$tip| "
    tooltip::tooltip $w $tip
}
proc onoff {args} {
    global tipsonoff
    if { $tipsonoff } {
        tooltip::tooltip on
    } else {
        tooltip::tooltip off    
    }
}

proc pick1 {args} {
    .text insert insert "$args"  $::texttag ;# put in here
    focus -force .text
}

proc bt {args} { ;# debugging aid
    if { $args eq "" } {
    puts [join [lsort [bind Text]] \n]
        puts ""
        return
    }
    puts [bind Text $args]
    
}
proc clip {w test} {
#   puts "w= |$w| test= |$test| "
    clear text ;# don't clear the config settings, user can use clear to do that
    set data [clipboard get]
    global s name dump dump2
    lassign $data s name dump
    if { $s ne "set" } {
        error "does not begin with a set command"
    }
    set ::tootip_varname $name
#   puts stderr $data
    puts "Command= |$s| name= |$name| list length= [llength $dump]"
    wm title . "$::wtitle $name"
    set dump2 [string range $dump 2 end ]
    restore $w  $dump2 
    testit $test

}

proc clear {what} {
    puts stderr "Clear $what"
    if       { $what eq "text" } {
        .text delete 1.0 end
        after 0 {focus -force .text}
        return
    } elseif { $what eq "all" } {
        .text delete 1.0 end
    } elseif { $what eq "config" } {
    } else {
        error "bad clear $what"
    }
    if { [info command $::tooltip::G(TOPLEVEL).text] eq ""} {
#       puts "not created yet"
        return
    }
    
# delete all tags and then restore them all, clears any set attributes
#   puts stderr "clear configurations"
    catch {destroy .con}
    
    if [catch {
        .text tag delete {*}[.text tag names]
        $::tooltip::G(TOPLEVEL).text tag delete {*}[$::tooltip::G(TOPLEVEL).text tag names]
        
        tooltip::set_tags $::tooltip::G(TOPLEVEL).text
        tooltip::set_tags .text
        foreach tag {51 52 53 54 55 56 57 58 59} { ;# recreate these, we lost them from above
            tooltip::tooltip .text -tag grey$tag "grey$tag"
        }

    } err_code] {
        puts "clear: $err_code"
    }
    after 0 {focus -force .text}

}

# overide these two actions that the text widget uses for
# its default bindings, so we can add the tags to pastes and insert
proc tk_textPaste {w} {
    if {![catch {::tk::GetSelection $w CLIPBOARD} sel]} {
        set oldSeparator [$w cget -autoseparators]
        if {$oldSeparator} {
            $w configure -autoseparators 0
            $w edit separator
        }
        if {[tk windowingsystem] ne "x11"} {
            catch { $w delete sel.first sel.last }
        }
        $w insert insert $sel $::texttag ;# modify here
        if {$oldSeparator} {
            $w edit separator
            $w configure -autoseparators 1
        }
    }
}
proc ::tk::TextInsert {w s} {
    if {$s eq "" || [$w cget -state] eq "disabled"} {
        return
    }
    set compound 0
    if {[TextCursorInSelection $w]} {
        set oldSeparator [$w cget -autoseparators]
        if {$oldSeparator} {
            $w configure -autoseparators 0
            $w edit separator
            set compound 1
        }
        $w delete sel.first sel.last
    }
    $w insert insert $s $::texttag ;# modify here
    $w see insert
    if {$compound && $oldSeparator} {
        $w edit separator
        $w configure -autoseparators 1
    }
}

proc make_border {lines pixels {grey 59}} { ;# if lines are 0 or less, place border around text
    set surround 0
    if { $lines <= 0 } {
        set surround 1
    }
    if { $surround  == 0} {
    clear text
    } else {
        set last [.text index end]
        set lines [lindex [split $last .] 0 ]
    }
    tooltip::tooltip configure -grey$grey {\
                -background grey90 -bgstipple {} -borderwidth {} -elide {} -fgstipple {} -font {} -foreground black     \
                -justify {} -lmargin1 {} -lmargin2 {} -lmargincolor {} -offset {} -overstrike {} -overstrikefg {}       \
                -relief {} -rmargin {} -rmargincolor {} -selectbackground lightblue -selectforeground {} -spacing1 {}   \
                -spacing2 {} -spacing3 {} -tabs {} -tabstyle {} -underline {} -underlinefg {} -wrap {}                  \
            }
    tooltip::tooltip configure -grey$grey   "-background \#969696 -borderwidth 5 -elide 0           \
                                -foreground black -lmargin1 3 -offset 0 -overstrike 0               \
                                -relief raised -selectbackground lightblue -spacing1 0 -spacing3 0  \
                                -tabs $pixels -tabstyle wordprocessor -underline 0"
    if { $surround  == 0} {
    .text insert 1.0 [string repeat "\n" $lines] "normal black"
    .text insert 1.end "\t " "normal grey$grey"
    .text insert $lines.end "\t " "normal grey$grey"
    } else {
        .text insert 1.0 "\n" "normal black"
        .text insert 1.end "\t " "normal grey$grey"
    }
    for {set m 2} {$m <= $lines -1} {incr m} {
        .text insert $m.0 " " "normal grey$grey"
        .text insert $m.end "\t" "normal black"
        .text insert $m.end " " "normal grey$grey"
    }
    if { $surround  == 1} {
        .text insert end " \t " "normal grey$grey"
    }
    after 100 {console eval {focus -force .console}}
    return
}
proc insert_clip {{indent 1}} {
    set data [clipboard get]
    set lines [split $data \n]
    set n 1
    foreach line $lines {
        .text insert [incr n].1 "[string repeat " " $indent]$line" "normal black"
        if { $n == [llength $lines] } { ;# skip last null entry that split makes
            break
        }
    }
    after 100 {console eval {focus -force .console}}
    return
}

# toggleFont --
#     Make the text appear in bold, italic, underline font or normal
#     also current color
# Arguments:
#     None
#
# Returns:
#     Nothing
#
# Side effects:
#     Next letters typed or pasted in the text widget have the selected font and color
#
proc toggleFont {} {
    global bold
    global italic
    global under
    global red
    global texttag
    global nowcolor
    set type ""

    if { $bold } {
        append type "bold"
    }
    if { $italic } {
        append type "italic"
    }
    if { $under } {
        append type "under"
    
    }
    if { $type eq "" } {
        set type "normal"
    }
    append type " $nowcolor"
    set texttag [string trim $type ]
#   puts "texttag= |$texttag| "
    Apply2Selection
    focus -force .text
}
proc colorset {} {
    global texttag nowcolor colorlab
    lassign $texttag style
    set texttag "$style $nowcolor"
#   puts $texttag
    set fg black
    if { $nowcolor in {black green red violet grey} } {
        set fg white
    }
    set bg $nowcolor
    if { $nowcolor eq "blue" } {
        set bg lightblue
    }
    
    $colorlab configure -background $bg -foreground $fg
    Apply2Selection
    focus -force .text

}
proc Apply2Selection {} {
    set s [.text tag ranges sel]
#   puts "s= |$s| "
    if { $s eq "" } { ;# isn't one
        return
    }
    lassign $s from to
    set chars [.text get {*}$s]
    .text delete {*}$s

#   puts "newattr=   $from $chars $::texttag "
    .text insert $from $chars $::texttag
    .text tag add sel $from $to
}
proc config2text {args} { ;# copy all the config args for colors into our text edit widget
    set opts2 {-red -green -blue -orange -violet -black -grey -grey51 -grey52 -grey53 -grey54 -grey55 -grey56 -grey57 -grey58 -grey59}
    set gt ${::tooltip::G(TOPLEVEL)}.text
    foreach c $opts2 {
        set tag [string range $c 1 end] 
        set all [$gt tag config $tag] ;# get all attr's even those that are null
        foreach item $all {
            lassign $item key x2 x3 x4 val
            set err_code ""
            if [catch {
                .text tag configure $tag  {*}[list $key $val]   ;# if null there, we will reset it to null in .text also
            } err_code] {
#               puts "tried to copy tag: |$tag| key/val: |$key|  |$val| got error: $err_code" 
                catch {.text tag configure $tag  {*}[list $key {}]} ;# if this gets a bad value, our tag becomes useless
            }
        }
    }
    .text configure -foreground [tooltip::tooltip configure -foreground]
#    .text configure -background [tooltip::tooltip configure -background]
    .text configure -wrap       [tooltip::tooltip configure -wrap]
    if { [tooltip::tooltip config -wrap] eq "word" } {
        set ::wraponoff 1
    } else {
        set ::wraponoff 0
    }

    
}
proc colchange {args} {;# someone changed the current color
    global nowcolor
    if { [info command .con] eq ".con"} { ;# if there's a configure dialog open, 
        con_dlog $nowcolor       ;# start it up again with the new color
    }
}
proc main_dlog {{color Borders}} { ;# configure dialog
    set savegeom ""
    if { [info command .win] eq ".win"} {
        set savegeom [wm geom .win]
    }
    destroy .win
    global configs
    if [catch {
        foreach ii [array names configs 1??,*] {unset ::configs($ii)}
    } err_code] {
        puts $err_code 
    }
    if { [info command .win] eq ".win" } {
        return ;# already or still exists
    }
    toplevel .win
    if { $::themex && $::darkonoff} {
        .win config -bg grey20
    }
    ttk::label .win.label1 -text "--Configuration--"
    label .win.label2 -text $color -background white -foreground black -relief ridge
    ttk::button  .win.apply     -text "Apply" -command [list doapplyx main]

    grid .win.label1 .win.label2  .win.apply  
    set fields [list \
            {background                     color       {value1 value2} "To reset, erase and apply (or Enter)"}\
            {-foreground                    color       {value1 value2}}\
            {-highlightbackground           color       {value1 value2}}\
            {-highlightcolor                color       {value1 value2}}\
            {relief                         combobox    {flat groove raised ridge solid sunken} "Works together with borderwidth"}\
            {{}                             separator    {0 50 1 0}}\
            {borderwidth                    spinbox     {0 30 1 0} "All spinboxes and comboboxes can be adjusted\nusing the mousewheel\n"}\
            {highlightthickness             spinbox      {0 50 1 0} "Tips cannot be highlighted, but this\nwill create an extra border around\nthe tip.\n"}\
            {{}                             separator    {0 50 1 0}}\
            {padx                           spinbox      {0 50 1 0} "Extra horizontal spacing on both sides"}\
            {pady                           spinbox      {0 50 1 0} "Extra vertical spacing, top and bottom"}\
            {{}                             separator    {0 50 1 0}}\
            {spacing1                       spinbox      {0 50 1 0} "Vertical spacing between lines"}\
            {-spacing2                          spinbox      {0 50 1 0}}\
            {spacing3                       spinbox      {0 50 1 0}}\
            {{}                             separator    {value1 value2}}\
        ]
    set i 100
    foreach field $fields {
        lassign $field name type values tips
        if { [string index $name 0] eq "-" } {
            continue
        }
        set ddd {} ;# assume no dialog needed
        if { $type ne "separator" } {
            set vnow [.text cget -$name] 
        }
        incr i
        set l [ttk::label .win.l$i -text "$name"  ]
        if { $tips ne "" } {
                tooltip::tooltip .win.l$i $tips
        }
        set ::configs($i,type) $type
        if       { $type eq "combobox" } {
            set w [ttk::combobox .win.w$i -textvariable ::configs($i,value) -values $values  ]
            bind .win.w$i <<ComboboxSelected>> [list setcombox $i $color]
            set ::configs($i,value) $vnow
            set ::configs($i,legal) $values
        } elseif { $type eq "spinbox" } {
            lassign $values from to by initial
            set w [ttk::spinbox .win.w$i -textvariable ::configs($i,value) -from $from -to $to -increment $by -command [list setspinx $i $color]]
            set ::configs($i,value) $vnow
            bind .win.w$i <Key-Return> [list setspinx $i main]
            if { $vnow eq "" } {
                .win.w$i set $initial
            } else {
                .win.w$i set $vnow  
            }
        } elseif { $type eq "separator" } {
            set w [ttk::separator .win.w$i] 
        } elseif { $type eq "color" } {
            set w [ttk::entry .win.w$i -textvariable ::configs($i,value)]
            set ::configs($i,value) $vnow
            set ddd [ttk::button .win.cw$i -command [list setcolorx $i $color] -text Color...]
            bind .win.w$i <Key-Return> [list doapplyx main]
        } elseif { $type eq "type" } {
        } else { ;# no default
            error "bad configure type $type"
        }
        grid $l $w {*}$ddd -sticky w -padx 10
        set configs($i,name) $name
    }
    tooltip::tooltip .win.apply "Apply values for typed in values\n"
#   button  .win.ok        -text "Ok"    -command {} 
#    ttk::button  .win.done    -text "Done"  -command {destroy .win} 
#    grid .win.apply  .win.done 
    if { $savegeom ne "" } {
        wm geom .win $savegeom
    } else {
        wm geom .win +9+3      
    }
}
proc main2tips {} {
    if {![winfo exists $::tooltip::G(TOPLEVEL)]} {
            ::tooltip::createToplevel
    }
    foreach fig {background    relief borderwidth highlightthickness padx pady spacing1  spacing3} {
        $::tooltip::G(TOPLEVEL).text config -$fig [.text cget -$fig]    
    }
}
proc dump_main_config {} {
    puts "\n\n#---------------------\n# Copy this to the user program:\n"
    puts "if \{!\[winfo exists \$::tooltip::G(TOPLEVEL)\]\} \{"
    puts "    ::tooltip::createToplevel"
    puts "\}"
    set figs {$::tooltip::G(TOPLEVEL).text configure }
    foreach fig {background   relief borderwidth highlightthickness padx pady spacing1 spacing3} {
        append figs " " -$fig " " [.text cget -$fig]    
    }
    puts $figs
    if { [info exist ::bgchange] } {
        set bg [tooltip::tooltip config -bg]
        puts "tooltip::tooltip config -background $bg"
    }
    puts "#---------------------\n"
}
proc con_dlog {{color grey51}} { ;# configure dialog
    set savegeom ""
    if { [info command .con] eq ".con"} {
        set savegeom [wm geom .con]
    }
    
    destroy .con
    global configs
    if [catch {
        foreach ii [array names configs 2??,*] {unset ::configs($ii)}
    } err_code] {
        puts $err_code 
    }
    if { [info command .con] eq ".con" } {
        return ;# already or still exists
    }
    toplevel .con
    if { $::themex && $::darkonoff} {
        .con config -bg grey20
    }
    
    ttk::label .con.label1 -text "--Configuration--"
    label .con.label2 -text $color -background white -foreground black -relief ridge
    ttk::button  .con.apply     -text "Apply" -command [list doapply $color]

    grid .con.label1 .con.label2  .con.apply  
    set fields [list \
            {background         color       {value1 value2}}\
            {foreground         color       {value1 value2}}\
            {-bgstipple         entry       {value1 value2}}\
            {-fgstipple         type        {value1 value2}}\
            {font               font        {value1 value2}}\
            {justify            combobox    {left center right}}\
            {offset             spinbox     {-25 25 1 0} "Raises or lowers the text"}\
            {borderwidth        spinbox     {0 30 1 0}}\
            {relief             combobox    {flat groove raised ridge solid sunken} "Works together with borderwidth"}\
            {lmargin1          spinbox      {0 100 1 0} "Only effective if text of this color\nbegins on a line\n"}\
            {lmargin2          spinbox      {0 100 1 0}}\
            {lmargincolor       color       {value1 value2}}\
            {rmargin           spinbox      {0 100 1 0}}\
            {rmargincolor       color       {value1 value2}}\
            {elide             checkbutton  {value1 value2} "Hides all text of this color"}\
            {overstrike         checkbutton {value1 value2}}\
            {underline          checkbutton {value1 value2}}\
            {overstrikefg       color       {value1 value2}}\
            {underlinefg        color       {value1 value2}}\
            {-selectbackground  color       {value1 value2}}\
            {-selectforeground  color       {value1 value2}}\
            {-                separator    {0 50 1 0}}\
            {spacing1          spinbox      {0 50 1 0}}\
            {spacing2          spinbox      {0 50 1 0}}\
            {spacing3          spinbox      {0 50 1 0}}\
            {tabs              entry        {value1 value2} "If tabs is a single integer, it can be\nadjusted by 1 unit with the mousewheel\nshift-wheel adjusts by 10\n"}\
            {tabstyle           combobox    {tabular wordprocessor}}\
            {wrap               combobox    {none char word} "Still experimental"}\
            {{}                 separator    {value1 value2}}\
        ]
    set i 200
    foreach field $fields {
        lassign $field name type values tips
        if { [string index $name 0] eq "-" } {
            continue
        }
        set ddd {} ;# assume no dialog needed
        if { $type ne "separator" } {
            set vnow [lindex [.text tag config $color -$name] end ]
        }
        incr i
        set l [ttk::label .con.l$i -text "$name"  ]
        if { $tips ne "" } {
                tooltip::tooltip .con.l$i $tips
        }
        set ::configs($i,type) $type
        if       { $type eq "combobox" } {
            set w [ttk::combobox .con.w$i -textvariable ::configs($i,value) -values $values  ]
            bind .con.w$i <<ComboboxSelected>> [list setcombo $i $color]
            set ::configs($i,value) $vnow
            set ::configs($i,legal) $values
        } elseif { $type eq "spinbox" } {
            lassign $values from to by initial
            set w [ttk::spinbox .con.w$i -textvariable ::configs($i,value) -from $from -to $to -increment $by -command [list setspin $i $color]]
            set ::configs($i,value) $vnow
            bind .con.w$i <Key-Return> [list setspin $i $color]
            if { $vnow eq "" } {
                .con.w$i set $initial
            } else {
                .con.w$i set $vnow  
            }
        } elseif { $type eq "separator" } {
            set w [ttk::separator .con.w$i] 
        } elseif { $type eq "checkbutton" } {
            set w [ttk::checkbutton .con.w$i -variable ::configs($i,value) -command [list setcheck $i $color]]
            set ::configs($i,value) $vnow
        } elseif { $type eq "font" } {
            set w [ttk::entry .con.w$i -textvariable ::configs($i,value)]
            set ::configs($i,value) $vnow
            set ::configs($i,legal) "-font"
            set ddd [ttk::button .con.cw$i -command [list setfont $i $color] -text Font...]
            bind .con.w$i <Key-Return> [list doapply $color]
        } elseif { $type eq "color" } {
            set w [ttk::entry .con.w$i -textvariable ::configs($i,value)]
            set ::configs($i,value) $vnow
            set ddd [ttk::button .con.cw$i -command [list setcolor $i $color] -text Color...]
            bind .con.w$i <Key-Return> [list doapply $color]
        } elseif { $type eq "entry" } {
            set w [ttk::entry .con.w$i -textvariable ::configs($i,value)]
            set ::configs($i,value) $vnow
            bind .con.w$i <Key-Return> [list doapply $color]
            bind .con.w$i <MouseWheel> [list entrywheel %D 1 $i $color 1 2000]
            bind .con.w$i <Shift-MouseWheel> [list entrywheel %D 10 $i $color 1 2000]
        } elseif { $type eq "type" } {
            
        } else { ;# no default
            error "bad configure type $type"
        }
        grid $l $w {*}$ddd -sticky w -padx 10
#       puts "$w / name= |$name| type= |$type| values= |$values| "
        set configs($i,name) $name
        
    }
    tooltip::tooltip .con.apply "Apply static values from text entries\nFor example font, named colors, or tabs\nNote: relief needs a borderwidth\nspinboxes should support mousewheel\nCan also type Return in any entry to apply\nMany attributes only apply if first used on a\nline e.g. spacing123, margins or tabs\n"
#   button  .con.ok        -text "Ok"    -command {} 
#    ttk::button  .con.done    -text "Done"  -command {destroy .con} 
#    grid .con.apply  .con.done 
    if { $savegeom ne "" } {
        wm geom .con $savegeom
    } else {
        wm geom .con +9+3      
    }

}
proc between {a b c} {
    if       { $b < $a } {
        return $a
    } elseif { $b > $c } {
        return $c
    } else {
        return $b
    }
}
proc copyg {from to} {
    if { ![string is integer $from ]  || ![string is integer $to ] } {
        error "$from and $to are not both integers"
    }
    if {!( [between 1 $from 9] == $from) || ! ( [between 1 $to 9] == $to)} {
        error "$from or $to out of range (1..9)"
    }
    tooltip::tooltip configure -grey5$to [tooltip::tooltip configureall -grey5$from]
    if { [info command .con] eq ".con" } {
        set geom [wm geom .con]
        con_dlog grey5$to
        set ::nowcolor grey5$to
        wm geom .con $geom
    }
}
proc tomain {grey} { ;# text
    set zoo [dict create {*}[tooltip::tooltip configureall -grey5$grey]]
    dict for {a b} $zoo {
        if [catch {
            $::tooltip::G(TOPLEVEL).text config {*}[list $a $b]
            foo $::tooltip::G(TOPLEVEL).text config {*}[list $a $b]
        } err_code] {
            puts stderr "                                                 {*}[list $a $b] -  $err_code" 
        }
    }
}
proc entrywheel {dir mult i color from to} {
    set v $::configs($i,value)
    if {! [string is integer $v] || $v eq ""} {
        set set ::configs($i,value) ""
        return ;# if not a simple integer, we just punt
    }
    if { $dir  > 0} {
        incr v $mult
    } else {
        incr v -$mult
    }
    set ::configs($i,value) [between $from $v  $to]
    doapply $color
}
proc doapply {color} {
    global configs
    for {set i 1} {$i < 100} {incr i} {
        if {! [info exist ::configs($i,name)] } {
#           puts "done doapply $i"
            return
        }
        if { $::configs($i,type) in {font color entry spinbox}} {
#           puts "apply to $color [format %10s $i ] with [format %15s |$::configs($i,name)| ] type= [format %10s |$::configs($i,type)| ] value = |$::configs($i,value)|"
            if [catch {
            tooltip::tooltip config -$color [list -$::configs($i,name) $::configs($i,value)]
            } err_code] {
                puts stderr "$err_code will try to set to null" 
                if [catch {
                    tooltip::tooltip config -$color [list -$::configs($i,name) {}]
                } err_code] {
                    puts stderr "couldn't set to null: $err_code"
                }
            }
        }
        
    }
}
proc doapplyx {args} {
    global configs
    for {set i 101} {$i < 200} {incr i} {
        if {! [info exist ::configs($i,name)] } {
            return
        }
        if { $::configs($i,type) in {font color entry spinbox}} {
            if [catch {
                set name $::configs($i,name)
                set value $::configs($i,value)
                if       { $name eq "background" } {
                    if { $value eq ""} {
                        tooltip::tooltip config -background lightyellow
                        .text config -background grey85
                        unset -nocomplain ::bgchange
                        set ::configs($i,value) grey85
                    } else {
                        tooltip::tooltip config -background $value
                        set ::bgchange 1
                    }
                } elseif { $name eq "foreground" } {
                    if { $value eq ""} {
                        tooltip::tooltip config -background black
                        .text config -background black
                        unset -nocomplain ::fgchange
                        set ::configs($i,value) black
                    } else {
                        tooltip::tooltip config -foreground $value
                        set ::fgchange 1
                    }
                } else {
                    .text config {*}[list -$name $value]
                }
            } err_code] {
                puts stderr "doapplyx: $err_code" 
            }
        }
    }
    wm geom . [wm geom .] ; main2tips
}
proc foo {args} { ;# for debug only
   puts "foo: args= |$args|"
}

proc setcombo {i color} {
#   puts "combo: i= |$i|  color= |$color| "
    if { $::configs($i,value) in $::configs($i,legal)} {
        tooltip::tooltip config -$color "-$::configs($i,name)  $::configs($i,value)"
    } else {
        puts stderr "invalid $::configs($i,name) not one of $::configs($i,legal)"
    }
    
}
proc setcombox {i color} {
    if { $::configs($i,value) in $::configs($i,legal)} {
        .text config {*}"-$::configs($i,name)  $::configs($i,value)"
    } else {
        puts stderr "invalid $::configs($i,name) not one of $::configs($i,legal)"
    }
    wm geom . [wm geom .] ; main2tips
}
proc setcheck {i color} {
#   puts "check: i= |$i|  color= |$color| "
        tooltip::tooltip config -$color "-$::configs($i,name)  $::configs($i,value)"
}
proc setspin {i color} {
#   puts "spin: i= |$i|  color= |$color| "
        tooltip::tooltip config -$color "-$::configs($i,name)  $::configs($i,value)"
}
proc setspinx {i color} {
        .text config  {*}"-$::configs($i,name)  $::configs($i,value)"
        wm geom . [wm geom .] ; main2tips
}
proc setcolor {i color} {
    set ncolor [tk_chooseColor -initialcolor $color -title "Choose color"]
#   puts "col: i= |$i| ncolor= |$ncolor| color= |$color| "
    if { $color ne "" } {
        set ::configs($i,value) $ncolor
        tooltip::tooltip config -$color "-$::configs($i,name) $ncolor"
        doapply $color
    }
}
proc setcolorx {i color} {
    set color [.text cget -bg]
    set ncolor [tk_chooseColor -initialcolor $color -title "Choose color"]
    if { $color ne "" } {
          set ::configs($i,value) $ncolor
        .text config -$::configs($i,name) $ncolor
        doapplyx main
    }
}

proc setfont {i color} {
    tk fontchooser configure -command [list gotfont $i $color]
    tk fontchooser show 
#   puts "col: i= |$i| ncolor= |$ncolor| color= |$color| "
}
proc gotfont {i color args} {
#   puts "gotfont i= |$i| color= |$color| args= |$args| "
    lassign $args nfont
    if { $color ne "" } {
         set ::configs($i,value) $nfont
         tooltip::tooltip config -$color "-$::configs($i,name) [list $nfont]"
    }
}
catch {unset conv}  ;# we generate this once only, takes time to build it
proc tocode {str} { ;# revert back to unicode \uFFFF so can be placed in source code
    global conv
    if { ![info exist conv] } {
        set conv [list "\t" "\\t" " " " "] ;# space to space for speedup, same as letter/symbols below
        set c {e i a n s o r t l c u d p m h g y b f v k w z x j q E I A N S O R T L C U D P M H G Y B F V K W Z X J Q 0 1 2 3 4 5 6 7 8 9 0}
        set d {! @ # $ % ^ & * ( ) _ + - = \{ \} [ ] ; ' : \" < > , . / ?}   
        foreach letter [concat {{ }} $c $d] {
            lappend conv $letter $letter
        }
        # most chars are not unicode, so the above is found quickly worst case is no hit at all
        for {set m 0x100} {$m < 0xffff} {incr m} {
            if { $m == 0x2424 || $m == 0x2409} {
                continue ;# except for our nl char or horizontal tab
            }
            set h [format %04x $m  ]
            set y "set x \\u$h"
            eval $y
            lappend conv $x "\\u$h" 
        }
    }
    return [string map $conv $str]
}
# storeText --
#     Dump the contents
#
# Arguments:
#     action 1=save 2=return text dump, with unicode back to \uxxxx form so we can use in a program
#
# Returns:
#     Nothing
#
# Side effects:
#     Prints the contents of the text widget in a console
#     and saves it based on action
#
proc storeText {action} {
#    console show
    global tags 

    set ender "end"
    set textdump [.text dump 1.0 $ender]
    if { [.text get end-1c end] eq "\n" } {
#       puts "trimming last newline"
        set textdump [lrange $textdump 0 end-3] 
    }

    if { $action == 1} {
        set ::save $textdump
        set io [open $::savefile w]
        fconfigure $io -encoding utf-8
        puts $io [tocode $::save]
        close $io
        
        set io [open $::savefile2 w]
        set out [tooltip::tooltip configureall]
        regsub -nocase -linestop -lineanchor -all {(-grey5[1-9]|-blue|-violet|-red|-green|-orange|-black|-grey )} $out "\\\n\\1" out2
        puts $io "tooltip::tooltip configure $out2"
        close $io
    }
    
    if { $action == 2} {
        set ans "##[tocode $textdump]" ;# so we can time this easier
        return $ans
    }
    set tcode "\nset $::tootip_varname \{##[tocode $textdump]\}\n\n" ;# code we can copy into our program
    clipboard clear ; clipboard append "$tcode" ;# the 2 #'s tell tooltip it's a complex tip
    set ::clipboard $tcode
    catch {unset ::tags}
    set cnt 0
    foreach {key value index}  $textdump { ;# fancy dump
        set color normal
        if { $key eq "text" } {
            set color red
            set value [string map {"\n" "\u2424" "\t" "\u2409" } $value]
        }
        
        if { $key eq "tagon" } {
                set color normal
                set ::tags($value) {}
                foreach item [.text tag configure $value] {
                    set value2 [lindex $item 4]
                    if {[string length $value2] > 0} {
                        set option [lindex $item 0]
                        lappend ::tags($value) $option $value2
                    }
                }
        }
        cputs $color "key= |[format %10s $key]| index= |[format %10s $index ]| value= [format %-30s |[tocode $value]|]  \n" 
        if { [incr cnt] % 100 == 0 } {
            update
        }
         
    }
    puts \n-----------------\n
    if [catch {
        parray ::tags
    } err_code] {
        puts $err_code
    }
    puts \n-----------------\n
}
proc loadf {w test} {
    set con 0
    if { [info command .con] eq ".con" } {
        set con 1 ;# there's a dialog open
        set geom [wm geom .con]
    }
    clear all
    set io [open $::savefile r]
    fconfigure $io -encoding utf-8 ;# for our unicode characters
    set ::save [read -nonewline $io]
     
    close $io
    if { [lindex $::save end-2] eq "text" &&  [lindex $::save end-1] eq "\n"} {
#       puts "trimming last newline on load"
        set ::save [lrange $::save 0 end-3]
    }

    restore $w $::save

    if { [file exist $::savefile2] } {
        source $::savefile2
    }
    
    testit $test
    if { [tooltip::tooltip config -wrap] eq "word" } {
        set ::wraponoff 1
    } else {
        set ::wraponoff 0
    }
    if { $con } {
        con_dlog $::nowcolor
        wm geom .con $geom
    }

}
proc restore {w savex} {
    set save [subst -nocommands -novariables $savex]
    # create items, restoring their attributes
    foreach {key value index} $save \
            {
        switch $key \
                {
            exec    { eval [string replace $value 0 [string first " " $value]-1 $w] }
            image   { $w image create $index -name $value }
            text    { $w insert $index $value }
            mark    {
                switch $value {
                    current { set currentIndex $index }
                    insert { set insertIndex $index}
                    default { $w mark set $value $index }
                }
            }
            tagon   { set tag($value) $index }
            tagoff  { $w tag add $value $tag($value) $index }
            window  { $w window create $index -window $value }
        }
    }
    $w mark set current $currentIndex
    $w mark set insert $insertIndex
}
proc cputs { color args} { ;# this (re-)defines my color puts
    if { $::tcl_platform(platform) eq "windows" || 1} {
        if { $color eq "red" } {
            puts -nonewline stderr {*}$args
        } else {
            puts -nonewline {*}$args
        }
    } else {
        puts -nonewline {*}$args
    }
}

#proc
# main --
#     Run the thing
#



# Include a console for non-windows and modify it

#puts "before console "
package require Tk
if { $::tcl_platform(platform)  ne "windows" } {
if {[catch {console show}]} { ;# if already done, bypass
puts "enable console" 
 
 
 
######################## linux console

 
########################################################################
# Check Tcl/Tk support
########################################################################
if {[catch {package require Tcl 8-}]} {
    package require Tcl 7.5-
}

if {[catch {package require Tk 8-}]} {
    if {[catch {package require Tk 4.1}]} {
        return -code error "Tk required but not loaded."
    }
}


########################################################################
# Provide the support which the Tk library script console.tcl assumes
########################################################################
# 1. Create an interpreter for the console window widget and load Tk
set consoleInterp [interp create]
$consoleInterp eval [list set tk_library $tk_library]
$consoleInterp alias exit exit
load "" Tk $consoleInterp
 
# 2. A command 'console' in the application interpreter
;proc console {sub {optarg {}}} [subst -nocommands {
    switch -exact -- \$sub {
        title {
            $consoleInterp eval wm title . [list \$optarg]
        }
        hide {
            $consoleInterp eval wm withdraw .
        }
        show {
            $consoleInterp eval wm deiconify .
        }
        eval {
            $consoleInterp eval \$optarg
        }
        default {
            error "bad option \\\"\$sub\\\": should be hide, show, or title"
        }
    }
}]

# 3. Alias a command 'consoleinterp' in the console window interpreter
#       to cause evaluation of the command 'consoleinterp' in the
#       application interpreter.
;proc consoleinterp {sub cmd} {
    switch -exact -- $sub {
        eval {
            uplevel #0 $cmd
        }
        record {
            history add $cmd
            catch {uplevel #0 $cmd} retval
            return $retval
        }
        default {
            error "bad option \"$sub\": should be eval or record"
        }
    }
}
if {[package vsatisfies [package provide Tk] 4]} {
    $consoleInterp alias interp consoleinterp
} else {
    $consoleInterp alias consoleinterp consoleinterp
}

# 4. Bind the <Destroy> event of the application interpreter's main
#    window to kill the console (via tkConsoleExit)
bind . <Destroy> [list +if {[string match . %W]} [list catch \
        [list $consoleInterp eval ::tk::ConsoleExit]]]

# 5. Redirect stdout/stderr messages to the console
if { 0 } { ;#no, don't use this, it doesn't work (unicode fail)
    # 5a. we can use TIP#230 channel transforms to achieve this simply:

} else {
    # 5b. Pre-8.6 needs to redefine 'puts' in order to redirect stdout
    #     and stderr messages to the console
    rename puts tcl_puts
    ;proc puts {args} [subst -nocommands {
        switch -exact -- [llength \$args] {
            1 {
                if {[string match -nonewline \$args]} {
                    if {[catch {uplevel 1 [linsert \$args 0 tcl_puts]} msg]} {
                        regsub -all tcl_puts \$msg puts msg
                        return -code error \$msg
                    }
                } else {
                    $consoleInterp eval [list ::tk::ConsoleOutput stdout \
                            "[lindex \$args 0]\n"]
                }
            }
            2 {
                if {[string match -nonewline [lindex \$args 0]]} {
                    $consoleInterp eval [list ::tk::ConsoleOutput stdout \
                            [lindex \$args 1]]
                } elseif {[string match stdout [lindex \$args 0]]} {
                    $consoleInterp eval [list ::tk::ConsoleOutput stdout \
                            "[lindex \$args 1]\n"]
                } elseif {[string match stderr [lindex \$args 0]]} {
                    $consoleInterp eval [list ::tk::ConsoleOutput stderr \
                            "[lindex \$args 1]\n"]
                } else {
                    if {[catch {uplevel 1 [linsert \$args 0 tcl_puts]} msg]} {
                        regsub -all tcl_puts \$msg puts msg
                        return -code error \$msg
                    }
                }
            }
            3 {
                if {![string match -nonewline [lindex \$args 0]]} {
                    if {[catch {uplevel 1 [linsert \$args 0 tcl_puts]} msg]} {
                        regsub -all tcl_puts \$msg puts msg
                        return -code error \$msg
                    }
                } elseif {[string match stdout [lindex \$args 1]]} {
                    $consoleInterp eval [list ::tk::ConsoleOutput stdout \
                            [lindex \$args 2]]
                } elseif {[string match stderr [lindex \$args 1]]} {
                    $consoleInterp eval [list ::tk::ConsoleOutput stderr \
                            [lindex \$args 2]]
                } else {
                    if {[catch {uplevel 1 [linsert \$args 0 tcl_puts]} msg]} {
                        regsub -all tcl_puts \$msg puts msg
                        return -code error \$msg
                    }
                }
            }
            default {
                if {[catch {uplevel 1 [linsert \$args 0 tcl_puts]} msg]} {
                    regsub -all tcl_puts \$msg puts msg
                    return -code error \$msg
                }
            }
        }
    }]
    $consoleInterp alias puts puts
    # Restore normal [puts] if console widget goes away...
    proc Oc_RestorePuts {slave} {
        rename puts {}
        rename tcl_puts puts
        interp delete $slave
    }
}

# 6. No matter what Tk_Main says, insist that this is an interactive  shell
set tcl_interactive 1

########################################################################
# Evaluate the Tk library script console.tcl in the console interpreter
########################################################################
$consoleInterp eval source [list [file join $tk_library console.tcl]]
if {[string match 8.3.4 $tk_patchLevel]} {
    # Workaround bug in first draft of the tkcon enhancments
    $consoleInterp eval {
        bind Console <Control-Key-v> {}
    }
}

$consoleInterp alias Oc_RestorePuts Oc_RestorePuts $consoleInterp
$consoleInterp eval {
    bind Console <Destroy> +Oc_RestorePuts
}


unset consoleInterp

console title "[wm title .] Console"


######################## end linux console
 
 
 
 
    
 } else { ;# already done
     puts "console already done"
 }
 } ;# if not windows

if [catch { ;# add our enhancements if not allready there (in the tclkit)
    console eval {
        
        if { ![info exist ::tk::do_scroll] } {
            
#           proc console stuff
            set vers [split [info patch] .]
            # was: $::tcl_platform(platform) eq "windows" && [package vcompare [info patch] 8.6.9]
            if { [.menubar.edit entrycget 5 -label] eq "Font..."} { ;# test for font... menu item, if there, +1 to find incr/decr font size
                set menuincr 1
                puts "incr menu, there's a Font... item"
            } else {
                set menuincr 0
                puts "no incr menu, there's no Font... item"
            }
            if [catch {
                .console config -inactiveselectbackground SystemInactiveBorder 
            } err_code] {
                puts stderr "inactive border setup: $err_code "
            }
            .console config -tabs {32 left} -tabstyle wordprocessor -width 30
#           wm geom . 110x30+446+9
            bind all <Button-3> {.menubar.edit invoke 2}
            bind all <Control-Button-3> {.menubar.edit invoke 1;.menubar.edit invoke 2}
            pack forget .console .sb .consoleframe
            
            pack [frame  .frame -bg black] -side left -fill y -ipady 10 -pady 10
            #pack [button .frame.eval -text Cev  -command {clipboard clear; clipboard append "console eval \{\}"; .menubar.edit invoke 2}] -side top -fill x
            pack [button .frame.clear -bg white -fg black -text Clear -command {.menubar.file invoke 2}] -side top -fill x
            pack [button .frame.smaller -bg white -text {font -} -command {.menubar.edit invoke [expr ( $menuincr+6 )];after 100 {.frame.repos invoke}}] -side top -fill x
            pack [button .frame.bigger  -bg white -text {font +} -command {.menubar.edit invoke [expr ( $menuincr+5 )];after 100 {.frame.repos invoke}}] -side top -fill x
            pack [button .frame.exit     -bg pink -text Exit -command {exit}] -side top -fill x
            pack [button .frame.repos -bg white -text BottOM -command {.console see end; .console mark set insert end}] -side top -fill x
            pack [frame  .frame.frame -bg black] -side top -fill x
            pack [button .frame.frame.pm20 -bg gray -text -x -command {wm geom . [expr [lindex [split [wm geom .] x] 0]-10]x[lindex [split [wm geom .] x] 1]}] -side left -fill x -expand 1
            pack [button .frame.frame.pp20 -bg gray -text +x -command {wm geom . [expr [lindex [split [wm geom .] x] 0]+10]x[lindex [split [wm geom .] x] 1]}] -side left -fill x -expand 1
            
            .frame.bigger invoke;.frame.bigger invoke;.frame.bigger invoke;.frame.bigger invoke
            
            pack [checkbutton .frame.scroll -bg white -fg black  -text scroll -variable ::tk::do_scroll] -side top -fill x
            pack .consoleframe -in . -anchor center -expand 1 -fill both -ipadx 0 -ipady 0 -padx 0 -pady 0 -side left
            pack .console -in .consoleframe -anchor center -expand 1 -fill both -ipadx 0 -ipady 0 -padx 1 -pady 1 -side left
            pack .sb -in .consoleframe -anchor center -expand 0 -fill both -ipadx 0 -ipady 0 -padx 1 -pady 1 -side right
            set ::tk::console::maxLines 10000
            .menubar add casc -label Extra2 -menu [menu .menubar.extra -tearoff 0]
            proc menu+ {head label cmd} {
                set cmd2 [list consoleinterp eval $cmd]
                .menubar.$head add command -label $label -command $cmd2
            }
#           menu+ extra {Widget Tree} {wtree}
            proc ::tk::ConsoleOutput {dest string} {
                set w .console
                $w insert output $string $dest
                ::tk::console::ConstrainBuffer $w $::tk::console::maxLines
                if {$::tk::do_scroll} {$w see insert}
            }
            set ::tk::do_scroll 1
        }
    }
} errstr ] {
    puts stderr "console error = $errstr"
}


mainWindow







The easiest way to include borders around tooltips is to configure the text widget that cooltip creates:

(The latest GUI code has a border menu that can do this interactively, and output the code to the console window).

# First, the toplevel must be createad, if not already done.
# Paste this into the gui editor's console (cntrl-v not the paste button) 
# to apply it there and see results in the editor's own tips as well as any 
# being created
# 

if {![winfo exists $::tooltip::G(TOPLEVEL)]} {
        ::tooltip::createToplevel
}
$::tooltip::G(TOPLEVEL).text config -bd 8 -relief ridge -pady 4 -padx 4