# $Id: search.tcl 1373 2008-02-05 19:19:06Z sergei $

namespace eval search {
    set winid 0
    set show_all 0
}

proc search::open {jid args} {
    variable winid

    foreach {opt val} $args {	
	switch -- $opt {
	    -connection { set connid $val }
	}
    }
    if {![info exists connid]} {
	return -code error "search::open: -connection is mandatory"
    }

    set sw .search$winid

    toplevel $sw -cursor watch
    wm group $sw .
    set title [format [::msgcat::mc "Search in %s"] $jid]
    wm title $sw $title
    wm iconname $sw $title
    wm transient $sw .
    if {$::tcl_platform(platform) == "macintosh"} {
        catch { unsupported1 style $sw floating sideTitlebar }
    } elseif {$::aquaP} {
        ::tk::unsupported::MacWindowStyle style $sw dBoxProc
    }
    wm resizable $sw 0 0

    wm withdraw $sw

    ButtonBox $sw.bbox -spacing 0 -padx 10 -default 0
    $sw.bbox add -text [::msgcat::mc "OK"] \
	-command [list search::search $sw $connid $jid] \
        -state disabled
    $sw.bbox add -text [::msgcat::mc "Cancel"] -command [list destroy $sw]
    pack $sw.bbox -padx 2m -pady 2m -anchor e -side bottom

    bind $sw <Return> [list ButtonBox::invoke $sw.bbox default]
    bind $sw <Escape> [list ButtonBox::invoke $sw.bbox 1]

    Separator::create $sw.sep -orient horizontal
    pack $sw.sep -side bottom -fill x -pady 1m
    
    frame $sw.fields -class Search

    pack $sw.fields -expand yes -fill both -anchor nw -padx 2m -pady 2m

    jlib::send_iq get \
	[jlib::wrapper:createtag query \
	     -vars [list xmlns jabber:iq:search]] \
	-to $jid -command [list search::recv_fields $sw $jid] \
	-connection $connid
    
    incr winid
}

proc search::recv_fields {sw jid res child} {
    debugmsg search "$res $child"

    if {![cequal $res OK]} {
        destroy $sw
	MessageDlg ${sw}_err -aspect 50000 -icon error \
	    -message [format [::msgcat::mc "Search: %s"] \
			  [error_to_string $child]] \
	    -type user -buttons ok -default 0 -cancel 0
	return
    }

    jlib::wrapper:splitxml $child tag vars isempty chdata children

    if {[cequal [jlib::wrapper:getattr $vars xmlns] jabber:iq:search]} {
	set focus [data::fill_fields $sw.fields $children]
    }

    $sw configure -cursor {}
    $sw.bbox itemconfigure 0 -state normal

    if {$focus != ""} {
	focus $focus
    }
    update idletasks
    wm deiconify $sw
}

proc search::search {sw connid jid} {
    variable data

    $sw configure -cursor watch
    $sw.bbox itemconfigure 0 -state disabled

    set restags [data::get_tags $sw.fields]

    jlib::send_iq set \
	[jlib::wrapper:createtag query \
	     -vars [list xmlns jabber:iq:search] \
	     -subtags $restags] \
	-to $jid -command [list search::recv_items $sw $jid $connid] \
	-connection $connid
}

