# alias.tcl --
#
# Code which handles aliases.
#
#  TkRat software and its included text is Copyright 1996,1997,1998 by
#  Martin Forssén.
#
#  Postilion software and its included text and images
#  Copyright (C) 1998, 1999, 2000 Nic Bernstein
#
#  The full text of the legal notices is contained in the files called
#  COPYING and COPYRIGHT.TkRat, included with this distribution.
#
#  This program is free software; you can redistribute it and/or
#  modify it under the terms of the GNU General Public License
#  as published by the Free Software Foundation; either version 2
#  of the License, or (at your option) any later version.
# 
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.

#  You should have received a copy of the GNU General Public License
#  along with this program; if not, write to the Free Software
#  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

# List of alias windows
set aliasWindows {}

# AliasesUpdateBooklist --
#
# Update the list of known books for an alias window
#
# Arguments:
# handler - The handler of the alias window

proc AliasesUpdateBooklist {handler args} {
    global aliasWindows option aliasBook

    if {"" == $handler} {
	set hds $aliasWindows
    } else {
	set hds $handler
    }

    foreach handler $hds {
	upvar #0 $handler hd

	$hd(showmenu) delete 1 end
	$hd(movemenu) delete 1 end
	$hd(defaultmenu) delete 1 end

	foreach a $option(addrbooks) {
	    set book [lindex $a 0]
	    if ![info exists hd(show,$book)] {
		set hd(show,$book) 1
	    }
	}
	set sysbook [lindex $option(system_aliases) 0]
	if {$option(use_system_aliases)} {
	    if ![info exists hd(show,$sysbook)] {
		set hd(show,$sysbook) 1
	    }
	} else {
	    catch {unset hd(show,$sysbook)}
	}

	foreach book [array names aliasBook writable,*] {
	    regsub writable, $book {} name
	    $hd(showmenu) add checkbutton -label $name \
		    -variable ${handler}(show,$name) \
		    -command "AliasesPopulate $handler"
	    if $aliasBook(writable,$name) {
		$hd(movemenu) add command -label $name \
			-command "AliasMoveTo move $handler $name"
		$hd(defaultmenu) add radiobutton -label $name \
			-variable option(default_book) -value $name \
			-command {set bookMod 1}
	    }
	}
    }
}

# AliasesPopulate --
#
# Populate the aliases window
#
# Arguments:
# handler - The handler of the alias window

proc AliasesPopulate {{handler {}}} {
    global aliasWindows

    if {"" == $handler} {
	set hds $aliasWindows
    } else {
	set hds $handler
    }

    foreach handler $hds {
	upvar #0 $handler hd

	set old {}
	foreach sel [$hd(list) curselection] {
	    lappend old $sel
	}
	RatAlias list alias
	set hd(aliasIds) [lsort [array names alias]]
	set top [lindex [$hd(list) yview] 0]
	$hd(list) delete 0 end
	set hd(aliasIds) ""
	foreach a [lsort [array names alias]] {
	    set book [lindex $alias($a) 0]
	    if $hd(show,$book) {
		lappend hd(aliasIds) $a
		$hd(list) insert end [format "%-10s  %-10s  %-20s  %s" \
			$book $a [lindex $alias($a) 1] \
			[lindex $alias($a) 2]]
	    }
	}
	$hd(list) yview moveto $top
	foreach o $old {
	    set i [lsearch -exact $hd(aliasIds) $o]
	    if { -1 != $i } {
		$hd(list) selection set $i
		$hd(list) see $i
	    }
	}
	SetAliasesState $handler
    }
}

# SetAliasesState --
#
# Update the status of the buttons
#
# Arguments:
# handler	- The id of this window

proc SetAliasesState {handler} {
    upvar #0 $handler hd
    global aliasBook

    set onlywr 1
    foreach selected [$hd(list) curselection] {
	set a [RatAlias get [lindex $hd(aliasIds) $selected]]
	if !$aliasBook(writable,[lindex $a 0]) {
	    set onlywr 0
	    break
	}
    }
    set editState disabled
    set moveState disabled
    set deleteState disabled
    set l [llength [$hd(list) curselection]]
    if {$l == 1 && $onlywr} {
	set editState normal
	set hd(current) [lindex $hd(aliasIds) [$hd(list) curselection]]
    }

    if {$l > 0} {
	if $onlywr {
	    set moveState normal
	    set deleteState normal
	}
    }
    $hd(w).b.edit configure -state $editState
    $hd(w).b.move configure -state $moveState
    $hd(w).b.delete configure -state $deleteState
}

