# exp.tcl --
#
# This file contains code for handling message selection expressions
#
#
#  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.

# Counter used to generate unique expression indexes for saved expressions
set expArrayId 0

# ExpCreate --
#
# Create the expression creation window
#
# Arguments:
# handler	- The handler which identifies the folder window that
#		  the selection should be done in.

proc ExpCreate {handler} {
    global t b option idCnt defaultFont

    # Create identifier
    set id expCreate[incr idCnt]
    set w .$id
    upvar #0 $id hd
    set hd(doSave) 0
    set hd(w) $w
    set hd(handler) $handler
    set hd(op) and

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

    # Populate window
    frame $w.top
    menubutton $w.top.mode -indicatoron 1 -menu $w.top.mode.m \
	    -textvariable ${id}(modeName) -bd 1 -relief raised -width 18
    menu $w.top.mode.m -tearoff 0
    $w.top.mode.m add command -label $t(basic_mode) -command "ExpModeBasic $id"
    $w.top.mode.m add command -label $t(advanced_mode) -command "ExpModeAdv $id"
    frame $w.top.c
    checkbutton $w.top.c.but -text $t(save_as): -variable ${id}(doSave)
    entry $w.top.c.entry -width 20 -textvariable ${id}(saveAs) \
	    -font $defaultFont
    bind $w.top.c.entry <KeyRelease> \
	    "if {0 < \[string length ${id}(saveAs)\]} { \
		 set ${id}(doSave) 1 \
	     } else { \
		 set ${id}(doSave) 0 \
	     }"
    pack $w.top.c.but \
	 $w.top.c.entry -side left
    pack $w.top.mode \
	 $w.top.c -side left -padx 20
    set b($w.top.mode) switch_expression
    set b($w.top.c.but) save_expr_as
    set b($w.top.c.entry) save_expr_as

    frame $w.f -bd 2 -relief ridge
    set hd(frame) $w.f

    frame $w.buttons
    frame $w.buttons.def -relief sunken -bd 1
    button $w.buttons.def.ok -text $t(ok) -command "ExpDone $id ok"
    pack $w.buttons.def.ok -padx 4 -pady 4
    button $w.buttons.clear -text $t(clear) -command "ExpClear $id"
    button $w.buttons.cancel -text $t(cancel) -command "ExpDone $id cancel"
    pack $w.buttons.def \
	 $w.buttons.clear \
	 $w.buttons.cancel -side left -expand 1
    bind $w <Return> "ExpDone $id ok"
    pack $w.top -side top -fill x -pady 5
    pack $w.f -side top -fill both -pady 5 -expand 1
    pack $w.buttons -side top -fill x -pady 5
    set b($w.buttons.def.ok) exp_ok
    set b($w.buttons.clear) clear
    set b($w.buttons.cancel) dismiss

    set hd(mode) {}
    if [string compare advanced $option(expression_mode)] {
	ExpModeBasic $id
    } else {
	ExpModeAdv $id
    }
    Place $w ratExpression
}

# ExpModeBasic --
#
# Configure window for basic mode
#
# Arguments:
# handler -	The handler which identifies the expression window

proc ExpModeBasic {handler} {
    upvar #0 $handler hd
    global t b defaultFont

    # Check that this is really neccessary
    if ![string compare $hd(mode) basic] {
	return
    }

    # Setup variables
    set hd(modeName) $t(basic_mode)
    set hd(mode) basic
    set w $hd(frame)

    # Clear frame
    foreach s [grid slaves $w] {
	destroy $s
    }

    frame $w.f
    radiobutton $w.f.and -text $t(and) -variable ${handler}(op) -value and
    radiobutton $w.f.or -text $t(or) -variable ${handler}(op) -value or
    pack $w.f.and \
	 $w.f.or -side left -padx 5
    grid $w.f -columnspan 2
    set b($w.f.and) exp_basic_and
    set b($w.f.or) exp_basic_or

    foreach f {subject from reply_to sender to cc} {
	label $w.l$f -text $t($f):
	entry $w.e$f -textvariable ${handler}($f) -width 50 -font $defaultFont
	grid $w.l$f $w.e$f -sticky e
	set b($w.l$f) exp_basic_field
	set b($w.e$f) exp_basic_field
    }
    focus $w.esubject
}

# ExpModeAdv --
#
# Configure window for advanced mode
#
# Arguments:
# handler -	The handler which identifies the expression window

proc ExpModeAdv {handler} {
    upvar #0 $handler hd
    global t b defaultFont

    # Check that this is really neccessary
    if ![string compare $hd(mode) advanced] {
	return
    }

    # Setup variables
    set hd(modeName) $t(advanced_mode)
    set hd(mode) advanced
    set w $hd(frame)

    # Clear frame
    foreach s [grid slaves $w] {
	destroy $s
    }

    # Pack windows
    frame $w.f
    text $w.f.text -relief sunken -bd 1 -width 80 -height 4 -wrap word \
	    -yscroll "$w.f.scroll set" -setgrid 1 -font $defaultFont
    $w.f.text tag configure error -underline 1
    $w.f.text tag bind error <KeyPress> "$w.f.text tag remove error 1.0 end"
    scrollbar $w.f.scroll -relief sunken -bd 1 -command "$w.f.text yview"
    # Scroll mouse support added by Stephen Ryan 14 Oct 1999
    bind $w.f.text <5> [list $w.f.text yview scroll 5 units]
    bind $w.f.text <4> [list $w.f.text yview scroll -5 units]
    bind $w.f.text <Shift-5> [list $w.f.text yview scroll 1 units]
    bind $w.f.text <Shift-4> [list $w.f.text yview scroll -1 units]
    bind $w.f.text <Control-5> [list $w.f.text yview scroll 1 pages]
    bind $w.f.text <Control-4> [list $w.f.text yview scroll -1 pages]

    pack $w.f.scroll -side right -fill y
    pack $w.f.text -expand 1 -fill both
    grid $w.f -columnspan 4 -sticky nsew
    set b($w.f.text) exp_adv_exp

    frame $w.f1
    label $w.f1.l -text $t(fields)
    grid $w.f1.l -sticky we -columnspan 2
    set i 1
    foreach n {to from subject sender cc reply_to size date} {
	button $w.f1.f$i -text $t($n) -width 10 \
		-command "$w.f.text insert insert \"$n \""
	set b($w.f1.f$i) exp_adv_fields
	incr i
    }
    grid $w.f1.f1 $w.f1.f5 -row 1
    grid $w.f1.f2 $w.f1.f6 -row 2
    grid $w.f1.f3 $w.f1.f7 -row 3
    grid $w.f1.f4 $w.f1.f8 -row 4

    frame $w.o1
    label $w.o1.l -text $t(operators)
    grid $w.o1.l -sticky we
    set i 1
    foreach n [list "has $t(has) has" "is $t(is) is" "> > gt" "< < lt"] {
	button $w.o1.o$i -text [lindex $n 1] -width 10 \
		-command "$w.f.text insert insert \"[lindex $n 0] \""
	grid $w.o1.o$i -row $i
	set b($w.o1.o$i) exp_adv_[lindex $n 2]
	incr i
    }

    frame $w.b1
    label $w.b1.l -text $t(booleans)
    grid $w.b1.l -sticky we
    set i 1
    foreach n {not and or} {
	button $w.b1.b$i -text $t($n) -width 10 \
		-command "$w.f.text insert insert \"$n \""
	grid $w.b1.b$i -row $i
	set b($w.b1.b$i) exp_adv_bool
	incr i
    }

    frame $w.g1
    label $w.g1.l -text $t(grouping)
    grid $w.g1.l -sticky we
    set i 1
    foreach n {( )} {
	button $w.g1.g$i -text $n -width 10 \
		-command "$w.f.text insert insert \"$n \""
	grid $w.g1.g$i -row $i
	set b($w.g1.g$i) exp_adv_p
	incr i
    }

    grid $w.f1 \
	 $w.o1 \
	 $w.b1 \
	 $w.g1 -sticky n

    focus $w.f.text
    bind $w.f.text <Return> "ExpDone $handler ok; break"
    set hd(text) $w.f.text
}

# ExpClear --
#
# Clears the current expression window
#
# Arguments:
# handler	- the handler which identifies the expression window

proc ExpClear {handler} {
    upvar #0 $handler hd

    if ![string compare basic $hd(mode)] {
	foreach f {subject from reply_to sender to cc} {
	    set hd($f) ""
	}
    } else {
	$hd(text) delete 1.0 end
    }
}

# ExpDone --
#
# Called when we are done with the expression window
#
# Arguments:
# handler	- the handler which identifies the expression window
# action	- what we should do

