# Copyright (c) 1993 by Sanjay Ghemawat
##############################################################################
# ItemWindow
#
#	Displays item contents as canvas item.
#
# Description
# ===========
# An ItemWindow displays item contents and allows editing.
# The ItemWindow is always maintained in a canvas.
#
# An ItemWindow has various callback slots --

# effects - Create ItemWindow in canvas.
class ItemWindow {view canvas font item date} {
    set slot(view) $view
    set slot(canvas) $canvas
    set slot(item) $item
    set slot(date) $date
    set slot(x) -100
    set slot(y) -100
    set slot(width) 1
    set slot(height) 1
    set slot(focus) 0

    $canvas create rectangle -100 -100 -101 -101\
	-fill [pref itemOverflowColor]\
	-stipple [pref itemOverflowStipple]\
	-width 1\
	-tags [list item $self back back.$self]

    $canvas create rectangle -100 -100 -101 -101\
	-fill [pref itemBg]\
	-width 0\
	-tags [list item $self rect.$self]

    $canvas create text -100 -100\
	-anchor nw\
	-fill [pref itemFg]\
	-font $font\
	-width 0\
	-text ""\
	-tags [list item $self text.$self]

    $canvas bind $self <Button-1> [list $self click %x %y]
    $canvas bind $self <B1-Motion> [list $self select_to %x %y]

    $self read
}

# effects - Destroy item
method ItemWindow destructor {} {
    # Unselect item if necessary
    if $slot(focus) {
	ical_action $slot(view) unselect
    }

    $slot(canvas) delete $self
}

# effects - Set item geometry
method ItemWindow geometry {x y w h} {
    set slot(x) $x
    set slot(y) $y
    set slot(width) $w
    set slot(height) $h

    $self place
}

# effects - Raise associated window
method ItemWindow raise {} {
    $slot(canvas) raise $self
}

method ItemWindow save {} {
    set item $slot(item)
    set new [lindex [$slot(canvas) itemconfig text.$self -text] 4]
    set old [$item text]

    if {[string compare $new $old] == 0} {return}

    $item text $new
}

method ItemWindow read {} {
    $slot(canvas) itemconfigure text.$self -text [$slot(item) text]
    $self place
}

method ItemWindow select {} {
    if $slot(focus) {return}
    set slot(focus) 1
    focus_interest $slot(canvas)
    $slot(canvas) itemconfig text.$self -fill [pref itemSelectFg]
    $slot(canvas) itemconfig rect.$self\
	-fill [pref itemSelectBg]\
	-width [pref itemSelectWidth]

    $slot(canvas) focus text.$self
    $self raise
}

method ItemWindow unselect {} {
    if !$slot(focus) {return}
    set slot(focus) 0
    focus_disinterest $slot(canvas)
    $slot(canvas) itemconfig text.$self -fill [pref itemFg]
    $slot(canvas) itemconfig rect.$self\
	-fill [pref itemBg]\
	-width 0
    $slot(canvas) focus ""
}

method ItemWindow click {x y} {
    ical_action $slot(view) select $slot(item)
    if !$slot(focus) {return}

    set x [$slot(canvas) canvasx $x]
    set y [$slot(canvas) canvasy $y]
    $slot(canvas) icursor text.$self @$x,$y
    $slot(canvas) select from text.$self @$x,$y
}

method ItemWindow select_to {x y} {
    set x [$slot(canvas) canvasx $x]
    set y [$slot(canvas) canvasy $y]
    $slot(canvas) select to text.$self @$x,$y
}

# effects - Return bounding box for specified item
method ItemWindow bbox {} {
    return [$slot(canvas) bbox $self]
}

# Internal Operations

# effects - Place window at appropriate position in canvas
method ItemWindow place {} {
    $slot(canvas) coords text.$self [expr $slot(x)+2] [expr $slot(y)+1]
    $slot(canvas) itemconfig text.$self -width $slot(width)

    set x1 $slot(x)
    set y1 $slot(y)
    set x2 [expr $x1 + $slot(width)]
    set y2 [expr $y1 + $slot(height)]

    # Auto size item.  Check for text overflow
    set bbox [$slot(canvas) bbox text.$self]
    set yt [lindex $bbox 3]
    if {$yt > $y2} {set y2 $yt}

    $slot(canvas) coords rect.$self $x1 $y1 $x2 $y2
}

method ItemWindow canvas {} {
    return $slot(canvas)
}

method ItemWindow item {} {
    return $slot(item)
}

##############################################################################
# ApptItemWindow
#
#	Displays appointment contents as canvas item.
#
# Description
# ===========
# An ApptItemWindow displays appt contents and allows editing.
# The ApptItemWindow is always maintained in a canvas.
#
# An ApptItemWindow is a subclass of ItemWindow that maintains
# the following extra slots --
#
#	move_callback
#		Execute {move_callback $item <y-coord>} when dragging
#		an itemwindow around.  <y-coord> is coordinate for top
#		of window (within parent canvas).
#		When drag is finished, {move_callback $item done} is called.
#
#	resize_callback
#		Execute {resize_callback $item <top> <bot>} when resizing
#		an itemwindow with the mouse.  <top>/<bot> are coordinates
#		for the top and bottom of the window (within parent canvas).
#		When resize is finished, {resize_callback $item done done}
#		is called.