# AliasSave --
#
# Save the aliases (if needed)
#
# Arguments:

proc AliasSave {} {
    global option aliasMod bookMod aliasBook

    if $aliasMod {
	set books $option(addrbooks)
	if $option(use_system_aliases) {
	    lappend books $option(system_aliases)
	}
	foreach book $books {
	    if $aliasBook(changed,[lindex $book 0]) {
		RatAlias save [lindex $book 0] [lindex $book 2]
	    }
	}
	set aliasMod 0
    }

    if $bookMod {
	SaveOptions
	set bookMod 0
    }
}

# AliasClose --
#   
# Close an aliases window.
#   
# Arguments:
# handler - The handler of the alias window
    
proc AliasClose {handler} {
    global aliasWindows aliasMod b
    upvar #0 $handler hd
    
    set i [lsearch -exact $aliasWindows $handler]
    if {-1 != $i} {
	set aliasWindows [lreplace $aliasWindows $i $i]
    }
    RecordSize $hd(list) aliasList
    RecordPos $hd(w) aliases
    foreach bn [array names b $hd(w)*] {unset b($bn)}
    destroy $hd(w)
}           

# AliasDetail --
#
# Show the alias detail window
#
# Arguments:
# handler	- The handler of the alias window
# mode		- What to do on OK
# template	- Template alias to use

proc AliasDetail {handler mode {template {}}} {
    global idCnt t b aliasBook option aliasDetail
    upvar #0 $handler ahd

    # Check if we have another window active first
    if {[string length $template] && [info exists aliasDetail($template)]} {
	wm deiconify $aliasDetail($template)
	return
    }

    # Create identifier
    set id aliasDetail[incr idCnt]
    set w .$id
    upvar #0 $id hd
    set hd(mode) $mode
    set hd(handler) $handler

    # Create toplevel
    toplevel $w -class Postilion
    wm title $w $t(alias)
    set hd(w) $w

    # Create fields
    label $w.book_label -text $t(addrbook):
    if [catch {set hd(book) $ahd(book)}] {
	set hd(book) $option(default_book)
	set hd(post) 0
    } else {
	set hd(post) 1
    }
    menubutton $w.book_but -textvariable ${id}(book) -relief raised \
	-menu $w.book_but.m
    menu $w.book_but.m
    foreach book [array names aliasBook writable,*] {
	if !$aliasBook($book) {
	    continue
	}
	regsub writable, $book {} name
	$w.book_but.m add radiobutton -label $name -value $name \
	    -variable ${id}(book)
    }
    grid $w.book_label -sticky e
    grid $w.book_but -row 0 -column 1 -sticky ew -padx 2 -pady 2

    set line 1
    foreach f {alias fullname} {
	label $w.${f}_label -text $t($f):
	entry $w.${f}_entry -textvariable ${id}($f)
	set b($w.${f}_entry) alias_$f
	grid $w.${f}_label -sticky e
	grid $w.${f}_entry -row $line -column 1 -sticky ew -padx 2 -pady 2
	incr line
    }
    label $w.content_label -text $t(content):
    text $w.content_text -wrap word -setgrid true
    set b($w.content_text) alias_content
    grid $w.content_label -sticky ne
    grid $w.content_text -row $line -column 1 -sticky nsew  -padx 2 -pady 2
    Size $w.content_text aliasText
    set hd(content_text) $w.content_text
    incr line
    label $w.comment_label -text $t(comment):
    text $w.comment_text -wrap word
    set b($w.comment_text) alias_comment
    grid $w.comment_label -sticky ne
    grid $w.comment_text -row $line -column 1 -sticky nsew  -padx 2 -pady 2
    Size $w.comment_text aliasText
    set hd(comment_text) $w.comment_text

    grid columnconfigure $w 1 -weight 1
    grid rowconfigure $w 3 -weight 1

    # Prevent spaces in alias names
    bind $w.alias_entry <Key-space> {break}
    bindtags $w.content_text [list Text $w.content_text . all]
    bind $w.content_text <Key-Return> {break}
    bindtags $w.comment_text [list Text $w.comment_text . all]
    bind $w.comment_text <Key-Return> {break}

    # Buttons
    OkButtons $w $t(ok) $t(cancel) "AliasDetailDone $id"
    grid $w.buttons - -pady 5 -sticky ew

    if [string length $template] {
	set aliasDetail($template) $w
	set hd(template) $template
	set a [RatAlias get $template]
	set hd(alias) $template
	set hd(book) [lindex $a 0]
	set hd(fullname) [lindex $a 1]
	$hd(content_text) insert 1.0 [lindex $a 2]
	$hd(comment_text) insert 1.0 [lindex $a 3]
	set hd(old,book) $hd(book)
    }
    set hd(old,alias) $hd(alias)

    focus $w.alias_entry

    Place $w aliasDetail
}

