1# $Id$ 2# 3# Annotations (XEP-0145) support 4# 5 6package require xmpp::roster::annotations 7 8namespace eval annotations { 9 # variable to store roster notes 10 array set notes {} 11} 12 13proc annotations::free_notes {xlib} { 14 variable notes 15 16 array unset notes $xlib,* 17} 18 19hook::add disconnected_hook [namespace current]::annotations::free_notes 20 21proc annotations::request_notes {xlib} { 22 ::xmpp::roster::annotations::retrieve $xlib \ 23 -command [list [namespace current]::process_notes $xlib] 24} 25 26hook::add connected_hook [namespace current]::annotations::request_notes 27 28proc annotations::process_notes {xlib status noteslist} { 29 variable notes 30 31 if {$status != "ok"} return 32 33 free_notes $xlib 34 35 foreach note $noteslist { 36 create_note $xlib $note 37 } 38} 39 40proc annotations::create_note {xlib note args} { 41 variable notes 42 43 set merge 0 44 foreach {opt val} $args { 45 switch -- $opt { 46 -merge { set merge $val } 47 default { 48 return -code error "Bad option \"$opt\":\ 49 must be -merge" 50 } 51 } 52 } 53 54 array set n $note 55 set jid $n(jid) 56 57 if {!$merge || [more_recent $xlib $n(jid) $n(cdate) $n(mdate)]} { 58 set notes($xlib,jid,$jid) $n(jid) 59 set notes($xlib,cdate,$jid) $n(cdate) 60 set notes($xlib,mdate,$jid) $n(mdate) 61 set notes($xlib,note,$jid) $n(note) 62 return 1 63 } else { 64 return 0 65 } 66} 67 68proc annotations::more_recent {xlib jid cdate mdate} { 69 variable notes 70 71 if {![info exists notes($xlib,jid,$jid)]} { 72 return 1 73 } elseif {[info exists notes($xlib,mdate,$jid)]} { 74 return [expr {$mdate > $notes($xlib,mdate,$jid)}] 75 } elseif {[info exists notes($xlib,cdate,$jid)]} { 76 return [expr {$cdate > $notes($xlib,cdate,$jid)}] 77 } else { 78 return 1 79 } 80} 81 82proc annotations::cleanup_and_store_notes {xlib args} { 83 variable notes 84 85 set roster_jids {} 86 foreach rjid [roster::get_jids $xlib] { 87 lappend roster_jids [::xmpp::jid::stripResource $rjid] 88 } 89 90 foreach idx [array names notes $xlib,jid,*] { 91 set jid $notes($idx) 92 if {[lsearch -exact $roster_jids $jid] < 0 || \ 93 ![info exists notes($xlib,note,$jid)] || \ 94 $notes($xlib,note,$jid) == ""} { 95 catch { unset notes($xlib,jid,$jid) } 96 catch { unset notes($xlib,cdate,$jid) } 97 catch { unset notes($xlib,mdate,$jid) } 98 catch { unset notes($xlib,note,$jid) } 99 } 100 } 101 102 eval [list store_notes $xlib] $args 103} 104 105proc annotations::serialize_notes {xlib} { 106 variable notes 107 108 set notelist {} 109 foreach idx [array names notes $xlib,jid,*] { 110 set jid $notes($idx) 111 112 if {![info exists notes($xlib,note,$jid)] || \ 113 $notes($xlib,note,$jid) == ""} continue 114 115 lappend notelist [list jid $jid \ 116 cdate $notes($xlib,cdate,$jid) \ 117 mdate $notes($xlib,mdate,$jid) \ 118 note $notes($xlib,note,$jid)] 119 } 120 121 return $notelist 122} 123 124proc annotations::store_notes {xlib args} { 125 set command [list [namespace current]::store_notes_result $xlib] 126 foreach {opt val} $args { 127 switch -- $opt { 128 -command { set command $val } 129 default { 130 return -code error "Bad option \"$opt\":\ 131 must be -command" 132 } 133 } 134 } 135 136 ::xmpp::roster::annotations::store $xlib [serialize_notes $xlib] \ 137 -command $command 138} 139 140proc annotations::store_notes_result {xlib res child} { 141 142 if {$res == "ok"} return 143 144 if {[winfo exists .store_notes_error]} { 145 destroy .store_notes_error 146 } 147 MessageDlg .store_notes_error -aspect 50000 -icon error \ 148 -message [::msgcat::mc "Storing roster notes failed: %s" \ 149 [error_to_string $child]] \ 150 -type user -buttons ok -default 0 -cancel 0 151} 152 153proc annotations::add_user_popup_info {infovar xlib jid} { 154 variable notes 155 upvar 0 $infovar info 156 157 set jid [::xmpp::jid::stripResource $jid] 158 159 if {[info exists notes($xlib,note,$jid)] && \ 160 $notes($xlib,note,$jid) != ""} { 161 append info "\n\tNote:\t" 162 append info [string map [list "\n" "\n\t\t"] "$notes($xlib,note,$jid)"] 163 164 if {0} { 165 if {[info exists notes($xlib,cdate,$jid)]} { 166 append info [format "\n\tNote created: %s" \ 167 [clock format $notes($xlib,cdate,$jid) \ 168 -format "%Y-%m-%d %T" -gmt false]] 169 } 170 if {[info exists notes($xlib,mdate,$jid)]} { 171 append info [format "\n\tNote modified: %s" \ 172 [clock format $notes($xlib,mdate,$jid) \ 173 -format "%Y-%m-%d %T" -gmt false]] 174 } 175 } 176 } 177} 178 179hook::add roster_user_popup_info_hook \ 180 [namespace current]::annotations::add_user_popup_info 80 181 182proc annotations::show_dialog {xlib jid} { 183 variable notes 184 185 set jid [::xmpp::jid::stripResource $jid] 186 187 set allowed_name [jid_to_tag $jid] 188 set w .note_edit_[psuffix $xlib]_$allowed_name 189 190 if {[winfo exists $w]} { 191 destroy $w 192 } 193 194 Dialog $w -title [::msgcat::mc "Edit roster notes for %s" $jid] \ 195 -modal none -separator 1 -anchor e \ 196 -default 0 -cancel 1 197 198 $w add -text [::msgcat::mc "Store"] \ 199 -command [list [namespace current]::commit_changes $w $xlib $jid] 200 $w add -text [::msgcat::mc "Cancel"] -command [list destroy $w] 201 202 set f [$w getframe] 203 204 if {[info exists notes($xlib,cdate,$jid)]} { 205 label $f.cdate -text [::msgcat::mc "Created: %s" \ 206 [clock format $notes($xlib,cdate,$jid) \ 207 -format "%Y-%m-%d %T" -gmt false]] 208 pack $f.cdate -side top -anchor w 209 } 210 211 if {[info exists notes($xlib,mdate,$jid)]} { 212 label $f.mdate -text [::msgcat::mc "Modified: %s" \ 213 [clock format $notes($xlib,mdate,$jid) \ 214 -format "%Y-%m-%d %T" -gmt false]] 215 pack $f.mdate -side top -anchor w 216 } 217 218 ScrolledWindow $f.sw 219 pack $f.sw -side top -expand yes -fill both 220 textUndoable $f.note -width 50 -height 5 -wrap word 221 if {[info exists notes($xlib,note,$jid)]} { 222 $f.note insert 0.0 $notes($xlib,note,$jid) 223 } 224 $f.sw setwidget $f.note 225 226 bind $f.note <Control-Key-Return> "[double% $w] invoke default 227 break" 228 bind $w <Key-Return> { } 229 bind $w <Control-Key-Return> "[double% $w] invoke default 230 break" 231 232 $w draw $f.note 233} 234 235proc annotations::commit_changes {w xlib jid} { 236 variable notes 237 238 set text [$w getframe].note 239 240 set date [clock seconds] 241 242 set notes($xlib,jid,$jid) $jid 243 if {![info exists notes($xlib,cdate,$jid)]} { 244 set notes($xlib,cdate,$jid) $date 245 } 246 set notes($xlib,mdate,$jid) $date 247 set notes($xlib,note,$jid) [$text get 0.0 "end -1 char"] 248 249 cleanup_and_store_notes $xlib 250 251 destroy $w 252} 253 254proc annotations::prefs_user_menu {m xlib jid} { 255 set rjid [roster::find_jid $xlib $jid] 256 if {$rjid == ""} { 257 set state disabled 258 } else { 259 set state normal 260 } 261 $m add command -label [::msgcat::mc "Edit item notes..."] \ 262 -command [list [namespace current]::show_dialog $xlib $rjid] \ 263 -state $state 264} 265 266hook::add chat_create_user_menu_hook \ 267 [namespace current]::annotations::prefs_user_menu 76 268hook::add roster_conference_popup_menu_hook \ 269 [namespace current]::annotations::prefs_user_menu 76 270hook::add roster_service_popup_menu_hook \ 271 [namespace current]::annotations::prefs_user_menu 76 272hook::add roster_jid_popup_menu_hook \ 273 [namespace current]::annotations::prefs_user_menu 76 274 275proc annotations::note_page {tab xlib jid editable} { 276 variable notes 277 278 if {$editable} return 279 280 set jid [::xmpp::jid::stripResource $jid] 281 282 if {![info exists notes($xlib,note,$jid)] || \ 283 $notes($xlib,note,$jid) == ""} { 284 return 285 } 286 287 set notestab [$tab insert end notes -text [::msgcat::mc "Notes"]] 288 set n [userinfo::pack_frame $notestab.notes [::msgcat::mc "Roster Notes"]] 289 290 if {[info exists notes($xlib,cdate,$jid)]} { 291 label $n.cdate -text [::msgcat::mc "Created: %s" \ 292 [clock format $notes($xlib,cdate,$jid) \ 293 -format "%Y-%m-%d %T" -gmt false]] 294 pack $n.cdate -side top -anchor w 295 } 296 297 if {[info exists notes($xlib,mdate,$jid)]} { 298 label $n.mdate -text [::msgcat::mc "Modified: %s" \ 299 [clock format $notes($xlib,mdate,$jid) \ 300 -format "%Y-%m-%d %T" -gmt false]] 301 pack $n.mdate -side top -anchor w 302 } 303 304 set sw [ScrolledWindow $n.sw -scrollbar vertical] 305 text $n.text -height 12 -wrap word 306 $sw setwidget $n.text 307 $n.text insert 0.0 $notes($xlib,note,$jid) 308 $n.text configure -state disabled 309 pack $sw -side top -fill both -expand yes 310 pack $n -fill both -expand yes 311} 312 313hook::add userinfo_hook [namespace current]::annotations::note_page 40 314 315# vim:ts=8:sw=4:sts=4:noet 316