proc ExpDone {handler action} {
    upvar #0 $handler hd
    upvar #0 $hd(handler) fHd
    global t b option expArrayId expList expName expExp

    if ![string compare ok $action] {
	# Build expression
	if ![string compare basic $hd(mode)] {
	    set exp ""
	    foreach f {subject from reply_to sender to cc} {
		if [string length $hd($f)] {
		    if [string length $exp] {
			set exp "$exp $hd(op) "
		    }
		    if [string compare reply_to $f] {
			set exp "${exp}$f has [list $hd($f)]"
		    } else {
			set exp "${exp}reply-to has [list $hd($f)]"
		    }
		}
	    }
	    if [catch {RatParseExp $exp} expId] {
		Popup $t(syntax_error_exp)
		return
	    }
	} else {
	    set exp [$hd(text) get 1.0 end]
	    if [catch {RatParseExp $exp} expId] {
	        set i [lindex $expId 0]
	        $hd(text) tag add error "1.$i wordstart" "1.$i wordend"
		Popup "$t(error_underlined): [lindex $expId 1]"
		return
	   }
	}
	# Apply expression
	set ids [$fHd(folder_handler) match $expId]
	if [string length $ids] {
	    SetFlag $hd(handler) flagged 1 $option(adv_on_flag) $ids
	}

	if $hd(doSave) {
	    if [string length $hd(saveAs)] {
		lappend expList $expArrayId
		set expName($expArrayId) $hd(saveAs)
		set expExp($expArrayId) $exp
		incr expArrayId
		ExpWrite
	    } else {
		Popup $t(need_name)
	    }
	}
	RatFreeExp $expId
    }
    RecordPos $hd(w) ratExpression
    ConfigMainMenu default
    destroy $hd(w)
    foreach bn [array names b $hd(w).*] {unset b($bn)}
    if [string compare $hd(mode) $option(expression_mode)] {
	set option(expression_mode) $hd(mode)
	SaveOptions
    }
    unset hd
}

# ExpWrite --
#
# Write the saved expressions to disk
#
# Arguments:

proc ExpWrite {} {
    global option expArrayId expList expName expExp

    set f [open $option(postilion_dir)/expressions w]
    puts $f "set expArrayId $expArrayId"
    puts $f "set expList [list $expList]"
    foreach e $expList {
	puts $f "set expName($e) [list $expName($e)]"
	puts $f "set expExp($e) [list $expExp($e)]"
    }
    close $f
}

# ExpRead --
#
# Read the saved expressions
#
# Arguments:

proc ExpRead {} {
    global option expArrayId expList expName expExp

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

# ExpBuildMenu
#
# Build a menu of saved expressions
#
# Arguments:
# m		- The menu to populate
# handler	- The handler which identifies the folder window that
#		  the selection should be done in.

proc ExpBuildMenu {m handler} {
    global expArrayId expList expName expExp
    $m delete 0 end

    if ![info exists expList] {
	return
    }
    foreach i $expList {
	$m add command -label $expName($i) -command "ExpMenuApply $i $handler"
    }
}

# ExpMenuApply
#
# Apply an expression from the menu
#
# Arguments:
# id		- The array index of the selected expression
# handler	- The handler which identifies the folder window that
#		  the selection should be done in.

proc ExpMenuApply {id handler} {
    upvar #0 $handler hd
    global expExp option

    set expId [RatParseExp $expExp($id)]
    set ids [$hd(folder_handler) match $expId]
    if [string length $ids] {
	SetFlag $handler flagged 1 $option(adv_on_flag) $ids
    }
    RatFreeExp $expId
}

# ExpHandleSaved
#
# Handle saved expressions
#
# Arguments:

proc ExpHandleSaved {} {
    global t b expArrayId expList expName idCnt defaultFont

    # Create identifier
    set id expDelete
    set w .$id

    # If a hierwin of this type already exists, exit, otherwise build one
    if [winfo exists $id] {
	wm deiconify $id
	raise $id
	return
    }

    upvar #0 $id hd
    set hd(win_type) expressions

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

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

    text $w.text -font $defaultFont -width 30 -height 5 -bd 1
    set b($w.text) exp_text

    button $w.delete -text $t(delete) -command "ExpDelete $w.list.list $w.text"
    set b($w.delete) exp_delete

    button $w.dismiss -text $t(dismiss) -command \
	    "RecordPos $w savedExp; \
	     RecordSize $w.list.list expList; \
	     unset b($w.list.list); \
	     unset b($w.text); \
	     unset b($w.delete); \
	     unset b($w.dismiss); \
	     destroy $w"
    set b($w.dismiss) dismiss

    pack $w.list -side top -padx 5 -pady 5 -expand 1 -fill both
    pack $w.text \
	 $w.delete -side top -fill x -padx 5 -pady 2
    pack $w.dismiss -pady 5 -padx 5
    Place $w savedExp

    if [info exists expList] {
	foreach i $expList {
	    $w.list.list insert end $expName($i)
	}
    }

    bind $w.list.list <1> " \
	    $w.text delete 1.0 end; \
	    $w.text insert end \
		    \$expExp(\[lindex \$expList \[%W index @%x,%y\]\]) \
	"
    bind $w <FocusIn> "ConfigMainMenu $id"
}

# ExpDelete --
#
# Delete the selected expression
#
# Arguments:
# lw - The listbox to delete from
# tw - The text widget to clear

proc ExpDelete {lw tw} {
    global t expArrayId expList expName expExp

    set i [$lw curselection]

    if { 1 != [llength $i]} {
	Popup $t(need_one_selected_exp)
	return
    }

    set arIndex [lindex $expList $i]
    set expList [lreplace $expList $i $i]
    unset expName($arIndex)
    unset expExp($arIndex)
    $lw delete $i
    $tw delete 1.0 end
    ExpWrite
}