# AliasDetailDone --
#
# Script called when the alias detail window may be done
#
# Arguments:
# handler -	The handler identifying the window
# action  -	The action we should take

proc AliasDetailDone {handler action} {
    global t b aliasMod aliasBook aliasDetail
    upvar #0 $handler hd

    if {1 == $action} {
	set hd(content) [string trim [$hd(content_text) get 1.0 end]]
	set hd(comment) [string trim [$hd(comment_text) get 1.0 end]]
	if { 0 == [string length $hd(alias)] || \
		0 == [string length $hd(content)]} {
	    Popup $t(need_alias_and_content)
	    return
	}
	if {"edit" == $hd(mode)} {
	    RatAlias delete $hd(old,alias)
	    set aliasBook(changed,$hd(old,book)) 1
	}
	RatAlias add $hd(book) $hd(alias) $hd(fullname) $hd(content) \
		$hd(comment)
	incr aliasMod
	set aliasBook(changed,$hd(book)) 1
	if $hd(post) {
	    AddressesPopulate $hd(handler)
	} else {
	    AliasesPopulate
	}
    }
    RecordPos $hd(w) aliasDetail
    RecordSize $hd(content_text) aliasText
    foreach bn [array names b $hd(w)*] {unset b($bn)}
    destroy $hd(w)
    if [info exists hd(template)] {
	unset aliasDetail($hd(template))
    }
    unset hd
}

# AliasDelete --
#
# Deletes aliases from the alias list --
#
# Arguments:
# handler -	The handler identifying the window

proc AliasDelete {handler} {
    upvar #0 $handler hd
    global aliasBook aliasMod

    foreach a [$hd(list) curselection] {
	set alias [lindex $hd(aliasIds) $a]
	set aliasBook(changed,[lindex [RatAlias get $alias] 0]) 1
	set aliasMod 1
	RatAlias delete $alias
    }
    AddressesPopulate $handler
}

# AddrbookImport --
#
# Import an address book in a different format
#
# Arguments:
# format -	The format of the file

proc AddrbookImport {format} {
    global idCnt t b

    # Create identifier
    set id addressBookImport[incr idCnt]
    set w .$id
    upvar #0 $id hd

    # Create toplevel
    toplevel $w -class Postilion
    wm title $w $t(import)
    set hd(w) $w
    set hd(format) $format

    label $w.label -text $t(name):
    entry $w.entry -textvariable ${id}(name) -width 20
    set b($w.entry) name_of_adrbook
    grid $w.label -sticky e
    grid $w.entry -row 0 -column 1 -sticky ew -pady 5

    frame $w.chooser
    set hd(fh) [FileSelectorCreate $w.chooser ${id}(fname) \
	    "AddrbookImportDone $id 1" 0 1]
    grid $w.chooser - -sticky nsew -pady 5 -padx 5

    grid columnconfigure $w 1 -weight 1
    grid rowconfigure $w 1 -weight 1

    # Buttons
    OkButtons $w $t(ok) $t(cancel) "AddrbookImportDone $id"
    grid $w.buttons - -pady 5 -sticky ew
    bind $w <Return> {}

    Place $w addrbookImport
    update
    focus $w.entry
}

# AddrbookImportDone
#
# Called when an address book import window is done
#
# Arguments
# handler - The handler of the import window
# action  - What to do