# effects - Create ApptItemWindow in canvas.
subclass ApptItemWindow ItemWindow {view canvas font item date m r} {
    super constructor $view $canvas $font $item $date

    set slot(move_callback) $m
    set slot(resize_callback) $r

    set slot(dragy) 0
    set slot(dragtop) 0
    set slot(dragbot) 0
    set slot(moving) 0
    set slot(resizing) 0
    set slot(minheight) 0

    $canvas bind $self <ButtonPress-2>		[list $self move_start %y]
    $canvas bind $self <B2-Motion>	 	[list $self move_continue %y]
    $canvas bind $self <ButtonRelease-2>	[list $self move_finish %y]

    $canvas bind $self <ButtonPress-3>		[list $self size_start %y]
    $canvas bind $self <B3-Motion>	 	[list $self size_continue %y]
    $canvas bind $self <ButtonRelease-3>	[list $self size_finish %y]
}

method ApptItemWindow save {} {
    set item $slot(item)
    set new [lindex [$slot(canvas) itemconfig text.$self -text] 4]
    set old [$item text]

    if {[string compare $new $old] == 0} {return}

    if ![cal option AllowOverflow] {
	# Prevent text from overflowing appointment
	if {[string length $new] > [string length $old]} {
	    set bbox [$slot(canvas) bbox text.$self]
	    if {[lindex $bbox 3] > ($slot(y) + $slot(height))} {
		# Refuse to enlarge text if it does not fit
		$self read
		return
	    }
	}
    }

    $item text $new
}

method ApptItemWindow unselect {} {
    set slot(moving) 0
    set slot(resizing) 0
    super unselect
}

# Internal Operations

# effects - Place window at appropriate position in canvas
method ApptItemWindow place {} {
    set text [$slot(item) text]

    $slot(canvas) itemconfig text.$self -text $text
    $slot(canvas) coords text.$self [expr $slot(x)+2] [expr $slot(y)+1]
    $slot(canvas) itemconfig text.$self -width $slot(width)

    set x1 $slot(x)
    set y1 $slot(y)
    set x2 [expr $x1 + $slot(width)]
    set y2 [expr $y1 + $slot(height)]

    $slot(canvas) coords rect.$self $x1 $y1 $x2 $y2

    if $slot(focus) {
	# Auto size item to avoid text overflow
	set bbox [$slot(canvas) bbox text.$self]
	set yt [lindex $bbox 3]
	if {$yt > $y2} {set y2 $yt}
    } else {
	# Truncate text to fit inside boundary
	set i [$slot(canvas) index text.$self @0,$y2]
	set text2 [string range $text 0 [expr $i-1]]
	if [string compare $text $text2] {
	    set text2 [string range $text 0 [expr $i-4]]...
	    $slot(canvas) itemconfigure text.$self -text $text2
	}
    }

    $slot(canvas) coords back.$self $x1 $y1 $x2 $y2
}

method ApptItemWindow move_start {y} {
    ical_action $slot(view) select $slot(item)
    if [cal readonly [$slot(item) calendar]] {return}

    set r [repeat_check [winfo toplevel $slot(canvas)] $slot(item) $slot(date)]
    if {$r != "unnecessary"} return

    set slot(moving) 1
    set slot(dragy) [expr $y-$slot(y)]
}

method ApptItemWindow move_continue {y} {
    if !$slot(moving) {return}

    set new_y [expr $y-$slot(dragy)]
    eval $slot(move_callback) $slot(item) $new_y
}

method ApptItemWindow move_finish {y} {
    if !$slot(moving) {return}
    $self move_continue $y
    set slot(moving) 0
    eval $slot(move_callback) $slot(item) done
}

method ApptItemWindow size_start {y} {
    ical_action $slot(view) select $slot(item)
    if [cal readonly [$slot(item) calendar]] {return}

    set r [repeat_check [winfo toplevel $slot(canvas)] $slot(item) $slot(date)]
    if {$r != "unnecessary"} return

    set slot(resizing) 1
    set slot(dragtop) 0
    set slot(dragbot) 0
    set slot(minheight) 1

    if ![cal option AllowOverflow] {
	set bbox [$slot(canvas) bbox text.$self]
	set slot(minheight) [expr [lindex $bbox 3] - [lindex $bbox 1]]
    }
}

method ApptItemWindow size_continue {y} {
    if !$slot(resizing) {return}

    set new_y [$slot(canvas) canvasy $y]
    set top $slot(y)
    set bot [expr $slot(y)+$slot(height)]

    if {$new_y < $top} {
	set slot(dragtop) 1
	set slot(dragbot) 0
	eval $slot(resize_callback) $slot(item) $new_y $bot
	return
    }

    if {$new_y > $bot} {
	set slot(dragtop) 0
	set slot(dragbot) 1
	eval $slot(resize_callback) $slot(item) $top $new_y
	return
    }

    if $slot(dragtop) {
	set top $new_y
    }

    if $slot(dragbot) {
	set bot $new_y
    }

    if {($bot - $top) >= $slot(minheight)} {
	eval $slot(resize_callback) $slot(item) $top $bot
    }
}

method ApptItemWindow size_finish {y} {
    if !$slot(resizing) {return}
    $self size_continue $y
    set slot(resizing) 0
    eval $slot(resize_callback) $slot(item) done done
}
