# vfolder.tcl --
#
# This file contains commands which interacts with the user about performing
# different types of folder operations
#
#
#  TkRat software and its included text is Copyright 1996 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.

# The ident of the next defined vfolder
set vFolderDefIdent 0

# The ident of the next defined vFolderStruct
set vFolderStructIdent 0
set vFolderChanged 0

# VFolderRead --
#
# Reads the list of vfolders from disk. If needed it calls FixVFolderList
# which upgrades the list from the old format to the new.
#
# Arguments:

proc VFolderRead {} {
    global vFolderDef vFolderStruct vFolderStructIdent vFolderDefIdent \
	vFolderVersion option vfolder_list vFolderInbox vFolderSave \
	vFolderStartup vFolderStartmin env vFolderChanged

    if [file readable $option(postilion_dir)/vfolderlist] {
	source $option(postilion_dir)/vfolderlist
    }

    # Upgrade old versions
    if { 0 == [info exists vFolderVersion]} {
	if [info exists vfolder_list] {
	    FixVFolderList
	}
	set vFolderVersion 5
    }

    # Add mailbox feature element
    if {3 == $vFolderVersion} {
	foreach id [array names vFolderDef] {
	    if { {} != [lindex $vFolderDef($id) 2]} {
		set vFolderDef($id) [lreplace $vFolderDef($id) 2 0 {}]
	    }
	}
	incr vFolderChanged
	set vFolderVersion 5
    }

    if ![info exists vFolderSave] {
	set vFolderSave {}
	set option(save_to) $vFolderSave
    } else {
	set option(save_to) $vFolderSave
    }

    # Optimize mailbox feature list from older versions
    if {4 == $vFolderVersion} {
	foreach id [array names vFolderDef] {
	    array set feat [lindex $vFolderDef($id) 2]
	    foreach elem [array names feat] {
		if [string compare $feat($elem) $option($elem)] {
		    lappend feats $elem $feat($elem)
		}
	    }
            if [info exists feats] {
	        set vFolderDef($id) [lreplace $vFolderDef($id) 2 2 $feats]
                unset feats
            }
	    unset feat
	}
	incr vFolderChanged
	set vFolderVersion 5
    }

    if {0 == [llength [array names vFolderDef]]} {
	set vFolderDefIdent 1
	set vFolderDef(0) $option(default_folder)
	set vFolderStruct(0) {{vfolder 0 INBOX}}
	set vFolderVersion 4
	set vFolderInbox 0
	set vFolderSave {}
	set vFolderStartup {}
	set vFolderStartmin {}
    }
    if ![info exists vFolderInbox] {
	set vFolderInbox 0
    }
    if ![info exists vFolderStartup] {
	set vFolderStartup {}
    }
    if ![info exists vFolderStartmin] {
	set vFolderStartmin {}
    }
    if ![info exists vFolderDef($vFolderInbox)] {
	set vFolderInbox [lindex [lsort -integer [array names vFolderDef]] 0]
    }
    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
		}
	    }
	}
	# Read in new system mailbox definitions
	source $option(system_mailboxes)
    }
}

# SelectFileFolder --
#
# Presents a file selector to the user and opens the selected folder (if any)
#
# Arguments:
# handler -	The handler to the folder window which requested the open

proc SelectFileFolder {handler} {
    global idCnt t option

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

    # Create toplevel
    toplevel $w -class Postilion
    wm transient $w .
    wm title $w $t(open_file)

    # Filechooser
    frame $w.chooser
    set fh [FileSelectorCreate $w.chooser ${id}(name) "set ${id}(done) 1" 0 0]

    # OK and cancel buttons
    frame $w.buttons
    button $w.buttons.ok -text $t(ok) -command "FileSelectorUpdate $fh 1"
    button $w.buttons.cancel -text $t(cancel) -command "set ${id}(done) 0"
    pack $w.buttons.ok \
	 $w.buttons.cancel -side left -expand 1

    # Pack it
    pack $w.buttons -side bottom -fill both
    pack $w.chooser -fill both -expand 1

    Place $w selectFileFolder
    grab $w
    tkwait variable ${id}(done)

    RecordPos $w selectFileFolder
    destroy $w
    focus $oldFocus

    # Do open
    if { 1 == $hd(done) } {
	if { 0 != [string compare $handler dummy]} {
	    set handler [FolderWindowInit 1]
	}
	upvar #0 $handler mh
	set mh(start_selection) $option(start_selection)
	set mh(list_format) $option(list_format)
	set mh(folder_sort) $option(folder_sort)
	set mh(signature) $option(signature)
	FileSelectorDone $fh
	FolderRead $handler "RatOpenFolder std $hd(name) $option(folder_sort)"
	focus $mh(w)
    }
    unset hd
}