proc AddrbookImportDone {handler action} {
    upvar #0 $handler hd
    global option t b bookMod aliasBook

    if $action {
	if ![string length $hd(name)] {
	    Popup $t(need_name)
	    return
	}
	if [info exists aliasBook(writable,$hd(name))] {
	    Popup $t(book_already_exists)
	    return
	}
	FileSelectorCleanup $hd(fh)
	if {![file isfile $hd(fname)] || ![file readable $hd(fname)]} {
	    Popup "$t(illegal_file_spec): $hd(fname)"
	    return
	}
	set aliasBook(writable,$hd(name)) 0
	set aliasBook(changed,$hd(name)) 0
	set bookMod 1
	lappend option(addrbooks) [list $hd(name) $hd(format) $hd(fname)]
	switch $hd(format) {
	    mail {ReadMailAliases $hd(fname) $hd(name)}
	    elm  {ReadElmAliases $hd(fname) $hd(name)}
	    pine {ReadPineAliases $hd(fname) $hd(name)}
	}
	AliasesPopulate
    }

    RecordPos $hd(w) addrbookAdd
    foreach bn [array names b $hd(w)*] {unset b($bn)}
    destroy $hd(w)
    unset hd
}

# AddrbookAdd --
#
# Create a new address book
#
# Arguments:

proc AddrbookAdd {} {
    global idCnt t b

    # Create identifier
    set id addressBookAdd[incr idCnt]
    set w .$id
    upvar #0 $id hd

    # Create toplevel
    toplevel $w -class Postilion
    wm title $w $t(new)
    set hd(w) $w

    label $w.label -text $t(name):
    entry $w.entry -textvariable ${id}(name) -width 20
    set b($w.entry) name_of_adrbook
    grid $w.label -sticky e
    grid $w.entry -row 0 -column 1 -sticky ew -pady 5

    frame $w.chooser
    set hd(fh) [FileSelectorCreate $w.chooser ${id}(fname) \
	    "AddrbookAddDone $id 1" 0 0]
    grid $w.chooser - -sticky nsew -pady 5 -padx 5

    grid columnconfigure $w 1 -weight 1
    grid rowconfigure $w 1 -weight 1

    # Buttons
    OkButtons $w $t(ok) $t(cancel) "AddrbookAddDone $id"
    bind $w <Return> ""
    grid $w.buttons - -pady 5 -sticky ew

    Place $w addrbookAdd
    update
    focus $w.entry
}

# AddrbookAddDone
#
# Called when an address book add window is done
#
# Arguments
# handler - The handler of the add window
# action  - What to do

proc AddrbookAddDone {handler action} {
    upvar #0 $handler hd
    global option t b bookMod aliasBook
    
    if $action {
	if ![string length $hd(name)] {
	    Popup $t(need_name)
	    return
	}
	if [info exists aliasBook(writable,$hd(name))] {
	    Popup $t(book_already_exists)
	    return
	}
	FileSelectorCleanup $hd(fh)
	set dir [file dirname $hd(fname)]
	if {([file exists $hd(fname)] && ![file readable $hd(fname)])
	    || [file isdirectory $hd(fname)]
	    || (![file exists $hd(fname)] && ![file writable $dir])} {
	    Popup "$t(illegal_file_spec): $hd(fname)"
	    return
	}
	if {([file isfile $hd(fname)] && [file writable $hd(fname)])
	    || (![file exists $hd(fname)] && [file isdirectory $dir]
		&& [file writable $dir])} {
	    set aliasBook(writable,$hd(name)) 1
	} else {
	    set aliasBook(writable,$hd(name)) 0
	}
	set aliasBook(changed,$hd(name)) 0
	set bookMod 1
	lappend option(addrbooks) [list $hd(name) postilion $hd(fname)]
	if [file exists $hd(fname)] {
	    RatAlias read $hd(fname)
	}
    }
    RecordPos $hd(w) addrbookAdd
    foreach bn [array names b $hd(w)*] {unset b($bn)}
    destroy $hd(w)
    unset hd
}

# AddrbookDelete --
#
# Delete a address book
#
# Arguments:

