# vfolderdef.tcl -
#
# Create and edit vfolders
#
# Each virtual folder is described by one entry in the vFolderDef array.
#
#
#  TkRat software and its included text is Copyright 1996, 1997, 1998
#  by Martin Forssen.
#
#  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.

set vFolderChanged 0

# RedrawVFolderList --
#
# Redraws the list of vfolders. This is done by first clearing the listbox
# and then calling VFolderBuildList which does the actual drawing.
#
# Arguments:
proc RedrawVFolderList {} {
    global folderWindowList mailbox

    set wins {}
    foreach win $folderWindowList {
	upvar #0 $win fh
	lappend wins $fh(mailboxview).fLBs
    }
    if [winfo exists .mailbox] {
	lappend wins .mailbox.fLBs
    }

    foreach win $wins {
	foreach handler [winfo children $win] {
	    upvar #0 $handler.lb current
	    # If this listbox was previously filled, update it
	    if ![catch {info exists $current(sIdent)}] {
		# save the active selection
		set active [$handler.lb get active]
		# clear out the listbox
	        catch {$handler.lb delete 0 end}
		# repopulate the listbox
		VFolderBuildList $handler.lb $current(sIdent)
		# restore the view
		catch {$handler.lb see active}
	    }
	}
    }
}

# VFolderPostMenu:
# Post a the menu for a given entry. The arguments are: the root window
# coordinates, the listbox, the ident of the vFolderStruct and the index
# of the active member.
#
# Post the menu for the given entry.
#
# Arguments:
# c	 -	The canvas widget command name
# x, y   -	Position of pointer, this is where we will popup the menu
# listW  -	Name of the list widget the entry belongs to
# sIdent -	Identifier of the vFolderStruct the entry belongs to
# index  -	Index in the above mentioned vFolderStruct
proc VFolderPostMenu {c x y listW sIdent index} {
    global vFolderCurrent vFolderStruct vFolderInbox vFolderSave t

    if [winfo exists $c.l] {
	return
    }

    set vFolderCurrent [list $sIdent $index]
    set elem [lindex $vFolderStruct($sIdent) $index]
    if ![llength $elem] {
	.vfolderdef.m entryconfigure 0 -state disabled
	.vfolderdef.m entryconfigure 1 -state disabled
	.vfolderdef.m entryconfigure 2 -state disabled
    } else {
	.vfolderdef.m entryconfigure 0 -state normal
	if [string compare $vFolderInbox [lindex $elem 1]] {
	    .vfolderdef.m entryconfigure 1 -state normal
	    .vfolderdef.m entryconfigure 2 -state normal
	} else {
	    .vfolderdef.m entryconfigure 1 -state disabled
	    .vfolderdef.m entryconfigure 2 -state disabled
	}
	if ![string compare vfolder [lindex $elem 0]] {
	    if [string compare $vFolderSave [lindex $elem 1]] {
		.vfolderdef.m entryconfigure 3 \
			-label $t(set_to_save_outgoing) -state normal
	    } else {
		.vfolderdef.m entryconfigure 3 \
			-label $t(unset_to_save_outgoing) -state normal
	    }
	} else {
	    .vfolderdef.m entryconfigure 3 \
		    -label $t(set_to_save_outgoing) -state disabled
	}
    }
    bind .vfolderdef.m <Unmap> "$listW selection clear $index"
    tk_popup .vfolderdef.m $x $y
}

# VFolderIsDescendant --
#
# Checks if one vfolder structure is descendant of another.
#
# Arguments:
# brat -	Structure to check for
# candidate -	Possible parent
proc VFolderIsDescendant {brat candidate} {
    global vFolderStruct

    foreach c $vFolderStruct($candidate) {
	if ![string compare struct [lindex $c 0]] {
	    if {$brat == [lindex $c 1]
		    || 1 == [VFolderIsDescendant $brat [lindex $c 1]]} {
		return 1
	    }
	}
    }
    return 0
}

# VFolderNew --
# This procedure is invoked when the user wants to create a new vfolder.
# The struct it should be inserted into is specified via the vFolderCurrent
# global variable.
#
# Arguments:
# type -	The type of the new vfolder
proc VFolderNew {type} {
    global vFolderCurrent vFolderDefIdent vFolderStruct vFolderChanged \
	   vFolderDef option env

    switch $type {
    file      {set vFolderDef($vFolderDefIdent) {{} file {} {}}}
    mh        {set vFolderDef($vFolderDefIdent) {{} mh {} {}}}
    dbase     {set vFolderDef($vFolderDefIdent) \
	        [list {} dbase {} {} $option(def_extype) $option(def_exdate)]}
    imap      {set vFolderDef($vFolderDefIdent) \
	        "{} imap {browse 1} \\\\{:$option(imap_port)/imap\\\\} $env(USER)"}
    pop3      {set vFolderDef($vFolderDefIdent) {{} pop3 {} \\{/pop3\\} {}}}
    dynamic   {set vFolderDef($vFolderDefIdent) {{} dynamic {} {} sender}}
    }
    set vFolderName [EditVFolder $vFolderDefIdent]
    if ![string length $vFolderName] {
	unset vFolderDef($vFolderDefIdent)
	return
    }
    set sIdent [lindex $vFolderCurrent 0]
    set index [lindex $vFolderCurrent 1]
    set vFolderStruct($sIdent) [linsert $vFolderStruct($sIdent) $index \
	    [list vfolder $vFolderDefIdent $vFolderName]]
    incr vFolderDefIdent
    RedrawVFolderList
}