# SelectDbasefolder --
#
# Lets the user search the database
#
# Arguments:
# handler -	The handler to the folder window which requested the open

proc SelectDbasefolder {handler} {
    global idCnt t option

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

    # Create toplevel
    toplevel $w -class Postilion
    wm transient $w .
    wm title $w $t(open_dbase)
    wm protocol $w WM_DELETE_WINDOW "set ${id}(done) 0"

    # Populate window
    frame $w.op
    radiobutton $w.op.and -text $t(and) -variable ${id}(op) -value and
    radiobutton $w.op.or -text $t(or) -variable ${id}(op) -value or
    pack $w.op.and \
	 $w.op.or -side left -padx 5
    frame $w.keywords
    label $w.keywords.label -text $t(keywords):
    entry $w.keywords.entry -textvariable ${id}(keywords) -relief sunken \
	   -width 20
    pack $w.keywords.entry \
	 $w.keywords.label -side right
    frame $w.subject
    label $w.subject.label -text $t(subject):
    entry $w.subject.entry -textvariable ${id}(subject) -relief sunken \
	-width 20
    pack $w.subject.entry \
	 $w.subject.label -side right
    frame $w.all
    label $w.all.label -text $t(all):
    entry $w.all.entry -textvariable ${id}(all) -relief sunken -width 20
    pack $w.all.entry \
	 $w.all.label -side right
    frame $w.to
    label $w.to.label -text $t(to):
    entry $w.to.entry -textvariable ${id}(to) -relief sunken -width 20
    pack $w.to.entry \
	 $w.to.label -side right
    frame $w.from
    label $w.from.label -text $t(from):
    entry $w.from.entry -textvariable ${id}(from) -relief sunken -width 20
    pack $w.from.entry \
	 $w.from.label -side right
    frame $w.cc
    label $w.cc.label -text $t(cc):
    entry $w.cc.entry -textvariable ${id}(cc) -relief sunken -width 20
    pack $w.cc.entry \
	 $w.cc.label -side right

    OkButtons $w $t(search) $t(cancel) "set ${id}(done)"

    # Pack it
    pack $w.op -side top
    pack $w.keywords \
	 $w.subject \
	 $w.all \
    	 $w.to \
	 $w.from \
	 $w.cc \
	 $w.buttons -side top -fill both

    Place $w selectDbaseFolder
    focus $w.keywords.entry
    grab $w
    tkwait variable ${id}(done)

    # Do search
    if { 1 == $hd(done) } {
	set exp $hd(op)
	if [string length $hd(to)] {
	    set exp "$exp to [list $hd(to)]"
	}
	if [string length $hd(from)] {
	    set exp "$exp from [list $hd(from)]"
	}
	if [string length $hd(cc)] {
	    set exp "$exp cc [list $hd(cc)]"
	}
	if [string length $hd(subject)] {
	    set exp "$exp subject [list $hd(subject)]"
	}
	if [string length $hd(keywords)] {
	    set exp "$exp keywords [list $hd(keywords)]"
	}
	if [string length $hd(all)] {
	    set exp "$exp all [list $hd(all)]"
	}
	if [string compare $hd(op) $exp] {
	    if { 0 != [string compare $handler dummy]} {
		set handler [FolderWindowInit 1]
	    }
	    upvar #0 $handler fh
	    set fh(start_selection) $option(start_selection)
	    set fh(list_format) $option(list_format)
	    set fh(folder_sort) $option(folder_sort)
	    set fh(signature) $option(signature)
	    FolderRead $handler "RatOpenFolder dbase {} {} {} $exp"
	    focus $fh(w)
	} else {
	    Popup $t(emptyexp)
	}
    }

    RecordPos $w selectDbaseFolder
    destroy $w
    unset hd
    focus $oldFocus
}

# VFolderBuildMenu --
#
# Constructs a menu of vfolders. When one item in the menu is choosen
# $cmd is executed and the vfolder definition of the choosen folder is
# appended to $cmd. If the write argument is 1 then only those
# folders that can be written to are included in the menu.
#
# Arguments:
# m   -		The menu in which to insert the entries
# id  -		The id of the struct to start with (this should always be 0)
# cmd -		Command to execute when an item is choosen
# write -	If this argument is 1 the folders are going to be used for
#		inserting messages.