proc AddrbookDelete {} {
    global idCnt t b option defaultFont

    # Create identifier
    set id addressBookDelete[incr idCnt]
    set w .$id
    upvar #0 $id hd

    # Create toplevel
    toplevel $w -class Postilion
    wm title $w $t(delete)
    set hd(w) $w

    # List of books
    frame $w.l
    scrollbar $w.l.scroll \
	    -bd 1 \
	    -highlightthickness 0 \
	    -command "$w.l.list yview"
    listbox $w.l.list \
	    -yscroll "$w.l.scroll set" \
	    -bd 1 \
	    -exportselection false \
	    -highlightthickness 0 \
	    -font $defaultFont \
	    -selectmode extended
    # Scroll mouse support added by Stephen Ryan 14 Oct 1999
    bind $w.l.list <5> [list $w.l.list yview scroll 5 units]
    bind $w.l.list <4> [list $w.l.list yview scroll -5 units]
    bind $w.l.list <Shift-5> [list $w.l.list yview scroll 1 units]
    bind $w.l.list <Shift-4> [list $w.l.list yview scroll -1 units]
    bind $w.l.list <Control-5> [list $w.l.list yview scroll 1 pages]
    bind $w.l.list <Control-4> [list $w.l.list yview scroll -1 pages]
    Size $w.l.list bookList
    set hd(booklist) $w.l.list
    pack $w.l.scroll -side right -fill y
    pack $w.l.list -expand 1 -fill both
    set b($w.l.list) list_of_books_to_delete

    # Buttons
    OkButtons $w $t(delete) $t(cancel) "AddrbookDeleteDone $id"

    # Pack it
    pack $w.l \
	 $w.buttons -side top -padx 5 -pady 5 -expand 1 -fill both

    # Populate list
    foreach book [lsort $option(addrbooks)] {
	$hd(list) insert end [lindex $book 0]
    }

    Place $w addrBookDelete
}

# AddrbookDeleteDone
#
# Called when an address book delete window is done
#
# Arguments
# handler  -	The handler of the delete window
# action   -	What to do

proc AddrbookDeleteDone {handler action} {
    upvar #0 $handler hd
    global option aliasBook bookMod t b

    if $action {
	foreach s [$hd(booklist) curselection] {
	    lappend del [$hd(booklist) get $s]
	}
	set keep {}
	set remove {}
	foreach book $option(addrbooks) {
	    if {-1 == [lsearch -exact $del [lindex $book 0]]} {
		lappend keep $book
	    } else {
		lappend remove [lindex $book 0]
	    }
	}
	if {-1 != [lsearch -exact $remove $option(default_book)]} {
	    set newDefault {}
	    foreach book [array names aliasBook writable,*] {
		if !$aliasBook($book) {
		    continue
		}
		regsub writable, $book {} name
		if {-1 == [lsearch -exact $remove $name]} {
		    set newDefault $name
		    break
		}
	    }
	    if ![string length $newDefault] {
		Popup $t(need_writable_book)
		return
	    }
	    set option(default_book) $newDefault
	}
	foreach r $remove {
	    unset aliasBook(writable,$r)
	    unset aliasBook(changed,$r)
	}
	set option(addrbooks) $keep
	RatAlias list alias
	foreach a [array names alias] {
	    if {-1 != [lsearch -exact $del [lindex $alias($a) 0]]} {
		RatAlias delete $a
	    }
	}
	AliasesPopulate
	set bookMod 1
    }

    foreach bn [array names b $hd(w)*] {unset b($bn)}
}

# AliasMoveTo --
#
# Move selected aliases to address book
#
# Arguments:
# op	  - Which operation to perform
# handler - The handler of the alias window
# dest    - Name of destination folder

proc AliasMoveTo {op handler dest} {
    upvar #0 $handler hd
    global aliasMod aliasBook

    foreach i [$hd(list) curselection] {
	set a [lindex $hd(aliasIds) $i]
	set alias [RatAlias get $a]
	RatAlias delete $a
	eval RatAlias add [lreplace $alias 0 0 $dest $a]
	set aliasBook(changed,[lindex $alias 0]) 1
	set aliasBook(changed,$dest) 1
    }
    AliasesPopulate
    incr aliasMod
}

# AliasExtract --
#
# Extracts aliases from the current message
#
# Arguments:
# handler - The handler of the folder window