# VFolderCreateMenu --
# Asks the user for a name and then creates a new empty submenu with
# that name. The new submenu is inserted into the structure at the place
# indicated by the vFolderCurrent global variable.
#
# Arguments:
proc VFolderCreateMenu {} {
    global vFolderCurrent t b idCnt

    # Create identifier
    set id createSubmenu[incr idCnt]
    set w .$id
    upvar #0 $id hd
    set hd(done) 0
    set oldFocus [focus]

    # Create toplevel
    toplevel $w -class Postilion
    wm title $w $t(create_submenu)

    # Folder name
    frame $w.name
    label $w.name.label -text $t(name):
    set b($w.name) vd_menuname
    entry $w.name.entry -textvariable ${id}(name) -width 20
    pack $w.name.entry \
	 $w.name.label -side right

    OkButtons $w $t(ok) $t(cancel) "set ${id}(done)"
    pack $w.name \
	 $w.buttons -side top -fill both -padx 5 -pady 5

    Place $w vFolderCreateMenu
    focus $w.name.entry
    grab $w
    tkwait variable ${id}(done)
    RecordPos $w vFolderCreateMenu
    unset b($w.name)
    destroy $w
    focus $oldFocus

    if {1 == $hd(done)} {
	global vFolderStruct vFolderStructIdent vFolderChanged
	incr vFolderChanged
	incr vFolderStructIdent
	set sIdent [lindex $vFolderCurrent 0]
	set index [lindex $vFolderCurrent 1]
	set vFolderStruct($sIdent) [linsert $vFolderStruct($sIdent) $index \
		[list struct $vFolderStructIdent $hd(name)]]
	set vFolderStruct($vFolderStructIdent) {}
	RedrawVFolderList
    }
    unset hd
}

# EditVFolderEntry --
#
# Edit an entry in the vfolder menu
#
# Arguments:
proc EditVFolderEntry {} {
    global t b idCnt vFolderCurrent vFolderStruct vFolderChanged vFolderDef

    set sIdent [lindex $vFolderCurrent 0]
    set index [lindex $vFolderCurrent 1]
    set elem [lindex $vFolderStruct($sIdent) $index]
    switch [lindex $elem 0] {
    struct  {
	    set ident [lindex $elem 1]
	    set id editMailbox[incr idCnt]
	    set w .$id
	    upvar #0 $id hd
	    set hd(done) 0
	    set oldFocus [focus]

	    # Create toplevel
	    toplevel $w -class Postilion
	    wm title $w $t(name)

	    # Folder name
	    set hd(name) [lindex $elem 2]
	    frame $w.name
	    label $w.name.label -text $t(name): -width 15 -anchor e
	    entry $w.name.entry -textvariable ${id}(name) -width 20
	    pack $w.name.label $w.name.entry -side left
	    pack $w.name -side top -expand 1 -padx 5 -pady 5
	    set b($w.name.entry) vd_menuname
	    OkButtons $w $t(ok) $t(cancel) "set ${id}(done)"
	    pack $w.buttons -fill both -padx 5 -pady 5

	    Place $w editVfolderEntry
	    focus $w.name.entry
	    grab $w
	    while 1 {
		tkwait variable ${id}(done)
		if { 1 == $hd(done) } {
		    if ![string length $hd(name)] {
			Popup $t(need_name)
			continue
		    }
		    incr vFolderChanged
		    set vFolderStruct($sIdent) \
			    [lreplace $vFolderStruct($sIdent) $index $index \
			     [list struct $ident $hd(name)]]
		}
		break
	    }
	    RecordPos $w editVfolderEntry
	    destroy $w
	    focus $oldFocus
	    unset b($w.name.entry)
	    unset hd
	}
    vfolder {
	    EditVFolder [lindex $elem 1]
	    set vFolderStruct($sIdent) \
		    [lreplace $vFolderStruct($sIdent) $index $index \
		      	  [list vfolder \
				[lindex $elem 1] \
				[lindex $vFolderDef([lindex $elem 1]) 0]]]
	}
    }
    RedrawVFolderList
}

# VFolderDelete --
#
# This procedure asks the user if it is really sure it wants to delete
# the specified item. The item is specified via the vFolderCurrent
# global variable.
#
# Arguments:
proc VFolderDelete {} {
    global t vFolderCurrent vFolderDef vFolderStruct vFolderChanged \
	   vFolderInbox vFolderSave

    set sIdent [lindex $vFolderCurrent 0]
    set index [lindex $vFolderCurrent 1]

    set elem [lindex $vFolderStruct($sIdent) $index]
    if ![string compare [lindex $elem 0] struct] {
	if [llength $vFolderStruct([lindex $elem 1])] {
	    if [RatDialog ! $t(submenu_not_empty) {} 0 $t(delete) \
		    $t(cancel)] {
		return
	    }
	    VFolderDeleteStruct -1 [lindex $elem 1]
	}
	unset vFolderStruct([lindex $elem 1])
    } else {
	set vd $vFolderDef([lindex $elem 1])
	if ![string compare imap [lindex $vd 1]] {
	    set ret [RatDialog ! $t(delete_imap) {} 1 $t(delete) \
		    $t(dont_delete) $t(cancel)]
	    if {2 == $ret} {
		return
	    } 
	    if {0 == $ret} {
		catch {RatImapDeleteFolder [lindex [lindex $vd 3] 0] \
			[lindex $vd 4]}
	    }
	}
	unset vFolderDef([lindex $elem 1])
    }
    set vFolderStruct($sIdent) [lreplace $vFolderStruct($sIdent) $index $index]
    if ![info exists vFolderDef($vFolderInbox)] {
	set vFolderInbox [lindex [lsort -integer [array names vFolderDef]] 0]
    }
    if ![info exists vFolderDef($vFolderInbox)] {
	set vFolderSave {}
    }
    incr vFolderChanged
    RedrawVFolderList
}

