14 proc ::tools::email {} {
15 global emailWin emailData
17 if {[
winfo exists $w]} {
24 wm title $w "Scid: Email Manager"
27 bind $w <Destroy> { set .emailWin 0 }
28 bind $w <F1> { helpWindow Email }
32 pack $w.f -side left -fill y
33 pack $w.b -side right -fill y
36 ttk::label $f.title -text "Opponent list" -font font_Bold
37 listbox $f.list -height 16 -width 40 -exportselection false \
38 -selectmode browse -selectbackground lightBlue -font font_Fixed \
39 -yscrollcommand "$f.scroll set" -background white -setgrid 1
40 ttk::scrollbar $f.scroll -command "$w.list yview" -takefocus 0
41 pack $f -side left -expand true -fill both
42 pack $f.title -side top
43 pack $f.scroll -side right -fill y
44 pack $f.list -side right -expand true -fill both
46 bind $f.list <ButtonRelease-1> ::tools::email::refreshButtons
47 bind $f.list <Enter> ::tools::email::refreshButtons
48 bind $f.list <Key-Up> ::tools::email::refreshButtons
49 bind $f.list <Key-Down> ::tools::email::refreshButtons
51 bind $f.list <Key-a> {.emailWin.b.add invoke}
52 bind $f.list <Key-e> {.emailWin.b.edit invoke}
53 bind $f.list <Key-d> {.emailWin.b.delete invoke}
54 bind $f.list <Key-l> {.emailWin.b.load invoke}
55 bind $f.list <Key-s> {.emailWin.b.send invoke}
56 bind $f.list <Key-t> {.emailWin.b.time.m post [winfo pointerx .] [winfo pointery .]}
60 ttk::button $b.add -text "Add..." -underline 0 -command {
61 set idx [llength $emailData]
62 lappend emailData [list "" "" "" "" ""]
63 modifyEmailDetails $idx
64 ::tools::email::refresh
67 ttk::button $b.edit -text [
tr Edit] -underline 0 -command ::tools::email::EditButton
68 ttk::button $b.delete -text "$::tr(Delete)..." -underline 0 -command ::tools::email::DeleteButton
69 ttk::button $b.load -text $::tr(LoadGame) -underline 0 -command ::tools::email::LoadButton
70 ttk::button $b.send -text "Send email..." -underline 0 -command ::tools::email::SendButton
71 ttk::menubutton $b.time -text $::tr(Time) -menu $b.time.m
73 $b.time.m add command -label "Received today" -underline 0 \
74 -command {::tools::email::TimesButton r}
75 $b.time.m add command -label "Sent today" -underline 0 \
76 -command {::tools::email::TimesButton s}
77 $b.time.m add command -label [
tr Edit] -underline 0 \
78 -command {::tools::email::TimesButton e}
80 ttk::button $b.config -text "$::tr(GlistEditField)..." -command ::tools::email::config
81 ttk::button $b.help -text $::tr(Help) -command { helpWindow Email }
82 ttk::button $b.close -text $::tr(Close) -command { destroy .emailWin }
83 pack $b.add $b.edit $b.delete $b.load $b.send $b.time \
84 -side top -pady 2 -padx "10 0" -fill x
85 pack $b.close $b.help $b.config -side bottom -pady 2 -padx "10 0" -fill x
87 bind $w <Destroy> { set emailWin 0 }
93 proc ::tools::email::config {} {
98 ttk::labelframe $w.use -text "Send email using"
100 ttk::radiobutton $w.smtp.b -text "SMTP server:" -variable email(smtp) -value 1
101 ttk::entry $w.smtp.s -width 30 -textvar email(server)
103 ttk::radiobutton $w.sm.b -text "sendmail process:" -variable email(smtp) -value 0
104 ttk::entry $w.sm.s -width 30 -textvar email(smproc)
105 pack $w.use -side top -fill x
106 pack $w.smtp $w.sm -side top -anchor e -in $w.use
107 pack $w.smtp.s $w.smtp.b -side right
108 pack $w.sm.s $w.sm.b -side right
110 ttk::labelframe $w.addr -text "Email address fields"
112 ttk::label $w.from.lab -text "From:"
113 ttk::entry $w.from.e -textvar email(from) -width 30
115 ttk::label $w.bcc.lab -text "Bcc:"
116 ttk::entry $w.bcc.e -textvar email(bcc) -width 30
117 pack $w.addr -side top -fill x -pady "10 0"
118 pack $w.from $w.bcc -side top -fill x -in $w.addr
119 pack $w.from.e $w.from.lab -side right
120 pack $w.bcc.e $w.bcc.lab -side right
122 pack [ttk::frame $w.b] -side top -fill x
123 ttk::button $w.b.ok -text [
tr OptionsSave] -command {
125 catch {grab release .emailConfig}
128 ttk::button $w.b.cancel -text $::tr(Cancel) \
129 -command "catch {grab release $w}; destroy $w"
135 proc ::tools::email::EditButton {} {
137 set sel [.emailWin.f.list curselection]
138 if {[
llength $sel] == 1} {
139 set idx [
lindex $sel 0]
140 if {[
llength $emailData] > $idx} {
147 proc ::tools::email::DeleteButton {} {
149 set sel [.emailWin.f.list curselection]
150 if {[
llength $sel] != 1} {
return}
151 set idx [
lindex $sel 0]
152 if {[
llength $emailData] <= $idx} {
return}
153 set confirm [
tk_messageBox -icon question -type yesno -default yes \
154 -parent .emailWin -title "Really delete opponent?" \
155 -message "Do you really want to delete this opponent?"]
156 if {$confirm == "yes"} {
157 set emailData [
lreplace $emailData $idx $idx]
163 proc ::tools::email::LoadButton {} {
165 set sel [.emailWin.f.list curselection]
166 if {[
llength $sel] != 1} {
return}
167 set idx [
lindex $sel 0]
168 if {[
llength $emailData] <= $idx} {
return}
169 set details [
lindex $emailData $idx]
170 if {[
llength [
lindex $details 3]] > 0} {
171 if {[
catch {
::game::Load [
lindex [
lindex $details 3] 0]} result]} {
172 tk_messageBox -type ok -icon warning -title "Scid" -message $result
179 proc ::tools::email::SendButton {} {
181 set sel [.emailWin.f.list curselection]
182 if {[
llength $sel] != 1} {
return}
183 set idx [
lindex $sel 0]
184 if {[
llength $emailData] <= $idx} {
return}
185 set details [
lindex $emailData $idx]
187 [
lindex $details 2] [
lindex $details 3] [
lindex $details 4]
192 proc ::tools::email::TimesButton {type} {
193 global emailData emailTimesIdx
194 set sel [.emailWin.f.list curselection]
195 if {[
llength $sel] != 1} {
return}
196 set idx [
lindex $sel 0]
197 if {[
llength $emailData] <= $idx} {
return}
198 set details [
lindex $emailData $idx]
199 while {[
llength $details] < 6} {
lappend details {}}
200 set timeList [
lindex $details 5]
201 set last [
lindex $timeList end]
203 if {$type == "r" || $type == "s"} {
208 set emailTimesIdx $idx
210 if {[
winfo exists $w]} {
return}
212 wm title $w "Scid: Email Times"
213 ttk::label $w.title -text "Email Times for [
lindex $details 0]"
215 text $w.t.text -height 15 -width 30 -font font_Fixed -setgrid 1 \
216 -yscrollcommand "$w.t.ybar set" -bg white -fg black
217 ttk::scrollbar $w.t.ybar -command "$w.t.text yview"
219 ttk::button $w.b.ok -text "OK" -command {
220 set details [lindex $emailData $emailTimesIdx]
221 set timeList [split [string trim [.emailTimesWin.t.text get 1.0 end]] "\n"]
222 set details [lreplace $details 5 5 $timeList]
223 set emailData [lreplace $emailData $emailTimesIdx $emailTimesIdx $details]
224 ::tools::email::writeOpponentFile $emailData
225 grab release .emailTimesWin
226 ::tools::email::refresh 0
227 catch {focus .emailWin}
228 destroy .emailTimesWin
230 ttk::button $w.b.cancel -text $::tr(Cancel) \
231 -command "grab release $w; catch {focus .emailWin}; destroy $w"
232 pack $w.title -side top -fill x
233 pack $w.t -side top -fill both
234 pack $w.t.ybar -side right -fill y
235 pack $w.t.text -side left -fill both -expand yes
236 pack $w.b -side bottom -fill x
238 foreach i $timeList {
239 $w.t.text insert end "$i\n"
244 proc ::tools::email::addSentReceived {idx type} {
246 if {[
llength $emailData] <= $idx} {
return}
247 set details [
lindex $emailData $idx]
248 while {[
llength $details] < 6} {
lappend details {}}
249 set timeList [
lindex $details 5]
250 set last [
lindex $timeList end]
253 if {$type == "r"} {
append new "Received "}
else {
append new "Sent "}
255 set oppGList [
lindex $details 3]
256 if {[
llength $oppGList] > 0} {
257 set oppGNum [
lindex $oppGList 0]
263 set m [
llength [
split [
sc_game moves coord list]]]
265 set m [
expr int(($m+1)/2)]
266 set mnum [
format "%3d " $m]
273 if {! [
string compare $last $new]} {
return}
274 lappend timeList $new
275 set details [
lreplace $details 5 5 $timeList]
276 set emailData [
lreplace $emailData $idx $idx $details]
281 proc ::tools::email::refreshButtons {} {
282 set sel [.emailWin.f.list curselection]
283 if {[
llength $sel] > 0} {
284 .emailWin.b.edit configure -state normal
285 .emailWin.b.delete configure -state normal
286 .emailWin.b.load configure -state normal
287 .emailWin.b.send configure -state normal
289 .emailWin.b.edit configure -state disabled
290 .emailWin.b.delete configure -state disabled
291 .emailWin.b.load configure -state disabled
292 .emailWin.b.send configure -state disabled
296 proc ::tools::email::refresh {{clearSelection 1}} {
297 global emailWin emailData
298 if {! [
winfo exists .emailWin]} {
return}
299 if {$clearSelection} {
301 .emailWin.f.list selection clear 0 end
303 set sel [
lindex [.emailWin.f.list curselection] 0]
305 .emailWin.f.list delete 0 end
307 foreach i $emailData {
308 set name [
lindex $i 0]
310 if {[
llength $i] == 6} {
311 set timeList [
lindex $i 5]
312 set time [
lindex $timeList end]
314 .emailWin.f.list insert end [
format "%-14s %s" $name $time]
317 .emailWin.f.list selection set $sel
324 set emailData_index 0
325 set emailData_name ""
326 set emailData_addr ""
327 set emailData_subj ""
328 set emailData_glist ""
329 set emailData_dates ""
330 set emailData_helpBar {}
331 array set ::tools::email::helpBar ""
334 trace variable emailData_glist w {::utils::validate::Regexp {^[0-9\ ]*$}}
344 proc emailMessageEditor {idx name addr subj gamelist sig} {
345 global emailCount emailData email
347 if {$emailCount >= 10000} {
set emailCount 1}
349 set w ".emailMessageWin$emailCount"
351 wm title $w "Send email to $name"
352 set f [ttk::frame $w.fields]
354 ttk::label $f.fromlab -text "From: "
356 $f.from insert end $email(from)
358 ttk::label $f.tolab -text "To: "
360 $f.to insert end $addr
362 ttk::label $f.subjlab -text "Subject: "
364 $f.subj insert end $subj
366 ttk::label $f.bcclab -text "Bcc: "
368 $f.bcc insert end $email(bcc)
370 grid $f.fromlab -row 0 -column 0 -sticky e
371 grid $f.from -row 0 -column 1 -sticky ew
372 grid $f.tolab -row 1 -column 0 -sticky e
373 grid $f.to -row 1 -column 1 -sticky ew
374 grid $f.subjlab -row 2 -column 0 -sticky e
375 grid $f.subj -row 2 -column 1 -sticky ew
376 grid $f.bcclab -row 3 -column 0 -sticky e
377 grid $f.bcc -row 3 -column 1 -sticky ew
378 grid columnconfigure $f 1 -weight 1
380 set f [ttk::frame $w.message]
381 pack $w.fields -fill x -padx 4 -pady 4
382 pack $w.message -expand yes -fill both -padx 4 -pady 4
384 ttk::scrollbar $f.ybar -command "$f.text yview"
385 ttk::scrollbar $f.xbar -orient horizontal -command "$f.text xview"
386 text $f.text -yscrollcommand "$f.ybar set" -xscrollcommand "$f.xbar set" \
387 -setgrid 1 -width 72 -height 20 -background white -wrap none
389 grid $f.text -row 0 -column 0 -sticky news
390 grid $f.ybar -row 0 -column 1 -sticky nse
391 grid $f.xbar -row 1 -column 0 -sticky news
392 ttk::frame $f.buttons
393 ttk::button $f.send -text " Send " -command "::tools::email::processMessage $w $idx"
394 ttk::button $f.cancel -text $::tr(Cancel) -command "destroy $w"
395 grid $f.buttons -row 2 -column 0 -columnspan 2 -sticky e
398 grid rowconfig $w.message 0 -weight 1 -minsize 0
399 grid columnconfig $w.message 0 -weight 1 -minsize 0
402 menu $f.text.edit -tearoff 0
403 $f.text.edit add command -label "Cut" -command "tk_textCut $f.text"
404 $f.text.edit add command -label "Copy" -command "tk_textCopy $f.text"
405 $f.text.edit add command -label "Paste" -command "tk_textPaste $f.text"
406 bind $f.text <ButtonPress-$::MB3> "tk_popup $f.text.edit %X %Y"
408 set text $w.message.text
410 $text insert end "\n"
411 foreach i $gamelist {
412 catch {
set gamePgn [
sc_game pgn -gameNumber $i -width 70 -tags 0 \
413 -variations 0 -comments 0]}
414 $text insert end "$gamePgn\n"
416 $text insert end $sig
420 proc ::tools::email::processMessage {w idx} {
422 set from [$w.fields.from get]
423 set to [$w.fields.to get]
424 set subj [$w.fields.subj get]
425 set bcc [$w.fields.bcc get]
426 set message [$w.message.text get 1.0 end]
427 if {[
string trim $to] == ""} {
428 tk_messageBox -icon error -type ok -title "Empty email address" \
429 -message "You must specify an email address."
432 set cmd {::tools::email::sendMessage $from $to $subj $bcc $message}
433 if {[
catch $cmd result] != 0} {
434 tk_messageBox -icon error -type ok -title "Error sending email" \
435 -message "Error sending email: $result"
438 tk_messageBox -icon info -type ok -title "Scid" -message $result
443 proc ::tools::email::sendMessage {from to subject bcc message} {
450 catch {
set copy_id [open [
file nativename $email(logfile)] "a+"]}
451 if {$copy_id == ""} {
452 return -code error "Unable to open $email(logfile)"
455 set cmdargs "-to {$to} -subject {$subject} "
456 if {$email(server) != ""} { ::ezsmtp::config -mailhost $email(server)}
457 if {$email(from) != ""} {
458 if {[
catch {::ezsmtp::config -from $from} result]} {
460 return -code error "Error configuring SMTP: $result"
462 append cmdargs "-from {$from} "
464 if {$email(bcc) != ""} {
465 append cmdargs "-bcc {$bcc} "
467 if {[
catch {
eval "::ezsmtp::send $cmdargs -body {$message}"} result]} {
469 return -code error "Error sending mail with SMTP: $result"
472 if {[
catch {open "| $email(smproc) -oi -t" "w"} ::tools::email::id]} {
474 return -code error "Scid could not find the sendmail program: $email(smproc)"
476 if {[
string trim $from] != ""} {
477 puts $::tools::email::id "From: $from"
479 puts $::tools::email::id "To: $to"
480 puts $::tools::email::id "Subject: $subject"
481 if {[
string trim $bcc] != ""} {
482 puts $::tools::email::id "Bcc: $bcc"
484 puts $::tools::email::id ""
485 puts $::tools::email::id $message
486 close $::tools::email::id
488 puts $copy_id "To: $to"
489 puts $copy_id "Subject: $subject"
491 puts $copy_id $message
493 return "The email message was sent; a copy was appended to $email(logfile)"
496 proc modifyEmailDetails {i} {
497 global emailData emailData_name emailData_addr emailData_glist emailData_subj
498 global emailData_sig emailData_index emailData_helpBar ::tools::email::helpBar
502 bind $w <F1> { helpWindow Email }
503 set emailData_index $i
504 if {[
lindex [
lindex $emailData $i] 0] == ""} {
505 wm title $w "Add opponent details"
507 wm title $w "Edit opponent details"
509 set f [ttk::frame $w.name]
510 ttk::label $f.label -text "Name: "
511 ttk::entry $f.entry -width 30 -textvariable emailData_name
512 set ::tools::email::helpBar(name) "Enter the opponent's name"
514 set f [ttk::frame $w.addr]
515 ttk::label $f.label -text "Email address: "
516 ttk::entry $f.entry -width 30 -textvariable emailData_addr
517 set ::tools::email::helpBar(addr) "Enter the opponent's email address"
519 set f [ttk::frame $w.subj]
520 ttk::label $f.label -text "Subject: "
521 ttk::entry $f.entry -width 30 -textvariable emailData_subj
522 set ::tools::email::helpBar(subj) "Enter the subject for each message"
524 set f [ttk::frame $w.glist]
525 ttk::label $f.label -text "Game Numbers: "
526 ttk::entry $f.entry -width 30 -textvariable emailData_glist
527 set ::tools::email::helpBar(glist) \
528 "Enter opponent's game numbers, separated by spaces"
530 foreach f {name addr subj glist} {
531 pack $w.$f -side top -fill x
532 pack $w.$f.entry $w.$f.label -side right -anchor e
534 bind $e <FocusIn> "$e configure -background lightYellow;
535 set emailData_helpBar \$::tools::email::helpBar($f)"
536 bind $e <FocusOut> "$e configure -background white"
541 set f [ttk::frame $w.sig]
542 ttk::label $f.label -text "Signature: " -anchor n
543 text $f.entry -width 30 -height 5 -background white
544 bind $f.entry <FocusIn> "$f.entry configure -background lightYellow
545 set emailData_helpBar {Enter the closing text for each message}"
546 bind $f.entry <FocusOut> "$f.entry configure -background white"
548 pack $f -side top -fill x -pady 5
549 pack $f.entry $f.label -side right -anchor n
553 set f [ttk::frame $w.buttons]
554 ttk::button $w.buttons.save -text "OK" -command {
555 set gNumberErr [::tools::email::validGameNumbers $emailData_glist]
556 if {$gNumberErr != -1} {
557 set nGames [sc_base numGames [sc_base current]]
558 tk_messageBox -icon error -type ok -title "Invalid data" \
559 -message "The games list contains an invalid game number: $gNumberErr; there are only $nGames games in this database."
561 set emailData [lreplace $emailData $emailData_index \
563 [list $emailData_name $emailData_addr $emailData_subj \
565 [.emailEditor.sig.entry get 1.0 end-1c]]]
566 ::tools::email::writeOpponentFile $emailData
568 ::tools::email::refresh
571 ttk::button $f.cancel -text $::tr(Cancel) -command {
572 set emailData [::tools::email::readOpponentFile]
574 ::tools::email::refresh
577 ttk::label $w.helpBar -width 1 -textvariable emailData_helpBar \
578 -font font_Small -anchor w
579 pack $w.helpBar -side top -fill x
580 pack $f -side top -anchor e
584 set details [
lindex $emailData $emailData_index]
585 set emailData_name [
lindex $details 0]
586 set emailData_addr [
lindex $details 1]
587 set emailData_subj [
lindex $details 2]
588 set emailData_glist [
lindex $details 3]
589 $w.sig.entry insert 1.0 [
lindex $details 4]
593 proc ::tools::email::validGameNumbers {numberList} {
595 foreach i $numberList {
596 if {$i < 1 || $i > $nGames} {
return $i}
601 proc ::tools::email::opponentFilename {} {
602 set filename [
sc_base filename $::curr_db]
603 append filename ".sem"
607 proc ::tools::email::readOpponentFile {} {
609 if {[
catch {
set f [open $filename "r"]}]} {
613 set data [read -nonewline $f]
618 proc ::tools::email::writeOpponentFile {data} {
620 if {[
catch {
set f [open $filename "w"]}]} {