Added simple images for user state representation and implemented ping
authorPat Thoyts <patthoyts@users.sourceforge.net>
Wed, 20 May 2009 21:41:54 +0000 (22:41 +0100)
committerPat Thoyts <patthoyts@users.sourceforge.net>
Wed, 20 May 2009 21:41:54 +0000 (22:41 +0100)
Signed-off-by: Pat Thoyts <patthoyts@users.sourceforge.net>
bin/bf_xmpp.tcl
bin/bullfrog.tcl
bin/images/chat.gif
bin/images/rcptreq.gif [new file with mode: 0644]
bin/images/rcptresp.gif [new file with mode: 0644]
bin/images/uparrow.gif [new file with mode: 0644]
bin/images/usr_avail.gif [new file with mode: 0644]
bin/images/usr_away.gif [new file with mode: 0644]
bin/images/usr_dnd.gif [new file with mode: 0644]
bin/images/usr_unavail.gif [new file with mode: 0644]
bin/message.tcl

index ecda551a7529617b3735e296b3bddb272a7f8986..d27dca837bc1f9275a9656835338485a0f8b4fb2 100644 (file)
@@ -30,20 +30,24 @@ package require messagewidget
 source [file join [file dirname [info script]] history.tcl]
 
 namespace eval ::xmppplugin {
-    variable version 0.2
+    variable version 0.3
     variable uid; if {![info exists uid]} { set uid 0 }
     variable defaults {
         -server   "all.tclers.tk"
         -resource "Bullfrog"
         -port     5222
         -channel  "tcl@tach.tclers.tk"
-        callback ""
-        motd     {}
-        users    {}
+        -nick     ""
+        callback  ""
+        motd      {}
+        users     {}
     }
     namespace export connect send post splituri
 }
 
