###############################################################################
# Floating Contact Plugin
###############################################################################
# $Id: floatingcontact.tcl 1732 2009-03-15 06:18:51Z sergei $

package require msgcat

namespace eval :: {
    ::msgcat::mcload [file join [file dirname [info script]] msgs]
}

namespace eval floatingcontact {
    if {![::plugins::is_registered floatingcontact]} {
	::plugins::register floatingcontact \
			    -namespace [namespace current] \
			    -source [info script] \
			    -description [::msgcat::mc "Whether the Floating Contact plugin is loaded."] \
			    -loadcommand [namespace code load] \
			    -unloadcommand [namespace code unload]
	return
    }
}

proc floatingcontact::load {} {
    hook::add connected_hook ::ifacetk::roster::load_float_list
    hook::add disconnected_hook ::ifacetk::roster::save_float_list
    hook::add client_presence_hook ::ifacetk::roster::float_on_change_jid_presence 100
    hook::add roster_service_popup_menu_hook ::ifacetk::roster::float_create_menu 80
    hook::add roster_jid_popup_menu_hook ::ifacetk::roster::float_create_menu 80

    foreach xlib [connections] {
	::ifacetk::roster::load_float_list $xlib
    }
}

proc floatingcontact::unload {} {
    foreach xlib [connections] {
	::ifacetk::roster::save_float_list $xlib
    }

    hook::remove connected_hook ::ifacetk::roster::load_float_list
    hook::remove disconnected_hook ::ifacetk::roster::save_float_list
    hook::remove client_presence_hook ::ifacetk::roster::float_on_change_jid_presence 100
    hook::remove roster_service_popup_menu_hook ::ifacetk::roster::float_create_menu 80
    hook::remove roster_jid_popup_menu_hook ::ifacetk::roster::float_create_menu 80

    foreach proc {load_float_list
		  save_float_list
		  float_toggle
		  float_redraw
		  float_on_change_jid_presence
		  float_start_drag
		  float_motion_drag
		  float_stop_drag
		  float_create_menu} {
	rename ::ifacetk::roster::$proc ""
    }

    foreach var {float_winid
		 float_savex
		 float_savey
		 float_x
		 float_y
		 float_is_float
		 float_floats} {
	catch {unset ::ifacetk::roster::$var}
    }
}

namespace eval ::ifacetk::roster {
    variable float_winid 0

#   {jid1 {rjid1 1 x y rjid2 0 x y} jid2 {rjid3 0 x y rjid4 0 x y}}
    custom::defvar floating_jids_list {} \
		   [::msgcat::mc "Stored floating JIDs."] \
		   -type string \
		   -group Hidden
}

proc ::ifacetk::roster::load_float_list {xlib} {
    variable floating_jids_list
    variable float_is_float
    variable float_x
    variable float_y

    array set fl $floating_jids_list
    set bjid [::xmpp::jid::normalize [connection_bare_jid $xlib]]

    if {![info exists fl($bjid)]} return

    foreach {jid float x y} $fl($bjid) {
	set float_is_float($xlib,$jid) $float
	set float_x($xlib,$jid) $x
	set float_y($xlib,$jid) $y
	float_toggle $xlib $jid
    }
}

proc ::ifacetk::roster::save_float_list {xlib} {
    variable floating_jids_list
    variable float_is_float
    variable float_x
    variable float_y
    variable float_floats

    array set fl $floating_jids_list
    set bjid [::xmpp::jid::normalize [connection_bare_jid $xlib]]

    set fl($bjid) {}

    foreach idx [array names float_is_float $xlib,*] {
	set jid [string range $idx [string length $xlib,] end]
	set float $float_is_float($xlib,$jid)

	set float_is_float($xlib,$jid) 0
	float_toggle $xlib $jid

	if {[info exists float_x($xlib,$jid)] && \
				[info exists float_y($xlib,$jid)]} {
	    lappend fl($bjid) $jid \
			      $float \
			      $float_x($xlib,$jid) \
			      $float_y($xlib,$jid)
	    unset float_x($xlib,$jid)
	    unset float_y($xlib,$jid)
	}
	unset float_is_float($xlib,$jid)
    }

    if {[llength $fl($bjid)] == 0} {
	unset fl($bjid)
    }

    set floating_jids_list [array get fl]
}