proc AliasExtract {handler} {
    global idCnt t b aliasBook option
    upvar #0 $handler fh

    # Extract the addresses
    set adrlist {}
    foreach a [$fh(current) get from return_path reply_to sender cc bcc to] {
	if [$a isMe] {
	    continue
	}
	set good 1
	foreach a2 $adrlist {
	    if ![$a compare $a2] {
		set good 0
		break
	    }
	}
	if $good {
	    lappend adrlist $a
	}
    }

    # Check that we found something
    if ![llength $adrlist] {
	Popup $t(could_not_find_adr)
	return
    }

    RatAlias list alias
    foreach a [array names alias] {
	foreach adr [split [lindex $alias($a) 2]] {
	    set present($adr) 1
	}
    }

    # Create identifier
    set id extractAddresses[incr idCnt]
    set w .$id
    upvar #0 $id hd

    # Create toplevel
    toplevel $w -class Postilion
    wm title $w $t(extract_adr)
    wm transient $w .
    set hd(w) $w

    # Create address book menu
    frame $w.book
    label $w.book.label -text $t(addrbook):
    set hd(book) $option(default_book)
    menubutton $w.book.menu -textvariable ${id}(book) -relief raised \
	    -menu $w.book.menu.m -width 20 -indicatoron 1
    set b($w.book.menu) aliases_adr_book
    menu $w.book.menu.m
    foreach book [array names aliasBook writable,*] {
	if !$aliasBook($book) {
	    continue
	}
	regsub writable, $book {} name
	$w.book.menu.m add radiobutton -label $name -value $name \
		-variable ${id}(book)
    }
    pack $w.book.label \
         $w.book.menu -side left
    pack $w.book -side top -pady 5

    # Create frame with aliases to add
    frame $w.f
    label $w.f.use -text $t(use)
    label $w.f.name -text $t(alias)
    label $w.f.fname -text $t(fullname)
    label $w.f.content -text $t(content)
    label $w.f.comment -text $t(comment)
    grid $w.f.use $w.f.name $w.f.fname $w.f.content $w.f.comment -sticky w
    set totlist ""
    foreach a $adrlist {
	if [string length $totlist] {
	    set totlist "$totlist,\n[$a get mail]"
	} else {
	    set totlist [$a get mail]
	}
	if [info exists present([$a get mail])] {
	    continue
	}
	incr idCnt
	set hd($idCnt,use) 1
	set name [string tolower [lindex [$a get name] 0]]
	if {![string length $name] || [info exists alias($name)]} {
	    set name2 [string tolower [lindex [split [$a get mail] @.] 0]]
	    if ![string length $name] {
		set name $name2
	    }
	    if [info exist alias($name2)] {
		for {set i 2} {[info exists alias($name2)]} {incr i} {
		    set name2 $name$i
		}
	    }
	    set name $name2
	}
	set alias($name) ""
	set hd($idCnt,name) $name
	set hd($idCnt,fname) [$a get name]
	set hd($idCnt,content) [$a get mail]
	checkbutton $w.f.c$idCnt -variable ${id}($idCnt,use)
	entry $w.f.en$idCnt -textvariable ${id}($idCnt,name) -width 8
	entry $w.f.ef$idCnt -textvariable ${id}($idCnt,fname) -width 20
	entry $w.f.ec$idCnt -textvariable ${id}($idCnt,content) -width 35
	entry $w.f.ek$idCnt -textvariable ${id}($idCnt,comment) -width 30
	set b($w.f.c$idCnt) aliasadd_use
	set b($w.f.en$idCnt) alias_alias
	set b($w.f.ef$idCnt) alias_fullname
	set b($w.f.ec$idCnt) alias_content
	set b($w.f.ek$idCnt) alias_comment
	grid $w.f.c$idCnt \
	     $w.f.en$idCnt \
	     $w.f.ef$idCnt \
	     $w.f.ec$idCnt \
	     $w.f.ek$idCnt -sticky we
    }
    set height [llength $adrlist]
    if {$height > 1} {
	incr idCnt
	set hd($idCnt,use) 0
	set hd($idCnt,content) $totlist
	checkbutton $w.f.c$idCnt -variable ${id}($idCnt,use)
	entry $w.f.en$idCnt -textvariable ${id}($idCnt,name) -width 8
	entry $w.f.ef$idCnt -textvariable ${id}($idCnt,fname) -width 20
	if {$height > 10} {
	    frame $w.f.ec$idCnt
	    scrollbar $w.f.ec$idCnt.scroll -relief sunken \
		    -command "$w.f.ec$idCnt.text yview" -highlightthickness 0
	    text $w.f.ec$idCnt.text -width 35 -height 10 -wrap none \
		    -yscroll "$w.f.ec$idCnt.scroll set"
            # Scroll mouse support added by Stephen Ryan 14 Oct 1999
            bind $w.f.ec$idCnt.text <5> \
		    [list $w.f.ec$idCnt.text yview scroll 5 units]
            bind $w.f.ec$idCnt.text <4> \
		    [list $w.f.ec$idCnt.text yview scroll -5 units]
            bind $w.f.ec$idCnt.text <Shift-5> \
		    [list $w.f.ec$idCnt.text yview scroll 1 units]
	    bind $w.f.ec$idCnt.text <Shift-4> \
		    [list $w.f.ec$idCnt.text yview scroll -1 units]
	    bind $w.f.ec$idCnt.text <Control-5> \
		    [list $w.f.ec$idCnt.text yview scroll 1 pages]
	    bind $w.f.ec$idCnt.text <Control-4> \
		    [list $w.f.ec$idCnt.text yview scroll -1 pages]
	    pack $w.f.ec$idCnt.scroll -side right -fill y
	    pack $w.f.ec$idCnt.text -expand yes -fill both
	    set hd(listcmd) $w.f.ec$idCnt.text
	} else {
	    text $w.f.ec$idCnt -width 35 -height $height -wrap none
	    set hd(listcmd) $w.f.ec$idCnt
	}
	$hd(listcmd) insert 1.0 $totlist
	set hd(listvar) $idCnt,content
	entry $w.f.ek$idCnt -textvariable ${id}($idCnt,comment) -width 30
	set b($w.f.c$idCnt) aliasadd_use
	set b($w.f.en$idCnt) alias_alias
	set b($w.f.ef$idCnt) alias_fullname
	set b($w.f.ec$idCnt) alias_content
	set b($w.f.ek$idCnt) alias_comment
	grid $w.f.c$idCnt \
	     $w.f.en$idCnt \
	     $w.f.ef$idCnt \
	     $w.f.ec$idCnt \
	     $w.f.ek$idCnt -sticky wen
    }
    grid columnconfigure $w.f 1 -weight 1
    grid columnconfigure $w.f 2 -weight 1
    grid columnconfigure $w.f 3 -weight 1
    grid columnconfigure $w.f 4 -weight 1
    pack $w.f -side top -fill both

    # Create buttons
    OkButtons $w $t(add_aliases) $t(cancel) "AliasExtractDone $id"
    pack $w.buttons -side bottom -pady 5 -fill x

    Place $w extractAlias
}