proc search::recv_items {sw jid connid res child} {
    variable lastsort

    debugmsg search "$res $child"

    if {![winfo exists $sw]} {
	return
    }

    if {![cequal $res OK]} {
	$sw configure -cursor {}
	$sw.bbox itemconfigure 0 -text [::msgcat::mc "Try again"] \
	    -command [list search::search_again $sw $jid $connid errormsg] \
	    -state normal
	$sw.bbox itemconfigure 1 -text [::msgcat::mc "Close"]

	if {[winfo exists $sw.errormsg]} {
	    destroy $sw.errormsg
	}

	message $sw.errormsg -aspect 50000 \
	    -text [format [::msgcat::mc "An error occurred when searching in %s\n\n%s"] \
		       $jid [error_to_string $child]]

	pack $sw.errormsg -expand yes -fill both -after $sw.fields -anchor nw -padx 1c -pady 1c
	pack forget $sw.fields

	return
    }

    wm withdraw $sw

    set rw [toplevel ${sw}results]
    wm group $rw .
    set title [format [::msgcat::mc "Search in %s"] $jid]
    wm title $rw $title
    wm iconname $rw $title
    wm withdraw $rw

    ButtonBox $rw.bbox -spacing 0 -padx 10 -default 0
    $rw.bbox add -text [::msgcat::mc "Search again"] \
	-command "destroy [list $rw]
		  search::search_again [list $sw] [list $jid] [list $connid]"
    $rw.bbox add -text [::msgcat::mc "Close"] -command "destroy [list $rw]
							destroy [list $sw]"
    pack $rw.bbox -padx 2m -pady 2m -anchor e -side bottom

    bind $rw <Return> [list ButtonBox::invoke $rw.bbox default]
    bind $rw <Escape> [list ButtonBox::invoke $rw.bbox 1]

    Separator::create $rw.sep -orient horizontal
    pack $rw.sep -side bottom -fill x -pady 1m

    set sww [ScrolledWindow $rw.items]

    ::mclistbox::mclistbox $sww.listbox \
	-resizeonecolumn 1 \
	-labelanchor w \
	-width 90 \
	-height 16

    pack $sww -expand yes -fill both -anchor nw -padx 2m -pady 2m
    $sww setwidget $sww.listbox

    set lastsort($sww.listbox) ""
    bind $sww.listbox <Destroy> +[list [namespace current]::delete_lastsort $sww.listbox]
    
    bind $sww.listbox <3> \
	"[namespace current]::select_and_popup_menu [list $sww.listbox] \
	     \[$sww.listbox nearest \[::mclistbox::convert %W -y %y\]\] \
	     $connid"

    bindscroll $sww $sww.listbox

    set reported_fields [data::get_reported_fields $sw.fields]

    jlib::wrapper:splitxml $child tag vars isempty chdata children

    if {[cequal [jlib::wrapper:getattr $vars xmlns] jabber:iq:search]} {
	set rows [fill_mclistbox $rw $jid $sww.listbox $reported_fields $children]
    }

    if {$rows <= 0} {
	pack forget $sww
	message $rw.errormsg -aspect 50000 \
	    -text [format [::msgcat::mc "Search in %s: No matching items found"] $jid]
	pack $rw.errormsg -expand yes -fill both -anchor nw -padx 1c -pady 1c
    } elseif {$rows <= 12} {
	$sww.listbox configure -height [expr {$rows - ($rows % 4) + 4}]
    }

    wm deiconify $rw
}

proc search::delete_lastsort {id} {
    variable lastsort

    if {[info exists lastsort($id)]} {
	unset lastsort($id)
    }
}

proc search::fill_mclistbox_x {sw jid w reported_fields items} {
    variable show_all

    set width(0) 3
    set name(0) N
    $w column add N -label " [::msgcat::mc #] "

    set row 0
    set col 1

    foreach item $items {
	jlib::wrapper:splitxml $item tag vars isempty chdata children

	switch -- $tag {
	    title {
		if {$chdata != ""} {
		    wm title $sw $chdata
		    wm iconname $sw $chdata
		}
	    }
	    reported {
		set reported_fields {}
		foreach field $children {
		    jlib::wrapper:splitxml $field tag vars isempty chdata children1

		    set lname [jlib::wrapper:getattr $vars "var"]
		    set label_name($lname) [jlib::wrapper:getattr $vars "label"]
		    lappend reported_fields $lname
		}
	    }
	    item {
		foreach field $children {
		    jlib::wrapper:splitxml $field tag vars isempty chdata children1

		    if {$tag == "field"} {
			set var [jlib::wrapper:getattr $vars "var"]

			foreach value $children1 {
			    
			    jlib::wrapper:splitxml $value tag vars isempty chdata children1

			    if {($tag == "value") && ($chdata != "")} {
				if {$show_all || ([lsearch -exact $reported_fields $var] != -1)} {
				    if {![info exists fieldcol($var)]} {
					set fieldcol($var) $col
					if {[info exists label_name($var)]} {
					    set l $label_name($var)
					} else {
					    set l $var
					}
					set width($col) [string length " $l "]
					set name($col) $var
					$w column add $var -label " $l "
					$w label bind $var <ButtonPress-1> "[namespace current]::sort %W $var"
					set lasttag $var

					incr col
				    }
				    set data($fieldcol($var),$row) $chdata

				    debugmsg search "$var $chdata"
				}
			    }
			}
		    }
		}
		set data(0,$row) [expr {$row + 1}]
		incr row
	    }
	    default {}
	}	
    }

    finalize_mclistbox $w $row $col name data width
}

