# floatingcontact.tcl --
#
#       Floating Contact plugin for the Tkabber XMPP client. It implements
#       a small roster for a single contact in a separate little window.

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 \
                      [double% $w] [double% $xlib] [double% $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]
}

# vim:ft=tcl:ts=8:sw=4:sts=4:et