# AliasExtractDone --
#
# The alias extract window is now done.
#
# Arguments:
# handler - The handler of the extract window
# action  - Which action we should take

proc AliasExtractDone {handler action} {
    upvar #0 $handler hd
    global aliasMod t b aliasBook

    # Find which entries we should use
    set ids {}
    foreach i [array names hd *,use] {
	if $hd($i) {
	    lappend ids [lindex [split $i ,] 0]
	}
    }

    if { 1 == $action} {
	if [info exists hd(listcmd)] {
	    set hd($hd(listvar)) [$hd(listcmd) get 1.0 end]
	}
	# Add the aliases
	foreach id $ids {
	    if ![string length $hd($id,name)] {
		Popup $t(missing_alias_name)
		continue
	    }
	    RatAlias add $hd(book) $hd($id,name) $hd($id,fname) \
		    $hd($id,content) $hd($id,comment)
	    set aliasBook(changed,$hd(book)) 1
	    incr aliasMod
	}
	if { 1 == $aliasMod } {
	    AliasesPopulate
	}
	AliasSave
    }

    RecordPos $hd(w) extractAlias
    foreach bn [array names b $hd(w)*] {unset b($bn)}
    destroy $hd(w)
    unset hd
}

# AddressSelect --
#
# Capture selection and insert into proper header field
#
# Arguments:
# handler        - The handler which controls this text widget
# id        - The window handle for the selection widget
# header    - Which header field this is for

proc AddressSelect {handler id header} {
    global option
    upvar #0 $handler mh
    upvar #0 $id hd

    set selection [lindex $hd(aliasIds) [$hd(list) curselection]]
    set w2 $mh(headerFrame).${header}_entry.t

    # if the alias is real...
    if [string length $selection] {
	# add a comma if necessary
	if [string length [string trim [$w2 get 1.0 end]]] {
	    $w2 insert end ,
	}
	# add the new address
	$w2 insert end $selection
	update idletasks
	ComposeHandleHE $w2 $mh(${header}_handler)
    }
}


# AliasChooserMoveSel --
#
# Move the selection in the chooser.
#
# Arguments:
# handler   - The handler which defines this selection window
# direction - Which direction we should move the selection.

