Index: art/syntax/bubble-generator.tcl ================================================================== --- art/syntax/bubble-generator.tcl +++ art/syntax/bubble-generator.tcl @@ -1,17 +1,22 @@ #!/bin/wish # # Run this wish script to generate syntax bubble diagrams from # text descriptions. # + +option add *busyCursor watch + +package require tkpath source [file join [file dirname [info script]] bubble-generator-data.tcl] # Top-level displays # toplevel .bb -canvas .c -bg white +wm protocol .bb WM_DELETE_WINDOW {destroy .} +tkp::canvas .c -bg white pack .c -side top -fill both -expand 1 wm withdraw . # Draw the button bar # @@ -32,44 +37,41 @@ set b $side.b$bn button $b -text Everything -command {draw_all_graphs} pack $b -side top -fill x -expand 1 set tagcnt 0 ;# tag counter -set font1 {Helvetica 16 bold} ;# default token font -set font2 {GillSans 14 bold} ;# default variable font +set font1 {Helvetica 13 bold} ;# default token font +set font2 {Helvetica 11 bold} ;# default variable font set RADIUS 9 ;# default turn radius -set HSEP 17 ;# horizontal separation -set VSEP 9 ;# vertical separation -set DPI 80 ;# dots per inch - +set HSEP 15 ;# horizontal separation +set VSEP 7 ;# vertical separation # Draw a right-hand turn around. Approximately a ")" # proc draw_right_turnback {tag x y0 y1} { global RADIUS if {$y0 + 2*$RADIUS < $y1} { set xr0 [expr {$x-$RADIUS}] set xr1 [expr {$x+$RADIUS}] - .c create arc $xr0 $y0 $xr1 [expr {$y0+2*$RADIUS}] \ - -width 2 -start 90 -extent -90 -tags $tag -style arc set yr0 [expr {$y0+$RADIUS}] set yr1 [expr {$y1-$RADIUS}] + .c create path "M $x $y0 A $RADIUS $RADIUS 0 0 1 $xr1 $yr0" \ + -strokewidth 2 -tags $tag if {abs($yr1-$yr0)>$RADIUS*2} { set half_y [expr {($yr1+$yr0)/2}] - .c create line $xr1 $yr0 $xr1 $half_y -width 2 -tags $tag -arrow last - .c create line $xr1 $half_y $xr1 $yr1 -width 2 -tags $tag - } else { - .c create line $xr1 $yr0 $xr1 $yr1 -width 2 -tags $tag - } - .c create arc $xr0 [expr {$y1-2*$RADIUS}] $xr1 $y1 \ - -width 2 -start 0 -extent -90 -tags $tag -style arc - } else { + .c create pline $xr1 $yr0 $xr1 $half_y -strokewidth 2 -tags $tag \ + -endarrow on + .c create pline $xr1 $half_y $xr1 $yr1 -strokewidth 2 -tags $tag + } else { + .c create pline $xr1 $yr0 $xr1 $yr1 -strokewidth 2 -tags $tag + } + .c create path "M $xr1 $yr1 A $RADIUS $RADIUS 0 0 1 $x $y1" \ + -strokewidth 2 -tags $tag + } else { set r [expr {($y1-$y0)/2.0}] - set x0 [expr {$x-$r}] - set x1 [expr {$x+$r}] - .c create arc $x0 $y0 $x1 $y1 \ - -width 2 -start 90 -extent -180 -tags $tag -style arc + .c create path "M $x $y0 A $r $r 0 0 0 $x $y1" \ + -strokewidth 2 -tags $tag } } # Draw a left-hand turn around. Approximately a "(" # @@ -76,50 +78,51 @@ proc draw_left_turnback {tag x y0 y1 dir} { global RADIUS if {$y0 + 2*$RADIUS < $y1} { set xr0 [expr {$x-$RADIUS}] set xr1 [expr {$x+$RADIUS}] - .c create arc $xr0 $y0 $xr1 [expr {$y0+2*$RADIUS}] \ - -width 2 -start 90 -extent 90 -tags $tag -style arc set yr0 [expr {$y0+$RADIUS}] set yr1 [expr {$y1-$RADIUS}] - if {abs($yr1-$yr0)>$RADIUS*3} { + .c create path "M $x $y0 A $RADIUS $RADIUS 0 0 0 $xr0 $yr0" \ + -strokewidth 2 -tags $tag + if {abs($yr1-$yr0)>$RADIUS*2} { set half_y [expr {($yr1+$yr0)/2}] if {$dir=="down"} { - .c create line $xr0 $yr0 $xr0 $half_y -width 2 -tags $tag -arrow last - .c create line $xr0 $half_y $xr0 $yr1 -width 2 -tags $tag - } else { - .c create line $xr0 $yr1 $xr0 $half_y -width 2 -tags $tag -arrow last - .c create line $xr0 $half_y $xr0 $yr0 -width 2 -tags $tag - } - } else { - .c create line $xr0 $yr0 $xr0 $yr1 -width 2 -tags $tag - } - # .c create line $xr0 $yr0 $xr0 $yr1 -width 2 -tags $tag - .c create arc $xr0 [expr {$y1-2*$RADIUS}] $xr1 $y1 \ - -width 2 -start 180 -extent 90 -tags $tag -style arc - } else { + .c create pline $xr0 $yr0 $xr0 $half_y -strokewidth 2 -tags $tag \ + -endarrow on + .c create pline $xr0 $half_y $xr0 $yr1 -strokewidth 2 -tags $tag + } else { + .c create pline $xr0 $yr1 $xr0 $half_y -strokewidth 2 -tags $tag \ + -endarrow on + .c create pline $xr0 $half_y $xr0 $yr0 -strokewidth 2 -tags $tag + } + } else { + .c create pline $xr0 $yr0 $xr0 $yr1 -strokewidth 2 -tags $tag + } + .c create path "M $xr0 $yr1 A $RADIUS $RADIUS 0 0 0 $x $y1" \ + -strokewidth 2 -tags $tag + } else { set r [expr {($y1-$y0)/2.0}] - set x0 [expr {$x-$r}] - set x1 [expr {$x+$r}] - .c create arc $x0 $y0 $x1 $y1 \ - -width 2 -start 90 -extent 180 -tags $tag -style arc + .c create path "M $x $y0 A $r $r 0 0 1 $x $y1" \ + -strokewidth 2 -tags $tag } } -# Draw a bubble containing $txt. +# Draw a bubble containing $txt. # proc draw_bubble {txt} { global tagcnt incr tagcnt set tag x$tagcnt - if {$txt=="nil"} { - .c create line 0 0 1 0 -width 2 -tags $tag + if {$txt eq "nil"} { + .c create pline 0 0 1 0 -strokewidth 2 -tags $tag return [list $tag 1 0] - } elseif {$txt=="bullet"} { - .c create oval 0 -3 6 3 -width 2 -tags $tag + } elseif {$txt eq "bullet"} { + .c create circle 2 0 -r 3 -strokewidth 2 -tags $tag return [list $tag 6 0] + } elseif {$txt eq "--"} { + set txt "- -" } if {[regexp {^/[a-z]} $txt]} { set txt [string range $txt 1 end] set font $::font2 set istoken 1 @@ -128,39 +131,42 @@ set istoken 0 } else { set font $::font1 set istoken 1 } - set id1 [.c create text 0 0 -anchor c -text $txt -font $font -tags $tag] + foreach {ffam fsize fweight} $font break + set id1 [.c create ptext 0 0 -text $txt -fontfamily $ffam \ + -fontsize $fsize -fontweight $fweight -textanchor c -tags $tag] foreach {x0 y0 x1 y1} [.c bbox $id1] break set h [expr {$y1-$y0+2}] set rad [expr {($h+1)/2}] set top [expr {$y0-2}] - set btm [expr {$y1}] + set btm [expr {$y1+2}] set fudge [expr {int(3*$istoken + [string length $txt]*1.4)}] #puts "fudge($txt)=$fudge" - set left [expr {$x0+$fudge}] - set right [expr {$x1-$fudge}] - if {$left>$right} { - set left [expr {($x0+$x1)/2}] - set right $left - } + set left [expr {$x0-$fudge}] + set right [expr {$x1+$fudge}] set tag2 x$tagcnt-box set tags [list $tag $tag2] if {$istoken} { - .c create arc [expr {$left-$rad}] $top [expr {$left+$rad}] $btm \ - -width 2 -start 90 -extent 180 -style arc -tags $tags - .c create arc [expr {$right-$rad}] $top [expr {$right+$rad}] $btm \ - -width 2 -start -90 -extent 180 -style arc -tags $tags - if {$left<$right} { - .c create line $left $top $right $top -width 2 -tags $tags - .c create line $left $btm $right $btm -width 2 -tags $tags + set w [expr {$right-$left}] + if {$w>1.3*$h} { + .c create prect $left $top $right $btm -rx $rad -strokewidth 2 \ + -tags $tags -fill gray80 -fillopacity 0.1 + } else { + set xx [expr {($x0+$x1)/2}] + set yy [expr {($y0+$y1)/2}] + .c create circle $xx $yy -r [expr {2+$h/2}] -strokewidth 2 \ + -tags $tags -fill gray80 -fillopacity 0.1 } } else { - .c create rect $left $top $right $btm -width 2 -tags $tags + .c create prect $left $top $right $btm -strokewidth 2 \ + -tags $tags -fill gray80 -fillopacity 0.1 } foreach {x0 y0 x1 y1} [.c bbox $tag2] break + set x0 [expr {$x0+3}] + set x1 [expr {$x1-3}] set width [expr {$x1-$x0}] .c move $tag [expr {-$x0}] 0 # Entry is always 0 0 # Return: TAG EXIT_X EXIT_Y @@ -183,24 +189,24 @@ set m [draw_diagram $term] foreach {t texx texy} $m break if {$exx>0} { set xn [expr {$exx+$sep}] .c move $t $xn $exy - .c create line [expr {$exx-1}] $exy $xn $exy \ - -tags $tag -width 2 -arrow last + .c create pline [expr {$exx-1}] $exy $xn $exy \ + -tags $tag -strokewidth 2 -endarrow on set exx [expr {$xn+$texx}] } else { set exx $texx } set exy $texy .c addtag $tag withtag $t .c dtag $t $t } - if {$exx==0} { + if {$exx==0} { set exx [expr {$sep*2}] - .c create line 0 0 $sep 0 -width 2 -tags $tag -arrow last - .c create line $sep 0 $exx 0 -width 2 -tags $tag + .c create pline 0 0 $sep 0 -strokewidth 2 -tags $tag -endarrow on + .c create pline $sep 0 $exx 0 -strokewidth 2 -tags $tag set exx $sep } return [list $tag $exx $exy] } @@ -225,21 +231,22 @@ foreach {tx0 ty0 tx1 ty1} [.c bbox $t] break set w [expr {$tx1-$tx0}] if {$exx>0} { set xn [expr {$exx+$sep}] .c move $t $xn 0 - .c create line $exx $exy $xn $exy -tags $tag -width 2 -arrow first + .c create pline $exx $exy $xn $exy -tags $tag -strokewidth 2 \ + -startarrow on set exx [expr {$xn+$texx}] } else { set exx $texx } set exy $texy .c addtag $tag withtag $t .c dtag $t $t } if {$exx==0} { - .c create line 0 0 $sep 0 -width 2 -tags $tag + .c create pline 0 0 $sep 0 -strokewidth 2 -tags $tag set exx $sep } return [list $tag $exx $exy] } @@ -285,65 +292,65 @@ set enter_x [expr {$sep*2 + $indent}] } set back_y [expr {$btm + $sep + 1}] if {$bypass_y>0} { set mid_y [expr {($bypass_y+$RADIUS+$back_y)/2}] - .c create line $bypass_x $bypass_y $bypass_x $mid_y \ - -width 2 -tags $tag -arrow last - .c create line $bypass_x $mid_y $bypass_x [expr {$back_y+$RADIUS}] \ - -tags $tag -width 2 + .c create pline $bypass_x $bypass_y $bypass_x $mid_y \ + -strokewidth 2 -tags $tag -endarrow on + .c create pline $bypass_x $mid_y $bypass_x [expr {$back_y+$RADIUS}] \ + -tags $tag -strokewidth 2 } .c move $t $enter_x $enter_y set e2 [expr {$exit_x + $sep}] - .c create line $exit_x $exit_y $e2 $exit_y \ - -width 2 -tags $tag + .c create pline $exit_x $exit_y $e2 $exit_y \ + -strokewidth 2 -tags $tag draw_right_turnback $tag $e2 $exit_y $back_y set e3 [expr {$enter_x-$sep}] set bypass_x [expr {$e3-$RADIUS}] set emid [expr {($e2+$e3)/2}] - .c create line $e2 $back_y $emid $back_y \ - -width 2 -tags $tag -arrow last - .c create line $emid $back_y $e3 $back_y \ - -width 2 -tags $tag + .c create pline $e2 $back_y $emid $back_y -strokewidth 2 -tags $tag \ + -endarrow on + .c create pline $emid $back_y $e3 $back_y -strokewidth 2 -tags $tag set r2 [expr {($enter_y - $back_y)/2.0}] draw_left_turnback $tag $e3 $back_y $enter_y down - .c create line $e3 $enter_y $enter_x $enter_y \ - -arrow last -width 2 -tags $tag + .c create pline $e3 $enter_y $enter_x $enter_y -endarrow on \ + -strokewidth 2 -tags $tag set exit_x [expr {$enter_x + $exx}] set exit_y [expr {$enter_y + $exy}] } .c addtag $tag withtag $t .c dtag $t $t set btm [lindex [.c bbox $tag] 3] incr i } if {$bypass} { + set r $RADIUS set fwd_y [expr {$btm + $sep + 1}] - set mid_y [expr {($next_bypass_y+$RADIUS+$fwd_y)/2}] - set descender_x [expr {$exit_x+$RADIUS}] - .c create line $bypass_x $next_bypass_y $bypass_x $mid_y \ - -width 2 -tags $tag -arrow last - .c create line $bypass_x $mid_y $bypass_x [expr {$fwd_y-$RADIUS}] \ - -tags $tag -width 2 - .c create arc $bypass_x [expr {$fwd_y-2*$RADIUS}] \ - [expr {$bypass_x+2*$RADIUS}] $fwd_y \ - -width 2 -start 180 -extent 90 -tags $tag -style arc - .c create arc [expr {$exit_x-$RADIUS}] $exit_y \ - $descender_x [expr {$exit_y+2*$RADIUS}] \ - -width 2 -start 90 -extent -90 -tags $tag -style arc - .c create arc $descender_x [expr {$fwd_y-2*$RADIUS}] \ - [expr {$descender_x+2*$RADIUS}] $fwd_y \ - -width 2 -start 180 -extent 90 -tags $tag -style arc - set exit_x [expr {$exit_x+2*$RADIUS}] + set mid_y [expr {($next_bypass_y+$r+$fwd_y)/2}] + set descender_x [expr {$exit_x+$r}] + .c create pline $bypass_x $next_bypass_y $bypass_x $mid_y \ + -strokewidth 2 -tags $tag -endarrow on + .c create pline $bypass_x $mid_y $bypass_x [expr {$fwd_y-$r}] \ + -tags $tag -strokewidth 2 + set path "M $bypass_x [expr {$fwd_y-$r}]" + append path " A $r $r 0 0 0 [expr {$bypass_x+$r}] $fwd_y" + .c create path $path -strokewidth 2 -tags $tag + set path "M $exit_x $exit_y" + append path " A $r $r 0 0 1 $descender_x [expr {$exit_y+$r}]" + .c create path $path -strokewidth 2 -tags $tag + set path "M $descender_x [expr {$fwd_y-$r}]" + append path " A $r $r 0 0 0 [expr {$descender_x+$r}] $fwd_y" + .c create path $path -strokewidth 2 -tags $tag + set exit_x [expr {$exit_x+2*$r}] set half_x [expr {($exit_x+$indent)/2}] - .c create line [expr {$bypass_x+$RADIUS}] $fwd_y $half_x $fwd_y \ - -width 2 -tags $tag -arrow last - .c create line $half_x $fwd_y $exit_x $fwd_y \ - -width 2 -tags $tag - .c create line $descender_x [expr {$exit_y+$RADIUS}] \ - $descender_x [expr {$fwd_y-$RADIUS}] \ - -width 2 -tags $tag -arrow last + .c create pline [expr {$bypass_x+$r}] $fwd_y $half_x $fwd_y \ + -strokewidth 2 -tags $tag -endarrow on + .c create pline $half_x $fwd_y $exit_x $fwd_y \ + -strokewidth 2 -tags $tag + .c create pline $descender_x [expr {$exit_y+$r}] \ + $descender_x [expr {$fwd_y-$r}] \ + -strokewidth 2 -tags $tag -endarrow on set exit_y $fwd_y } set width [lindex [.c bbox $tag] 2] return [list $tag $exit_x $exit_y] } @@ -376,41 +383,43 @@ if {$fw>$bw} { if {$fexx<$fw && $fexx>=$bw} { set dx [expr {($fexx-$bw)/2}] .c move $bt $dx 0 set bexx [expr {$dx+$bexx}] - .c create line 0 $biny $dx $biny -width 2 -tags $bt - .c create line $bexx $bexy $fexx $bexy -width 2 -tags $bt -arrow first + .c create pline 0 $biny $dx $biny -strokewidth 2 -tags $bt + .c create pline $bexx $bexy $fexx $bexy -strokewidth 2 -tags $bt \ + -startarrow on set mxx $fexx } else { set dx [expr {($fw-$bw)/2}] .c move $bt $dx 0 set bexx [expr {$dx+$bexx}] - .c create line 0 $biny $dx $biny -width 2 -tags $bt - .c create line $bexx $bexy $fx1 $bexy -width 2 -tags $bt -arrow first + .c create pline 0 $biny $dx $biny -strokewidth 2 -tags $bt + .c create pline $bexx $bexy $fx1 $bexy -strokewidth 2 -tags $bt \ + -startarrow on set mxx $fexx } } elseif {$bw>$fw} { set dx [expr {($bw-$fw)/2}] .c move $ft $dx 0 set fexx [expr {$dx+$fexx}] - .c create line 0 0 $dx $fexy -width 2 -tags $ft -arrow last - .c create line $fexx $fexy $bx1 $fexy -width 2 -tags $ft + .c create pline 0 0 $dx $fexy -strokewidth 2 -tags $ft -endarrow on + .c create pline $fexx $fexy $bx1 $fexy -strokewidth 2 -tags $ft set mxx $bexx } .c addtag $tag withtag $bt .c addtag $tag withtag $ft .c dtag $bt $bt .c dtag $ft $ft .c move $tag $sep 0 set mxx [expr {$mxx+$sep}] - .c create line 0 0 $sep 0 -width 2 -tags $tag + .c create pline 0 0 $sep 0 -strokewidth 2 -tags $tag draw_left_turnback $tag $sep 0 $biny up draw_right_turnback $tag $mxx $fexy $bexy foreach {x0 y0 x1 y1} [.c bbox $tag] break set exit_x [expr {$mxx+$::RADIUS}] - .c create line $mxx $fexy $exit_x $fexy -width 2 -tags $tag + .c create pline $mxx $fexy $exit_x $fexy -strokewidth 2 -tags $tag return [list $tag $exit_x $fexy] } proc draw_toploop {forward back} { global tagcnt @@ -434,32 +443,33 @@ if {$fw>$bw} { set dx [expr {($fw-$bw)/2}] .c move $bt $dx 0 set bexx [expr {$dx+$bexx}] - .c create line 0 $biny $dx $biny -width 2 -tags $bt - .c create line $bexx $bexy $fx1 $bexy -width 2 -tags $bt -arrow first + .c create pline 0 $biny $dx $biny -strokewidth 2 -tags $bt + .c create pline $bexx $bexy $fx1 $bexy -strokewidth 2 -tags $bt \ + -startarrow on set mxx $fexx } elseif {$bw>$fw} { set dx [expr {($bw-$fw)/2}] .c move $ft $dx 0 set fexx [expr {$dx+$fexx}] - .c create line 0 0 $dx $fexy -width 2 -tags $ft - .c create line $fexx $fexy $bx1 $fexy -width 2 -tags $ft + .c create pline 0 0 $dx $fexy -strokewidth 2 -tags $ft + .c create pline $fexx $fexy $bx1 $fexy -strokewidth 2 -tags $ft set mxx $bexx } .c addtag $tag withtag $bt .c addtag $tag withtag $ft .c dtag $bt $bt .c dtag $ft $ft .c move $tag $sep 0 set mxx [expr {$mxx+$sep}] - .c create line 0 0 $sep 0 -width 2 -tags $tag + .c create pline 0 0 $sep 0 -strokewidth 2 -tags $tag draw_left_turnback $tag $sep 0 $biny down draw_right_turnback $tag $mxx $fexy $bexy foreach {x0 y0 x1 y1} [.c bbox $tag] break - .c create line $mxx $fexy $x1 $fexy -width 2 -tags $tag + .c create pline $mxx $fexy $x1 $fexy -strokewidth 2 -tags $tag return [list $tag $x1 $fexy] } proc draw_or {lx} { global tagcnt @@ -479,11 +489,11 @@ if {$w>$mxw} {set mxw $w} incr i } set x0 0 ;# entry x - set x1 $sep ;# decender + set x1 $sep ;# decender set x2 [expr {$sep*2}] ;# start of choice set xc [expr {$mxw/2}] ;# center point set x3 [expr {$mxw+$x2}] ;# end of choice set x4 [expr {$x3+$sep}] ;# accender set x5 [expr {$x4+$sep}] ;# exit x @@ -497,46 +507,50 @@ .c move $t $dx 0 set texx [expr {$texx+$dx}] set m($i) [list $t $texx $texy] foreach {tx0 ty0 tx1 ty1} [.c bbox $t] break if {$i==0} { - if {$dx>$x2} {set ax last} {set ax none} - .c create line 0 0 $dx 0 -width 2 -tags $tag -arrow $ax - .c create line $texx $texy [expr {$x5+1}] $texy -width 2 -tags $tag + if {$dx>$x2} {set ax {-endarrow on}} {set ax {}} + .c create pline 0 0 $dx 0 -strokewidth 2 -tags $tag {*}$ax + .c create pline $texx $texy [expr {$x5+1}] $texy -strokewidth 2 \ + -tags $tag set exy $texy - .c create arc -$sep 0 $sep [expr {$sep*2}] \ - -width 2 -start 90 -extent -90 -tags $tag -style arc + .c create path "M 0 0 A $sep $sep 0 0 1 $sep $sep" \ + -strokewidth 2 -tags $tag set btm $ty1 } else { set dy [expr {$btm - $ty0 + $vsep}] if {$dy<2*$sep} {set dy [expr {2*$sep}]} .c move $t 0 $dy set texy [expr {$texy+$dy}] if {$dx>$x2} { - .c create line $x2 $dy $dx $dy -width 2 -tags $tag -arrow last - if {$dx<$xc-2} {set ax last} {set ax none} - .c create line $texx $texy $x3 $texy -width 2 -tags $tag -arrow $ax + .c create pline $x2 $dy $dx $dy -strokewidth 2 -tags $tag -endarrow on + if {$dx<$xc-2} {set ax {-endarrow on}} {set ax {}} + .c create pline $texx $texy $x3 $texy -strokewidth 2 -tags $tag {*}$ax } set y1 [expr {$dy-2*$sep}] - .c create arc $x1 $y1 [expr {$x1+2*$sep}] $dy \ - -width 2 -start 180 -extent 90 -style arc -tags $tag + .c create path \ + "M $x1 [expr {$y1+$sep}] A $sep $sep 0 0 0 [expr {$x1+$sep}] $dy" \ + -strokewidth 2 -tags $tag set y2 [expr {$texy-2*$sep}] - .c create arc [expr {$x3-$sep}] $y2 $x4 $texy \ - -width 2 -start 270 -extent 90 -style arc -tags $tag + .c create path \ + "M $x3 [expr {$y2+2*$sep}] A $sep $sep 0 0 0 $x4 [expr {$y2+$sep}]" \ + -strokewidth 2 -tags $tag if {$i==$n-1} { - .c create arc $x4 $exy [expr {$x4+2*$sep}] [expr {$exy+2*$sep}] \ - -width 2 -start 180 -extent -90 -tags $tag -style arc - .c create line $x1 [expr {$dy-$sep}] $x1 $sep -width 2 -tags $tag - .c create line $x4 [expr {$texy-$sep}] $x4 [expr {$exy+$sep}] \ - -width 2 -tags $tag + .c create path \ + "M $x4 [expr {$exy+$sep}] A $sep $sep 0 0 1 [expr {$x4+$sep}] $exy" \ + -strokewidth 2 -tags $tag + .c create pline $x1 [expr {$dy-$sep}] $x1 $sep -strokewidth 2 -tags $tag + .c create pline $x4 [expr {$texy-$sep}] $x4 [expr {$exy+$sep}] \ + -strokewidth 2 -tags $tag } set btm [expr {$ty1+$dy}] } .c addtag $tag withtag $t .c dtag $t $t } - return [list $tag $x5 $exy] + return [list $tag $x5 $exy] } proc draw_tail_branch {lx} { global tagcnt incr tagcnt @@ -549,36 +563,38 @@ set m($i) [set mx [draw_diagram $term]] incr i } set x0 0 ;# entry x - set x1 $sep ;# decender + set x1 $sep ;# decender set x2 [expr {$sep*2}] ;# start of choice for {set i 0} {$i<$n} {incr i} { foreach {t texx texy} $m($i) break foreach {tx0 ty0 tx1 ty1} [.c bbox $t] break set dx [expr {$x2+10}] .c move $t $dx 0 foreach {tx0 ty0 tx1 ty1} [.c bbox $t] break if {$i==0} { - .c create line 0 0 $dx 0 -width 2 -tags $tag -arrow last - .c create arc -$sep 0 $sep [expr {$sep*2}] \ - -width 2 -start 90 -extent -90 -tags $tag -style arc + .c create pline 0 0 $dx 0 -strokewidth 2 -tags $tag -endarrow on + .c create path "M 0 0 A $sep $sep 0 0 1 $sep $sep" \ + -strokewidth 2 -tags $tag set btm $ty1 } else { set dy [expr {$btm - $ty0 + $vsep}] if {$dy<2*$sep} {set dy [expr {2*$sep}]} .c move $t 0 $dy if {$dx>$x2} { - .c create line $x2 $dy $dx $dy -width 2 -tags $tag -arrow last + .c create pline $x2 $dy $dx $dy -strokewidth 2 -tags $tag -endarrow on } set y1 [expr {$dy-2*$sep}] - .c create arc $x1 $y1 [expr {$x1+2*$sep}] $dy \ - -width 2 -start 180 -extent 90 -style arc -tags $tag + set path "M $x1 [expr {$y1+$sep}]" + append path " A $sep $sep 0 0 0 [expr {$x1+$sep}] $dy" + .c create path $path -strokewidth 2 -tags $tag if {$i==$n-1} { - .c create line $x1 [expr {$dy-$sep}] $x1 $sep -width 2 -tags $tag + .c create pline $x1 [expr {$dy-$sep}] $x1 $sep -strokewidth 2 \ + -tags $tag } set btm [expr {$ty1+$dy}] } .c addtag $tag withtag $t .c dtag $t $t @@ -642,42 +658,99 @@ return [draw_or [lrange $spec 1 end]] } error "unknown operator: $cmd" } -proc draw_graph {name spec {do_xv 1}} { +proc copy_to_surface {surf} { + foreach id [.c find withtag all] { + switch -- [.c type $id] { + pline { + # tricky: arrow heads adjust coords, but we want the original ones! + set sa [.c itemcget $id -startarrow] + set ea [.c itemcget $id -endarrow] + .c itemconfig $id -startarrow off -endarrow off + $surf create pline [.c coords $id] \ + -strokewidth [.c itemcget $id -strokewidth] \ + -stroke [.c itemcget $id -stroke] \ + -startarrow $sa -endarrow $ea + .c itemconfig $id -startarrow $sa -endarrow $ea + } + path { + $surf create path [.c coords $id] \ + -strokewidth [.c itemcget $id -strokewidth] \ + -stroke [.c itemcget $id -stroke] + } + circle { + $surf create circle [.c coords $id] \ + -r [.c itemcget $id -r] \ + -strokewidth [.c itemcget $id -strokewidth] \ + -stroke [.c itemcget $id -stroke] + } + prect { + $surf create prect [.c coords $id] \ + -rx [.c itemcget $id -rx] \ + -ry [.c itemcget $id -ry] \ + -strokewidth [.c itemcget $id -strokewidth] \ + -stroke [.c itemcget $id -stroke] \ + -fillopacity [.c itemcget $id -fillopacity] \ + -fill [.c itemcget $id -fill] + } + ptext { + $surf create ptext [.c coords $id] \ + -strokewidth [.c itemcget $id -strokewidth] \ + -stroke [.c itemcget $id -stroke] \ + -fontfamily [.c itemcget $id -fontfamily] \ + -fontsize [.c itemcget $id -fontsize] \ + -text [.c itemcget $id -text] \ + -textanchor [.c itemcget $id -textanchor] \ + -fontweight [.c itemcget $id -fontweight] + } + } + } +} + +proc draw_graph {name spec {do_surf 0}} { .c delete all wm deiconify . wm title . $name draw_diagram "line bullet [list $spec] bullet" foreach {x0 y0 x1 y1} [.c bbox all] break .c move all [expr {2-$x0}] [expr {2-$y0}] foreach {x0 y0 x1 y1} [.c bbox all] break - .c create rect -100 -100 [expr {$x1+100}] [expr {$y1+100}] \ - -fill white -outline white -tags bgrect + .c create prect -100 -100 [expr {$x1+100}] [expr {$y1+100}] \ + -fill white -stroke white -tags bgrect .c lower bgrect .c config -width $x1 -height $y1 - update - .c postscript -file $name.ps -width [expr {$x1+2}] -height [expr {$y1+2}] - global DPI .c delete bgrect - exec convert -density ${DPI}x$DPI -antialias $name.ps $name.gif - if {$do_xv} { - if {[catch {exec xv $name.gif &}]} { - exec display $name.gif & - } + catch {tk busy hold .} + update + catch {tk busy forget .} + if {$do_surf} { + set ws [expr {$x1+2}] + set hs [expr {$y1+2}] + set surf [tkp::surface new $ws $hs] + $surf erase 0 0 $ws $hs + $surf create prect 0 0 $ws $hs -fill white + copy_to_surface $surf + set img [image create photo] + $surf copy $img + $surf destroy + # $img write $name.png -format png + $img write $name.gif -format gif + image delete $img } } proc draw_all_graphs {} { global all_graphs + catch {tk busy hold .bb} set f [open all.html w] foreach {name graph} $all_graphs { if {[regexp {^X-} $name]} continue puts $f "