proc VFolderBuildMenu {m id cmd write} {
    global vFolderStruct vFolderDef

    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
	    }
	    VFolderBuildMenu $nm [lindex $elem 1] $cmd \
		    $write
	} else {
	    set vfolder $vFolderDef([lindex $elem 1])
	    set nm $m.m[lindex $elem 1]
	    if {![string compare dynamic [lindex $vfolder 1]] && 0 == $write} {
		$m add cascade -label [lindex $elem 2] -menu $nm
		if ![winfo exists $nm] {
		    menu $nm -postcommand "FixMenu $nm"
		} else {
		    $nm delete 1 end
		}
		set height [expr [winfo screenheight $nm]-20]
		foreach f [lsort [glob -nocomplain [lindex $vfolder 3]/*]] {
		    if [file isfile $f] {
			$nm add command -label [file tail $f] \
			    -command "$cmd {[list [file tail $f] file \
					              [lindex $vfolder 2] $f]}"
		    }
		}
	    } elseif {[string compare pop3 [lindex $vfolder 1]] \
			  || 0==$write } {
		if [string compare date [lindex $vfolder 4]] {
		    $m add command -label [lindex $elem 2] \
			-command "$cmd [list $vfolder]"
		} 
	    }
	}
    }
}


# VFolderOpen --
#
# Opens the specified vfolder
#
# Arguments:
# handler -	The handler to the folder window which requested the open
# vfolder -	The definition of the vfolder to be opened

proc VFolderOpen {handler vfolder} {
    global t inbox vFolderDef vFolderInbox option env

    # If we're passed a dummy handler, create a new folder window
    if ![string comp $handler dummy] {
	set handler [FolderWindowInit 1]
    }

    upvar #0 $handler fh
    upvar #0 $fh(text) texth
    ClkSetTime 0 $texth(clkcan)
    set fh(stream) [lindex $vfolder 3]

    # Initialize Folder Features array
    array set features [lindex $vfolder 2]

    # Initialize browse mode
    switch $option(browse) {
	normal {set mode 0}
	browse {set mode 1}
	folder {
	    if ![info exists features(browse)] {
		set features(browse) 1
	    }
	    set mode $features(browse)
	}
    }
    set fh(browse) $mode

    # Initialize other features
    foreach feature {
	{sort_mode folder_sort}
	{list_mode list_format}
	{sig_mode signature}
	{strsel_mode start_selection}
	{from_mode from}
	{reply_mode reply_to}
    } {
	set feat_mode [lindex $feature 0]
	set feat [lindex $feature 1]
	switch $option($feat_mode) {
	    normal {set mode $option($feat)}
	    folder {
		if ![info exists features($feat)] {
		    set features($feat) $option($feat)
		}
		set mode $features($feat)
	    }
	}
	set fh($feat) $mode
    }
    
    # Initialize save_to
    if [info exists features(save_to)] {
        set fh(save_to) $features(save_to)
    }

    focus .$handler
    switch [lindex $vfolder 1] {
	dbase {
	    set folder [FolderRead $handler \
			    [list RatOpenFolder dbase [lindex $vfolder 3] \
				 [lindex $vfolder 5] [lindex $vfolder 4] \
				 and keywords [lindex $vfolder 3]]]
	}
	imap {
	    if [string compare [lindex $vfolder 4] UsEr] {
		set folder [FolderRead $handler "RatOpenFolder std \
		    [lindex $vfolder 3] $fh(folder_sort) [lindex $vfolder 4] \
		    IMAP"]
	    } else {
		set folder [FolderRead $handler "RatOpenFolder std \
		    [lindex $vfolder 3] $fh(folder_sort) $env(USER) IMAP"]
	    }
	}
	pop3 {
	    set folder [FolderRead $handler "RatOpenFolder std \
		[lindex $vfolder 3] $fh(folder_sort) [lindex $vfolder 4] POP3"]
	}
	default {
	    set folder [FolderRead $handler "RatOpenFolder std \
		    [lindex $vfolder 3] $fh(folder_sort)"]
	}
    }
    if [string length $folder] {
	$folder setName [lindex $vfolder 0]
	set fh(folder_name) [lindex [$folder info] 0]
	wm title .$handler "Mailbox: $fh(folder_name)"
	wm iconname .$handler $fh(folder_name)
	FolderBind $handler
    }
    if ![string compare $vfolder $vFolderDef($vFolderInbox)] {
	set inbox $folder
    }
    catch {$fh(message_list) see Active.first}
    update idletasks
    focus .$handler
}

# VFolderInsert --
#
# Inserts the given messages into the given vfolder
#
# Arguments:
# handler  -	The handler to the folder window which requested the operation
# advance  -	1 if we should move to the next message on success
# messages -	The messages to move
# vfolder  -	Vfolder to insert the message into

proc VFolderInsert {handler advance messages vfolder} {
    upvar #0 $handler fh
    global option t env

    if ![llength $vfolder] {
	return
    } else {
	if {![string compare [lindex $vfolder 1] imap] \
		&& ![string compare [lindex $vfolder 4] UsEr]} {
		    set vfolder [lreplace $vfolder 4 4 $env(USER)]
		}
    }
    foreach msg $messages {
	FolderSelect $handler [$fh(folder_handler) find $msg] 0
	if [catch {$msg copy $vfolder} result] {
	    RatLog 4 "$t(insert_failed): $result"
	    break
	} else {
	    SetFlag $handler deleted 1 0 [$fh(folder_handler) find $msg]
	    if { 1 == $advance } {
		FolderNext $handler
	    }
	}
    }
}

# InsertIntoFile --
#
# Inserts the given message into a file. The user must specify the file.
#
# Arguments:

proc InsertIntoFile {} {
    global idCnt t option

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

    # Create toplevel
    toplevel $w -class Postilion
    wm transient $w .
    wm title $w $t(save_to_file)

    # Filechooser
    frame $w.chooser
    set fh [FileSelectorCreate $w.chooser ${id}(name) "set ${id}(done) 1" 0 0]

    # OK, perm and cancel buttons
    frame $w.buttons
    button $w.buttons.ok -text $t(ok) -command "FileSelectorUpdate $fh 1"  -bd 1
    CreateModeMenu $w.buttons.perm $id $option(permissions)
    button $w.buttons.cancel -text $t(cancel) -command "set ${id}(done) 0" -bd 1
    pack $w.buttons.ok \
	 $w.buttons.perm \
	 $w.buttons.cancel -side left -expand 1

    # Pack it
    pack $w.buttons -side bottom -fill both
    pack $w.chooser -fill both -expand 1

    Place $w insertIntoFile
    grab $w
    tkwait variable ${id}(done)

    if { 1 == $hd(done) } {
	FileSelectorDone $fh
	set result [list $hd(name) file {} $hd(name) [GetMode $id]]
    } else {
	set result {}
    }
    unset hd
    RecordPos $w insertIntoFile
    destroy $w
    focus $oldFocus

    return $result
}

# InsertIntoDBase --
#
# Inserts the given message into the database. The user must specify
# some of the arguments for the insert operation.
#
# Arguments:

proc InsertIntoDBase {} {
    global idCnt t option

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

    # Create toplevel
    toplevel $w -class Postilion
    wm transient $w .
    wm title $w $t(insert_into_dbase)

    # Populate window
    frame $w.keywords
    label $w.keywords.label -text $t(keywords):
    entry $w.keywords.entry -textvariable ${id}(keywords) -relief sunken \
	    -width 20
    pack $w.keywords.entry \
	 $w.keywords.label -side right
    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
    radiobutton $w.extype.b.remove -text $t(remove) -variable ${id}(extype) \
	    -value remove
    radiobutton $w.extype.b.incoming -text $t(add_incoming) \
	    -variable ${id}(extype) -value 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
    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) -relief sunken \
	    -width 20
    pack $w.exdate.entry \
	 $w.exdate.label -side right
    frame $w.buttons
    frame $w.buttons.def -relief sunken -bd 1
    button $w.buttons.def.ok -text $t(insert) -command "set ${id}(done) 1"
    pack $w.buttons.def.ok -padx 4 -pady 4
    button $w.buttons.cancel -text $t(cancel) -command "set ${id}(done) 0"
    pack $w.buttons.def \
         $w.buttons.cancel -side left -expand 1 -pady 4
    pack $w.keywords \
	 $w.extype \
	 $w.exdate \
	 $w.buttons -side top -fill both

    set hd(extype) $option(def_extype)
    set hd(exdate) $option(def_exdate)
    bind $w <Return> "set ${id}(done) 1"
    Place $w insertIntoDbase
    focus $w.keywords.entry
    grab $w
    tkwait variable ${id}(done)

    # Do insert
    if { 1 == $hd(done) } {
	set result [list DBase dbase {} $hd(keywords) $hd(extype) $hd(exdate)]
    } else {
	set result {}
    }
    RecordPos $w insertIntoDbase
    destroy $w
    focus $oldFocus
    unset hd
    return $result
}

# VFolderGetSave --
#
# Returns the default save folder specification or an empty string
#
# Arguments:

proc VFolderGetSave {} {
    global vFolderSave vFolderDef

    if {![string length $vFolderSave] 
	    || ![info exists vFolderDef($vFolderSave)]} {
	return ""
    }
    return $vFolderDef($vFolderSave)
}