# VFolderSetToSpecial --
#
# This procedure marks the selected folder as a special mailbox.
#
# Arguments:
# what - What kind of of special feature this mailbox has
proc VFolderSetToSpecial {what} {
    global t vFolderCurrent vFolderStruct vFolderChanged
    upvar #0 vFolder$what special

    set sIdent [lindex $vFolderCurrent 0]
    set index [lindex $vFolderCurrent 1]
    set elem [lindex $vFolderStruct($sIdent) $index]
    set mbox [lindex $elem 1]
    if {![string compare Save $what] &&
	    ![string compare $special $mbox]} {
	set special {}
    } elseif ![string compare Startup $what] {
	if ![lsearch -exact $special $mbox] {
	    set special [lreplace special [lsearch -exact $special $mbox] \
			     [lsearch -exact $special $mbox]]
	} else {
	    lappend special $mbox
	}
    } elseif ![string compare Startmin $what] {
	if ![lsearch -exact $special $mbox] {
	    set special [lreplace $special [lsearch -exact $special $mbox] \
			     [lsearch -exact $special $mbox]]
	} else {
	    lappend special $mbox
	}
    } else {
	set special $mbox
    }
    incr vFolderChanged
    RedrawVFolderList
}

# VFolderDeleteStruct --
#
# Remove a struct and all contained folders (as well as all children
#
# Arguments:
# op	 - Operation to perform on IMAP-folders (-1=dont_know 0=delete 1=keep)
# sIdent - id of struct
proc VFolderDeleteStruct {op sIdent} {
    global vFolderStruct vFolderDef t

    foreach elem $vFolderStruct($sIdent) {
	if ![string compare [lindex $elem 0] struct] {
	    VFolderDeleteStruct $op [lindex $elem 1]
	    unset vFolderStruct([lindex $elem 1])
	} else {
	    set vd $vFolderDef([lindex $elem 1])
	    if {![string compare imap [lindex $vd 1]] && 1 != $op} {
		if { -1 == $op} {
		    set op [RatDialog ! $t(delete_imap) {} 1 \
			    $t(delete) \
		    	    $t(dont_delete)]
		}
		if {0 == $op} {
		    catch {RatImapDeleteFolder [lindex [lindex $vd 3] 0] \
			    [lindex $vd 4]}
		}
	    }
	    unset vFolderDef([lindex $elem 1])
	}
    }
}

# EditVFolder --
#
# Lets the user edit a vfolder.
#
# Arguments:
# ident -	The identity in the vFolderDef array of the vfolder
proc EditVFolder {ident} {
    global t b idCnt vFolderChanged vFolderDef option vFolderSave env
    upvar #0 vFolderDef($ident) def

    # Create identifier
    set id editMailbox[incr idCnt]
    set w .$id
    upvar #0 $id hd
    set hd(done) 0
    set oldFocus [focus]

    # Create toplevel
    toplevel $w -class Postilion
    wm title $w "$t(vfolderedit)  ([lindex $def 1])"

    # Folder name
    set hd(name) [lindex $def 0]
    frame $w.name
    label $w.name.label -text $t(folder_name): -width 15 -anchor e
    entry $w.name.entry -textvariable ${id}(name) -width 20
    pack $w.name.label $w.name.entry -side left
    pack $w.name -side top -expand 1 -padx 5 -pady 5
    bind $w <Return> {}
    set b($w.name.entry) vd_name

    # Features
    array set hd [lindex $def 2]
    set feats {}

    if ![string compare $option(browse) folder] {
	# Set browse mode
	if ![info exists hd(browse)] {
	    set hd(browse) 0
	}
	lappend feats browse
	checkbutton $w.browse -text $t(default_to_browse) \
	    -variable ${id}(browse) -anchor w
	pack $w.browse -side top -fill x -padx 10
    }

    if ![string compare $option(sort_mode) folder] {
	# Set folder sort order
	frame $w.sort -relief flat
	label $w.sort.label -text $t(sort_order)
	if ![info exists hd(folder_sort)] {
	    set hd(folder_sort) $option(folder_sort)
	}
	lappend feats folder_sort
	set fs_choices [list [list subject $t(subject_by_date)] \
			    [list subjectonly $t(subjectonly)] \
			    [list sender $t(bysender)] \
			    [list folder $t(natural_order)] \
			    [list reverseFolder $t(reverse_natural_order)] \
			    [list date $t(by_date)] \
			    [list reverseDate $t(reverse_date)]]
	OptionMenu $w.sort.but ${id}(folder_sort) ${id}(fs_text) $fs_choices
	pack $w.sort.label -side left -padx 5 -expand no
	pack $w.sort.but -side right -padx 5 -expand no
	pack $w.sort -side top -padx 10 -fill x
    }

    if ![string compare $option(strsel_mode) folder] {
	# Set Start selection mode
	frame $w.strsel -relief flat
	label $w.strsel.label -text $t(start_selection)
	if ![info exists hd(start_selection)] {
	    set hd(start_selection) $option(start_selection)
	}
	lappend feats start_selection
	set ss_choices [list [list first $t(first_message)] \
			    [list last $t(last_message)] \
			    [list first_new $t(first_new_message)] \
			    [list before_new $t(before_first_new_message)]]
	OptionMenu $w.strsel.but ${id}(start_selection) ${id}(ss_text) $ss_choices
	pack $w.strsel.label -side left -padx 5 -expand no
	pack $w.strsel.but -side right -padx 5 -expand no
	pack $w.strsel -side top -padx 10 -fill x
    }

    if ![string compare $option(list_mode) folder] {
	# Set List format
	frame $w.lformat -relief flat
	label $w.lformat.label -text $t(list_format)
	if ![info exists hd(list_format)] {
	    set hd(list_format) $option(list_format)
	}
	lappend feats list_format
	entry $w.lformat.entry -textvariable ${id}(list_format)
	pack $w.lformat.label -side left -padx 5 -expand no
	pack $w.lformat.entry -side right -padx 5 -expand yes -fill x
	pack $w.lformat -side top -padx 10 -fill x
    }

    if ![string compare $option(from_mode) folder] {
	# Set From address
	frame $w.from -relief flat
	label $w.from.label -text $t(from)
	if ![info exists hd(from)] {
	    set hd(from) $option(from)
	}
#puts [list new $hd(from)]
	lappend feats from
	entry $w.from.entry -textvariable ${id}(from)
	pack $w.from.label -side left -padx 5 -expand no
	pack $w.from.entry -side right -padx 5 -expand yes -fill x
	pack $w.from -side top -padx 10 -fill x
    }

    if ![string compare $option(reply_mode) folder] {
	# Set Reply_To address
	frame $w.reply_to -relief flat
	label $w.reply_to.label -text $t(reply_to)
	if ![info exists hd(reply_to)] {
	    set hd(reply_to) $option(reply_to)
	}
	lappend feats reply_to
	entry $w.reply_to.entry -textvariable ${id}(reply_to)
	pack $w.reply_to.label -side left -padx 5 -expand no
	pack $w.reply_to.entry -side right -padx 5 -expand yes -fill x
	pack $w.reply_to -side top -padx 10 -fill x
    }

    if ![string compare $option(sig_mode) folder] {
	# Set Signature file
	frame $w.signature -relief flat
	label $w.signature.label -text $t(signature)
	if ![info exists hd(signature)] {
	    set hd(signature) $option(signature)
	}
	lappend feats signature
	entry $w.signature.entry -textvariable ${id}(signature)
	pack $w.signature.label -side left -padx 5 -expand no
	pack $w.signature.entry -side right -padx 5 -expand yes -fill x
	pack $w.signature -side top -padx 10 -fill x
    }

    # Set save_to folder
    frame $w.save_to -relief flat
    label $w.save_to.label -text $t(save_to)
    if ![info exists hd(save_to)] {
	set hd(save_to) $vFolderSave
    }
    if {[string length $hd(save_to)] != 0} {
	set hd(save_to_label) [lindex $vFolderDef($hd(save_to)) 0]
    }
    lappend feats save_to
    menubutton $w.save_to.menu -relief raised \
	-textvariable ${id}(save_to_label) -indicatoron 1 \
	-menu $w.save_to.menu.m
    menu $w.save_to.menu.m -tearoff 0 -postcommand "PostSaveTo $id $w.save_to.menu.m 0"
    pack $w.save_to.label -side left -padx 5 -expand no
    pack $w.save_to.menu -side right -padx 5 -expand no
    pack $w.save_to -side top -padx 10 -fill x
    
    # Insert type specific widgets
    switch [lindex $def 1] {
	file {
	    set hd(fname) [lindex $def 3]
	    frame $w.chooser -relief sunken -bd 1
	    set fh [FileSelectorCreate $w.chooser ${id}(fname) \
			"set ${id}(done) 1" 0 0]
	    pack $w.chooser -side top -fill both -expand 1 -padx 5 -pady 5
	}
	mh {
	    set hd(mh_name) [string range [lindex $def 3] 4 end]
	    frame $w.mhn
	    label $w.mhn.label -text $t(mh_name):
	    entry $w.mhn.entry -textvariable ${id}(mh_name) -width 20
	    frame $w.chooser -relief sunken -bd 1
	    pack $w.mhn.label \
		$w.mhn.entry -side left -pady 5
	    pack $w.mhn -side top
	    set b($w.mhn.entry) vd_mhname
	}
	dbase {
	    set hd(keywords) [lindex $def 3]
	    set hd(extype) [lindex $def 4]
	    set hd(exdate) [lindex $def 5]
	    frame $w.keywords
	    label $w.keywords.label -text $t(keywords):
	    entry $w.keywords.entry -textvariable ${id}(keywords) -width 20
	    pack $w.keywords.entry \
		 $w.keywords.label -side right
	    set b($w.keywords.entry) keywords
	    frame $w.extype
	    label $w.extype.label -text $t(extype):
	    frame $w.extype.b
	    radiobutton $w.extype.b.none -text $t(none) \
		    -variable ${id}(extype) -value none
	    set b($w.extype.b.none) exp_none
	    radiobutton $w.extype.b.remove -text $t(remove) \
		    -variable ${id}(extype) -value remove
	    set b($w.extype.b.none) exp_remove
	    radiobutton $w.extype.b.incoming -text $t(add_incoming) \
		    -variable ${id}(extype) -value incoming
	    set b($w.extype.b.none) exp_incoming
	    radiobutton $w.extype.b.backup -text $t(backup) \
		-variable ${id}(extype) -value backup
	    radiobutton $w.extype.b.custom -variable ${id}(extype) \
		-value custom -state disabled
	    set b($w.extype.b.none) exp_backup
	    pack $w.extype.b.none \
		$w.extype.b.remove \
		$w.extype.b.incoming \
		$w.extype.b.backup \
		$w.extype.b.custom -side top -anchor w
	    pack $w.extype.b \
		$w.extype.label -side right -anchor nw
	    frame $w.exdate
	    label $w.exdate.label -text $t(exdate):
	    entry $w.exdate.entry -textvariable ${id}(exdate) -width 20
	    set b($w.exdate.entry) exp_date
	    pack $w.exdate.entry \
		$w.exdate.label -side right
	    pack $w.keywords \
		$w.extype \
		$w.exdate -side top -fill both
	}
	imap {
	    set hd(user) [lindex $def 4]
	    if ![string compare $hd(user) UsEr] {
		set hd(user) $env(USER)
	    }
	    set h [lindex [lindex $def 3] 0]
	    regsub {(/|\}).+} [string trim $h \{] {} host
	    set hlist [split $host :]
	    set hd(host) [lindex $hlist 0]
	    if {2 == [llength $hlist]} {
		set hd(port) [lindex $hlist 1]
	    }
	    regsub {.+\}} $h {} hd(mbox)
	    set oldHost $hd(host)
	    set oldMbox $hd(mbox)
	    foreach e {host user mbox port} {
		frame $w.$e
		label $w.$e.label -text $t($e): -width 17 -anchor e
		entry $w.$e.entry -textvariable ${id}($e) -width 30
		pack $w.$e.label $w.$e.entry -side left
		pack $w.$e -side top -fill both
		set b($w.$e.entry) vd_$e
	    }
	}
	pop3 {
	    set hd(user) [lindex $def 4]
	    set h [lindex [lindex $def 3] 0]
	    regsub {(/|\}).+} [string trim $h \{] {} host
	    set hlist [split $host :]
	    set hd(host) [lindex $hlist 0]
	    if {2 == [llength $hlist]} {
		set hd(port) [lindex $hlist 1]
	    }
	    foreach e {host user port} {
		frame $w.$e
		label $w.$e.label -text $t($e): -width 17 -anchor e
		entry $w.$e.entry -textvariable ${id}($e) -width 30
		pack $w.$e.label $w.$e.entry -side left
		pack $w.$e -side top -fill both
		set b($w.$e.entry) vd_$e
	    }
	}
	dynamic {
	    set hd(fname) [lindex $def 3]
	    set hd(key) [lindex $def 4]
	    frame $w.key
	    label $w.key.label -text Key:
	    frame $w.key.b
	    radiobutton $w.key.b.sender -text $t(sender) \
		-variable ${id}(key) -value sender
	    radiobutton $w.key.b.recipient -text $t(final-recipient) \
		-variable ${id}(key) -value recipient
	    radiobutton $w.key.b.date -text $t(date) \
		-variable ${id}(key) -value date
	    pack $w.key.b.sender \
		$w.key.b.recipient \
		$w.key.b.date -side top -anchor w
	    pack $w.key.b $w.key.label -side right -anchor nw
	    frame $w.chooser -relief sunken -bd 1
	    set fh [FileSelectorCreate $w.chooser ${id}(fname) \
			"set ${id}(done) 1" 1 1]
	    pack $w.key $w.chooser -side top -fill both -expand 1 -padx 5 -pady 5
	}
    }
    
    # OK and cancel buttons
    OkButtons $w $t(ok) $t(cancel) "set ${id}(done)"
    pack $w.buttons -side bottom -fill both -padx 5 -pady 5

    Place $w editVfolder
    focus $w.name.entry
    grab $w
    while 1 {
	tkwait variable ${id}(done)

	if { 1 == $hd(done) } {
	    set hd(name) [string trim $hd(name)]
	    if ![string length $hd(name)] {
		Popup $t(need_name)
		continue
	    }
	    set features {}
	    foreach elem $feats {
		if [string compare $hd($elem) $option($elem)] {
		    lappend features $elem $hd($elem)
		}
	    }
	    switch [lindex $def 1] {
	    file {
		    FileSelectorCleanup $fh
		    if {![file isfile $hd(fname)] && ([file exists $hd(fname)]
			    || ![file isdirectory [file dirname $hd(fname)]])} {
			Popup "$t(illegal_file_spec): $hd(fname)"
			continue
		    }
		    FileSelectorDone $fh
		    set vFolderDef($ident) [list $hd(name) file $features \
			    $hd(fname)]
		}
	    mh {
		    if {![string length $hd(mh_name)]} {
		        Popup $t(need_mh_name)
			continue
		    }
		    set vFolderDef($ident) [list $hd(name) mh $features \
			    "#MH/$hd(mh_name)"]
		}
	    dbase {
		    if ![string length $hd(keywords)] {
		        Popup $t(need_keyword)
			continue
		    }
		    set vFolderDef($ident) [list $hd(name) dbase $features \
			    $hd(keywords) $hd(extype) $hd(exdate)]
		}
	    imap {
		    if {![string length $hd(host)]
			    || ![string length $hd(user)]} {
		        Popup $t(need_host_and_user)
			continue
		    }
		    if [string length $hd(port)] {
			set host $hd(host):$hd(port)
		    } else {
			set host $hd(host)
		    }
		    set vFolderDef($ident) [list $hd(name) imap $features \
			    \{{$host}$hd(mbox)\} $hd(user)]
		    if {[string compare $oldHost $hd(host)]
			    || [string compare $oldMbox $hd(mbox)]} {
			catch {RatImapCreateFolder "{$host}$hd(mbox)" $hd(user)}
		    }
		}
	    pop3 {
		    if {![string length $hd(host)]
			    || ![string length $hd(user)]} {
		        Popup $t(need_host_and_user)
			continue
		    }
		    if [string length $hd(port)] {
			set host $hd(host):$hd(port)
		    } else {
			set host $hd(host)
		    }
		    set vFolderDef($ident) [list $hd(name) pop3 $features \
			    \{{$host/pop3}\} $hd(user)]
		}
	    dynamic {
		FileSelectorCleanup $fh
		if {![file isdirectory $hd(fname)]} {
		    Popup "$t(illegal_file_spec): $hd(fname)"
		    continue
		}
		FileSelectorDone $fh
		set vFolderDef($ident) \
		    [list $hd(name) dynamic $features $hd(fname) $hd(key)]
		}
	    }
	    incr vFolderChanged
	    RecordPos $w editVfolder
	    destroy $w
	    focus $oldFocus
	    set name $hd(name)
	    unset hd
	    foreach bn [array names b $w.*] {unset b($bn)}
	    return $name
	} else {
	    RecordPos $w editVfolder
	    destroy $w
	    focus $oldFocus
	    unset hd
	    foreach bn [array names b $w.*] {unset b($bn)}
	    return ""
	}
    }
}

# PostSaveTo --
#
# Constructs a menu of vfolders. When one item in the menu is choosen
# the save_to value of handler is set to that vfolder index number
#
# Arguments:
# handler -     The handler which called us
# m   -		The menu in which to insert the entries
# id  -		The id of the struct to start with (this should always be 0)
proc PostSaveTo {hd m id} {
    global vFolderStruct vFolderDef

    $m delete 0 end
    if ![info exists vFolderStruct($id)] {
	return
    }
    foreach elem $vFolderStruct($id) {
	if ![string compare [lindex $elem 0] struct] {
	    set nm $m.m[lindex $elem 1]
	    $m add cascade -label [lindex $elem 2] -menu $nm
	    if ![winfo exists $nm] {
		menu $nm -postcommand "FixMenu $nm"
	    } else {
		$nm delete 1 end
	    }
	    PostSaveTo $hd $nm [lindex $elem 1]
	} else {
	    set num [lindex $elem 1]
	    set name [lindex $elem 2]
	    set vfolder $vFolderDef($num)
	    set nm $m.m$num
	    if [string compare pop3 [lindex $vfolder 1]] {
		if [string compare date [lindex $vfolder 4]] {
		    $m add command -label [lindex $elem 2] \
			-command "set ${hd}(save_to_label) \"$name\"; set ${hd}(save_to) $num"
		} 
	    }
	}
    }
}

