ycl

Artifact [e85456ae72]
Login

Artifact [e85456ae72]

Artifact e85456ae724ed12d9026d0ff722b95bf55da42b6:


#! /bin/env tclsh

package require Tk

package require {ycl tk canvas}
namespace import [yclprefix]::tk::canvas::textgrow
namespace import [yclprefix]::tk::canvas::textshrink
package require {ycl coro call}
namespace import [yclprefix]::coro::call::hi

package require {ycl string}
namespace import [yclprefix]::string::dedent
package require {ycl list}
namespace import [yclprefix]::list::ss

.my .routine textgrow
.my .routine textshrink


proc advance {_ count} {
	namespace upvar $_ autoscaledown autoscaledown autoscaleup autoscaleup \
		c c slide slide slideconfs slideconfs slides slides
	incr slide $count 
	if {![dict exists $slideconfs $slide]} {
		incr slide [expr -$count]
		return
	}
	set config [dict get $slideconfs $slide]
	if {$slide >= [llength $slides]} {
		set slide [expr {[llength $slides] - 1}]
	}
	if {$slide < 0} {
		set slide -1 
	}
	set items [$c find withtag [list slide $slide]]
	if {[llength $items]} {
		$_ layout $items 
		if {[$config get autoscaleup]} {
			$_ autoscaleup $items
		}
		if {[$config get autoscaledown]} {
			$_ autoscaledown $items
		}
	}
}
.my .method advance


proc autoscaledown {_ items} {
	namespace upvar $_ c c wheight wheight wwidth wwidth
	if {![llength $items]} return
	while 1 {
		lassign [$c bbox {*}$items] bx1 by1 bx2 by2
		if {$bx1 < 0 || $by1 < 0 || $bx2 >= $wwidth || $by2 >= $wheight} {
			$_ scaledown
		} else {
			break
		}
	}
}
.my .method autoscaledown


proc autoscaleup {_ items} {
	# No need to to worry about scaling down if we go to far, because [scaleup]
	# calls [layout], which calls [scaledown]
	namespace upvar $_ c c wheight wheight wwidth wwidth

	lassign [$c bbox {*}$items] bx1 by1 bx2 by2
	if {$bx1 > 0 && $by1 > 0 && $bx2 < $wwidth && $by2 < $wheight} {
		$_ scaleup
	}
	$_ autoscaledown $items
}
.my .method autoscaleup

