label selection

MGS - Sometimes I want to be able to select the text in a label to paste into another application. So I wrote this little package to do just that. It wasn't as straightforward as I thought it might be, but at least I learned a few things about selection and the clipboard :-)

  # label.tcl --
  
  # Label selection.
  
  # You can double-click (Button 1) any label to set the selection to the
  # label's text. If a label can take focus (with the -takefocus option),
  # a single click (Button 1) will focus the label and then you can Cut,
  # Copy and Paste with <Control-x>, <Control-c> and <Control-v>.
  
  # Version   : 0.0.1
  # Author    : Mark G. Saye
  # Email     : [email protected]
  # Copyright : Copyright (C) 2003
  # Date      : February 20, 2003
  
  # ======================================================================
  
    namespace eval label {}
  
    package require Tk
    package provide label 0.0.1
  
  # ======================================================================
  
  proc label::button1 {W} {
  
    if { [string equal [$W cget -state] disabled] } { return }
  
    if { [$W cget -takefocus] } { focus $W }
  
  }
  
  # ======================================================================
  
  proc label::copy {W} {
  
    clipboard clear
    clipboard append [$W cget -text]
  
  }
  
  # ======================================================================
  
  proc label::cut {W} {
  
    copy $W
  
    if { ![string equal [$W cget -state] disabled] } {
      $W configure -text ""
    }
  
  }
  
  # ======================================================================
  
  proc label::paste {W} {
  
    if { [catch {clipboard get -displayof $W} clip] } {
      puts stderr "\[$::errorCode\] $clip"
      return
    }
  
    if { ![string equal [$W cget -state] disabled] } {
      $W configure -text $clip
    }
  
  }
  
  # ======================================================================
  
  proc label::focus:in {W} {
  
  }
  
  # ======================================================================
  
  proc label::focus:out {W} {
  
  }
  
  # ======================================================================
  
  proc label:handle {W offset maxChars} {
  
    return \
      [string range [$W cget -text] $offset [expr {$offset+$maxChars-1}]]
  
  }
  
  # ======================================================================
  
  proc label::normal {W} {
  
    # label has lost the selection - reset state to normal
    if { [string equal [$W cget -state] active] } {
      $W configure -state normal
    }
  
  }
  
  # ======================================================================
  
  proc label::select {W} {
  
    if { [string equal [$W cget -state] disabled] } { return }
  
    $W configure -state active
  
    selection own -command [list label::normal $W] $W
  
    selection handle -type UTF8_STRING $W [list label:handle $W]
    selection handle                   $W [list label:handle $W]
  
  }
  
  # ======================================================================
  
    bind Label <FocusIn>   [list label::focus:in  %W]
    bind Label <FocusOut>  [list label::focus:out %W]
  
    bind Label <<Copy>>    [list label::copy  %W]
    bind Label <<Cut>>     [list label::cut   %W]
    bind Label <<Paste>>   [list label::paste %W]
  
    bind Label <Double-1>  [list label::select  %W]
    bind Label <Button-1>  [list label::button1 %W]
  
    bind Label <Control-x> [list event generate %W <<Cut>>]
    bind Label <Control-c> [list event generate %W <<Copy>>]
    bind Label <Control-v> [list event generate %W <<Paste>>]
  
  # ======================================================================
  
  # demo code
  
  proc main {{argc 0} {argv {}}} {
  
    entry  .e
    .e insert end "Entry - Hello World"
  
    label .l1 -text "Label Without Focus"
  
    label .l2 -text "Label With Focus" \
      -takefocus 1 \
      -highlightthickness 1 \
      -relief sunken -bd 1
  
    global state
    set state [.l2 cget -state]
  
    foreach _state {normal active disabled} {
      set r .$_state
      radiobutton $r \
        -text $_state \
        -variable state \
        -value $_state \
        -command [list .l2 configure -state $_state]
    }
  
    button .b -text Close -default active -command exit
  
    pack .b        -side bottom -anchor se
    pack .e        -side top    -expand 0 -fill x -padx 20 -pady 20
    pack .l1       -side top    -expand 0 -fill x -padx 20 -pady 20
    pack .l2       -side top    -expand 0 -fill x -padx 20 -pady 20
    pack .normal   -side top    -expand 0 -fill x
    pack .active   -side top    -expand 0 -fill x
    pack .disabled -side top    -expand 0 -fill x
  
  }
  
  # ======================================================================
  
    if { [info exists argv0] && [string equal [info script] $argv0] } {
      main $argc $argv
    }
  
  # ======================================================================

KBK A very pretty discussion of bindings and clipboard management. Nevertheless, most of us prefer an entry or text with -state disabled for implementing a UI component that supports selection but not modification.

MGS - Agreed. But this way, you can load this on top of any existing code, without modification, to get the desired functionality. And you can use features of a label that an entry doesn't have (e.g. compound images). And labels do use (slightly) less memory then entry widgets. I also prefer labels if you want text wrapping. See label wrapping.

Those interested in selection and clipboard differences might like to check out Primary Transfer vs. the Clipboard.


TV Isn't the memory question a bit overly concerned, I've used tcl/tk on a windows 3.1 machine with what is it 8 meg of main mem, and could easily run Bwise for instance. How much data is in a label or overseeable displayed text anyhow?

MGS - Agreed, memory consumption is not really a concern, I was just listing the features in favour of labels vs entry and text widgets. I am also looking at Tcl/Tk for embedded/handheld use, so every little bit of efficiency can help.


MGS [2003/04/20] - I have combined the above code with that in label wrapping and created a package which you can find in the Links section at Mark G. Saye.


WJG (25/09/13) This is a standard feature of the gnocl::label widget, just set the -selectable option to '1'.


See also: