# Copyright (c) 1993 by Sanjay Ghemawat
##############################################################################
# NoteList
#
#	Maintains list of notices for a certain date.
#
# Description
# ===========
# A NoteList displays notices for a particular date.

# Autoload support
proc NoteList {} {}

class NoteList {name parent} {
    set slot(window) $name
    set slot(parent) $parent
    set slot(date) [date today]
    set slot(items) ""
    set slot(focus) ""
    set slot(width) [pref itemWindowWidth]

    frame $name -bd 0
    scrollbar $name.s -relief raised -bd 1 -orient vertical\
	-command [list $name.c yview]
    canvas $name.c -bd 1 -relief raised -yscroll [list $name.s set]

    $self configure
    $self background

    pack append $name $name.c {right expand fill}
    pack append $name $name.s {left filly}

    # Establish bindings
    $name.c bind bg <1> [list $self new]
    bind $name.c <Configure> [list $self canvas_resize %w %h]
    bind $name.c <Any-KeyPress> [list $parent key %A]

    # Handle triggers
    trigger on add	[list $self change]
    trigger on delete	[list $self remove]
    trigger on change	[list $self change]
    trigger on text	[list $self textchange]
    trigger on exclude	[list $self exclude]
    trigger on include	[list $self rescan]
}

method NoteList set_date {date} {
    set slot(date) $date
    $self rescan
    $slot(window).c yview 0
}

# effects - Cleanup on destruction
method NoteList cleanup {} {
    trigger remove add		[list $self change]
    trigger remove delete	[list $self remove]
    trigger remove change	[list $self change]
    trigger remove text		[list $self textchange]
    trigger remove exclude	[list $self exclude]
    trigger remove include	[list $self rescan]

    foreach item $slot(items) {
	class_kill $slot(window.$item)
    }

    destroy $slot(window)
}

##############################################################################
# Internal Procedures

# effects - Compute various dimensions for NoteList
method NoteList configure {} {
    set name $slot(window)

    # Set canvas geometry
    $name.c configure\
	-width [pref itemWindowWidth]\
	-height [expr [pref noteLines]*[pref itemLineHeight]]\
	-confine 1\
	-scrollincrement [pref itemLineHeight]\
	-scrollregion [list\
		0\
		0\
		[pref itemWindowWidth]\
		[expr ([pref noteLines]+1)*[pref itemLineHeight]]]
    $name.c yview 0

    # Allow vertical scrolling with middle mouse button
    $name.c bind all <2> [list $name.c scan mark 0 %y]
    $name.c bind all <B2-Motion> [list $name.c scan dragto 0 %y]
}

method NoteList background {} {
    $slot(window).c create rectangle 0 0 1 1\
	-fill ""\
	-outline ""\
	-width 0\
	-tags bg
}

method NoteList new {} {
    if {$slot(focus) != ""} {
	# Just unfocus
	$slot(window.$slot(focus)) unfocus
	return
    }

    if [cal readonly] {
	error_notify [winfo toplevel $slot(window)] "Permission denied"
	return
    }

    set id [notice]
    $id length 30
    $id date $slot(date)
    cal add $id

    set list $slot(items)
    lappend list $id
    set slot(items) $list

    $self make_window $id
    $slot(window.$id) focus

    trigger fire add $id
}

method NoteList change {item} {
    set list $slot(items)
    if {[$item is note] && [$item contains $slot(date)]} {
        if {[lsearch $list $item] < 0} {
            # Add item
            lappend list $item
            set slot(items) $list
            $self make_window $item
        } else {
            $slot(window.$item) read
        }
        $self layout
        return
    }

    if [lremove list $item] {
        set slot(items) $list
        class_kill $slot(window.$item)
        unset slot(window.$item)
        unset slot(position.$item)
        $self layout
    }
}

method NoteList textchange {item} {
    if {[$item is note] && [$item contains $slot(date)]} {
	$slot(window.$item) read
    }
}

method NoteList exclude {calendar} {
    set oldlist $slot(items)
    set slot(items) ""
    set newlist ""
    foreach item $oldlist {
	if {[$item calendar] == $calendar} {
	    class_kill $slot(window.$item)
	    unset slot(window.$item)
	    unset slot(position.$item)
	} else {
	    lappend newlist $item
	}
    }
    set slot(items) $newlist
    $self layout
}

method NoteList remove {item} {
    set list $slot(items)
    if [lremove list $item] {
	set slot(items) $list
	class_kill $slot(window.$item)
	unset slot(window.$item)
	unset slot(position.$item)
	$self layout
    }
}

# args are ignored - they just allow trigger to call us directly.
method NoteList rescan {args} {
    set list $slot(items)
    set slot(items) ""

    foreach appt $list {
        class_kill $slot(window.$appt)
	unset slot(window.$appt)
        unset slot(position.$appt)
    }

    set list {}
    cal query $slot(date) $slot(date) item d {
        if [$item is note] {
            lappend list $item
            $self make_window $item
        }
    }
    set slot(items) $list
    $self layout
}

method NoteList layout {} {
    set y 0
    foreach item $slot(items) {
	set slot(position.$item) $y
	$self place $item
	incr y [$item length]
    }

    # Set canvas geometry
    set lines [expr "($y+29)/30"]
    if {$lines < [pref noteLines]} {
	set lines [pref noteLines]
    }
    $slot(window).c configure -scrollregion [list\
	0\
	0\
	[pref itemWindowWidth]\
	[expr "($lines+1)*[pref itemLineHeight]"]]
}

method NoteList make_window {item} {
    set w [ItemWindow $slot(window).c $item $slot(date)]
    set slot(window.$item) $w
    set slot(position.$item) 0

    $w set_resize_callback  [list $self resize]
    $w set_focus_callback   [list $self focus]
    $w set_unfocus_callback [list $self unfocus]
}

method NoteList place {item} {
    set x [pref itemPad]
    set y [expr "($slot(position.$item)*[pref itemLineHeight])/30+1"]
    set width [expr "$slot(width) - 2*[pref itemPad]"]
    set height [expr "([pref itemLineHeight]*[$item length])/30"]

    $slot(window.$item) geometry $x $y $width $height
}

method NoteList canvas_resize {w h} {
    $slot(window).c coord bg 0 0 $w [expr [pref itemLineHeight]*100]
    if {$slot(width) != $w} {
	set slot(width) $w
	foreach item $slot(items) {
	    $self place $item
	}
    }
}

method NoteList resize {item top bot} {
    if {$top == "done"} {
	cal changed $item
	trigger fire change $item
	return
    }

    set len [expr "(((($bot-$slot(position.$item))*30)/[pref itemLineHeight])/15)*15"]

    if {$len < 30} {set len 30}
    if {$len != [$item length]} {
	$item length $len
	$self place $item
    }
}

method NoteList focus {item} {
    set slot(focus) $item
    $self layout

    $slot(parent) focus $item $slot(window.$item)
}

method NoteList unfocus {} {
    set slot(focus) ""
    $self layout
    $slot(parent) unfocus
}