proc search::finalize_mclistbox {w row col n d wi} {
    upvar $n name
    upvar $d data
    upvar $wi width

    $w column add lastcol -label "" -width 0
    $w configure -fillcolumn lastcol
    
    for {set j 0} {$j < $row} {incr j} {
	set datalist {}
	for {set i 0} {$i < $col} {incr i} {
	    if {[info exists data($i,$j)]} {
		set wd [string length " $data($i,$j) "]
		if {$wd > $width($i)} {
		    set width($i) $wd
		}
		lappend datalist " $data($i,$j) "
	    } else {
		lappend datalist ""
	    }
	}
	lappend datalist ""
	$w insert end $datalist
    }
    for {set i 0} {$i < $col} {incr i} {
	$w column configure $name($i) -width $width($i)
    }

    return $row
}

proc search::fill_mclistbox {sw jid w reported_fields items} {
    variable show_all

    foreach item $items {
	jlib::wrapper:splitxml $item tag vars isempty chdata children
	if {$tag == "x" &&
	    [jlib::wrapper:getattr $vars xmlns] == "jabber:x:data"} {
	    return [fill_mclistbox_x $sw $jid $w $reported_fields $children]
	}
    }

    set width(0) 3
    set name(0) N
    $w column add N -label " [::msgcat::mc #] "

    set fieldcol(jid) 1
    set width(1) 5
    set name(1) jid
    $w column add jid -label " jid "
    $w label bind jid <ButtonPress-1> "[namespace current]::sort %W jid"
 
    set row 0
    set col 2

    foreach item $items {
	jlib::wrapper:splitxml $item tag vars isempty chdata children

	switch -- $tag {
	    item {
		set itemjid [jlib::wrapper:getattr $vars jid]
		set data(1,$row) $itemjid

		foreach field $children {
		    jlib::wrapper:splitxml $field tag vars isempty \
			chdata children1

		    if {$chdata != ""} {
			if {$show_all || ([lsearch -exact $reported_fields $tag] != -1)} {
			    if {![info exists fieldcol($tag)]} {
				set fieldcol($tag) $col
				set width($col) [string length " $tag "]
				set name($col) $tag
				$w column add $tag -label " $tag "
				$w label bind $tag <ButtonPress-1> "[namespace current]::sort %W $tag"
				set lasttag $tag

				incr col
			    }
			    set data($fieldcol($tag),$row) $chdata

			    debugmsg search "$tag $chdata"
			}
		    }
		}
		set data(0,$row) [expr {$row + 1}]
		incr row
	    }
	    default {}
	}	
    }

    finalize_mclistbox $w $row $col name data width
}

proc search::sort {w tag} {
    variable lastsort

    set data [$w get 0 end]
    set index [lsearch -exact [$w column names] $tag]
    if {$lastsort($w) != $tag} {
	set result [lsort -dictionary -index $index $data]
	set lastsort($w) $tag
    } else {
	set result [lsort -decreasing -dictionary -index $index $data]
	set lastsort($w) ""
    }
    set result1 {}
    set i 0
    foreach row $result {
	lappend result1 [lreplace $row 0 0 " [incr i] "]
    }
    $w delete 0 end
    eval $w insert end $result1
}

proc search::search_again {sw jid connid {delwidget ""}} {
    $sw configure -cursor {}
    if {![cequal $delwidget ""]} {
	pack $sw.fields -expand yes -fill both -after $sw.$delwidget -anchor nw -padx 2m -pady 2m
	pack forget $sw.$delwidget

	$sw.bbox itemconfigure 0 -text [::msgcat::mc "OK"] \
	    -command [list search::search $sw $connid $jid] \
	    -state normal
	$sw.bbox itemconfigure 1 -text [::msgcat::mc "Cancel"]
    } else {
	$sw.bbox itemconfigure 0 -state normal
	wm deiconify $sw
    }
}

proc search::select_and_popup_menu {w index connid} {

    $w selection clear 0 end
    $w selection set $index
    set jid [string trim [lindex [$w get $index] 1]]

    if {[winfo exists [set m .searchpopupmenu]]} {
	destroy $m
    }
    menu $m -tearoff 0

    hook::run search_popup_menu_hook $m $connid $jid

    tk_popup $m [winfo pointerx .] [winfo pointery .]
}

proc search::add_separator {m connid jid} {
    $m add separator
}

hook::add search_popup_menu_hook \
    [namespace current]::search::add_separator 40
hook::add search_popup_menu_hook \
    [namespace current]::search::add_separator 50

hook::add postload_hook \
    [list disco::browser::register_feature_handler jabber:iq:search \
	[namespace current]::search::open \
	-desc [list * [::msgcat::mc "Search"]]]