.my $ config [[yclprefix] shelf shelf .spawn config_[info cmdcount]]
[.my $ config] .eval {
	package require {ycl parse tcl}
	package require {ycl parse tcl commands}

	proc get {_ args} {
		set confinterp [$_ $ confinterp]
		if {[llength $args] == 1} {
			set name [lindex $args 0]
			set script [$_ $ $name]
			switch $name {
				fontsize {
					expr {entier(max(1 ,entier([$_ num $name [$confinterp eval $script]])))}
				}
				width {
					expr {max(0,[$_ num $name [$confinterp eval $script]])}
				}
				
				default {
					$confinterp eval $script 
				}
			}
		} elseif {![llength $args]} {
			error [list whattdddd?]
		}
	}
	.my .method get 

	proc init {_ args} {
		dict update args slideshow slideshow {}
		if {[info exists slideshow]} {
			$_ $ slideshow $slideshow
		}
		$_ $ confinterp [interp create -safe]
		[$_ $ confinterp] alias page $_ page
		[$_ $ confinterp] alias get $_ get
		[$_ $ confinterp] alias num $_ num
		return $_
	}
	.my .method init

	proc num {_ name args} {
		if {[llength $args]} {
			lassign $args val
			# First quantifier is non-greedy, 
			if {![regexp {^\s*?([-+]?)\s*?([^\s]+?)\s*(%?)$} $val \
				-> sign num percent]} {
				return -code error [list {no match} $val]
			}
			if {![string is entier $num]} {
				return -code error [list {bad numeric value} $val]
			}
			if {$sign eq {}} {
				if {$percent ne {}} {
					set val [expr {[[$_ .basis] get $name] * .$num}]
				}
			} else {
				if {$percent ne {}} {
					set val [expr .$num * [[$_ .basis] get $name]]
				}
				set val [expr [[$_ .basis] get $name] $sign $val]
			}
		}
		return $val
	}
	.my .method num

	namespace eval page {
		namespace ensemble create -parameters _
		namespace export *
		proc width {_} {
			[$_ $ slideshow] $ wwidth
		}
	}
	.my .method page


	proc parse {_ config} {
		set res {}
		set config [[yclprefix] parse tcl commands commands $config]
		foreach line $config {
			set line [[yclprefix] parse tcl words $line]
			if {[llength $line] > 2} {
				return -code error [list {bad syntax} $line]
			}
			lassign $line key val
			# Assumption:  [regexp] does not modify val
			if {![regexp {^\s*?\[(.*?)\]\s*$} $val -> val]} {
				set val "lindex [list $val]"
			}
			dict set res $key $val
		}
		return $res
	}
	.my .method parse


	proc read {_ args} {
		namespace upvar $_ confinterp confinterp
		set names {anchor autoscaledown autoscaleup font fontsize virtual weight width}
		if {[llength $args]} {
			lassign $args parsed
		} else {
			set parsed {}
		}
		dict for {key val} $parsed { 
			switch $key {
				anchor - autoscaledown - autoscaleup - font - fontsize -
				scaledownfactor - scaleupfactor - virtual - weight - width {
					$_ $ $key $val 
				}
				width {
				}
				default {
					return -code error [list {unknown configuration item} $key]
				}
			}
		}
		return
	}
	.my .method read 

}
[.my $ config] init

[.my $ config] read [[.my $ config] parse {
	autoscaledown 1
	autoscaleup 1
	font Courier
	fontsize 12
	anchor center
	weight normal 
	width [page width]
	scaleupfactor 1.05
	scaledownfactor 0.95
	virtual 0 
}]