proc AliasChooserMoveSel {handler direction} {
    upvar #0 $handler hd

    set cur [$hd(list) curselection]
    $hd(list) selection clear $cur

    if [string compare up $direction] {
	if {[incr cur] > [$hd(list) size]} {
	    incr cur -1
	}
	if {[expr $cur/[$hd(list) size].0] >= [lindex [$hd(list) yview] 1]} {
	    $hd(list) yview $cur
	}
    } else {
	if {$cur > 0} {
	    incr cur -1
	    if {[expr $cur/[$hd(list) size].0] < [lindex [$hd(list) yview] 0]} {
		$hd(list) yview scroll -1 pages
	    }
	}
    }
    $hd(list) selection set $cur
    set hd(search) ""
}

# AliasChooserSearch --
#
# Searches the chooser list.
#
# Arguments:
# handler   - The handler which defines this selection window
# key	    - The pressed key

proc AliasChooserSearch {handler key} {
    upvar #0 $handler hd

    if {1 != [scan $key "%s" key2]} {
	return
    }
    set hd(search) "$hd(search)$key2"
    set i [lsearch -glob $hd(aliasIds) "$hd(search)*"]
    if {-1 == $i} {
	bell
	set hd(search) ""
    } else {
	$hd(list) selection clear [$hd(list) curselection]
	$hd(list) selection set $i
	$hd(list) see $i
    }
}

# ElmGets --
#
# Fix elm alias file reading to handle multiple line aliases
#
# Arguments:
# fh      - File handle
# linevar - variable to store line in

proc ElmGets {fh linevar} {
    upvar $linevar line
    set haveline 0
    set line ""
    while {$haveline <= 0 && -1 != [gets $fh sline]} {
        set sline [string trim $sline]
        if {[string match {#*} $sline] || 0==[string length $sline]} {
            continue
        }
        set line "${line}${sline} "
        if {![string match {?*=?*=?* } $line] || [string match {*, } $line]} {
            set haveline 0
        } else {
            set haveline 1
        }
    }
    if {$haveline <= 0} {
       return $haveline
    } else {
       return [string length $line]
    }
}


# ReadElmAliases --
#
# Read aliases.text files generated by elm
#
# Arguments:
# file -	Filename to read aliases from
# book -	Address book to insert them into

proc ReadElmAliases {file book} {
    set n 0
    set fh [open $file r]
    while { 0 < [ElmGets $fh line]} {
	if {[string match {*=*=*} $line] && [string length [lindex $line 0]]} {
	    set a [split $line =]
	    RatAlias add $book \
			 [string trim [lindex $a 0]] \
			 [string trim [lindex $a 1]] \
			 [string trim [lindex $a 2]] {}
	    incr n
	}
    }
    close $fh
    return $n
}


# ReadMailAliases --
#
# Get aliases out of mailrc files generated by mail and others
#
# Arguments:
# file -	FIlename to read aliases from
# book -	Address book to insert them into

proc ReadMailAliases {file book} {
    set n 0
    set fh [open $file r]
    while { -1 != [gets $fh line]} {
	if [string match {alias *} $line] {
 	    if [regexp "^alias\[ \t\]+(\[a-zA-Z0-9_-\]+)\[ \t\]+(.+)$" $line \
 		    {} name content] {
 		RatAlias add $book $name $name $content {}
		incr n
	    }
	}
    }
    close $fh
    return $n
}

# ReadPineAliases --
#
# Read the .addressbook files generated by pine
#
# Arguments:
# file -        Filename to read aliases from
# book -	Address book to insert them into
 
proc ReadPineAliases {file book} {
    set fh [open $file r]
    set aliases {}
    while { -1 != [gets $fh line]} {
        if [regsub {^ } $line "" cont] {
            set aliases [lreplace $aliases end end \
		    "[lindex $aliases end] $cont"]
        } else {
            lappend aliases $line
        }
    }
    close $fh

    set n 0
    foreach a $aliases {
        if [regexp {^#DELETED} $a] {
            continue
        }
        set sa [split $a "\t"]
	if [string length [lindex $sa 0]] {
	    incr n
	    set content [lindex $sa 2]
	    regexp {^\((.+)\)$} $content notUsed content
	    RatAlias add $book [lindex $sa 0] [lindex $sa 1] $content {}
	}
    }
    return $n
}