proc ::ifacetk::roster::float_toggle {xlib jid} {
    variable float_is_float
    variable float_x
    variable float_y
    variable float_winid
    variable float_floats
    variable config
    variable iroster

    if {![info exists float_is_float($xlib,$jid)] || !$float_is_float($xlib,$jid)} {
	if {[info exists float_floats($xlib,$jid)]} {
	    set w $float_floats($xlib,$jid)
	    unset float_floats($xlib,$jid)
	    catch {destroy $w}
	    array unset iroster $w,*
	}
    } else {
	set w [toplevel .float[incr float_winid] -relief flat -bd 1 -class Balloon]
	$w configure -bg [option get $w foreground Balloon]
	set float_floats($xlib,$jid) $w

	wm withdraw $w
	wm overrideredirect $w 1

	catch {
	    if {[lsearch -exact [wm attributes $w] -topmost] >= 0} {
		wm attributes $w -topmost 1
	    }
	}

	if {![info exists float_x($xlib,$jid)] || \
		    ![info exists float_y($xlib,$jid)]} {
	    set float_x($xlib,$jid) [expr {[winfo screenwidth $w] / 2}]
	    set float_y($xlib,$jid) [expr {[winfo screenheight $w] / 2}]
	}

	wm geometry $w +$float_x($xlib,$jid)+$float_y($xlib,$jid)

	canvas $w.canvas -bg $config(background) \
			 -highlightthickness 0 \
			 -scrollregion {0 0 0 0} \
			 -width 0 -height 0
	pack $w.canvas

	set iroster($w,ypos) 1
	set iroster($w,width) 0
	set iroster($w,grouppopup) {}
	set iroster($w,popup) [namespace current]::popup_menu
	set iroster($w,singleclick) {}
	set iroster($w,doubleclick) [namespace current]::jid_doubleclick

	float_redraw $xlib

	bind $w <ButtonPress-1> \
		[list [namespace current]::float_start_drag %x %y]
	bind $w <Button1-Motion> \
		[list [namespace current]::float_motion_drag $w $xlib $jid %X %Y]
	bind $w <ButtonRelease-1> \
		[list [namespace current]::float_stop_drag %x %y]

	wm deiconify $w
    }
}

proc ::ifacetk::roster::float_redraw {xlib} {
    variable float_floats
    variable config
    variable iroster

    foreach idx [array names float_floats $xlib,*] {
	lassign [split $idx ,] xlib jid

	set w $float_floats($idx)
	if {![winfo exists $w]} continue

	clear $w 0

	set cjid [list $xlib $jid]
	set jids [get_jids_of_user $xlib $jid]
	set numjids [llength $jids]
	addline $w jid [roster::get_label $xlib $jid] $cjid {} {} 0 $jids \
		       [get_jid_icon $xlib $jid] \
		       [get_jid_foreground $xlib $jid]
	if {$numjids > 1} {
	    foreach subjid $jids {
		set csubjid [list $xlib $subjid]
		set subjid_resource [::xmpp::jid::resource $subjid]
		if {$subjid_resource != ""} {
		    addline $w jid2 $subjid_resource $csubjid {} {} 0 [list $subjid] \
				    [get_jid_icon $xlib $subjid] \
				    [get_jid_foreground $xlib $subjid]
		}
	    }
	}
	$w.canvas configure \
	    -width [expr {$iroster($w,width) + $config(jidindent)}] \
	    -height $iroster($w,ypos)
	update_scrollregion $w
    }
}

proc ::ifacetk::roster::float_on_change_jid_presence {xlib jid type x args} {
    after idle [list [namespace current]::float_redraw $xlib]
}

proc ::ifacetk::roster::float_start_drag {x y} {
    variable float_savex $x
    variable float_savey $y
}

proc ::ifacetk::roster::float_motion_drag {w xlib jid X Y} {
    variable float_x
    variable float_y
    variable float_savex
    variable float_savey
    set float_x($xlib,$jid) [expr {$X-$float_savex}]
    set float_y($xlib,$jid) [expr {$Y-$float_savey}]
    wm geometry $w +$float_x($xlib,$jid)+$float_y($xlib,$jid)
}

proc ::ifacetk::roster::float_stop_drag {x y} {
    variable float_savex
    variable float_savey
    catch {unset float_savex}
    catch {unset float_savey}
}

proc ::ifacetk::roster::float_create_menu {m xlib jid} {
    set rjid [roster::find_jid $xlib $jid]
    if {$rjid == ""} {
	set rjid [::xmpp::jid::stripResource $jid]
    }

    $m add checkbutton -label [::msgcat::mc "Floating contact"] \
	   -variable [namespace current]::float_is_float($xlib,$rjid) \
	   -command [list [namespace current]::float_toggle $xlib $rjid]
}