+#autosocks::config -proxy socks5 -proxyhost localhost -proxyport 9050
+
+
 proc ::xmppplugin::connect {callback args} {
     variable defaults
     variable uid
@@ -51,8 +55,9 @@ proc ::xmppplugin::connect {callback args} {
     upvar #0 $context xmpp
     array set xmpp $defaults
     array set xmpp $args         ;# see XmppLogin for the list of pairs
-
+    set xmpp(msgid) 0
     set xmpp(callback) $callback
+    if {$xmpp(-nick) eq ""} {set xmpp(-nick) $xmpp(-username)}
     set xmpp(jlib) [jlib::new [namespace origin OnNetwork] \
                         -messagecommand [namespace origin OnMessage] \
                         -presencecommand [namespace origin OnPresence] \
@@ -108,8 +113,8 @@ proc ::xmppplugin::connect {callback args} {
     } else {
         $xmpp(jlib) connect connect $xmpp(jid) $xmpp(-passwd) \
             -command [list [namespace origin OnConnect] $context] \
-            -secure 1 -method tlssasl \
-            -ip $xmpp(-server) -port $xmpp(-port)
+            -secure 1 -method tlssasl -transport tcp\
+            -ip $xmpp(-server) -port $xmpp(-port) -timeout 120000
     }
     return $context
 }
@@ -166,6 +171,11 @@ proc ::xmppplugin::post {ctx channel msg} {
                 return
             }
             "/me *" { }
+            "/ping *" {
+                set target [string trim [string range $msg 6 end]]
+                ping $ctx $target
+                return
+            }
             default {
                 Callback $ctx system $channel [mc "unrecognised chat command '%s'" $msg]
                 return
@@ -195,6 +205,11 @@ proc ::xmppplugin::post {ctx channel msg} {
     }
     set margs [list -type $type -body $msg -xlist $xlist]
     if {$thread ne ""} { lappend margs -thread $thread }
+    if {$type eq "chat"} {
+        # we _SHOULD_ only do this if we negotiated support with the remote client
+        lappend margs -id [incr xmpp(msgid)] -xlist \
+            [list [wrapper::createtag request -attrlist {xmlns urn:xmpp:receipts}]]
+    }
     eval [linsert $margs 0 $xmpp(jlib) send_message $channel]
     if {$type eq "chat"} {
         set mtype normal
@@ -287,10 +302,10 @@ proc ::xmppplugin::OnConnect {ctx jlib type args} {
         ok {
             Callback $ctx connect
             $jlib send_presence
-            $jlib send_presence -priority 2 -show available
+            $jlib send_presence -priority 2
             $jlib roster send_get -command [list [namespace origin OnRosterGet] $ctx]
             if {$xmpp(-autoconnect) && $xmpp(-channel) ne {}} {
-                Log $ctx "Attempting to join $xmpp(-channel)"
+                Log $ctx "Attempting to join '$xmpp(-channel)' as '$xmpp(-nick)'"
                 JoinMUC $ctx $xmpp(-channel) $jlib $xmpp(-nick)
             }
         }
@@ -303,6 +318,7 @@ proc ::xmppplugin::OnConnect {ctx jlib type args} {
 }
 
 proc ::xmppplugin::JoinMUC {ctx channel jlib nick} {
+    Log $ctx "JoinMUC '$channel' as '$nick'"
     #set t 1202713200
     #set since [list since [clock format $t -format {%Y-%m-%dT%T}]]
     set since [list maxstanzas 100]
@@ -539,7 +555,7 @@ proc ::xmppplugin::OnRosterGet {ctx args} {
 }
 
 proc ::xmppplugin::OnRosterChange {ctx jlib what {jid {}} args} {
-    #Log $ctx "Roster '$what' '$jid' '$args'"
+    Log $ctx "Roster '$what' '$jid' '$args'"
     #enterroster | exitroster | set jid args | remove jid | 
     #switch -exact -- $what {}
     return 0
@@ -581,6 +597,18 @@ proc ::xmppplugin::OnPresenceChange {ctx jlib xmldata} {
                                 [wrapper::getfirstchildwithtag $xmldata status]]
                 Callback $ctx traffic left $room $nick {} $time -status $status
             }
+        } else {
+            # chat or roster user - if we have an open chat for them, fix the icon
+            # FIX ME: move into callback and fix up session
+            set Session ::xmpp1
+            upvar #0 $Session session
+            array set a [linsert [wrapper::getattrlist $xmldata] 0 type available]
+            set w [XmppFindWindow $Session $a(from)]
+            if {[winfo exists $w] && $w ne $session(window)} {
+                set image [XmppGetPresenceImage $Session $a(from)]
+                $session(app).nb tab $w -image $image
+            }
+            #Callback $ctx presence $a(from)
         }
     } err]} { puts stderr "OnPresenceChange: $err" }
     return 0
@@ -589,7 +617,7 @@ proc ::xmppplugin::OnPresenceChange {ctx jlib xmldata} {
 proc ::xmppplugin::OnPresence {jlib xmldata} {
     set ctx [set [set jlib]::AppContext]
     if {[catch {OnPresence2 $ctx $jlib $xmldata} res]} {
-        Log $ctx "OnPresence: $err" error
+        Log $ctx "OnPresence: $res" error
         return 0
     }
     return $res
@@ -602,7 +630,7 @@ proc ::xmppplugin::OnPresence2 {ctx jlib xmldata} {
 proc ::xmppplugin::OnIq {jlib xmldata} {
     set ctx [set [set jlib]::AppContext]
     if {[catch {OnIq2 $ctx $jlib $xmldata} res]} {
-        Log $ctx "OnIq: $err" error
+        Log $ctx "OnIq: $res" error
         return 0
     }
     return $res
@@ -612,6 +640,31 @@ proc ::xmppplugin::OnIq2 {ctx jlib xmldata} {
     return 0
 }
 
+proc ::xmppplugin::ping {ctx jid} {
+    upvar #0 $ctx xmpp
+    set xmllist [wrapper::createtag ping -attrlist [list xmlns urn:xmpp:ping]]
+    $xmpp(jlib) send_iq get [list $xmllist] -to $jid \
+        -command [namespace code [list on_ping $ctx $jid \
+                                      [clock clicks -milliseconds]]]
+    return
+}
+proc ::xmppplugin::on_ping {ctx jid sent type args} {
+    switch -exact -- $type {
+        result {
+            set t [expr {[clock clicks -milliseconds] - $sent}]
+            Callback $ctx status "ping to $jid took $t ms"
+        }
+        error {
+            set t [expr {[clock clicks -milliseconds] - $sent}]
+            set msg [lindex [lindex $args 0] 0]
+            Callback $ctx status "ping to $jid failed in $t ms: $msg"
+        }
+        default {
+            Log $ctx "on_ping: $ctx $jid $sent ms $type '$args'"
+        }
+    }
+}
+    
 proc ::xmppplugin::OnMessage {jlib xmldata} {
     set ctx [set [set jlib]::AppContext]
     if {[catch {OnMessage2 $ctx $jlib $xmldata} err]} {
@@ -748,7 +801,7 @@ proc ::xmppplugin::OnMessage2 {ctx jlib xmldata} {
         }
         headline {
             Callback $ctx chat $a(to) $a(from) \
-                "header: [wrapper::etcdata [lindex $body 0]]" normal $time
+                "header: [wrapper::getcdata [lindex $body 0]]" normal $time
             Log $ctx "$a(from)->$a(to) headline $xmldata"
         }
         error {
@@ -1087,18 +1140,55 @@ proc XmppAddChannel {Session channel "-type" type} {
     }
     $chan(window) hook add post [list ::xmppplugin::post $chan(xmpp) $channel]
     bind $chan(window) <Destroy> "+unset -nocomplain $Channel"
-    
+    XmppAddTab $Session $chan(window) $channel
+    after idle [list $session(app).nb select $chan(window)]
+    return $chan(window)
+}
+
+proc XmppAddTab {Session window jid} {
+    upvar #0 $Session session
+    upvar #0 $session(xmpp) xmpp
     # If the channel domain is a muc then use the resource.
-    upvar #0 $chan(xmpp) xmpp
-    jlib::splitjidex $channel node domain resource
+    jlib::splitjidex $jid node domain resource
     if {[$xmpp(jlib) muc isroom $node@$domain]} {
         set title $resource
+        set image ::img::msgchat
+        $session(app).nb add $window -text $title -image $image -compound left
     } elseif {$node ne {}} {
         set title $node
-    } else { set title $domain }
-    $session(app).nb add $chan(window) -text $title
-    after idle [list $session(app).nb select $chan(window)]
-    return $chan(window)
+        set rs [dict create {*}[$xmpp(jlib) roster getpresence\
+                                    $node@$domain -resource $resource]]
+        set image [XmppGetPresenceImage $Session $jid]
+        $session(app).nb add $window -text $title -image $image -compound left
+    } else {
+        set title $domain
+        $session(app).nb add $window -text $title -compound none
+    }
+}
+
+proc XmppGetPresenceImage {Session jid} {
+    upvar #0 $Session session
+    upvar #0 $session(xmpp) xmpp
+    jlib::splitjidex $jid node domain resource
+    if {$resource eq {}} {
+        array set info [lindex [$xmpp(jlib) roster getpresence $node@$domain] 0]
+    } else {
+        array set info [$xmpp(jlib) roster getpresence $node@$domain -resource $resource]
+    }
+    if {[info exist info(-show)]} {set show $info(-show)} else {set show available}
+    if {$info(-type) eq "unavailable"} {set show unavailable}
+    switch -exact -- $show {
+        available { set image ::img::presence::available }
+        unavailable { set image ::img::presence::unavailable }
+        chat { set image ::img::presence::chat }
+        away { set image ::img::presence::away }
+        xa   { set image ::img::presence::xa }
+        dnd  { set image ::img::presence::dnd }
+        default {
+            return -code error "unknown presence type \"$show\""
+        }
+    }
+    return $image
 }
 
 proc XmppChatstate {Chat chatstate} {
@@ -1199,7 +1289,11 @@ proc XmppFindWindow {Session target} {
             if {$name eq $target} {
                 # check for detached window
                 if {[lsearch -exact [$session(app).nb tabs] $wid] == -1} {
-                    if {[wm state $wid] eq "withdrawn"} { wm deiconify $wid }
+                    if {[winfo toplevel $wid] eq $wid} {
+                        if {[wm state $wid] eq "withdrawn"} { wm deiconify $wid }
+                    } else {
+                        XmppAddTab $Session $wid $target
+                    }
                 } else {
                     if {[$session(app).nb tab $wid -state] eq "hidden"} {
                         $session(app).nb tab $wid -state normal
@@ -1425,6 +1519,9 @@ proc XmppCallback {Session context state args} {
             foreach {type line} $args break
             Debug $Session $line $type
         }
+        status {
+            Status $Session [lindex $args 0]
+        }
         version { return "" }
         default {
             puts stderr "*** unknown xmpp callback \"$state\": $args"
index b4695986d5a316c54f4ea933c67de0de9a5917e7..373fa2788206467b33bef923438a73454054b13e 100644 (file)
@@ -15,7 +15,7 @@
 package require Tk 8.5
 package require chatwidget 1.1;  # tklib 
 package require tooltip 1.4;     # tklib 
-package require msgcat;          # tcl core 
+package require msgcat;          # tcl core
 namespace import ::msgcat::mc
 
 if {![catch {package require autoproxy}]} {
@@ -32,6 +32,16 @@ source [file join $root bf_irc.tcl]
 source [file join $root bf_xmpp.tcl]
 
 # -------------------------------------------------------------------------
+# Load images
+namespace eval ::img {
+    set imgdir [file join $root images]
+    image create photo ::img::presence::available -file $imgdir/usr_avail.gif
+    image create photo ::img::presence::chat -file $imgdir/usr_avail.gif
+    image create photo ::img::presence::away -file $imgdir/usr_away.gif
+    image create photo ::img::presence::xa -file $imgdir/usr_away.gif
+    image create photo ::img::presence::dnd -file $imgdir/usr_dnd.gif
+    image create photo ::img::presence::unavailable -file $imgdir/usr_unavail.gif
+}
 
 proc Main {args} {
     global env
@@ -382,8 +392,6 @@ proc GotoURL {w url} {
     $dlg configure -cursor {}
 }
 
-    
-
 # -------------------------------------------------------------------------
 
 if {![info exists initialized] && !$tcl_interactive} {
index facea22f38f127c63ffcc9f754cdb8a6086b3aa3..0b5d67f955339cba7ae16327148e11995c83a41b 100644 (file)
Binary files a/bin/images/chat.gif and b/bin/images/chat.gif differ
diff --git a/bin/images/rcptreq.gif b/bin/images/rcptreq.gif
new file mode 100644 (file)
index 0000000..d192deb
Binary files /dev/null and b/bin/images/rcptreq.gif differ
diff --git a/bin/images/rcptresp.gif b/bin/images/rcptresp.gif
new file mode 100644 (file)
index 0000000..7c1efea
Binary files /dev/null and b/bin/images/rcptresp.gif differ
diff --git a/bin/images/uparrow.gif b/bin/images/uparrow.gif
new file mode 100644 (file)
index 0000000..f874ec3
Binary files /dev/null and b/bin/images/uparrow.gif differ
diff --git a/bin/images/usr_avail.gif b/bin/images/usr_avail.gif
new file mode 100644 (file)
index 0000000..acc10f6
Binary files /dev/null and b/bin/images/usr_avail.gif differ
diff --git a/bin/images/usr_away.gif b/bin/images/usr_away.gif
new file mode 100644 (file)
index 0000000..f401511
Binary files /dev/null and b/bin/images/usr_away.gif differ
diff --git a/bin/images/usr_dnd.gif b/bin/images/usr_dnd.gif
new file mode 100644 (file)
index 0000000..d1241ef
Binary files /dev/null and b/bin/images/usr_dnd.gif differ
diff --git a/bin/images/usr_unavail.gif b/bin/images/usr_unavail.gif
new file mode 100644 (file)
index 0000000..7b30547
Binary files /dev/null and b/bin/images/usr_unavail.gif differ
index 74a859a9fe29d882e3f2d531e1eacb15b924cd2c..405533c0b86ae879491ffcd98e7026c1c74d0ca0 100644 (file)
@@ -35,9 +35,25 @@ namespace eval messagewidget {
             [font actual TkTextFont] -slant italic
     }
     namespace eval ::img {}
+
+
+    proc ImageSetTransparency {img} {
+        set clr [$img get 0 0]
+        set width [image width $img]
+        set height [image height $img]
+        for {set r 0} {$r < $height} {incr r} {
+            for {set c 0} {$c < $width} {incr c} {
+                if {[$img get $c $r] eq $clr} {
+                    $img transparency set $c $r 1
+                }
+            }
+        }
+    }
+    
     set imgdir [file join [file dirname [info script]] images]
     image create photo ::img::msgnorm -file [file join $imgdir mail.gif]
     image create photo ::img::msgchat -file [file join $imgdir chat.gif]
+    #ImageSetTransparency ::img::msgchat
 }
 
 proc messagewidget::messagewidget {w args} {
@@ -47,6 +63,7 @@ proc messagewidget::messagewidget {w args} {
     return $w
 }
 
+# widget method dispatch
 proc messagewidget::WidgetProc {self cmd args} {
     upvar #0 [namespace current]::$self state
     switch -exact -- $cmd {
@@ -63,6 +80,7 @@ proc messagewidget::WidgetProc {self cmd args} {
     }
 }
 
+# Accessor for the summary tree widget.
 proc messagewidget::Summary {self args} {
     upvar #0 [namespace current]::$self state
     if {[llength $args] == 0} {
@@ -71,6 +89,7 @@ proc messagewidget::Summary {self args} {
     return [uplevel 1 [list $state(mlist)] $args]
 }
 
+# Accessor for the body display widget.
 proc messagewidget::Body {self args} {
     upvar #0 [namespace current]::$self state
     if {[llength $args] == 0} {
@@ -145,7 +164,13 @@ proc messagewidget::DisplayTime {time} {
     return $r
 }
 
-# -date : date/time as clock seconds
+# Add --
+#      Add a new message item. Options are:
+#       -from    jid
+#       -to      jid
+#       -subject string
+#       -date    date/time as clock seconds
+#       -body    data
 # 
 proc messagewidget::Add {self args} {
     upvar #0 [namespace current]::$self state
@@ -174,6 +199,11 @@ proc messagewidget::OnSummaryClick {self w x y} {
         "[dict get $M -body]\n" body
 }
 
+# -------------------------------------------------------------------------
+# Data store --
+#proc messagewidget::StoreItem
+#proc messagewidget::Store
+
 # -------------------------------------------------------------------------
 
 package provide messagewidget $messagewidget::version