# VFolderWrite --
#
# Writes the list of vfolders to disk
#
# Arguments:
proc VFolderWrite {} {
    global option vFolderDef vFolderStruct vFolderDefIdent vFolderStructIdent \
	vFolderVersion vFolderInbox vFolderSave vFolderStartup \
	vFolderStartmin

    set fh [open $option(postilion_dir)/vfolderlist w]
    puts $fh "set vFolderVersion $vFolderVersion"
    puts $fh "set vFolderStructIdent $vFolderStructIdent"
    puts $fh "set vFolderDefIdent $vFolderDefIdent"
    puts $fh "set vFolderInbox $vFolderInbox"
    puts $fh "set vFolderSave [list $vFolderSave]"
    puts $fh "set vFolderStartup [list $vFolderStartup]"
    puts $fh "set vFolderStartmin [list $vFolderStartmin]"

    # First, get rid of System definitions, no point in saving those
    if {[file readable $option(system_mailboxes)] \
	    && $option(use_system_mboxes)} {
	# Throw away any existing vfolderdefs which are really system
	# defs, these may be left over from a save
	foreach vfd [array names vFolderDef "S*"] {
	    unset vFolderDef($vfd)
	}
	# Do the same for vfolderstructs
	foreach vfs [array names vFolderStruct "S*"] {
	    unset vFolderStruct($vfs)
	}
	# Get rid of lingering references within other structdefs
	foreach str [array names vFolderStruct] {
	    set i 0
	    foreach elem $vFolderStruct($str) {
		if [string match S* [lindex $elem 1]] {
		    set vFolderStruct($str) [lreplace $vFolderStruct($str) $i $i]
		} else {
		    incr i
		}
	    }
	}
    }

    foreach str [array names vFolderStruct] {
	set vs {}
	foreach e $vFolderStruct($str) {
	    switch [lindex $e 0] {
	    struct  {
		    set keep [info exists vFolderStruct([lindex $e 1])]
		}
	    vfolder {
		    set keep [info exists vFolderDef([lindex $e 1])]
		}
	    }
	    
	    if $keep {
		lappend vs $e
	    } else {
		puts "Warning, removing {$e}"
	    }
	}
	puts $fh "set vFolderStruct($str) [list $vs]"
    }
    foreach elem [array names vFolderDef] {
	puts $fh "set vFolderDef($elem) [list $vFolderDef($elem)]"
    }

    # Restore System definitions
    if {[file readable $option(system_mailboxes)] \
	    && $option(use_system_mboxes)} {
	source $option(system_mailboxes)
    }

    close $fh
}

# VFolderWindowDestroy --
#
# This procedure is called when the vfolder window is destroyed. It
# writes any changes to the vfolder structre to disk, if needed.
#
# Arguments:
proc VFolderWindowDestroy {} {
    global vFolderChanged
    if $vFolderChanged {
	VFolderWrite
    }
}

# VFolderImportDirectory --
#
# Import a folder structure from disk (i.e. the folders are stored in
# directories on the disk.
#
# Arguments:
proc VFolderImportDirectory {} {
    global vFolderDefIdent vFolderStruct vFolderChanged vFolderStructIdent\
	   vFolderDef t b idCnt

    # Create identifier
    set id importMailDir[incr idCnt]
    set w .$id
    upvar #0 $id hd
    set hd(done) 0
    set hd(file_pattern) {*}
    set hd(dir_pattern) {*}

    # Create toplevel
    toplevel $w -class Postilion
    wm title $w $t(import_directory)

    # Create fileselector
    set hd(fname) {}
    frame $w.chooser -relief sunken -bd 1
    set fh [FileSelectorCreate $w.chooser ${id}(fname) "set ${id}(done) 1" 1 1]
    pack $w.chooser -side top -fill both -expand 1 -padx 5 -pady 5

    # The patterns
    frame $w.pat
    label $w.pat.lab -text $t(patterns_to_use)
    label $w.pat.labf -text $t(files):
    entry $w.pat.entf -textvariable ${id}(file_pattern)
    set b($w.pat.entf) vd_filepat
    label $w.pat.labd -text $t(directories):
    entry $w.pat.entd -textvariable ${id}(dir_pattern)
    set b($w.pat.entd) vd_dirpat
    grid $w.pat.lab -columnspan 2
    grid $w.pat.labf -row 1 -column 0 -sticky e
    grid $w.pat.entf -row 1 -column 1 -sticky ew
    grid $w.pat.labd -row 2 -column 0 -sticky e
    grid $w.pat.entd -row 2 -column 1 -sticky ew
    pack $w.pat

    OkButtons $w $t(ok) $t(cancel) "set ${id}(done)"
    pack $w.buttons \
	 $w.chooser -side bottom -fill both -padx 5 -pady 5

    Place $w vFolderImportDirectory
    grab $w
    tkwait variable ${id}(done)
    RecordPos $w vFolderImportDirectory
    unset b($w.pat.entf)
    unset b($w.pat.entd)
    destroy $w

    if {1 == $hd(done)} {
	FileSelectorDone $fh
	incr vFolderChanged
	set sid [incr vFolderStructIdent]

	set name [file tail [string trimright $hd(fname) /]]
	if [file isdirectory $hd(fname)] {
	    lappend vFolderStruct(0) [list struct $sid $name]
	    VFolderScanDir $sid $hd(fname) $hd(file_pattern) $hd(dir_pattern)
	} else {
	    lappend vFolderStruct(0) [list vfolder $vFolderDefIdent $name]
	    set vFolderDef($vFolderDefIdent) [list $name file {} $hd(fname)]
	    incr vFolderDefIdent
	}
	if [winfo exists .mailbox] {RedrawVFolderList}
    }
    unset hd
}


