1# JivePhone.tcl --
2#
3#       JivePhone bindings for the jive server and Asterisk.
4#
5#       Contributions and testing by Antonio Cano damas
6#
7#   This program is free software: you can redistribute it and/or modify
8#   it under the terms of the GNU General Public License as published by
9#   the Free Software Foundation, either version 3 of the License, or
10#   (at your option) any later version.
11#
12#   This program is distributed in the hope that it will be useful,
13#   but WITHOUT ANY WARRANTY; without even the implied warranty of
14#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15#   GNU General Public License for more details.
16#
17#   You should have received a copy of the GNU General Public License
18#   along with this program.  If not, see <http://www.gnu.org/licenses/>.
19#
20# $Id: JivePhone.tcl,v 1.37 2008-06-09 09:50:59 matben Exp $
21
22# My notes on the present "Phone Integration Proto-JEP" document from
23# Jive Software:
24#
25#   1) server support for this is indicated by the disco child of the server
26#      where it should instead be a disco info feature element.
27#
28#   2) "The username must be set as the node attribute on the query"
29#      when obtaining info if a particular user has support for this.
30#      This seems wrong since only a specific instance of a user specified
31#      by an additional resource can have specific features.
32
33#    I could imagine a dialer as a tab page, but then we need nice buttons.
34#
35
36namespace eval ::JivePhone {
37
38    component::define JivePhone  \
39      "VoIP notifications from the Openfire plugin Asterisk-IM"
40}
41
42proc ::JivePhone::Init {} {
43
44    component::register JivePhone
45
46    # Add event hooks.
47    ::hooks::register presenceHook          ::JivePhone::PresenceHook
48    ::hooks::register newMessageHook        ::JivePhone::MessageHook
49    ::hooks::register loginHook             ::JivePhone::LoginHook
50    ::hooks::register logoutHook            ::JivePhone::LogoutHook
51    ::hooks::register rosterPostCommandHook ::JivePhone::RosterPostCommandHook
52
53    ::hooks::register buildChatButtonTrayHook      ::JivePhone::buildChatButtonTrayHook
54
55    variable xmlns
56    set xmlns(jivephone) "http://jivesoftware.com/xmlns/phone"
57
58    # Note the difference!
59    variable feature
60    set feature(jivephone) "http://jivesoftware.com/phone"
61
62    variable statuses {AVAILABLE RING DIALED ON_PHONE HANG_UP}
63
64
65    #--------------- Variables Uses For PopUP Menus -------------------------
66    variable popMenuDef
67    variable popMenuType
68    set popMenuDef(call) {
69	command  mJiveCall {[mc "Call"]} {::JivePhone::DialJID $jid "DIAL"}
70    }
71    set popMenuDef(forward) {
72	command  mJiveForward {[mc "Forward Call"]} {::JivePhone::DialJID $jid "FORWARD"}
73    }
74    set popMenuType(call) {
75	mJiveCall {user available}
76    }
77    set popMenuType(forward) {
78	mJiveCall {user available}
79    }
80
81    variable menuDef
82    set menuDef  \
83      {command  mJiveCall     {[mc "Call"]} {::JivePhone::DoDial "DIAL"}    {}}
84
85
86    #--------------- Variables Uses For SpeedDial Addressbook Tab ----------------
87    variable wtab -
88    variable abline [list]
89
90    set popMenuDef(addressbook) {
91        mJiveCall      jid       {[mc "Call"]}   {::JivePhone::DialExtension $jid "DIAL"}
92        separator      {}        {}              {}
93        mNewAB         jid       {[mc "New"]}    {::JivePhone::NewAddressbookDlg}
94        mModifyAB      jid       {[mc "Modify"]} {::JivePhone::ModifyAddressbookDlg  $jid}
95        mRemoveAB      jid       {[mc "Remove"]} {::JivePhone::RemoveAddressbookDlg  $jid}
96    }
97
98
99    InitState
100}
101
102proc ::JivePhone::InitState { } {
103    variable state
104
105    array set state {
106	phoneserver     0
107	setui           0
108	win             .dial
109	wstatus         -
110	phone		-
111        abphonename     -
112        abphonenumber   -
113    }
114}
115
116
117#----------------------------------------------------------------------------
118#-------------------- JEP Messages Function Handlers ------------------------
119#----------------------------------------------------------------------------
120proc ::JivePhone::LoginHook { } {
121
122    set server [::Jabber::GetServerJid]
123    ::Jabber::Jlib disco get_async items $server ::JivePhone::OnDiscoServer
124}
125
126proc ::JivePhone::OnDiscoServer {jlibname type from subiq args} {
127    variable state
128
129    Debug "::JivePhone::OnDiscoServer"
130
131    # See comments above what my opinion is...
132    if {$type eq "result"} {
133	set childs [::Jabber::Jlib disco children $from]
134	foreach service $childs {
135	    set name [::Jabber::Jlib disco name $service]
136
137	    Debug "\t service=$service, name=$name"
138
139	    if {$name eq "phone"} {
140		set state(phoneserver) 1
141		set state(service) $service
142		break
143	    }
144	}
145    }
146    if {$state(phoneserver)} {
147
148	# @@@ It is a bit unclear if we shall disco the phone service with
149	# the username as each node.
150
151	# We may not yet have obtained the roster. Sync issue!
152	if {[::Jabber::RosterCmd haveroster]} {
153	    DiscoForUsers
154	} else {
155	    ::hooks::register rosterExit ::JivePhone::RosterHook
156	}
157    }
158}
159
160proc ::JivePhone::RosterHook {} {
161
162    Debug "::JivePhone::RosterHook"
163    ::hooks::deregister rosterExit ::JivePhone::RosterHook
164    DiscoForUsers
165}
166
167proc ::JivePhone::DiscoForUsers {} {
168    variable state
169
170    Debug "::JivePhone::DiscoForUsers"
171    set users [::Jabber::RosterCmd getusers]
172
173    # We add ourselves to this list to figure out if we've got a jive phone.
174    lappend users [::Jabber::Jlib getthis myjid2]
175
176    foreach jid $users {
177	jlib::splitjidex $jid node domain -
178	if {[::Jabber::GetServerJid] eq $domain} {
179	    ::Jabber::Jlib disco get_async info $state(service)  \
180	      ::JivePhone::OnDiscoUserNode -node $node
181	}
182    }
183}
184
185proc ::JivePhone::OnDiscoUserNode {jlibname type from subiq args} {
186    variable xmlns
187    variable state
188    variable feature
189
190    Debug "::JivePhone::OnDiscoUserNode"
191
192    if {$type eq "result"} {
193	set node [wrapper::getattribute $subiq "node"]
194	set havePhone [::Jabber::Jlib disco hasfeature $feature(jivephone)  \
195	  $from $node]
196
197	Debug "\t from=$from, node=$node, havePhone=$havePhone"
198
199	if {$havePhone} {
200
201	    # @@@ What now?
202	    # @@@ But if we've already got phone presence?
203
204	    # Really stupid! It assumes user exist on login server.
205	    set server [::Jabber::Jlib getserver]
206	    set jid [jlib::joinjid $node $server ""]
207	    #puts "\t jid=$jid"
208
209	    # Cache this info.
210	    #set state(phone,$jid)
211
212	    # Since we added ourselves to the list take action if have phone.
213	    set myjid2 [::Jabber::Jlib getthis myjid2]
214	    if {[jlib::jidequal $jid $myjid2]} {
215		WeHavePhone
216	    } else {
217
218		# Attempt to set icon only if this user is unavailable since
219		# we do not have the full jid!
220		# This way we shouldn't interfere with phone presence.
221		# We could use [roster isavailable $jid] instead.
222
223		set item [::RosterTree::FindWithTag [list jid $jid]]
224		if {$item ne ""} {
225		    set image [::Rosticons::ThemeGet phone/online]
226		    ::RosterTree::StyleSetItemAlternative $jid jivephone  \
227		      image $image
228		}
229	    }
230	}
231    }
232}
233
234proc ::JivePhone::WeHavePhone { } {
235    variable state
236    variable popMenuDef
237    variable popMenuType
238    variable menuDef
239
240    NewPage
241    if {$state(setui)} {
242	return
243    }
244    ::Roster::RegisterPopupEntry $popMenuDef(call) $popMenuType(call)
245    ::JUI::RegisterMenuEntry  action $menuDef
246
247    set image [::Rosticons::ThemeGet [string tolower phone/online]]
248    set win [::JUI::SetAlternativeStatusImage jivephone $image]
249    bind $win <Button-1> [list ::JivePhone::DoDial "DIAL"]
250    ::balloonhelp::balloonforwindow $win [mc "Call"]
251
252    set state(wstatus) $win
253    set state(setui)   1
254
255}
256
257proc ::JivePhone::LogoutHook { } {
258    variable state
259    variable wtab
260    variable abline
261
262    ::Roster::DeRegisterPopupEntry mJiveCall
263    ::Roster::DeRegisterPopupEntry mJiveForward
264    ::JUI::DeRegisterMenuEntry action mJiveCall
265    ::JUI::RemoveAlternativeStatusImage jivephone
266
267    if {[winfo exists $state(wstatus)]} {
268	destroy $state(wstatus)
269    }
270    unset -nocomplain state
271
272    destroy $wtab
273    set abline [list]
274
275    InitState
276}
277
278# JivePhone::PresenceHook --
279#
280#       A user's presence is updated when on a phone call.
281
282proc ::JivePhone::PresenceHook {jid type args} {
283    variable xmlns
284    variable state
285
286    Debug "::JivePhone::PresenceHook jid=$jid, type=$type, $args"
287
288    if {$type ne "available"} {
289	return
290    }
291
292    array set argsArr $args
293    if {[info exists argsArr(-xmldata)]} {
294	set xmldata $argsArr(-xmldata)
295	set elems [wrapper::getchildswithtagandxmlns $xmldata  \
296	  phone-status $xmlns(jivephone)]
297	if {$elems ne ""} {
298	    set from [wrapper::getattribute $xmldata from]
299	    set elem [lindex $elems 0]
300	    set status [wrapper::getattribute $elem "status"]
301	    if {$status eq ""} {
302		set status available
303	    }
304	    # Cache this info.
305	    # @@@ How do we get unavailable status?
306	    # Must check for "normal" presence info.
307	    set state(status,$from) $status
308
309	    set image [::Rosticons::ThemeGet [string tolower phone/$status]]
310	    ::RosterTree::StyleSetItemAlternative $from jivephone image $image
311	    eval {::hooks::run jivePhonePresence $from $type} $args
312	}
313    }
314    return
315}
316
317# JivePhone::MessageHook --
318#
319#       Events are sent to the user when their phone is ringing, ...
320#       ... message packets are used to send events for the time being.
321
322proc ::JivePhone::MessageHook {xmldata uuid} {
323    variable xmlns
324    variable popMenuDef
325    variable popMenuType
326    variable state
327    variable callID
328
329    Debug "::JivePhone::MessageHook"
330
331    set elem [wrapper::getfirstchildwithtag $xmldata "phone-event"]
332    if {[llength $elem]} {
333	set status [wrapper::getattribute $elem "status"]
334	if {$status eq ""} {
335	    set status available
336	}
337	set cidElem [wrapper::getfirstchildwithtag $elem callerID]
338	if {$cidElem != {}} {
339	    set cid [wrapper::getcdata $cidElem]
340	} else {
341	    set cid [mc "Unknown"]
342	}
343	set image [::Rosticons::ThemeGet [string tolower phone/$status]]
344
345	set win [::JUI::SetAlternativeStatusImage jivephone $image]
346	set type [wrapper::getattribute $elem "type"]
347
348	# @@@ What to do more?
349	if {$type eq "RING" } {
350	    set callID [wrapper::getattribute $elem "callID"]
351
352	    ::Roster::RegisterPopupEntry $popMenuDef(forward) $popMenuType(forward)
353	    bind $win <Button-1> [list ::JivePhone::DoDial "FORWARD"]
354	    ::balloonhelp::balloonforwindow $win [mc "Forward current call to"]...
355	    ::hooks::run jivePhoneEvent $type $cid $callID $xmldata
356	}
357	if {$type eq "HANG_UP"} {
358	    ::Roster::DeRegisterPopupEntry mJiveForward
359
360	    bind $win <Button-1> [list ::JivePhone::DoDial "DIAL"]
361	    ::balloonhelp::balloonforwindow $win [mc "Call"]
362	    ::hooks::run jivePhoneEvent $type $cid "" $xmldata
363	}
364
365	# Provide a default notifier?
366	#	    if {[hooks::info jivePhoneEvent] eq {}} {
367	#		NotifyCall::InboundCall{ $cid }
368	#		set title [mc "Ring, ring"]...
369	#		set msg [mc "Phone is ringing from %s" $cid]
370	#		ui::dialog -icon info -buttons {} -title $title  \
371	#		  -message $msg -timeout 4000
372	#	    }
373    }
374    return
375}
376
377proc ::JivePhone::RosterPostCommandHook {m jidL clicked presL} {
378    variable state
379
380    set jid3 [lindex $jidL 0]
381    jlib::splitjid $jid3 jid2 -
382    set jid $jid2
383
384    Debug "RosterPostCommandHook $jidL $clicked $presL"
385
386    if {$clicked ne "user"} {
387	return
388    }
389    if {[llength $jidL] != 1} {
390	return
391    }
392    if {[lsearch $presL "available"] < 0} {
393	return
394    }
395    if {[info exists state(phone,$jid]} {
396	if {[info exists state(status,$jid)]} {
397
398	    switch -- $state(status,$jid3) {
399		AVAILABLE - HANG_UP {
400		    set midx [::AMenu::GetMenuIndex $m mJiveCall]
401		    if {$midx eq ""} {
402			# Probably a submenu.
403			return
404		    }
405		    $m entryconfigure $midx -state normal
406		}
407		XXXX {
408		    # @@@ ???
409		    set midx [::AMenu::GetMenuIndex $m mJiveForward]
410		    if {$midx eq ""} {
411			# Probably a submenu.
412			return
413		    }
414		    $m entryconfigure $midx -state normal
415		}
416	    }
417	}
418    }
419}
420
421#-----------------------------------------------------------------------
422#------------------------ JivePhone Dialer Window ----------------------
423#---------------------- (Dial/Forward - Extension/Jid) -----------------
424#-----------------------------------------------------------------------
425
426
427# JivePhone::DoDial --
428#
429#       type: FORWARD | DIAL
430
431proc ::JivePhone::DoDial {type {jid ""}} {
432    variable state
433    variable phoneNumber
434
435    set win $state(win)
436    if {$jid eq ""} {
437	BuildDialer $win $type
438    } else {
439	jlib::splitjidex $jid node domain -
440	if {[::Jabber::GetServerJid] eq $domain} {
441	    set phoneNumber ""
442	    OnDial $win $type $jid
443	} else {
444	    BuildDialer $win $type
445	}
446    }
447}
448
449# JivePhone::BuildDialer --
450#
451#       A toplevel dialer.
452
453proc ::JivePhone::BuildDialer {w type} {
454    variable state
455    variable phoneNumber
456
457    # Make sure only single instance of this dialog.
458    if {[winfo exists $w]} {
459	raise $w
460	return
461    }
462
463    ::UI::Toplevel $w -class PhoneDialer \
464      -usemacmainmenu 1 -macstyle documentProc -macclass {document closeBox} \
465      -closecommand [namespace current]::CloseDialer
466
467    if {$type eq "DIAL"} {
468	wm title $w [mc "Call to"]...
469    } else {
470	wm title $w [mc "Forward to"]...
471    }
472
473    ::UI::SetWindowPosition $w
474    set phoneNumber ""
475
476    # Global frame.
477    ttk::frame $w.f
478    pack  $w.f  -fill x
479
480    ttk::label $w.f.head -style Headlabel -text [mc "Phone"]
481    pack  $w.f.head  -side top -fill both -expand 1
482
483    ttk::separator $w.f.s -orient horizontal
484    pack  $w.f.s  -side top -fill x
485
486    set wbox $w.f.f
487    ttk::frame $wbox -padding [option get . dialogPadding {}]
488    pack  $wbox  -fill both -expand 1
489
490    set box $wbox.b
491    ttk::frame $box
492    pack $box -side bottom -fill x
493
494    ttk::label $box.l -text [mc "Number"]:
495    ttk::entry $box.e -textvariable [namespace current]::phoneNumber  \
496      -width 18
497    ttk::button $box.dial -text [mc "Dial"]  \
498      -command [list [namespace current]::OnDial $w $type]
499
500    grid  $box.l  $box.e  $box.dial -padx 1 -pady 4
501
502    focus $box.e
503    wm resizable $w 0 0
504}
505
506proc ::JivePhone::CloseDialer {w} {
507
508    ::UI::SaveWinGeom $w
509}
510
511#-------------------------------------------------------------------------
512#------------------- JivePhone Send IQ Actions ---------------------------
513#-------------------------------------------------------------------------
514
515proc ::JivePhone::OnDial {w type {jid ""}} {
516    variable phoneNumber
517    variable xmlns
518    variable state
519    variable callID
520
521    Debug "::JivePhone::OnDial w=$w, type=$type, phoneNumber=$phoneNumber"
522
523    if {!$state(phoneserver)} {
524	return
525    }
526
527    if {$jid ne ""} {
528	set dnid $jid
529	set extensionElem [wrapper::createtag "jid" -chdata $jid]
530    } elseif {$phoneNumber ne ""} {
531	set extensionElem [wrapper::createtag "extension" -chdata $phoneNumber]
532	set dnid $phoneNumber
533    } else {
534	Debug "\t return"
535	return
536    }
537
538    if {$type eq "DIAL"} {
539	set command "DIAL"
540	set attr [list xmlns $xmlns(jivephone) type $command]
541    } else {
542	set command "FORWARD"
543	set attr [list xmlns $xmlns(jivephone) id $callID type $command]
544    }
545    set phoneElem [wrapper::createtag "phone-action"  \
546      -attrlist $attr -subtags [list $extensionElem]]
547
548    ::Jabber::Jlib send_iq set [list $phoneElem]  \
549      -to $state(service) -command [list ::JivePhone::DialCB $dnid]
550
551    ::hooks::run jivePhoneEvent $command $dnid $callID
552
553    destroy $w
554}
555
556proc ::JivePhone::DialJID {jid type {callID ""}} {
557    variable state
558    variable xmlns
559
560    if {!$state(phoneserver)} {
561	return
562    }
563    set extensionElem [wrapper::createtag "jid" -chdata $jid]
564
565    if {$type eq "DIAL"} {
566	set command "DIAL"
567	set attr [list xmlns $xmlns(jivephone) type $command]
568    } else {
569	# @@@ Where comes callID from?
570	set command "FORWARD"
571	set attr [list xmlns $xmlns(jivephone) id $callID type $command]
572    }
573    set phoneElem [wrapper::createtag "phone-action"  \
574      -attrlist $attr -subtags [list $extensionElem]]
575
576    ::Jabber::Jlib send_iq set [list $phoneElem]  \
577      -to $state(service) -command [list ::JivePhone::DialCB $jid]
578
579    ::hooks::run jivePhoneEvent $command $jid $callID
580}
581
582proc ::JivePhone::DialExtension {extension type {callID ""}} {
583    variable state
584    variable xmlns
585
586    if {!$state(phoneserver)} {
587        return
588    }
589    set extensionElem [wrapper::createtag "extension" -chdata $extension]
590
591    if {$type eq "DIAL"} {
592        set command "DIAL"
593        set attr [list xmlns $xmlns(jivephone) type $command]
594    } else {
595        # @@@ Where comes callID from?
596        set command "FORWARD"
597        set attr [list xmlns $xmlns(jivephone) id $callID type $command]
598    }
599
600    set phoneElem [wrapper::createtag "phone-action"  \
601      -attrlist $attr -subtags [list $extensionElem]]
602
603    ::Jabber::Jlib send_iq set [list $phoneElem]  \
604      -to $state(service) -command [list ::JivePhone::DialCB $extension]
605
606    ::hooks::run jivePhoneEvent $command $extension $callID
607}
608
609proc ::JivePhone::DialCB {dnid type subiq args} {
610
611    if {$type eq "error"} {
612	ui::dialog -title [mc "Error"] -icon error -type ok \
613	  -message [mc "Failed calling %s" $dnid] -detail $subiq
614    }
615}
616
617
618#---------------------------------------------------------------------------
619#------------------- JivePhone Addressbook SpeedDial Tab -------------------
620#---------------------------------------------------------------------------
621
622proc ::JivePhone::NewPage {} {
623    variable wtab
624
625    set wnb [::JUI::GetNotebook]
626    set wtab $wnb.ab
627    if {![winfo exists $wtab]} {
628        Build $wtab
629        $wnb add $wtab -text [mc "Address Book"]
630    }
631}
632
633# JivePhone::Build --
634#
635#       This is supposed to create a frame which is pretty object like,
636#       and handles most stuff internally without intervention.
637#
638# Arguments:
639#       w           frame for everything
640#       args
641#
642# Results:
643#       w
644
645proc ::JivePhone::Build {w args} {
646    global  prefs this jprefs
647
648    variable waddressbook
649    variable wtree
650    variable wwave
651    upvar ::Jabber::jstate jstate
652    upvar ::Jabber::jserver jserver
653    variable abline
654
655    ::Debug 2 "::JivePhone::Build w=$w"
656    set jstate(wpopup,addressbook)    .jpopupab
657    set waddressbook $w
658    set wwave   $w.fs
659    set wbox    $w.box
660    set wtree   $wbox.tree
661    set wxsc    $wbox.xsc
662    set wysc    $wbox.ysc
663
664    # The frame.
665    ttk::frame $w -class AddressBook
666
667    # D = -border 1 -relief sunken
668    frame $wbox
669    pack  $wbox -side top -fill both -expand 1
670
671    ttk::scrollbar $wxsc -orient horizontal -command [list $wtree xview]
672    ttk::scrollbar $wysc -orient vertical -command [list $wtree yview]
673
674    ::ITree::New $wtree $wxsc $wysc   \
675      -buttonpress ::JivePhone::Popup         \
676      -buttonpopup ::JivePhone::Popup
677
678    grid  $wtree  -row 0 -column 0 -sticky news
679    grid  $wysc   -row 0 -column 1 -sticky ns
680    grid  $wxsc   -row 1 -column 0 -sticky ew
681    grid columnconfigure $wbox 0 -weight 1
682    grid rowconfigure $wbox 0 -weight 1
683
684    #--------- Load Entries of AddressBook into NewPage Tab ---------
685    LoadEntries
686    if { [llength $abline] } {
687        foreach {name phone} $abline {
688            set opts {-text "$name ($phone)"}
689            if {$name ne ""} {
690               lappend opts -text "$name ($phone)"
691               eval {::ITree::Item $wtree $phone} $opts
692            }
693        }
694    }
695    return $w
696}
697
698proc ::JivePhone::LoadEntries {} {
699    variable abline
700    global  prefs this
701
702    set fileName [file join $this(prefsPath) addressbook.csv]
703
704    set abline [list]
705    if { [ file exists $fileName ] } {
706        set hFile [open $fileName "r"]
707        while {[eof $hFile] <= 0} {
708           gets $hFile line
709           set temp [split $line ":"]
710           foreach i $temp {
711               lappend abline $i
712           }
713        }
714        close $hFile
715    }
716}
717
718# JivePhone::Popup --
719#
720#       Handle popup menus in JivePhone, typically from right-clicking.
721#
722# Arguments:
723#       w           widget that issued the command: tree or text
724#       v           for the tree widget it is the item path,
725#                   for text the jidhash.
726#
727# Results:
728#       popup menu displayed
729
730proc ::JivePhone::Popup {w v x y} {
731    global  wDlgs this
732    variable popMenuDef
733
734    upvar ::Jabber::jstate jstate
735
736    ::Debug 2 "::JivePhone::Popup w=$w, v='$v', x=$x, y=$y"
737
738    # The last element of $v is either a jid, (a namespace,)
739    # a header in roster, a group, or an agents xml tag.
740    # The variables name 'jid' is a misnomer.
741    # Find also type of thing clicked, 'typeClicked'.
742
743    set typeClicked ""
744
745    set jid [lindex $v end]
746    set jid3 $jid
747    set childs [::ITree::Children $w $v]
748
749    if {$jid ne ""} {
750        set typeClicked jid
751    }
752
753    if {[string length $jid] == 0} {
754        set typeClicked ""
755    }
756    set X [expr {[winfo rootx $w] + $x}]
757    set Y [expr {[winfo rooty $w] + $y}]
758
759    ::Debug 2 "\t jid=$jid, typeClicked=$typeClicked"
760
761    # Mads Linden's workaround for menu post problem on mac:
762    # all in menubutton commands i add "after 40 the_command"
763    # this way i can never have to posting error.
764    # it is important after the tk_popup f.ex to
765    #
766    # destroy .mb
767    # update
768    #
769    # this way the .mb is destroyd before the next window comes up, thats how I
770    # got around this.
771
772    # Make the appropriate menu.
773    set m $jstate(wpopup,addressbook)
774    set i 0
775    catch {destroy $m}
776    menu $m -tearoff 0
777
778    foreach {item type lname cmd} $popMenuDef(addressbook) {
779        if {[string index $cmd 0] == "@"} {
780            set mt [menu ${m}.sub${i} -tearoff 0]
781            set locname [eval concat $lname]
782            $m add cascade -label $locname -menu $mt -state disabled
783            eval [string range $cmd 1 end] $mt
784            incr i
785        } elseif {[string equal $item "separator"]} {
786            $m add separator
787            continue
788        } else {
789
790            # Substitute the jid arguments. Preserve list structure!
791            set cmd [eval list $cmd]
792            set locname [eval concat $lname]
793            $m add command -label $locname -command [list after 40 $cmd]  \
794              -state disabled
795        }
796
797        # If a menu should be enabled even if not connected do it here.
798
799        if {![::Jabber::IsConnected]} {
800            continue
801        }
802        if {[string equal $type "any"]} {
803            $m entryconfigure $locname -state normal
804            continue
805        }
806
807        # State of menu entry. We use the 'type' and 'typeClicked' to sort
808        # out which capabilities to offer for the clicked item.
809        set state disabled
810
811	if {[string equal $item "mNewAB"]} {
812	    set state normal
813	}
814
815        if {[string equal $type $typeClicked]} {
816            set state normal
817        }
818        if {[string equal $state "normal"]} {
819            $m entryconfigure $locname -state normal
820        }
821    }
822
823    # This one is needed on the mac so the menu is built before it is posted.
824    update idletasks
825
826    # Post popup menu.
827    tk_popup $m [expr {int($X) - 10}] [expr {int($Y) - 10}]
828
829    # Mac bug... (else can't post menu while already posted if toplevel...)
830    if {[string equal "macintosh" $this(platform)]} {
831        catch {destroy $m}
832        update
833    }
834}
835
836proc ::JivePhone::RemoveAddressbookDlg {jid} {
837    variable abline
838    variable wtree
839
840    set index [lsearch -exact $abline $jid]
841
842    set tmp [lreplace $abline [expr {$index-1}] $index]
843    set abline $tmp
844
845    eval {::ITree::DeleteItem $wtree $jid}
846
847    SaveEntries
848
849}
850
851
852proc ::JivePhone::NewAddressbookDlg {} {
853    global  this wDlgs
854
855    variable abName
856    variable abPhoneNumber
857
858    set abName ""
859    set abPhoneNumber ""
860
861    set w ".nadbdlg"
862    ::UI::Toplevel $w \
863      -macstyle documentProc -macclass {document closeBox} -usemacmainmenu 1 \
864      -closecommand [namespace current]::CloseCmd
865    wm title $w [mc "New address book"]
866
867    set nwin [llength [::UI::GetPrefixedToplevels $wDlgs(jmucenter)]]
868    if {$nwin == 1} {
869        ::UI::SetWindowPosition $w ".nadbdlg"
870    }
871
872    # Global frame.
873    ttk::frame $w.frall
874    pack $w.frall -fill both -expand 1
875
876    set wbox $w.frall.f
877    ttk::frame $wbox -padding [option get . dialogPadding {}]
878    pack $wbox -fill both -expand 1
879
880    ttk::label $wbox.msg -style Small.TLabel \
881      -padding {0 0 0 6} -wraplength 260 -justify left -text [mc "New address book"]
882    pack $wbox.msg -side top -anchor w
883
884    set frmid $wbox.frmid
885    ttk::frame $frmid
886    pack $frmid -side top -fill both -expand 1
887
888    ttk::label $frmid.lname -text [mc "Name"]:
889    ttk::entry $frmid.ename -textvariable [namespace current]::abName
890
891    ttk::label $frmid.lphone -text [mc "Phone number"]:
892    ttk::entry $frmid.ephone -textvariable [namespace current]::abPhoneNumber
893
894    grid  $frmid.lname    $frmid.ename        -  -sticky e -pady 2
895    grid  $frmid.lphone    $frmid.ephone   -  -sticky e -pady 2
896    grid  $frmid.ephone  $frmid.ename  -sticky ew
897    grid columnconfigure $frmid 1 -weight 1
898
899    # Button part.
900    set frbot $wbox.b
901    set wenter  $frbot.btok
902    ttk::frame $frbot
903    ttk::button $wenter -text [mc "Enter"] \
904      -default active -command [list [namespace current]::addItemAddressBook $w]
905    ttk::button $frbot.btcancel -text [mc "Cancel"]  \
906      -command [list [namespace current]::CancelEnter $w]
907
908    set padx [option get . buttonPadX {}]
909    if {[option get . okcancelButtonOrder {}] eq "cancelok"} {
910        pack $frbot.btok -side right
911        pack $frbot.btcancel -side right -padx $padx
912    } else {
913        pack $frbot.btcancel -side right
914        pack $frbot.btok -side right -padx $padx
915    }
916    pack $frbot -side bottom -fill x
917
918    wm resizable $w 0 0
919
920    bind $w <Return> [list $wenter invoke]
921
922    # Trick to resize the labels wraplength.
923    set script [format {
924        update idletasks
925        %s configure -wraplength [expr {[winfo reqwidth %s] - 20}]
926    } $wbox.msg $w]
927    after idle $script
928}
929
930proc ::JivePhone::ModifyAddressbookDlg {jid} {
931    global  this wDlgs
932
933    variable abName
934    variable abPhoneNumber
935    variable abline
936
937    #Get Entry data from abline list
938    set index [lsearch -exact $abline $jid]
939    set abName [lindex $abline [expr {$index-1}]]
940    set abPhoneNumber [lindex $abline [expr {$index}]]
941    set oldPhoneNumber $abPhoneNumber
942
943    set w ".madbdlg"
944    ::UI::Toplevel $w \
945      -macstyle documentProc -macclass {document closeBox} -usemacmainmenu 1 \
946      -closecommand [namespace current]::CloseCmd
947    wm title $w [mc "Modify address book"]
948
949    set nwin [llength [::UI::GetPrefixedToplevels $wDlgs(jmucenter)]]
950    if {$nwin == 1} {
951        ::UI::SetWindowPosition $w ".madbdlg"
952    }
953
954    # Global frame.
955    ttk::frame $w.frall
956    pack $w.frall -fill both -expand 1
957
958    set wbox $w.frall.f
959    ttk::frame $wbox -padding [option get . dialogPadding {}]
960    pack $wbox -fill both -expand 1
961
962    ttk::label $wbox.msg -style Small.TLabel \
963      -padding {0 0 0 6} -wraplength 260 -justify left -text [mc "Modify address book"]
964    pack $wbox.msg -side top -anchor w
965
966    set frmid $wbox.frmid
967    ttk::frame $frmid
968    pack $frmid -side top -fill both -expand 1
969
970    ttk::label $frmid.lname -text [mc "Name"]:
971    ttk::entry $frmid.ename -textvariable [namespace current]::abName
972
973    ttk::label $frmid.lphone -text [mc "Phone number"]:
974    ttk::entry $frmid.ephone -textvariable [namespace current]::abPhoneNumber
975
976    grid  $frmid.lname    $frmid.ename        -  -sticky e -pady 2
977    grid  $frmid.lphone    $frmid.ephone   -  -sticky e -pady 2
978    grid  $frmid.ephone  $frmid.ename  -sticky ew
979    grid columnconfigure $frmid 1 -weight 1
980
981    # Button part.
982    set frbot $wbox.b
983    set wenter  $frbot.btok
984    ttk::frame $frbot
985    ttk::button $wenter -text [mc "Enter"] \
986      -default active -command [list [namespace current]::modifyItemAddressBook $w $oldPhoneNumber]
987    ttk::button $frbot.btcancel -text [mc "Cancel"]  \
988      -command [list [namespace current]::CancelEnter $w]
989
990    set padx [option get . buttonPadX {}]
991    if {[option get . okcancelButtonOrder {}] eq "cancelok"} {
992        pack $frbot.btok -side right
993        pack $frbot.btcancel -side right -padx $padx
994    } else {
995        pack $frbot.btcancel -side right
996        pack $frbot.btok -side right -padx $padx
997    }
998    pack $frbot -side bottom -fill x
999
1000    wm resizable $w 0 0
1001
1002    bind $w <Return> [list $wenter invoke]
1003
1004    # Trick to resize the labels wraplength.
1005    set script [format {
1006        update idletasks
1007        %s configure -wraplength [expr {[winfo reqwidth %s] - 20}]
1008    } $wbox.msg $w]
1009    after idle $script
1010}
1011
1012
1013proc ::JivePhone::addItemAddressBook {w} {
1014    variable abName
1015    variable abPhoneNumber
1016    variable abline
1017    variable wtree
1018
1019    if { $abName ne "" && $abPhoneNumber ne ""} {
1020        lappend abline $abName
1021        lappend abline $abPhoneNumber
1022
1023        set opts {-text "$abName ($abPhoneNumber)"}
1024        eval {::ITree::Item $wtree $abPhoneNumber} $opts
1025        SaveEntries
1026
1027        ::UI::SaveWinGeom $w
1028        destroy $w
1029    }
1030}
1031
1032proc ::JivePhone::modifyItemAddressBook {w oldPhoneNumber} {
1033    variable abName
1034    variable abPhoneNumber
1035    variable abline
1036    variable wtree
1037
1038    if { $abName ne "" && $abPhoneNumber ne "" } {
1039        #---------- Updates Memory Addressbook -----------------
1040        set index [lsearch -exact $abline $oldPhoneNumber]
1041
1042        set tmp [lreplace $abline [expr {$index-1}] $index $abName $abPhoneNumber]
1043        set abline $tmp
1044
1045        #----- Updates GUI ---------
1046        eval {::ITree::DeleteItem $wtree $oldPhoneNumber}
1047        set opts {-text "$abName ($abPhoneNumber)"}
1048        eval {::ITree::Item $wtree $abPhoneNumber} $opts
1049
1050        #----- Updates Database -------
1051        SaveEntries
1052
1053        ::UI::SaveWinGeom $w
1054        destroy $w
1055    }
1056}
1057
1058proc ::JivePhone::CancelEnter {w} {
1059
1060    ::UI::SaveWinGeom $w
1061    destroy $w
1062}
1063
1064proc ::JivePhone::CloseCmd {w} {
1065
1066    ::UI::SaveWinGeom $w
1067}
1068
1069proc ::JivePhone::SaveEntries {} {
1070    variable abline
1071    global  prefs this
1072
1073    # @@@ Mats
1074    set hFile [open [file join $this(prefsPath) addressbook.csv] "w"]
1075
1076    foreach {name phonenumber} $abline {
1077       if {$name ne ""} {
1078           puts $hFile "$name:$phonenumber"
1079       }
1080    }
1081
1082    close $hFile
1083}
1084
1085proc ::JivePhone::Debug {msg} {
1086
1087    if {0} {
1088	puts "-------- $msg"
1089    }
1090}
1091
1092proc ::JivePhone::buildChatButtonTrayHook {wtray dlgtoken args} {
1093    global  this prefs wDlgs
1094    variable state
1095
1096    if { $state(phoneserver) == 1 } {
1097	variable $dlgtoken
1098	upvar 0 $dlgtoken dlgstate
1099
1100	set w $dlgstate(w)
1101
1102        set iconCall    [::Theme::FindIconSize 32 phone-call]
1103        set iconCallDis [::Theme::FindIconSize 32 phone-call-Dis]
1104
1105        $wtray newbutton call  \
1106          -text [mc "Call"] -image $iconCall  \
1107          -disabledimage $iconCallDis   \
1108          -command [list [namespace current]::chatCall $dlgtoken]
1109    }
1110}
1111
1112proc ::JivePhone::chatCall {dlgtoken} {
1113    set chattoken [::Chat::GetActiveChatToken $dlgtoken]
1114    variable $chattoken
1115    upvar 0 $chattoken chatstate
1116    set jid $chatstate(fromjid)
1117
1118    DialJID $jid "DIAL"
1119}
1120
1121#-------------------------------------------------------------------------------
1122