# Copyright (c) 2001, Khamis Abuelkomboz
# 
# This file is part of Khamis Abuelkomboz Source-Navigator Extensions.
# 
# Source-Navigator Extensions 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
# 
# Source-Navigator Extensions 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.
#
# CompleteWord.tcl
# Display matched symbols and let user choose something
itcl_class CompleteWord {
    inherit sourcenav::Window
    constructor {{config ""}} {
	global sn_options
	
        withdraw
	
        set topw $itk_component(hull)
        on_close "$itk_component(hull) window_close dummy"
	
	set bg_color gray
	set fg_color black
	
	$this configure -background $bg_color
	wm overrideredirect [$this window_name] 1
	
	set tree ${topw}.list
        Tree $tree \
	      -fillselection 1 \
	      -selectmode browse \
	      -exportselection 1 \
	      -font $sn_options(def,edit-font) \
	      -bestfit 1 \
	      -truncate 0 \
	      -tabsize 0 \
	      -tabs {80 80 80 120 200} \
	      -labels [list Name Class Type Parameters] \
	      -width 24 \
	      -height 8 \
	      -propagate 1 \
	      -filter ""
	set treew [$tree tree]
	::bind $treew <space> "+$this gotselection; break"
	::bind $treew <KeyRelease-Return> "+$this gotselection; break"
	::bind $treew <ButtonRelease-1> "+$this gotselection; break"
	::bind $treew <FocusOut> "+$this window_close dummy; break"
	pack ${topw}.list -side top -fill both -expand y \
		-padx 2 -pady 2
	#don't show horizontal scrollbar
	pack forget $tree.x
	
	set bbox [$editor bbox $startindex]
	set x [expr [winfo rootx ${editor}] + [lindex $bbox 0] - 27]
	set y [expr [winfo rooty ${editor}] + [lindex $bbox 1] + [lindex $bbox 3]]

	# make help window be completely visible
	set w [expr [winfo reqwidth ${treew}] + 4]
	set h [expr [winfo reqheight ${treew}] + 4]
	if {${x} + ${w} > [winfo screenwidth .]} {
	    set x [expr [winfo screenwidth .] - ${w}]
	}
	#display window over the line, when no space below
	if {${y} + ${h} > [winfo screenheight .]} {
	    set y [expr [winfo rooty ${editor}] + [lindex $bbox 1] - $h]
	}
    
	wm geometry $itk_component(hull) +$x+$y
	
	#add found matching to the tree (with icons!!)
	set otxt ""
	foreach l $result {
	    set txt [lindex [split $l "("] 0]
	    set img [lindex [split $l "("] 1]
	    set img [lindex [split $img ")"] 0]
	    if {[string compare $txt $otxt] != 0} {
		$treew insert end -image type_${img}_image -text $txt
		set otxt $txt
	    }
	}
	
	#forward tree to the initialword the user has
	#try to find the closest word in the list to the initialword
	#(>= initialword)
	set wrd ${initialword}
	if {[string compare $wrd ""] == 0} {
	    set off 0
	} else {
	    set off -1
	}
	while {$off == -1 && [string length $wrd] > 0} {
	    set off [sn_tree_table_search_in_widget ${treew} ${wrd} 0]
	    if {$off == -1 && [string length $wrd] > 0} {
		set wrd [string range $wrd 0 [expr [string length $wrd] - 2]]
	    }
	}
	if {$off != -1 && [string compare $wrd $initialword] != 0} {
	    while {$off < [$treew size]} {
		set w [string tolower [$treew get $off]]
		if {[string first $wrd $w] == 0 && [string compare [string tolower $initialword] $w] > 0} {
		    incr off
		} else {
		    break
		}
	    }
	}
	
        if {$off == -1} {
	    set off [expr [$treew size] - 1]
	}
	if {$off != -1} {
	    if {$off > [expr [$treew size] - 7]} {
		$treew yview see [expr ${off} - 7]
	    } else {
		${treew} yview see ${off}
	    }
	    ${treew} activate ${off}
	    ${treew} activate ${off}
	    
	    #initial tree selection to the pattern so that the user can
	    #complete pressing the correct characters
	    set txt [$treew get $off]
	    upvar #0 $treew-pat pat; #see sn_tree_table_search_region in tree.tcl for more details
	    for {set i 0} {$i < [string length $initialword]} {incr i} {
		set chr [string tolower [string range $initialword $i $i]]
		if {$chr != [string tolower [string range $txt $i $i]]} {
		    break
		}
		append pat $chr
	    }
	}
	
	if {[$treew size] == 1} {
	    gotselection
	    return
	}
	
        after idle "window_configure $itk_component(hull) deiconify [${topw}.list tree]; focus [${topw}.list tree]"
    }
    destructor {
        foreach v [::info globals "${this}-*"] {
            catch {uplevel #0 unset ${v}}
        }
    }
    method config {config} {
    }
    method display_contents {} {
    }
    method window_close {m} {
	itcl::delete object ${this}
    }
    
    method gotselection {} {
	set idx [$tree curselection]
	if {[string compare $idx ""] == 0} {
	    set idx [$tree index active]
	}
	if {[string compare $idx ""] != 0} {
	    set i [$editor index $startindex]
	    set j [$editor index $endindex]
	    tkTextReplace $editor $i $j [lindex [$tree get $idx] 0]
	}
    	window_close dummy
    }
    
    protected tree ""
    public editor ""
    public pattern ""
    public result ""
    public initialword ""
    public startindex "insert-1c wordstart"
    public endindex "insert-1c wordend"
    public ismember false
}

proc complete_word {w {onlymembers false}} {
    global completeWindow
    
    if {$onlymembers} {
	set i [$w index "insert"]
	set j [$w index "insert"]
	set word ""
	set initialword ""
    } else {
	set i [$w index "insert-1c wordstart"]
	set j [$w index "insert-1c wordend"]
	set word [$w get $i [$w index insert]]
	set initialword [$w get $i $j]
	
	set notWord [regexp {[^a-zA-Z_0-9]+} $word]
	
	if {[string compare $initialword "."] == 0} {
	    set i [$w index "insert"]
	    set j [$w index "insert"]
	    set word ""
	    set initialword ""
	    set onlymembers true
	} elseif {$notWord || [string first $initialword " \t\n"] == 0} {
	    set i [$w index "insert"]
	    set j [$w index "insert"]
	    set word ""
	    set initialword ""
	} elseif {[string compare [$w get [$w index "$i-1c"] $i] "."] == 0} {
	    #it's a member
	    set onlymembers true
	}
	
	#replace the word just after insert
	if {[string compare $initialword ""] == 0} {
	    set initialword [$w get [$w index "insert wordstart"] [$w index "insert wordend"]]
	    if {! [regexp {[a-zA-Z_0-9].+} $initialword]} {
		set initialword ""
	    } else {
		set i [$w index "insert+1c wordstart"]
		set j [$w index "insert+1c wordend"]
	    }
	}
    }
    
    if {$onlymembers} {
	set scopes {md mi fr iv}
    } else {
	set scopes all
	#don't list members
	#global sn_scopes
	#foreach s $sn_scopes {
	#    if {[lsearch -exact {md mi fr iv} $s] == -1} {
	#	lappend scopes $s
	#    }
	#}
    }
    set word [string trim $word]
    set initialword [string trim $initialword]
    set exact "-glob"
    
    if {$word == "."} {
	set word ""
	set exact "-exact"
    } else {
	set exact "-glob"
    }
    
    set pattern "${word}*"
    
    set res [read_matched_from_db "" $scopes $exact $pattern]
    if {[string compare $res ""] == 0} {
	return
    }
    
    #only one result found
    if {[llength $res] == 1} {
    	set txt [lindex [split [lindex $res 0] "("] 0]
	if {[string compare $txt $initialword] != 0} {
	    tkTextReplace $w $i $j $txt
	} else {
	    #same word, just jumb to the end (no changes)
	    ${w} mark set insert $j
	    ${w} mark set anchor insert
	    ${w} tag remove sel 0.0 end
	    ${w} see insert
	}
	return
    }

    #make sure existing windows are deleted
    catch {itcl::delete object ${completeWindow}}
    set completeWindow [CompleteWord .cmplWin \
		    -editor $w \
		    -pattern $pattern \
		    -result $res \
		    -initialword $initialword \
		    -startindex $i \
		    -endindex $j \
		    -ismember $onlymembers]
}