# VFolderScanDir --
#
# Scan a directory and build a structure from it.
#
# Arguments:
# sid	       - Structure id for our struct entry
# dirName      - Name of the directory
# file_pattern - pattern to match files against
# dir_pattern  - pattern to match directories against
proc VFolderScanDir {sid dirName file_pattern dir_pattern} {
    global vFolderDefIdent vFolderStruct vFolderDef vFolderStructIdent t 

    set vFolderStruct($sid) {}
    foreach f [lsort [glob -nocomplain $dirName/*]] {
	set name [file tail $f]
	if ![file readable $f] {
	    Popup "$t(cant_read): $f"
	    continue
	}
	if [file isdirectory $f] {
	    if ![string match $dir_pattern $name] {
		continue
	    }
	    lappend vFolderStruct($sid) \
		    [list struct [incr vFolderStructIdent] $name]
	    VFolderScanDir $vFolderStructIdent $f $file_pattern $dir_pattern
	} elseif [file isfile $f] {
	    if ![string match $file_pattern $name] {
		continue
	    }
	    lappend vFolderStruct($sid) [list vfolder $vFolderDefIdent $name]
	    set vFolderDef($vFolderDefIdent) [list $name file {} $f]
	    incr vFolderDefIdent
	} else {
	    Popup "$f $t(ignored_not_file_not_dir)"
	}
    }
}

# VFolderImportMHDirectory --
#
# Import a folder structure from disk (i.e. the folders are stored in
# directories on the disk.
#
# Arguments:
proc VFolderImportMHDirectory {} {
    global vFolderDefIdent vFolderStruct vFolderChanged vFolderStructIdent\
	   vFolderDef t idCnt

    # Create identifier
    set id importMHDir[incr idCnt]
    set w .$id
    upvar #0 $id hd
    set hd(done) 0
    set hd(file_pattern) {*}
    set hd(dir_pattern) {*}

    # Create toplevel
    toplevel $w -class Postilion
    wm title $w $t(import_directory)

    # Create fileselector
    set hd(fname) {}
    frame $w.chooser -relief sunken -bd 1
    set fh [FileSelectorCreate $w.chooser ${id}(fname) "set ${id}(done) 1" 1 1]
    pack $w.chooser -side top -fill both -expand 1 -padx 5 -pady 5

    # The patterns
    frame $w.pat
    label $w.pat.lab -text $t(patterns_to_use)
    label $w.pat.labf -text $t(files): -anchor e
    entry $w.pat.entf -textvariable ${id}(file_pattern)
    label $w.pat.labd -text $t(directories): -anchor e
    entry $w.pat.entd -textvariable ${id}(dir_pattern)
    grid $w.pat.lab -columnspan 2
    grid $w.pat.labf $w.pat.entf -sticky ew
    grid $w.pat.labd $w.pat.entd -sticky ew
    pack $w.pat

    OkButtons $w $t(ok) $t(cancel) "set ${id}(done)"
    pack $w.buttons \
    	 $w.chooser -side bottom -fill both -expand 1 -padx 5 -pady 5

    Place $w vFolderImportDirectory
    grab $w
    tkwait variable ${id}(done)
    RecordPos $w vFolderImportDirectory
    destroy $w

    if {1 == $hd(done)} {
	FileSelectorDone $fh
	incr vFolderChanged
	set sid [incr vFolderStructIdent]

	set name [file tail [string trimright $hd(fname) /]]
	if [VFolderMHHasSubfolders $hd(fname)] {
	    lappend vFolderStruct(0) [list struct $sid "$name/"]
	    VFolderMHScanDir $sid $hd(fname) $hd(file_pattern) $hd(dir_pattern)
	}
	if [winfo exists .mailbox] {RedrawVFolderList}
    }
    unset hd
}

proc VFolderMHHasSubfolders { dirName } {
    if { [file tail $dirName] == "{}" } {
       return 0
    }
    if [catch "glob $dirName/*/."] {
        return 0
    } else {
        return 1
    }
}

# VFolderMHScanDir --
#
# Scan a directory and build a structure from it.
#
# Arguments:
# sid	       - Structure id for our struct entry
# dirName      - Name of the directory
# file_pattern - pattern to match files against
# dir_pattern  - pattern to match directories against
proc VFolderMHScanDir {sid dirName file_pattern dir_pattern {folder_prefix {/}}} {
    global vFolderDefIdent vFolderStruct vFolderDef vFolderStructIdent t 
    set vFolderStruct($sid) {}
    foreach f [lsort [glob -nocomplain $dirName/*]] {
	set name [file tail $f]
	if ![file readable $f] {
	    Popup "$t(cant_read): $f"
	    continue
	}
	if [VFolderMHHasSubfolders $f] {
	    if ![string match $dir_pattern $name] {
		continue
	    }
	    lappend vFolderStruct($sid) \
		    [list struct [incr vFolderStructIdent] "$name/"]
	    VFolderMHScanDir $vFolderStructIdent $f $file_pattern $dir_pattern $folder_prefix$name/
	    lappend vFolderStruct($sid) [list vfolder $vFolderDefIdent $name]
	    set vFolderDef($vFolderDefIdent) [list $name mh {} "#MH$folder_prefix$name"]
	    incr vFolderDefIdent
	} elseif [file isfile $f] {
	    # nothing to do...
	} else {
	    if ![string match $file_pattern $name] {
		continue
	    }
	    lappend vFolderStruct($sid) [list vfolder $vFolderDefIdent $name]
	    set vFolderDef($vFolderDefIdent) [list $name mh {} "#MH$folder_prefix$name"]
	    incr vFolderDefIdent
	}
    }
}

# VFolderImportIMAP --
#
# Import a folder structure from an IMAP server.
#
# Arguments:
proc VFolderImportIMAP {} {
    global t b env idCnt vFolderChanged option \
	   vFolderStruct vFolderStructIdent

    # Create identifier
    set id importIMAP[incr idCnt]
    set w .$id
    upvar #0 $id hd
    set hd(done) 0

    # Create toplevel
    toplevel $w -class Postilion
    wm title $w $t(import_IMAP)

    # Create the entries
    set hd(host) {}
    set hd(port) $option(imap_port)
    set hd(user) $env(USER)
    set hd(pattern) {*}
    foreach e {host port user pattern} {
	label $w.${e}_label -text $t($e): -anchor e
	entry $w.${e}_entry -textvariable ${id}($e) -width 32
	set b($w.${e}_entry) vd_$e
	grid $w.${e}_label $w.${e}_entry -sticky ew -padx 5
    }

    OkButtons $w $t(ok) $t(cancel) "set ${id}(done)"
    grid $w.buttons -columnspan 2 -pady 10 -sticky ew

    Place $w vFolderImportIMAP
    focus $w.host_entry
    grab $w
    while 1 {
	tkwait variable ${id}(done)
	if {(1 == $hd(done)) && (0 == [string length $hd(host)])} {
	    Popup $t(host_required)
	} else {
	    break
	}
    }
    RecordPos $w vFolderImportIMAP
    destroy $w

    if {1 == $hd(done)} {
	if [string length $hd(port)] {
	    set cmd {RatListIMAP $hd(host):$hd(port) $hd(user) $hd(pattern)}
	} else {
	    set cmd {RatListIMAP $hd(host) $hd(user) $hd(pattern)}
	}
	if [catch $cmd folders] {
	    Popup "$t(import_failed): $folders"
	} else {
	    incr vFolderChanged
	    if [info exists vFolderStructIdent] {
		incr vFolderStructIdent
	    } else {
		set vFolderStructIdent 1
	    }
	    lappend vFolderStruct(0) [list struct $vFolderStructIdent \
		    $hd(user)@$hd(host)]
	    VFolderBuildIMAPStruct $folders $vFolderStructIdent $hd(user)
	    if [winfo exists .mailbox] {RedrawVFolderList}
	}
    }
    unset hd
}

# VFolderBuildIMAPStruct --
#
# Recursively build the folder structure from the data returned by RatListIMAP.
#
# Arguments:
# flist  - List of folders on this level
# sIdent - The ident of the struct to build
# user   - The username
proc VFolderBuildIMAPStruct {flist sIdent user} {
    global vFolderDefIdent vFolderStruct vFolderStructIdent vFolderDef

    set vFolderStruct($sIdent) {}
    set folders [lsort $flist]
    foreach f $folders {
	if [lindex $f 1] {
	    lappend vFolderStruct($sIdent) [list vfolder $vFolderDefIdent \
		    [lindex $f 0]]
	    set vFolderDef($vFolderDefIdent) \
		[list [lindex $f 0] imap {browse 1} [lindex $f 2] $user]
	    incr vFolderDefIdent
	} else {
	    incr vFolderStructIdent
	    lappend vFolderStruct($sIdent) [list struct $vFolderStructIdent \
		    [lindex $f 0]]
	    VFolderBuildIMAPStruct [lindex $f 2] $vFolderStructIdent $user
	}
    }
}

# VFolderCut --
#
# This procedure prunes the selected item from the vfolder tree.
# The item is specified via the vFolderCurrent global variable.
#
# Arguments:
proc VFolderCut {} {
    global t vFolderCurrent vFolderDef vFolderStruct vFolderChanged \
	   vFolderInbox vFolderSave vFolderCut

    set sIdent [lindex $vFolderCurrent 0]
    set index [lindex $vFolderCurrent 1]
    set vFolderCut [lindex $vFolderStruct($sIdent) $index]

    set vFolderStruct($sIdent) [lreplace $vFolderStruct($sIdent) $index $index]
    if ![info exists vFolderDef($vFolderInbox)] {
	set vFolderInbox [lindex [lsort -integer [array names vFolderDef]] 0]
    }
    if ![info exists vFolderDef($vFolderInbox)] {
	set vFolderSave {}
    }
    incr vFolderChanged
    RedrawVFolderList
    
}

# VFolderPaste --
#
# Paste a previously cut vFolderDef into a new location in the hierarchy
#
# Arguments:
proc VFolderPaste {} {
    global t vFolderCurrent vFolderDef vFolderStruct vFolderChanged \
	   vFolderInbox vFolderSave vFolderCut

    set sIdent [lindex $vFolderCurrent 0]
    set index [lindex $vFolderCurrent 1]
    set vFolderStruct($sIdent) \
	[linsert $vFolderStruct($sIdent) $index $vFolderCut]
    set vFolderCut ""
    incr vFolderChanged
    RedrawVFolderList
}