proc coords {_ item curh curv} {
	namespace upvar $_ c c citem citem 
	set type [$c type $item]
	switch $type {
		line - rectangle {
			$_ coords4 $item $curh $curv
			if {[dict exists $citem $item slave]} {
				set slave [dict get $citem $item slave]
				switch [$c type $slave] {
					text {
						$c coords $slave $curh $curv
					}
					default {
						return -code error [list {don't know how to manage the coordinates for type} [$c type $slave]]
					}
				}
			}
		}
		default {
			return -code error [
				list {unknown canvas item type} $type {for item} $item]
		}
	}
}
.my .method coords


proc coords4 {_ item curh curv} {
	namespace upvar $_ c c 
	lassign [$c coords $item] x1 y1 x2 y2
	set xd [expr {$x2 - $x1}]
	set yd [expr {$y2 - $y1}]
	$c coords $item [expr {$curh - ($xd / 2.0)}] [
		expr {$curv - ($yd / 2.0)}] [expr {$curh + ($xd / 2.0)}] [
			expr {$curv + ($yd / 2.0)}]
}
.my .method coords4


proc init {_ argv0 argv} {
	$_ $ argv0 $argv0
	$_ $ argv $argv
	# state
	$_ $ wheight -1
	$_ $ wwidth -1
	$_ $ hcenter -1 
	$_ $ vcenter -1

	$_ $ currentfontsize 0
	$_ $ scaling_busy 0
	$_ $ shiftrate 1
	$_ $ shiftratereset {}
	$_ $ slide -1
	$_ $ slides {}
	$_ $ userevent_busy 0

	$_ $ citem {}

	$_ $ config [[[$_ $ config] .spawn [info cmdcount]] init slideshow $_]

	coroutine [$_ .namespace]::douserevent apply [list {_ args} {
		while 1 {
			set args [yieldto return -level 0 [info coroutine]]
			if {[catch $args cres copts]} {
				puts stderr [dict get $copts -errorinfo]
			}
			$_ $ userevent_busy 0
		}
	} [namespace current]] $_ 
	$_ .method douserevent

	return $_
}
.my .method init


proc userevent {_ args} {
	set userevent_busy [$_ $ userevent_busy]
	if {$userevent_busy} {
		return
	}
	$_ $ userevent_busy 1
	$_ douserevent {*}$args
}
.my .method userevent


proc layout {_ items args} {
	namespace upvar $_ c c hcenter hcenter vcenter vcenter wheight wheight wwidth wwidth
	dict for {arg val} $args {
		switch $arg {
			scale {
				set $arg $val
			}
			default {
				return -code error [list {unknown argument} $arg]
			}
		}
	}

	set htotal 0
	set vtotal 0
	set curv $vcenter
	set curh $hcenter
	set visible [$c find overlapping 0 0 $wwidth $wheight]
	foreach item $visible {
		$c moveto $item -9999 -9999
	}
	foreach item $items {
		lassign [$c bbox $item] bx1 by1 bx2 by2
		set bwidth [expr {$bx2 - $bx1}]
		set bheight [expr {$by2 - $by1}]
		set bheight2 [expr {$bheight / 2.0}]
		set curv [expr {$curv + $bheight2}]
		$_ coords $item $curh $curv
		set curv [expr {$curv + $bheight2}]

		set htotal [expr {$htotal + $bwidth}]
		incr vtotal [expr {$vtotal + $bheight}]

		# Recenter viewable items
		set visible [$c find overlapping 0 0 $wwidth $wheight]
		foreach item $visible {
			$c move $item 0 [expr -$bheight2]
		}
		
	}
}
.my .method layout

apply [list {} {
	foreach {name subs} {scaleup {
		@factor@ scaleupfactor 
		@scaletext@ textgrow
	} scaledown {
		@factor@ scaledownfactor 
		@scaletext@ textshrink
	}} {
		proc $name _ [string map [list @name@ [list $name] {*}$subs] {
			namespace upvar $_ c c citem citem scale scale \
				@factor@ @factor@ scaling_busy scaling_busy slide slide 
			if {$scaling_busy} {
				return 
			}
			set scaling_busy 1
			foreach item [$c find withtag [list slide $slide]] {
				switch [$c type $item] {
					line - rectangle {
						set config [dict get $citem $item config]
						set factor [$config get @factor@]
						lassign [$c coords $item] x1 y1 x2 y2
						$c scale $item [expr {($x1 + $x2) / 2.0}] [
							expr {($y1 + $y2) / 2.0}] $factor $factor
						if {[dict exists $citem $item slave]} {
							set slave [dict get $citem $item slave]
							switch [$c type $slave] {
								text {
									$_ @scaletext@ $c $item $slave
								}
								default {
									return -code error [list {don't know how to
									scale} type [$c type $slave] for $slave]
								}
							}
						}
					}
					default {
						return [code error {don't know how to scale} type [
							$c type $item] for $item]
					}
				}
			}
			set scaling_busy 0
			$_ layout [$c find withtag [list slide $slide]]
		}]
		.my .method $name
	}
} [namespace current]]


proc reorient _ {
	namespace upvar $_ c c hcenter hcenter vcenter vcenter slide slide \
		wheight wheight wwidth wwidth
	set oldwheight $wheight
	set oldwwidth $wwidth
	set wheight [winfo height $c]
	set wwidth [winfo width $c]
	set hcenter [expr {$wwidth / 2.0}]
	set vcenter [expr {$wheight / 2.0}]
	$_ layout [$c find withtag [list slide $slide]]
}
.my .method reorient


proc run {_ args} {
	coroutine [info cmdcount] apply [list {_ args}  {
		dict for {arg val} $args {
			switch $arg {
				slides {
					$_ $ slides $val
				}
				default {
					return -code error [list {unknown arguent} $arg]
				}
				
			}
		}
		
		namespace upvar $_ c c slide slide
		wm attributes . -fullscreen
		tkwait visibility .
		set c [canvas .[info cmdcount]]
		pack $c -expand 1 -fill both
		$c configure -bg white
		bind $c <Configure> [list $_ reorient]
		bind . <KeyPress-plus> [list $_ userevent scaleup]
		bind . <KeyPress-minus> [list $_ userevent scaledown]
		foreach keyname {Left Right Up Down}  {
			bind . <KeyPress-$keyname> [list $_ shift [string tolower $keyname]]
		}
		bind . <j> [list $_ advance 1]
		bind . <k> [list $_ advance -1]
		$_ slides
		#event generate $c k
	} [namespace current]] $_ {*}$args
}
.my .method run


proc shift {_ direction} {
	namespace upvar $_ c c citem citem hcenter hcenter shiftratereset shiftratereset \
		vcenter vcenter slide slide shiftrate shiftrate
	set items [$c find withtag [list slide $slide]]
	lassign [dict get {left {-1 0} right {1 0} up {0 -1} down {0 1}} $direction] xamount yamount
	set xamount  [expr {$xamount * min(15,$shiftrate)}]
	set yamount  [expr {$yamount * min(15,$shiftrate)}]
	set hcenter [expr {$hcenter + $xamount}]
	set vcenter [expr {$vcenter + $yamount}]
	foreach item $items {
		$c move $item $xamount $yamount 
		if {[dict exists $citem $item slave]} {
			$c move [dict get $citem $item slave] $xamount $yamount
		}
	}
	set shiftrate [expr {$shiftrate * 1.4}]
	after cancel $shiftratereset
	set shiftratereset [after 200 [list $_ shiftratereset]]
}
.my .method shift


proc shiftratereset _ {
	namespace upvar $_ shiftrate shiftrate
	set shiftrate 1
}
.my .method shiftratereset


proc slide {_ slide num} {
	namespace upvar $_ c c citem citem config config
	set i -1
	foreach content [ss $slide] {
		if {[llength $content] > 3} {
			return -code error [list {wrong number components} $content]
		}
		set currentconfig [$_ $ config] 
		lassign $content type val configsource
		incr i
		switch $type {
			line {
				set config [[[$_ $ config] .spawn config_[info cmdcount]] init]
				set val [$config parse $val]
				$config read $val
				if {[llength $content] == 1} {
					$config $ virtual 1
				}
				set item [$c create line -9999 -9999 [expr {-9999 + [
					$config get width]}] -9999 -tags [list [list slide $num] [list seq $i]]]
				dict set citem $item config [$_ $ config]
			}
			text {
				set config [[[$_ $ config] .spawn config_[info cmdcount]] init]
				set configsource [$config parse $configsource]
				$config read $configsource
				dedent val
				set font [font create -size [$config get fontsize] -weight [$config get weight]]
				set item [$c create text -9999 -9999 -text $val -font $font]
				set bbox [$c bbox $item]
				set item2 [$c create rectangle {*}$bbox -width 0 -tags [
					list [list slide $num] [list seq $i]]]
				dict set citem $item2 config $config 
				dict set citem $item master $item2
				dict set citem $item2 slave $item
			}
			default {
				# slide-level configuration
				set config [[[$_ $ config] .spawn config_[info cmdcount]] init]
				set configsource [$config parse $configsource]
				$config read $configsource
				continue
			}
		}
		after 0 [list after idle [list [info coroutine]]]
		yield
		$_ $ config $currentconfig
	}
}
.my .method slide


proc slides _ {
	set slides [$_ $ slides]
	namespace upvar $_ c c  slideconfs slideconfs
	set i 0 
	foreach slide [ss $slides] {
		lassign $slide content configsource
		set currentconfig [$_ $ config]
		set config [[$currentconfig .spawn config_[info cmdcount]] init]
		set configsource [$config parse $configsource]
		$config read $configsource
		if {$content eq {}} {
			# global-level configuration
			continue
		}
		dict set slideconfs $i $config 
		$_ slide $content $i 
		$_ $ config $currentconfig
		incr i
	}
}
.my .method slides