$name:

" puts $f "" - draw_graph $name $graph 0 + draw_graph $name $graph 1 set img($name) 1 set children($name) {} set parents($name) {} } close $f @@ -699,10 +772,11 @@ set px [lsort $parents($name)] puts $f [list set syntax_linkage($name) [list $cx $px]] } puts $f [list set syntax_order $order] close $f + catch {tk busy forget .bb} wm withdraw . } proc walk_graph_extract_names {graph varname} { upvar 1 $varname v Index: pages/changes.in ================================================================== --- pages/changes.in +++ pages/changes.in @@ -30,18 +30,32 @@
  • On unix, if a symlink to a database file is opened, then the corresponding journal files are based on the actual filename, not the symlink name.
  • Added the "--transaction" option to [sqldiff].
  • Added the [sqlite3_db_cacheflush()] interface.
  • Added the [sqlite3_strlike()] interface. +
  • When using [memory-mapped I/O] map the database file read-only so that stray pointers + and/or array overruns in the application cannot accidently modify the database file. +
  • Added the experimental [sqlite3_snapshot_get()], [sqlite3_snapshot_open()], + and [sqlite3_snapshot_free()] interfaces. These are subject to change or removal in + a subsequent release. +
  • Enhance the ['utc' modifier] in the [date and time functions] so that it is a no-op if + the date/time is known to already be in UTC. (This is not a compatibility break since + the behavior has long been documented as "undefined" in that case.) +
  • Added the [json_group_array()] and [json_group_object()] SQL functions in the + [json] extension.
  • Many small performance optimizations.

    Portability enhancements:

  • Work around a sign-exension bug in the optimizer of the HP C compiler on HP/UX. + [https://www.sqlite.org/src/fdiff?sbs=1&v1=869c95b0fc73026d&v2=232c242a0ccb3d67|(details)] +

    Enhancements to the [command-line shell]: +

  • Added the ".changes ON|OFF" and ".vfsinfo" [dot-commands]. +
  • Translate between MBCS and UTF8 when + running in [https://en.wikipedia.org/wiki/Cmd.exe|cmd.exe] on Windows.

    Enhancements to makefiles: -

  • Added the --enable-editline option to the various autoconf-generated configure - scripts. -
  • Omit all use of "awk" in the makefiles, - to make building easier for MSVC users. +
  • Added the --enable-editline and --enable-static-shell options + to the various autoconf-generated configure scripts. +
  • Omit all use of "awk" in the makefiles, to make building easier for MSVC users.

    Important fixes:

  • Fix inconsistent integer to floating-point comparison operations that could result in a corrupt index if the index is created on a table column that contains both large integers and floating point values of similar magnitude. Ticket Index: pages/cli.in ================================================================== --- pages/cli.in +++ pages/cli.in @@ -118,11 +118,12 @@

    Be careful when using the ".save" command as it will overwrite any preexisting database files having the same name without prompting for confirmation. As with the ".open" command, you might want to use a full pathname with forward-slash directory separators to avoid ambiguity. -

    Special commands to sqlite3

    +hd_fragment dotcmd {dot-commands} +

    Special commands to sqlite3 (dot-commands)

    Most of the time, sqlite3 just reads lines of input and passes them on to the SQLite library for execution. But if an input line begins with a dot ("."), then @@ -139,10 +140,11 @@ DisplayCode { sqlite> (((.help))) .backup ?DB? FILE Backup DB (default "main") to FILE .bail on|off Stop after hitting an error. Default OFF .binary on|off Turn binary output on or off. Default OFF +.changes on|off Show number of rows changed by SQL .clone NEWDB Clone data into NEWDB from the existing database .databases List names and files of attached databases .dbinfo ?DB? Show status information about the database .dump ?TABLE? ... Dump the database in an SQL text format If TABLE specified, only dump tables matching @@ -196,10 +198,11 @@ If TABLE specified, only list tables matching LIKE pattern TABLE. .timeout MS Try opening locked tables for MS milliseconds .timer on|off Turn SQL timer on or off .trace FILE|off Output each SQL statement as it is run +.vfsinfo ?AUX? Information about the top-level VFS .vfsname ?AUX? Print the name of the VFS stack .width NUM1 NUM2 ... Set column widths for "column" mode Negative values right-justify sqlite> } Index: pages/json1.in ================================================================== --- pages/json1.in +++ pages/json1.in @@ -2,14 +2,14 @@ hd_keywords json1 {the json1 extension} {JSON SQL functions}

    The JSON1 Extension

    The json1 extension is a [loadable extension] that -implements eleven [application-defined SQL functions] and +implements thirteen [application-defined SQL functions] and two [table-valued functions] that are useful for managing [http://json.org/ | JSON] content stored in an SQLite database. -These are the SQL functions implemented by json1: +These are the scalar SQL functions implemented by json1:

    set tabcnt 0 @@ -91,10 +91,26 @@ } jtype tabentry {json_valid(json)} { Return true (1) if the input text is a valid JSON string } jvalid + +
    + +

    There are two aggregate SQL functions: + +

    + +tabentry {json_group_array(value)} { + Return a JSON array composed of all value elements + in the aggregation. +} jgrouparray + +tabentry {json_group_object(name,value)} { + Return a JSON object composed of all name and value pairs + in the aggregation. +} jgroupobject

    The [table-valued functions] implemented by this routine are: @@ -510,14 +526,29 @@ jexample \ {json_valid('{"x":35}')} 1 \ "json_valid('\173\"x\":35')" 0 + +hd_fragment jgrouparray {json_group_array SQL function} \ + {json_group_array} +hd_fragment jgroupobject {json_group_object SQL function} \ + {json_group_object} + +

    3.10 The json_group_array() and json_group_object() +aggregate SQL functions

    + +

    The json_group_array(X) function is an +[Aggregate Functions|aggregate SQL function] that returns a JSON array +comprised of all X values in the aggregation. +Similarly, the json_group_object(NAME,VALUE) function returns a JSON object +comprised of all NAME/VALUE pairs in the aggregation. + hd_fragment jeach {json_each table-valued function} {json_each} hd_fragment jtree {json_tree table-valued function} {json_tree} -

    3.9 The json_each() and json_tree() table-valued functions

    +

    3.11 The json_each() and json_tree() table-valued functions

    The json_each(X) and json_tree(X) [table-valued functions] walk the JSON value provided as their first argument and return one row for each element. The json_each(X) function only walks the immediate children of the top-level array or object or @@ -588,11 +619,11 @@ The "path" column is the path to the array or object container the holds the current row, or the path to the current row in the case where the iteration starts on a primitive type and thus only provides a single row of output. -

    3.10.1 Examples using json_each() and json_tree()

    +

    3.11.1 Examples using json_each() and json_tree()

    Suppose the table "CREATE TABLE user(name,phone)" stores zero or more phone numbers as a JSON array object in the user.phone field. To find all users who have any phone number with a 704 area code: Index: pages/lang.in ================================================================== --- pages/lang.in +++ pages/lang.in @@ -1950,11 +1950,11 @@ D can reduce its maximum parameter number below the compile-time maximum using the [sqlite3_limit](D, [SQLITE_LIMIT_VARIABLE_NUMBER],...) interface.)^

    hd_fragment like LIKE ESCAPE -

    The LIKE, GLOB, and REGEXP operators

    +

    The LIKE, GLOB, REGEXP, and MATCH operators

    ^The LIKE operator does a pattern matching comparison. ^The operand to the right of the LIKE operator contains the pattern and the left hand operand contains the string to match against the pattern. hd_puts "^A percent symbol (\"%\") in the LIKE pattern matches any @@ -2919,16 +2919,17 @@ Due to precision limitations imposed by the implementations use of 64-bit integers, the "unixepoch" modifier only works for dates between 0000-01-01 00:00:00 and 5352-11-01 10:52:47 (unix times of -62167219200 through 10675199167).

    -hd_fragment localtime {localtime modifier} +hd_fragment localtime {localtime modifier} {'utc' modifier}

    ^The "localtime" modifier (12) assumes the time string to its left is in Universal Coordinated Time (UTC) and adjusts the time string so that it displays localtime. If "localtime" follows a time that is not UTC, then the behavior is undefined. -^(The "utc" is the opposite of "localtime". "utc" assumes that the string +^(The "utc" modifier is the opposite of "localtime". +"utc" assumes that the string to its left is in the local timezone and adjusts that string to be in UTC.)^ If the prior string is not in localtime, then the result of "utc" is undefined.

    Examples