# Copyright (c) 2000, Red Hat, Inc.
# 
# This file is part of Source-Navigator.
# 
# Source-Navigator is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as published
# by the Free Software Foundation; either version 2, or (at your option)
# any later version.
# 
# Source-Navigator is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.
# 
# You should have received a copy of the GNU General Public License along
# with Source-Navigator; see the file COPYING.  If not, write to
# the Free Software Foundation, 59 Temple Place - Suite 330, Boston,
# MA 02111-1307, USA.
# 
itcl_class Browser& {
	inherit Busy&

	constructor {config} {
		global sn_options

		set class [$this info class]
		@@rename $this $this-tmp-
		@@frame $this -class $class
		@@rename $this $this-win-
		@@rename $this-tmp- $this

		regexp {[A-Za-z0-9_]+} $class class

		set widget $this.t
		if {$spacing3 == -1} {
			set spacing3 $sn_options(def,browser-spacing)
		}

		text $widget\
			-xscrollcommand "$this.x set"\
			-yscrollcommand "$this.y set"\
			-wrap $wrap -state $state\
			-exportselection $exportselection\
			-spacing3 $spacing3 -width $width -height $height\
			-cursor top_left_arrow \
			-takefocus 1 \
			-font $sn_options(def,default-font) \
			-selectforeground $sn_options(def,select-fg) \
			-selectbackground $sn_options(def,select-bg) \
			-fg $sn_options(def,default-fg) \
			-bg $sn_options(def,default-bg)
		@@bind $widget <Tab> {focus [tk_focusNext %W]}
		@@bind $widget <Shift-Tab> {focus [tk_focusPrev %W]}

		frame $this.size -height 8

		set handle1 $this.hand_0
		frame $handle1 -relief raised -bd 2\
			-width 8 -height 13 -cursor	sb_h_double_arrow

		if {[winfo exists $info_label]} {
			balloon_bind_info $handle1 [get_indep String EINFODragDrop]
		}
		set handle2 $this.hand_1
		frame $handle2 -relief raised -bd 2\
			-width 8 -height 13 -cursor	sb_h_double_arrow

		if {[winfo exists $info_label]} {
			balloon_bind_info $handle2 [get_indep String EINFODragDrop]
		}

		sn_remove_tags $widget {Text}
		sn_add_tags $widget $class

		$widget mark set active 1.0
		$widget tag configure activetag -underline 0

		scrollbar $this.x -command "$widget xview" -orient horiz
		scrollbar $this.y -command "$widget yview"
		@@bind $this.y <ButtonRelease-1> "[info class] @@ resize_widget $widget"

		frame $this.filter
		label $this.filter.label -text [get_indep String Pattern]

		set entry $this.filter.entry

		entry $entry -relief sunken -exportselection 0 -width 3

		$entry insert end $filter
		$entry icursor 0

		@@bind $entry <Return> "$this fill; break"

		pack $this.filter.label -side left
		pack $entry -side left -fill x -expand y -padx 3

		if {$filter_window} {
			pack $this.filter\
				-side bottom -fill x
		}

		pack $this.size -side top -fill x -anchor nw
		pack $this.t -fill both -expand y

		if {[string compare $contents ""] != 0} {
			$this fill 0
		}

		$this bind <KeyPress> "$this search_region %A %s"
		
#		$this bind <Control-KeyPress> { }
		$this bind <$sn_options(sys,alt-traverse)-1> "$this config -sort_column 0; break"
		$this bind <$sn_options(sys,alt-traverse)-2> "$this config -sort_column 1; break"
		$this bind <$sn_options(sys,alt-traverse)-3> "$this config -sort_column 2; break"
		$this bind <Delete> "$this search_cri_backspace"
		$this bind <BackSpace> "$this search_cri_backspace"
		$this bind <Control-h> "$this search_cri_backspace"
		$this bind <Control-u> "$this config -filter {*}; focus %W"
		$this bind <Control-l> [$this bind <Control-u>]
		$this bind <Control-r> [$this bind <Control-u>]
		$this bind <Escape> "$this clear_search_pat"
		$this bind <Control-p> "$this print_dialog_box"

		foreach attr $config {
			config -$attr [set $attr]
		}

		tkTextSetup $widget

		$this clear_search_pat
	}

	common resizing

	proc rebind_config {} {
		if {[info exists resizing]} {
			foreach w [array names resizing] {
				catch {@@bind $w <Configure> "[info class] @@ resize_widget %W"}
			}
			catch {unset resizing}
		}
	}

	proc exec_resize {} {
		foreach W [array names resizing] {
			if {[catch {set f [winfo parent $W]}]} {
				catch {unset resizing($W)}
				continue
			}

			if {[$W yview] == "0 1"} {
				set bef $W
				pack forget $f.y
			} else {
				set bef $f.y
				pack $f.y -side right -fill y -before $W
			}

			if {[$W xview] == "0 1"} {
				pack forget $f.x
			} else {
				pack $f.x -side bottom -fill x -before $bef
			}

			if {[winfo ismapped $W]} {
				pack propagate $f 0
			}
		}
		after idle "[info class] @@ rebind_config"
	}

	proc resize_widget {W} {
		if {![info exists resizing]} {
			after idle "[info class] @@ exec_resize"

		}
		set resizing($W) 1
		@@bind $W <Configure> { }
	}

	protected linestart "linestart"
	public filter_window 1

	destructor {
		catch {@@rename $this-win- {}}
		catch {destroy $this}

		foreach v [@@info globals "$this-*"] {
			catch {uplevel #0 unset $v}
		}
	}

	proc class_bind {class linest} {
		@@bind $class <3> {browser&_post_menu %W %X %Y}
		@@bind $class <Control-3> {browser&_post_menu %W %X %Y}
		@@bind $class <Shift-3> {browser&_post_menu %W %X %Y}
	
		@@bind $class <1> "
			tkTextButton1 %W %x %y
			%W tag remove sel 0.0 end
	
			%W mark set active \"@%x,%y $linest\"
	
			set tkPriv(selectMode) line
			tkTextSelectTo %W %x %y
	
			catch {%W mark set insert sel.first}
			catch {%W mark set active sel.first}
			
			%W tag remove activetag 0.0 end
			%W tag add activetag \"@%x,%y $linest\" {@%x,%y lineend}
	
			focus %W
		"
	
		@@bind $class <B1-Motion> "
			set tkPriv(x) %x
			set tkPriv(y) %y
	
			%W mark set active \"@%x,%y $linest\"
	
			%W tag remove activetag 0.0 end
			%W tag remove sel 0.0 end
			%W tag add activetag \"@%x,%y $linest\" {@%x,%y lineend}
			%W tag add sel \"@%x,%y $linest\" {@%x,%y lineend +1c}
		"
	
		@@bind $class <Double-1> {break}
		@@bind $class <Triple-1> {break}
	
		@@bind $class <B1-Leave> {
			set tkPriv(x) %x
			set tkPriv(y) %y
			browser&_tkTextAutoScan %W
		}
	
		@@bind $class <B1-Enter> {
			tkCancelRepeat
		}
	
		@@bind $class <ButtonRelease> {
			tkCancelRepeat
		}
	
		@@bind $class <2> {
			%W mark set insert @%x,%y
			%W scan mark %x %y
			set tkPriv(x) %x
			set tkPriv(y) %y
			set tkPriv(mouseMoved) 1
		}
	
		@@bind $class <B2-Motion> {
			%W scan dragto %x %y
		}
	
		@@bind $class <Left> {
			%W xview scroll -1 unit
		}
	
		@@bind $class <Right> {
			%W xview scroll 1 unit
		}
	
		@@bind $class <Up> "
			set pos \[tkTextPlaceLine %W -\]
			browser&_tkTextSetCursor %W \$pos $linest
			set tkText(%W,prevPos) \$pos
		"
	
		@@bind $class <Down> "
			set pos \[tkTextPlaceLine %W +\]
			browser&_tkTextSetCursor %W \$pos $linest
			set tkText(%W,prevPos) \$pos
		"
	
		@@bind $class <Control-Left> {
			%W xview scroll -1 page
		}
	
		@@bind $class <Control-Right> {
			%W xview scroll 1 page
		}
	
		@@bind $class <Prior> "
			browser&_tkTextSetCursor %W \[tkTextScrollPages %W -1\] $linest
		"
	
		@@bind $class <Shift-Prior> {
			tkTextKeySelect %W [tkTextScrollPages %W -1]
		}
	
		@@bind $class <Next> "
			browser&_tkTextSetCursor %W \[tkTextScrollPages %W 1\] $linest
		"
	
		@@bind $class <Shift-Next> {
			tkTextKeySelect %W [tkTextScrollPages %W 1]
		}
	
		@@bind $class <Control-Prior> {
			%W xview scroll -1 page
		}
	
		@@bind $class <Control-Next> {
			%W xview scroll 1 page
		}
	
		@@bind $class <Home> {
			%W xview moveto 0
		}

		@@bind $class <Shift-Home> {
			%W xview moveto 1
		}
	
		@@bind $class <End> {
			%W xview moveto 1
		}
	
		@@bind $class <Shift-End> {
			%W xview moveto 0
		}

		@@bind $class <Control-Home> \
			"browser&_tkTextSetCursor %W 1.0 $linest"
	
		@@bind $class <Control-Shift-Home> {
			tkTextKeySelect %W 1.0
		}
	
		@@bind $class <Control-End> \
			"browser&_tkTextSetCursor %W {end - 1 char} $linest"
	
		@@bind $class <Control-Shift-End> {
			tkTextKeySelect %W {end - 1 char}
		}
	
		@@bind $class <FocusIn> {
			catch {%W tag configure activetag -underline 1}
		}
	
		@@bind $class <FocusOut> {
			catch {%W tag configure activetag -underline 0}
		}

		@@bind $class <Control-c> {browser&_textCopy %W;break}
		@@bind $class <F16> {browser&_textCopy %W;break}
		#@@bind $class <Configure> "[info class] @@ resize_widget %W"

		#bind_focus_enter $class {focus %W}
	}

	proc resize {h p t x num {h2 ""}} {
		set x [expr $x - [winfo rootx $p]]
		if {$x < 50} {
			set x 50
		}

		set tb [$t cget -tabs]
		set t1 [lindex $tb 0]

		place config $h -x $x

		if {$num == 0} {
			set t2 [lindex $tb 1]
			if {$t2 == ""} {
				set t2 [expr [font_avg_width $t] * 8]
			}
			incr t2 [expr $x - $t1]

			if {[lsearch -exact [place info $h2] "-in"] != -1} {
				place config $h2 -x $t2
			}
			if {[catch {set tb [lreplace $tb 0 1 $x $t2]}]} {
				set tb [list $x $t2]
			}
		} else {
			if {[catch {set tb [lreplace $tb 1 1 $x]}]} {
				lappend tb $x
			}
		}
		$t config -tabs $tb

		if {[winfo ismapped $t]} {
			resize_widget $t
		}
	}

	method bbox {index} {
		return [eval $widget bbox [trim_text_index $index]]
	}

	method cget {args} {
		return [eval $widget cget $args]
	}

	method compare {args} {
		return [eval $widget compare $args]
	}

	method config {config} {
	}

	method config_tk {args} {
		if {[winfo exists $widget]} {
			return [eval $widget config $args]
		}
		return ""
	}

	method debug {args} {
		return [eval $widget debug $args]
	}

	method delete_tk args {
		return [eval $widget delete [list_text_index $args]]
	}

	method deleteline {{line "insert"}} {
		$widget config -state normal
		$widget delete "$line linestart" "$line lineend+1c"
		$widget tag remove sel 0.0 end
		$widget tag add sel {active linestart} {active lineend+1c}
		$widget config -state disabled
	}
	
	#select and show a line in the browser
	method select {{line 0.0}} {
		# Wrap around the top and the bottom of the widget
		# if we move up past the top or down past the bottom
		# Its kind of hairy because we need to handle 3 special cases
		# 1. If no line is selected init by selecting the first line
		# 2. Back when 1st line is selected should goto end
		# 3. Forward when last line is selected should goto start

		if {[catch {$widget index active}]} {
			# init case
			$widget mark set active 1.0
			select 1.0
			return
		}

		# Get the index that is currently selected
		set selindex [$widget index active]
		set topindex 1.0
		set bottomindex [$widget index end-2line]

		if {$selindex > $bottomindex} {
			# init after lines have been removed or inserted
			set line 1.0
		}

		# Get the direction the selection is moving
		set direction ""
		if {[string match *+* $line]} {
			set direction down
		}
		if {[string match *-* $line]} {
			set direction up
		}

		if {$selindex == $topindex && $direction == "up"} {
			# Wrap to bottom
			set line $bottomindex
		}

		if {$selindex == $bottomindex && $direction == "down"} {
			# Wrap to top
			set line $topindex
		}

		$widget tag remove sel 0.0 end
		$widget tag remove activetag 0.0 end
		
		$widget tag add activetag "$line linestart" "$line lineend"
		$widget tag add sel "$line linestart" "$line lineend+1c"
		
		$widget mark set active $line
		$widget mark set insert active
		
		$widget see active
	}

	method dlineinfo {args} {
		return [eval $widget dlineinfo $args]
	}

	method get {args} {
		set idx [list_text_index $args]
		if {[llength $idx] == 1} {
			lappend idx "$idx lineend"
		}

		return [eval $widget get $idx]
	}

	method index {args} {
		return [eval $widget index [list_text_index $args]]
	}

	method insert {args} {
		set st $state
		config -state normal
		set ret [eval $widget insert [list_text_index $args]]
		config -state $st
	}

	method mark {args} {
		return [eval $widget mark $args]
	}

	method scan {args} {
		return [eval $widget scan $args]
	}

	method search {args} {
		return [eval $widget search $args]
	}

	method see {args} {
		return [eval $widget see $args]
	}

	method tag {args} {
		return [eval $widget tag $args]
	}

	method window {args} {
		return [eval $widget window $args]
	}

	method xview {args} {
		return [eval $widget xview $args]
	}

	method yview {args} {
		return [eval $widget yview $args]
	}

	public background {} {
		config_tk -background $background
	}

	public bgstipple {} {
		config_tk -bgstipple $bgstipple
	}

	public borderwidth {} {
		config_tk -borderwidth $borderwidth
	}

	public bd {} {
		config_tk -bd $bd
	}

	public cursor {} {
		config_tk -cursor $cursor
	}

	public exportselection {0} {
		config_tk -exportselection $exportselection
	}

	public fgstipple {} {
		config_tk -bgstipple $fgstipple
	}

	public font {} {
		config_tk -font $font
	}

	public foreground {} {
		config_tk -foreground $foreground
	}

	public height {25} {
		if {[winfo exists $widget]} {
			config_tk -height $height
		}
	}

	public highlightbackground {} {
		config_tk -highlightbackground $highlightbackground
	}

	public highlightcolor {} {
		config_tk -highlightcolor $highlightcolor
	}

	public highlightthickness {} {
		config_tk -highlightthickness $highlightthickness
	}

	public insertbackground {} {
		config_tk -insertbackground $insertbackground
	}

	public insertborderwidth {} {
		config_tk -insertborderwidth $insertborderwidth
	}

	public insertofftime {} {
		config_tk -insertofftime $insertofftime
	}

	public insertontime {} {
		config_tk -insertontime $insertontime
	}

	public insertwidth {} {
		config_tk -insertwidth $insertwidth
	}

	public padx {} {
		config_tk -padx $padx
	}

	public pady {} {
		config_tk -pady $pady
	}

	public relief {} {
		config_tk -relief $relief
	}

	public selectbackground {} {
		config_tk -selectbackground $selectbackground
	}

	public selectborderwidth {} {
		config_tk -selectborderwidth $selectborderwidth
	}

	public selectforeground {} {
		config_tk -selectforeground $selectforeground
	}

	public setgrid {} {
		config_tk -setgrid $setgrid
	}

	public spacing1 {} {
		config_tk -spacing1 $spacing1
	}

	public spacing2 {} {
		config_tk -spacing2 $spacing2
	}

	public spacing3 {-1} {
		config_tk -spacing3 $spacing3
	}

	public state {disabled} {
		config_tk -state $state
	}

	public tabs {} {
		config_tk -tabs $tabs
		if {$columns > 1} {
			set tabs [$widget cget -tabs]
			set tab1 [lindex $tabs 0]
			if {$tab1 == ""} {
				set tab1 [expr [font_avg_width $widget] * 8]
			}
			place config $handle1 -x $tab1

			if {$columns > 2} {
				set tab2 [lindex $tabs 1]
				if {$tab2 == ""} {
					set tab2 [expr [font_avg_width $widget] * 16]
				}
				place config $handle2 -x $tab2
			} else {
				catch {place forget $handle2}
			}
		}
		catch {
			if {[winfo ismapped $widget]} {
				resize_widget $widget
			}
		}
	}

	public ctab1 {8} {
		if {[winfo exists $widget]} {
			if {$ctab1 == ""} {
				set ctab1 8
			}
			set tabs [$widget cget -tabs]
			set tab_stop [expr [font_avg_width $widget] * $ctab1]
			if {$tabs == ""} {
				set tabs $tab_stop
			} else {
				set tabs [lreplace $tabs 0 0 $tab_stop]
			}
			config -tabs $tabs
		}
	}

	public ctab2 {16} {
		if {[winfo exists $widget]} {
			if {$ctab2 == ""} {
				set ctab2 16
			}
			set tabs [$widget cget -tabs]

			set tab_stop [expr [font_avg_width $widget] * $ctab2]
			if {$tabs == ""} {
				set tabs $tab_stop
			} else {
				if {[catch {set tabs [lreplace $tabs 1 1 $tab_stop]}]} {
					eval lappend tabs $tab_stop
				}
			}
			config -tabs $tabs
		}
	}

	proc font_avg_width {w} {
		set font [$w cget -font]
		set text_avg_width [font measure $font "M"]

		return $text_avg_width
	}

	public takefocus {} {
		config_tk -takefocus $takefocus
	}

	public xscrollcommand {} {
		config_tk -xscrollcommand $xscrollcommand
	}

	public yscrollcommand {} {
		config_tk -yscrollcommand $yscrollcommand
	}

	public width {30} {
		config_tk -width $width
	}

	public wrap {none} {
		config_tk -wrap $wrap
	}
# Listbox simulation.
	method activate {index} {

		$widget tag remove activetag 0.0 end
		set idx [$this list_text_index $index]

		$widget tag add activetag "$idx $linestart" "$idx lineend"
		$widget mark set active "$idx $linestart"
	}

	method bind {args} {
		return [@@eval bind $widget $args]
	}

	method focus {args} {
		return [@@eval focus $widget $args]
	}

	method nearest {y} {
		set idx [$widget index @0,$y]
		return [expr int($idx)]
	}

	method size {} {
		set idx [$widget index "end -1c"]
		set idx [lindex [split $idx "."] 0]

		return $idx
	}

	method curselection {} {
		if {[catch {set beg [$widget index sel.first]}]} {
			return ""
		}
		set end [$widget index "sel.last -1c"]
		set beg [lindex [split $beg "."] 0]
		set end [lindex [split $end "."] 0]
		if {$beg == $end} {
			return $beg
		}

		return [list $beg $end]
	}

# Listbox simulation end.
	method list_text_index {args} {
		set idx [lindex $args 0]
		set idx1 [lindex $args 0]
		set idx2 [lindex $args 1]
		if {![catch {set idx1 [expr double($idx1)]}]} {
			catch {set idx2 [expr double($idx2)]}
		}

		if {$idx2 != ""} {
			lappend idx1 $idx2
		}

		return $idx1
	}

	method set_selection {} {
	    if {$direct_ent != ""} {
			global $this-e-name
			@@set $this-e-name [$this marked]
	    }
	    $this clear_search_pat

	    return [$this marked]
	}

	method clear_search_pat {} {
	    set search_cri ""
	}

	method search_cri_backspace {} {
	    set len [string length $search_cri]
	    set a [string index $search_cri [expr $len - 7]]
	    set search_cri [string range $search_cri 0 [expr $len - 9]]
	    $this search_region $a
	}

	#clear selection
	method clear {} {
		$widget tag remove activetag 0.0 end
		$widget tag remove sel 0.0 end
	}

	public search_pref ""

	method search_region {a ks} {
		#altkey is pressed?
		if {[expr {$ks & 8}] == 8} {
			return
		}
		if {[string compare $a ""] == 0} {
			return
		}

		if {[$this size] > 15000} {
			config -cursor watch
			update idletasks
		}

		regsub -all {\^|\.|\*|\+|\-|\[|\]|\(|\)} $a {\\&} a

		set k "${search_cri}${a}"
		set fnd [$widget search -forwards -nocase\
			-regexp "^${search_pref}${k}" "active linestart"]
		if {$fnd == ""} {
			$this clear_search_pat

			set fnd [$widget search -forwards -nocase\
				-regexp "^${search_pref}${a}" "active + 1line linestart"]
			if {$fnd == ""} {
				$this clear_search_pat
				bell -displayof $this
		    } else {
				set search_cri $a
			}
		} else {
			set search_cri $k
		}
		
		if {$fnd != ""} {
			set pos [lindex $fnd 0]

			$this activate [expr int($pos)]
			$widget mark set insert active
			$widget see active

			set df [expr abs(int([$widget index "@0,[winfo height $widget]"]) - int($pos))]

# If the target is closer to the bottom of the page than 3 lines, center it!
			if {$df < 3} {
				$widget yview scroll\
					[expr int([text_page_num_lines $widget] / 2)] units
			}
		}
		config -cursor {}
	}

# This method is a little bit better than just "see".
	method center {pos} {
		set top [$this index "@0,0"]
		set bot [$this index "@0,[winfo height $widget]"]
		set size [expr $bot - $top]
		set midle [expr int($pos - $size / 2)]
		if {$midle < 0} {
			set midle 0
		}
		set diff [expr abs([$this index active] - $pos)]
		if {$diff > [expr int($size / 4)]} {
			$this yview $midle
		} else {
			$this see $pos
		}
	}

	protected search_cri ""

	method fill {{bsy 1}} {
		if {![winfo exists $this]} {
			return
		}
		set y [lindex [$widget yview] 0]
		
		$widget tag remove activetag 0.0 end

		$this clear_search_pat

		set filter [$entry get]
		if {[string compare $filter ""] == 0} {
			set filter "*"
			$entry insert end "*"
			$entry icursor 0
		}

		set save_st $state
		$widget config -state normal

		if {[string compare $contents ""] == 0} {
			$this delete_tk 0.0 end
			
			$widget config -state $save_st

			$widget mark set active 0.0
			$widget mark set insert 0.0

			resize_widget $widget

			return
		}

		if {$bsy} {
			$this hold
		}

		$this delete_tk 0.0 end

		if {[string compare $filter "*"] != 0} {
			if {$nocase} {
				set flt [nocase_glob_pattern $filter]
			} else {
				set flt $filter
			}
		} else {
			set flt $filter
		}			

#		It is complicated but uses less memory !!!
		if {$eval} {
			set cmd $contents;		# It contains a tcl command.

			regsub {%f} $cmd [list $flt] cmd

			catch {
				if {$sort != ""} {
					if {$nocase} {
						tk_text_insert $widget end\
							[@@lsort -index $sort_column -dictionary $sort [@@eval $cmd]]
					} else {
						tk_text_insert $widget end [@@lsort -index $sort_column $sort [@@eval $cmd]]
					}
				} else {
					tk_text_insert $widget end [@@eval $cmd]
				}
			}
		} else {
			if {[string compare $flt "*"] != 0} {
				if {$sort != ""} {
					if {$nocase} {
						tk_text_insert $widget end [@@lsort -index $sort_column \
							-dictionary $sort [lmatch $contents $flt]]
					} else {
						tk_text_insert $widget end [@@lsort -index $sort_column \
							$sort [lmatch $contents $flt]]
					}
				} else {
					tk_text_insert $widget end [lmatch $contents $flt]
				}
			} else {
				if {$sort != ""} {
					if {$nocase} {
						tk_text_insert $widget end [@@lsort -index $sort_column \
							-dictionary $sort $contents]
					} else {
						tk_text_insert $widget end [@@lsort -index $sort_column \
							$sort]
					}
				} else {
					tk_text_insert $widget end $contents
				}
			}
		}

		$widget config -state $save_st

		$widget mark set active 0.0
		$widget mark set insert 0.0

		$widget yview moveto $y

		resize_widget $widget

		if {$bsy} {
			$this release
		}
	}

	proc nocase_glob_pattern {flt} {
		if {[string compare $flt "*"] == 0} {
			return $flt
		}
	    for {set c 0; set brace_lev 0; set m ""; set glb_pat "";\
			set flt [string tolower $flt]} \
			{$c < [string length $flt]} {incr c} {
	
			set ch [string index $flt $c]
			switch -glob -- $ch {
			{[A-Za-z]} {
				if {$brace_lev <= 0} {
				    append m \[ [string tolower $ch] \
						[string toupper $ch] \]
				} else {
					append glb_pat $ch
				}
			    }
			{\[}	{
				append m \[
				set glb_pat ""
				incr brace_lev
			    }
			{\]}	{
				for {set k 0} {$k < [string length $glb_pat]} \
				    {incr k} {
				    set cc [string index $glb_pat $k]
				    set nc [string index $glb_pat [expr $k + 1]]
				    append m [string tolower $cc]
				    if {[string compare $nc "-"] == 0} {
					incr k 2
					set nc [string index $glb_pat $k]
					append m "-" [string tolower $nc] \
					    [string toupper $cc] "-" [string toupper $nc]
				    } else {
					append m [string toupper $cc]
				    }
				}
				set glb_pat ""
				append m \]
				incr brace_lev -1
			}
			{\\}	{
				incr c
				append m "\\" [string index $flt $c]
			    }
			default {
				if {$brace_lev <= 0} {
				    append m $ch
				} else {
				    append glb_pat $ch
				}
			    }
			}
		}
		return $m
	}

	method marked {{str 1}} {
		set sel [$this curselection]

		if {$str} {
			set val ""
			foreach s $sel {
				lappend val [$this get $s]
			}
			return $val
		} else {
			return $sel
		}
	}

	method direct_ent {args} {
		if {$direct_ent != ""} {
			return [eval $direct_ent $args]
		}
	}

	method values {} {
		return [$this get 0 end]
	}

	method filter {{pattern ""}} {
	    if {[string compare $pattern ""] == 0} {
			return $filter
	    }
	    $this config -filter $pattern
	}

	public uniq {0} {
	}

	method contents {} {
		return $contents
	}
	public contents {} {
		setcontents $contents
	}
	method setcontents {cnt} {
		if {!$eval && $sort != ""} {
			if {$nocase} {
				if {$uniq} {
					set contents [@@lunique [@@lsort $sort -dictionary $cnt]]
				} else {
					set contents [@@lsort $sort -dictionary $cnt]
				}
			} elseif {$uniq} {
				set contents [lunique [@@lsort $sort $cnt]]
			} else {
				set contents [@@lsort $sort $cnt]
			}
		}
		$this fill 0
	}

	public filter {*} {
		if {[winfo exists $this]} {
			$entry delete 0 end
			$entry insert end $filter
			$entry icursor 0

			if {[string compare $contents ""] != 0} {
				if {$eval} {
					set bsy 1
				} else {
					set bsy 0
				}

				$this fill $bsy
			}
		}
	}

	public script {}

	protected entry {}
	protected widget {}
	public sort {-increasing}
	public direct_ent {}
	public advised {}
	public nocase 1
	public eval 0

	public active_focus 1 {
		if {$active_focus} {
			#bind_focus_enter $widget {focus %W}
			$this focus
		} else {
			$this bind <Enter> { }
		}
	}

	public scroll {default} {
		global sn_scroll_pos

		if {[catch {set defstr $sn_scroll_pos}]} {
			set defstr "right bottom"
		}

		regsub "default" $scroll $defstr scroll
	}
	public selectmode {} {
	}
	public info_label ""
	public columns {1} {
		if {[winfo exists $widget]} {
			if {$columns > 1} {
				set curs	cross
				@@bind $handle1 <B1-Motion>\
					"[info class] @@ resize $handle1 $this.size $widget %X 0 $handle2"
				@@bind $handle2 <B1-Motion> \
					"[info class] @@ resize $handle2 $this.size $widget %X 1"
				@@bind $this.size <1> [@@bind $handle1 <B1-Motion>]

				set tabs [$widget cget -tabs]

				set tab1 [lindex $tabs 0]
				if {$tab1 == ""} {
					set tab1 [expr [font_avg_width $widget] * $ctab1]
				}
				place $handle1 -in $this.size -x $tab1 -anchor w

				set tab2 [lindex $tabs 1]
				if {$tab2 == ""} {
					set tab2 [expr [font_avg_width $widget] * $ctab2]
				}
				lappend tab1 $tab2
				if {$columns > 2} {
					place $handle2 -in $this.size -x $tab2 -anchor w

					@@bind $this.size <2> \
						"[info class] @@ resize $handle2 $this.size $widget %X 1"
				} else {
					catch {place forget $handle2}
					@@bind $this.size <2> " "
				}
				config_tk -tabs $tab1
			} else {
				set curs ""
				@@bind $handle1 <B1-Motion> " "
				@@bind $handle2 <B1-Motion> " "
				@@bind $this.size <1> " "
				@@bind $this.size <2> " "
				catch {place forget $handle1}
				catch {place forget $handle2}
			}
			$this.size config -cursor	$curs
			@@bind $handle1 <B2-Motion> [@@bind $handle1 <B1-Motion>]
			@@bind $handle2 <B2-Motion> [@@bind $handle2 <B1-Motion>]
			@@bind $handle1 <B3-Motion> [@@bind $handle1 <B1-Motion>]
			@@bind $handle2 <B3-Motion> [@@bind $handle2 <B1-Motion>]
			@@bind $this.size <3> [@@bind $this.size <2>]
		}
	}

	method print_dialog_box {{sub_tit ""}} {
		global sn_options
		global tcl_platform

		if {[winfo exists $print_dialog]} {
			$print_dialog raise
			return
		}
		set toplw [winfo toplevel $this]

		set print_dialog [Toplevel& [Toplevel& @@ next_window_name]\
			-leader $toplw]

		$print_dialog transient $toplw

		sn_motif_buttons $print_dialog bottom 0 [get_indep String ok]\
				[get_indep String cancel]
		$print_dialog.button_0 config -command "$this print_it"
		$print_dialog.button_1 config -command "$print_dialog delete"

		global $print_dialog-ptarget $print_dialog-cmd
		@@set $print_dialog-ptarget "all"
		
		set tit [wm title $toplw]
		if {[string compare $sub_tit ""] != 0} {
			append tit " " $sub_tit
		}
		@@set $print_dialog-cmd [format $sn_options(def,ascii-print-command) $tit]
		$print_dialog title [list [get_indep String Print] $tit]

		if {$tcl_platform(platform) != "windows"} {
			frame $print_dialog.command
			label $print_dialog.command.prompt\
				-text [get_indep String SQLPprintercmd]
	
			entry $print_dialog.command.cmd -width 70\
				-textvariable $print_dialog-cmd
			@@focus $print_dialog.command.cmd
			@@bind $print_dialog.command.cmd <Any-Return> \
				"$print_dialog.button_0 invoke"
		
			pack $print_dialog.command.prompt -side left
			pack $print_dialog.command.cmd -side left -padx 10 -fill x -expand y
			pack $print_dialog.command -fill x -expand y
		}
		radiobutton $print_dialog.marked -text [get_indep String Marked]\
			-variable $print_dialog-ptarget -value marked

		radiobutton $print_dialog.all -text [get_indep String All]\
			-variable $print_dialog-ptarget -value all

		radiobutton $print_dialog.view -text [get_indep String View]\
			-variable $print_dialog-ptarget -value view

		pack $print_dialog.marked -anchor w -padx 60
		pack $print_dialog.all -anchor w -padx 60
		pack $print_dialog.view -anchor w -padx 60

		$print_dialog move_to_mouse
		catch {$print_dialog resizable 1 0}
		
		$print_dialog take_focus
	}

	method print_it {} {
		upvar #0 $print_dialog-ptarget target
		upvar #0 $print_dialog-cmd cmd


		switch -- $target {
		"all" {
				set lst [$this get 0.0 end]
			}
		"marked" {
				if {[catch {set lst [$this get sel.first sel.last]}]} {
					return
				}
			}
		"view" -
		default {
				set lst [$this get @0,0 "@0,[winfo height $this] lineend +1c"]
			}
		}

		$print_dialog delete

		set tmpf [sn_tmpFileName]
		set fd [open $tmpf "w+"]
		fconfigure $fd -encoding $sn_options(def,system-encoding)
		puts $fd $lst
		close $fd

		sn_print_file $cmd $tmpf

		file delete -- $tmpf

		set print_dialog ""

		catch {unset target}
		catch {unset cmd}
	}
	protected print_dialog {}

	protected handle1
	protected handle2

	public propagate {0} {
		catch {pack propagate [winfo parent $widget] $propagate}
	}
	
	public sort_column 0 {
		if {[winfo exists $widget]} {
			if {$sort_column > [expr $columns - 1]} {
				set sort_column [expr $columns - 1]
			}
			$this fill 1
		}
	}
}

proc browser&_post_menu {w x y} {
	set m .sn_pop_menu_listbox
	# It has to be destroyed because we might have problems with "tk_popup"!
	catch {tk_destroy $m}
	menu $m -tearoff 0\
		-postcommand "browser&_post_menu_update $m $w"
	wm overrideredirect $m 1
	tk_popup $m $x $y
}

proc browser&_post_menu_update {m w} {
	$m delete 0 end
	if {[catch {set beg [$w index sel.first]}]} {
		set sel_size 0
	} else {
		set beg [expr int($beg)]
		if {[catch {set end [expr int([$w index sel.last])]}]} {
			set sel_size 0
		} else {
			set sel_size [expr $end - $beg]
		}
	}
	set size [$w index "end -1c"]
	set size [expr int($size)]

	$m add command -label [format [get_indep String  ListSize] $size $sel_size]
}

proc browser&_tkTextSetCursor {w pos linestart} {
	global tkPriv

	if [$w compare $pos == end] {
		set pos {end - 1 chars}
	}
	$w mark set insert $pos
	$w tag remove sel 1.0 end

	$w tag remove activetag 0.0 end
	$w tag add activetag "insert $linestart" "insert lineend"
	$w mark set active insert

	$w see insert
}

# browser&_textCopy --
# This procedure copies the selection from a text widget into the
# clipboard.
#
# Arguments:
# w -		Name of a text widget.

proc browser&_textCopy w {
	if {![catch {set sel [$w get sel.first "sel.last -1c"]}]} {
		clipboard clear -displayof $w
		clipboard append -displayof $w $sel
    }
}

proc browser&_tkTextAutoScan w {
	if {![winfo exists $w]} {
		return
	}
	global tkPriv
	if {$tkPriv(y) >= [winfo height $w]} {
		$w yview scroll 2 units
	}\
	elseif {$tkPriv(y) < 0} {
		$w yview scroll -2 units
	}\
	elseif {$tkPriv(x) >= [winfo width $w]} {
		$w xview scroll 2 units
	}\
	elseif {$tkPriv(x) < 0} {
		$w xview scroll -2 units
	} else {
		return
	}
#	tkTextSelectTo $w $tkPriv(x) $tkPriv(y)
	update idletasks
	set tkPriv(afterId) [after 50 browser&_tkTextAutoScan $w]
}

Browser& @@ class_bind Browser "linestart"

