1*74217cc0Sdrh# A Tk console widget for SQLite. Invoke sqlitecon::create with a window name, 2*74217cc0Sdrh# a prompt string, a title to set a new top-level window, and the SQLite 3*74217cc0Sdrh# database handle. For example: 4*74217cc0Sdrh# 5*74217cc0Sdrh# sqlitecon::create .sqlcon {sql:- } {SQL Console} db 6*74217cc0Sdrh# 7*74217cc0Sdrh# A toplevel window is created that allows you to type in SQL commands to 8*74217cc0Sdrh# be processed on the spot. 9*74217cc0Sdrh# 10*74217cc0Sdrh# A limited set of dot-commands are supported: 11*74217cc0Sdrh# 12*74217cc0Sdrh# .table 13*74217cc0Sdrh# .schema ?TABLE? 14*74217cc0Sdrh# .mode list|column|multicolumn|line 15*74217cc0Sdrh# .exit 16*74217cc0Sdrh# 17*74217cc0Sdrh# In addition, a new SQL function named "edit()" is created. This function 18*74217cc0Sdrh# takes a single text argument and returns a text result. Whenever the 19*74217cc0Sdrh# the function is called, it pops up a new toplevel window containing a 20*74217cc0Sdrh# text editor screen initialized to the argument. When the "OK" button 21*74217cc0Sdrh# is pressed, whatever revised text is in the text editor is returned as 22*74217cc0Sdrh# the result of the edit() function. This allows text fields of SQL tables 23*74217cc0Sdrh# to be edited quickly and easily as follows: 24*74217cc0Sdrh# 25*74217cc0Sdrh# UPDATE table1 SET dscr = edit(dscr) WHERE rowid=15; 26*74217cc0Sdrh# 27*74217cc0Sdrh 28*74217cc0Sdrh 29*74217cc0Sdrh# Create a namespace to work in 30*74217cc0Sdrh# 31*74217cc0Sdrhnamespace eval ::sqlitecon { 32*74217cc0Sdrh # do nothing 33*74217cc0Sdrh} 34*74217cc0Sdrh 35*74217cc0Sdrh# Create a console widget named $w. The prompt string is $prompt. 36*74217cc0Sdrh# The title at the top of the window is $title. The database connection 37*74217cc0Sdrh# object is $db 38*74217cc0Sdrh# 39*74217cc0Sdrhproc sqlitecon::create {w prompt title db} { 40*74217cc0Sdrh upvar #0 $w.t v 41*74217cc0Sdrh if {[winfo exists $w]} {destroy $w} 42*74217cc0Sdrh if {[info exists v]} {unset v} 43*74217cc0Sdrh toplevel $w 44*74217cc0Sdrh wm title $w $title 45*74217cc0Sdrh wm iconname $w $title 46*74217cc0Sdrh frame $w.mb -bd 2 -relief raised 47*74217cc0Sdrh pack $w.mb -side top -fill x 48*74217cc0Sdrh menubutton $w.mb.file -text File -menu $w.mb.file.m 49*74217cc0Sdrh menubutton $w.mb.edit -text Edit -menu $w.mb.edit.m 50*74217cc0Sdrh pack $w.mb.file $w.mb.edit -side left -padx 8 -pady 1 51*74217cc0Sdrh set m [menu $w.mb.file.m -tearoff 0] 52*74217cc0Sdrh $m add command -label {Close} -command "destroy $w" 53*74217cc0Sdrh sqlitecon::create_child $w $prompt $w.mb.edit.m 54*74217cc0Sdrh set v(db) $db 55*74217cc0Sdrh $db function edit ::sqlitecon::_edit 56*74217cc0Sdrh} 57*74217cc0Sdrh 58*74217cc0Sdrh# This routine creates a console as a child window within a larger 59*74217cc0Sdrh# window. It also creates an edit menu named "$editmenu" if $editmenu!="". 60*74217cc0Sdrh# The calling function is responsible for posting the edit menu. 61*74217cc0Sdrh# 62*74217cc0Sdrhproc sqlitecon::create_child {w prompt editmenu} { 63*74217cc0Sdrh upvar #0 $w.t v 64*74217cc0Sdrh if {$editmenu!=""} { 65*74217cc0Sdrh set m [menu $editmenu -tearoff 0] 66*74217cc0Sdrh $m add command -label Cut -command "sqlitecon::Cut $w.t" 67*74217cc0Sdrh $m add command -label Copy -command "sqlitecon::Copy $w.t" 68*74217cc0Sdrh $m add command -label Paste -command "sqlitecon::Paste $w.t" 69*74217cc0Sdrh $m add command -label {Clear Screen} -command "sqlitecon::Clear $w.t" 70*74217cc0Sdrh $m add separator 71*74217cc0Sdrh $m add command -label {Save As...} -command "sqlitecon::SaveFile $w.t" 72*74217cc0Sdrh catch {$editmenu config -postcommand "sqlitecon::EnableEditMenu $w"} 73*74217cc0Sdrh } 74*74217cc0Sdrh scrollbar $w.sb -orient vertical -command "$w.t yview" 75*74217cc0Sdrh pack $w.sb -side right -fill y 76*74217cc0Sdrh text $w.t -font fixed -yscrollcommand "$w.sb set" 77*74217cc0Sdrh pack $w.t -side right -fill both -expand 1 78*74217cc0Sdrh bindtags $w.t Sqlitecon 79*74217cc0Sdrh set v(editmenu) $editmenu 80*74217cc0Sdrh set v(history) 0 81*74217cc0Sdrh set v(historycnt) 0 82*74217cc0Sdrh set v(current) -1 83*74217cc0Sdrh set v(prompt) $prompt 84*74217cc0Sdrh set v(prior) {} 85*74217cc0Sdrh set v(plength) [string length $v(prompt)] 86*74217cc0Sdrh set v(x) 0 87*74217cc0Sdrh set v(y) 0 88*74217cc0Sdrh set v(mode) column 89*74217cc0Sdrh set v(header) on 90*74217cc0Sdrh $w.t mark set insert end 91*74217cc0Sdrh $w.t tag config ok -foreground blue 92*74217cc0Sdrh $w.t tag config err -foreground red 93*74217cc0Sdrh $w.t insert end $v(prompt) 94*74217cc0Sdrh $w.t mark set out 1.0 95*74217cc0Sdrh after idle "focus $w.t" 96*74217cc0Sdrh} 97*74217cc0Sdrh 98*74217cc0Sdrhbind Sqlitecon <1> {sqlitecon::Button1 %W %x %y} 99*74217cc0Sdrhbind Sqlitecon <B1-Motion> {sqlitecon::B1Motion %W %x %y} 100*74217cc0Sdrhbind Sqlitecon <B1-Leave> {sqlitecon::B1Leave %W %x %y} 101*74217cc0Sdrhbind Sqlitecon <B1-Enter> {sqlitecon::cancelMotor %W} 102*74217cc0Sdrhbind Sqlitecon <ButtonRelease-1> {sqlitecon::cancelMotor %W} 103*74217cc0Sdrhbind Sqlitecon <KeyPress> {sqlitecon::Insert %W %A} 104*74217cc0Sdrhbind Sqlitecon <Left> {sqlitecon::Left %W} 105*74217cc0Sdrhbind Sqlitecon <Control-b> {sqlitecon::Left %W} 106*74217cc0Sdrhbind Sqlitecon <Right> {sqlitecon::Right %W} 107*74217cc0Sdrhbind Sqlitecon <Control-f> {sqlitecon::Right %W} 108*74217cc0Sdrhbind Sqlitecon <BackSpace> {sqlitecon::Backspace %W} 109*74217cc0Sdrhbind Sqlitecon <Control-h> {sqlitecon::Backspace %W} 110*74217cc0Sdrhbind Sqlitecon <Delete> {sqlitecon::Delete %W} 111*74217cc0Sdrhbind Sqlitecon <Control-d> {sqlitecon::Delete %W} 112*74217cc0Sdrhbind Sqlitecon <Home> {sqlitecon::Home %W} 113*74217cc0Sdrhbind Sqlitecon <Control-a> {sqlitecon::Home %W} 114*74217cc0Sdrhbind Sqlitecon <End> {sqlitecon::End %W} 115*74217cc0Sdrhbind Sqlitecon <Control-e> {sqlitecon::End %W} 116*74217cc0Sdrhbind Sqlitecon <Return> {sqlitecon::Enter %W} 117*74217cc0Sdrhbind Sqlitecon <KP_Enter> {sqlitecon::Enter %W} 118*74217cc0Sdrhbind Sqlitecon <Up> {sqlitecon::Prior %W} 119*74217cc0Sdrhbind Sqlitecon <Control-p> {sqlitecon::Prior %W} 120*74217cc0Sdrhbind Sqlitecon <Down> {sqlitecon::Next %W} 121*74217cc0Sdrhbind Sqlitecon <Control-n> {sqlitecon::Next %W} 122*74217cc0Sdrhbind Sqlitecon <Control-k> {sqlitecon::EraseEOL %W} 123*74217cc0Sdrhbind Sqlitecon <<Cut>> {sqlitecon::Cut %W} 124*74217cc0Sdrhbind Sqlitecon <<Copy>> {sqlitecon::Copy %W} 125*74217cc0Sdrhbind Sqlitecon <<Paste>> {sqlitecon::Paste %W} 126*74217cc0Sdrhbind Sqlitecon <<Clear>> {sqlitecon::Clear %W} 127*74217cc0Sdrh 128*74217cc0Sdrh# Insert a single character at the insertion cursor 129*74217cc0Sdrh# 130*74217cc0Sdrhproc sqlitecon::Insert {w a} { 131*74217cc0Sdrh $w insert insert $a 132*74217cc0Sdrh $w yview insert 133*74217cc0Sdrh} 134*74217cc0Sdrh 135*74217cc0Sdrh# Move the cursor one character to the left 136*74217cc0Sdrh# 137*74217cc0Sdrhproc sqlitecon::Left {w} { 138*74217cc0Sdrh upvar #0 $w v 139*74217cc0Sdrh scan [$w index insert] %d.%d row col 140*74217cc0Sdrh if {$col>$v(plength)} { 141*74217cc0Sdrh $w mark set insert "insert -1c" 142*74217cc0Sdrh } 143*74217cc0Sdrh} 144*74217cc0Sdrh 145*74217cc0Sdrh# Erase the character to the left of the cursor 146*74217cc0Sdrh# 147*74217cc0Sdrhproc sqlitecon::Backspace {w} { 148*74217cc0Sdrh upvar #0 $w v 149*74217cc0Sdrh scan [$w index insert] %d.%d row col 150*74217cc0Sdrh if {$col>$v(plength)} { 151*74217cc0Sdrh $w delete {insert -1c} 152*74217cc0Sdrh } 153*74217cc0Sdrh} 154*74217cc0Sdrh 155*74217cc0Sdrh# Erase to the end of the line 156*74217cc0Sdrh# 157*74217cc0Sdrhproc sqlitecon::EraseEOL {w} { 158*74217cc0Sdrh upvar #0 $w v 159*74217cc0Sdrh scan [$w index insert] %d.%d row col 160*74217cc0Sdrh if {$col>=$v(plength)} { 161*74217cc0Sdrh $w delete insert {insert lineend} 162*74217cc0Sdrh } 163*74217cc0Sdrh} 164*74217cc0Sdrh 165*74217cc0Sdrh# Move the cursor one character to the right 166*74217cc0Sdrh# 167*74217cc0Sdrhproc sqlitecon::Right {w} { 168*74217cc0Sdrh $w mark set insert "insert +1c" 169*74217cc0Sdrh} 170*74217cc0Sdrh 171*74217cc0Sdrh# Erase the character to the right of the cursor 172*74217cc0Sdrh# 173*74217cc0Sdrhproc sqlitecon::Delete w { 174*74217cc0Sdrh $w delete insert 175*74217cc0Sdrh} 176*74217cc0Sdrh 177*74217cc0Sdrh# Move the cursor to the beginning of the current line 178*74217cc0Sdrh# 179*74217cc0Sdrhproc sqlitecon::Home w { 180*74217cc0Sdrh upvar #0 $w v 181*74217cc0Sdrh scan [$w index insert] %d.%d row col 182*74217cc0Sdrh $w mark set insert $row.$v(plength) 183*74217cc0Sdrh} 184*74217cc0Sdrh 185*74217cc0Sdrh# Move the cursor to the end of the current line 186*74217cc0Sdrh# 187*74217cc0Sdrhproc sqlitecon::End w { 188*74217cc0Sdrh $w mark set insert {insert lineend} 189*74217cc0Sdrh} 190*74217cc0Sdrh 191*74217cc0Sdrh# Add a line to the history 192*74217cc0Sdrh# 193*74217cc0Sdrhproc sqlitecon::addHistory {w line} { 194*74217cc0Sdrh upvar #0 $w v 195*74217cc0Sdrh if {$v(historycnt)>0} { 196*74217cc0Sdrh set last [lindex $v(history) [expr $v(historycnt)-1]] 197*74217cc0Sdrh if {[string compare $last $line]} { 198*74217cc0Sdrh lappend v(history) $line 199*74217cc0Sdrh incr v(historycnt) 200*74217cc0Sdrh } 201*74217cc0Sdrh } else { 202*74217cc0Sdrh set v(history) [list $line] 203*74217cc0Sdrh set v(historycnt) 1 204*74217cc0Sdrh } 205*74217cc0Sdrh set v(current) $v(historycnt) 206*74217cc0Sdrh} 207*74217cc0Sdrh 208*74217cc0Sdrh# Called when "Enter" is pressed. Do something with the line 209*74217cc0Sdrh# of text that was entered. 210*74217cc0Sdrh# 211*74217cc0Sdrhproc sqlitecon::Enter w { 212*74217cc0Sdrh upvar #0 $w v 213*74217cc0Sdrh scan [$w index insert] %d.%d row col 214*74217cc0Sdrh set start $row.$v(plength) 215*74217cc0Sdrh set line [$w get $start "$start lineend"] 216*74217cc0Sdrh $w insert end \n 217*74217cc0Sdrh $w mark set out end 218*74217cc0Sdrh if {$v(prior)==""} { 219*74217cc0Sdrh set cmd $line 220*74217cc0Sdrh } else { 221*74217cc0Sdrh set cmd $v(prior)\n$line 222*74217cc0Sdrh } 223*74217cc0Sdrh if {[string index $cmd 0]=="." || [$v(db) complete $cmd]} { 224*74217cc0Sdrh regsub -all {\n} [string trim $cmd] { } cmd2 225*74217cc0Sdrh addHistory $w $cmd2 226*74217cc0Sdrh set rc [catch {DoCommand $w $cmd} res] 227*74217cc0Sdrh if {![winfo exists $w]} return 228*74217cc0Sdrh if {$rc} { 229*74217cc0Sdrh $w insert end $res\n err 230*74217cc0Sdrh } elseif {[string length $res]>0} { 231*74217cc0Sdrh $w insert end $res\n ok 232*74217cc0Sdrh } 233*74217cc0Sdrh set v(prior) {} 234*74217cc0Sdrh $w insert end $v(prompt) 235*74217cc0Sdrh } else { 236*74217cc0Sdrh set v(prior) $cmd 237*74217cc0Sdrh regsub -all {[^ ]} $v(prompt) . x 238*74217cc0Sdrh $w insert end $x 239*74217cc0Sdrh } 240*74217cc0Sdrh $w mark set insert end 241*74217cc0Sdrh $w mark set out {insert linestart} 242*74217cc0Sdrh $w yview insert 243*74217cc0Sdrh} 244*74217cc0Sdrh 245*74217cc0Sdrh# Execute a single SQL command. Pay special attention to control 246*74217cc0Sdrh# directives that begin with "." 247*74217cc0Sdrh# 248*74217cc0Sdrh# The return value is the text output from the command, properly 249*74217cc0Sdrh# formatted. 250*74217cc0Sdrh# 251*74217cc0Sdrhproc sqlitecon::DoCommand {w cmd} { 252*74217cc0Sdrh upvar #0 $w v 253*74217cc0Sdrh set mode $v(mode) 254*74217cc0Sdrh set header $v(header) 255*74217cc0Sdrh if {[regexp {^(\.[a-z]+)} $cmd all word]} { 256*74217cc0Sdrh if {$word==".mode"} { 257*74217cc0Sdrh regexp {^.[a-z]+ +([a-z]+)} $cmd all v(mode) 258*74217cc0Sdrh return {} 259*74217cc0Sdrh } elseif {$word==".exit"} { 260*74217cc0Sdrh destroy [winfo toplevel $w] 261*74217cc0Sdrh return {} 262*74217cc0Sdrh } elseif {$word==".header"} { 263*74217cc0Sdrh regexp {^.[a-z]+ +([a-z]+)} $cmd all v(header) 264*74217cc0Sdrh return {} 265*74217cc0Sdrh } elseif {$word==".tables"} { 266*74217cc0Sdrh set mode multicolumn 267*74217cc0Sdrh set cmd {SELECT name FROM sqlite_master WHERE type='table' 268*74217cc0Sdrh UNION ALL 269*74217cc0Sdrh SELECT name FROM sqlite_temp_master WHERE type='table'} 270*74217cc0Sdrh $v(db) eval {PRAGMA database_list} { 271*74217cc0Sdrh if {$name!="temp" && $name!="main"} { 272*74217cc0Sdrh append cmd "UNION ALL SELECT name FROM $name.sqlite_master\ 273*74217cc0Sdrh WHERE type='table'" 274*74217cc0Sdrh } 275*74217cc0Sdrh } 276*74217cc0Sdrh append cmd { ORDER BY 1} 277*74217cc0Sdrh } elseif {$word==".fullschema"} { 278*74217cc0Sdrh set pattern % 279*74217cc0Sdrh regexp {^.[a-z]+ +([^ ]+)} $cmd all pattern 280*74217cc0Sdrh set mode list 281*74217cc0Sdrh set header 0 282*74217cc0Sdrh set cmd "SELECT sql FROM sqlite_master WHERE tbl_name LIKE '$pattern' 283*74217cc0Sdrh AND sql NOT NULL UNION ALL SELECT sql FROM sqlite_temp_master 284*74217cc0Sdrh WHERE tbl_name LIKE '$pattern' AND sql NOT NULL" 285*74217cc0Sdrh $v(db) eval {PRAGMA database_list} { 286*74217cc0Sdrh if {$name!="temp" && $name!="main"} { 287*74217cc0Sdrh append cmd " UNION ALL SELECT sql FROM $name.sqlite_master\ 288*74217cc0Sdrh WHERE tbl_name LIKE '$pattern' AND sql NOT NULL" 289*74217cc0Sdrh } 290*74217cc0Sdrh } 291*74217cc0Sdrh } elseif {$word==".schema"} { 292*74217cc0Sdrh set pattern % 293*74217cc0Sdrh regexp {^.[a-z]+ +([^ ]+)} $cmd all pattern 294*74217cc0Sdrh set mode list 295*74217cc0Sdrh set header 0 296*74217cc0Sdrh set cmd "SELECT sql FROM sqlite_master WHERE name LIKE '$pattern' 297*74217cc0Sdrh AND sql NOT NULL UNION ALL SELECT sql FROM sqlite_temp_master 298*74217cc0Sdrh WHERE name LIKE '$pattern' AND sql NOT NULL" 299*74217cc0Sdrh $v(db) eval {PRAGMA database_list} { 300*74217cc0Sdrh if {$name!="temp" && $name!="main"} { 301*74217cc0Sdrh append cmd " UNION ALL SELECT sql FROM $name.sqlite_master\ 302*74217cc0Sdrh WHERE name LIKE '$pattern' AND sql NOT NULL" 303*74217cc0Sdrh } 304*74217cc0Sdrh } 305*74217cc0Sdrh } else { 306*74217cc0Sdrh return \ 307*74217cc0Sdrh ".exit\n.mode line|list|column\n.schema ?TABLENAME?\n.tables" 308*74217cc0Sdrh } 309*74217cc0Sdrh } 310*74217cc0Sdrh set res {} 311*74217cc0Sdrh if {$mode=="list"} { 312*74217cc0Sdrh $v(db) eval $cmd x { 313*74217cc0Sdrh set sep {} 314*74217cc0Sdrh foreach col $x(*) { 315*74217cc0Sdrh append res $sep$x($col) 316*74217cc0Sdrh set sep | 317*74217cc0Sdrh } 318*74217cc0Sdrh append res \n 319*74217cc0Sdrh } 320*74217cc0Sdrh if {[info exists x(*)] && $header} { 321*74217cc0Sdrh set sep {} 322*74217cc0Sdrh set hdr {} 323*74217cc0Sdrh foreach col $x(*) { 324*74217cc0Sdrh append hdr $sep$col 325*74217cc0Sdrh set sep | 326*74217cc0Sdrh } 327*74217cc0Sdrh set res $hdr\n$res 328*74217cc0Sdrh } 329*74217cc0Sdrh } elseif {[string range $mode 0 2]=="col"} { 330*74217cc0Sdrh set y {} 331*74217cc0Sdrh $v(db) eval $cmd x { 332*74217cc0Sdrh foreach col $x(*) { 333*74217cc0Sdrh if {![info exists cw($col)] || $cw($col)<[string length $x($col)]} { 334*74217cc0Sdrh set cw($col) [string length $x($col)] 335*74217cc0Sdrh } 336*74217cc0Sdrh lappend y $x($col) 337*74217cc0Sdrh } 338*74217cc0Sdrh } 339*74217cc0Sdrh if {[info exists x(*)] && $header} { 340*74217cc0Sdrh set hdr {} 341*74217cc0Sdrh set ln {} 342*74217cc0Sdrh set dash --------------------------------------------------------------- 343*74217cc0Sdrh append dash ------------------------------------------------------------ 344*74217cc0Sdrh foreach col $x(*) { 345*74217cc0Sdrh if {![info exists cw($col)] || $cw($col)<[string length $col]} { 346*74217cc0Sdrh set cw($col) [string length $col] 347*74217cc0Sdrh } 348*74217cc0Sdrh lappend hdr $col 349*74217cc0Sdrh lappend ln [string range $dash 1 $cw($col)] 350*74217cc0Sdrh } 351*74217cc0Sdrh set y [concat $hdr $ln $y] 352*74217cc0Sdrh } 353*74217cc0Sdrh if {[info exists x(*)]} { 354*74217cc0Sdrh set format {} 355*74217cc0Sdrh set arglist {} 356*74217cc0Sdrh set arglist2 {} 357*74217cc0Sdrh set i 0 358*74217cc0Sdrh foreach col $x(*) { 359*74217cc0Sdrh lappend arglist x$i 360*74217cc0Sdrh append arglist2 " \$x$i" 361*74217cc0Sdrh incr i 362*74217cc0Sdrh append format " %-$cw($col)s" 363*74217cc0Sdrh } 364*74217cc0Sdrh set format [string trimleft $format]\n 365*74217cc0Sdrh if {[llength $arglist]>0} { 366*74217cc0Sdrh foreach $arglist $y "append res \[format [list $format] $arglist2\]" 367*74217cc0Sdrh } 368*74217cc0Sdrh } 369*74217cc0Sdrh } elseif {$mode=="multicolumn"} { 370*74217cc0Sdrh set y [$v(db) eval $cmd] 371*74217cc0Sdrh set max 0 372*74217cc0Sdrh foreach e $y { 373*74217cc0Sdrh if {$max<[string length $e]} {set max [string length $e]} 374*74217cc0Sdrh } 375*74217cc0Sdrh set ncol [expr {int(80/($max+2))}] 376*74217cc0Sdrh if {$ncol<1} {set ncol 1} 377*74217cc0Sdrh set nelem [llength $y] 378*74217cc0Sdrh set nrow [expr {($nelem+$ncol-1)/$ncol}] 379*74217cc0Sdrh set format "%-${max}s" 380*74217cc0Sdrh for {set i 0} {$i<$nrow} {incr i} { 381*74217cc0Sdrh set j $i 382*74217cc0Sdrh while 1 { 383*74217cc0Sdrh append res [format $format [lindex $y $j]] 384*74217cc0Sdrh incr j $nrow 385*74217cc0Sdrh if {$j>=$nelem} break 386*74217cc0Sdrh append res { } 387*74217cc0Sdrh } 388*74217cc0Sdrh append res \n 389*74217cc0Sdrh } 390*74217cc0Sdrh } else { 391*74217cc0Sdrh $v(db) eval $cmd x { 392*74217cc0Sdrh foreach col $x(*) {append res "$col = $x($col)\n"} 393*74217cc0Sdrh append res \n 394*74217cc0Sdrh } 395*74217cc0Sdrh } 396*74217cc0Sdrh return [string trimright $res] 397*74217cc0Sdrh} 398*74217cc0Sdrh 399*74217cc0Sdrh# Change the line to the previous line 400*74217cc0Sdrh# 401*74217cc0Sdrhproc sqlitecon::Prior w { 402*74217cc0Sdrh upvar #0 $w v 403*74217cc0Sdrh if {$v(current)<=0} return 404*74217cc0Sdrh incr v(current) -1 405*74217cc0Sdrh set line [lindex $v(history) $v(current)] 406*74217cc0Sdrh sqlitecon::SetLine $w $line 407*74217cc0Sdrh} 408*74217cc0Sdrh 409*74217cc0Sdrh# Change the line to the next line 410*74217cc0Sdrh# 411*74217cc0Sdrhproc sqlitecon::Next w { 412*74217cc0Sdrh upvar #0 $w v 413*74217cc0Sdrh if {$v(current)>=$v(historycnt)} return 414*74217cc0Sdrh incr v(current) 1 415*74217cc0Sdrh set line [lindex $v(history) $v(current)] 416*74217cc0Sdrh sqlitecon::SetLine $w $line 417*74217cc0Sdrh} 418*74217cc0Sdrh 419*74217cc0Sdrh# Change the contents of the entry line 420*74217cc0Sdrh# 421*74217cc0Sdrhproc sqlitecon::SetLine {w line} { 422*74217cc0Sdrh upvar #0 $w v 423*74217cc0Sdrh scan [$w index insert] %d.%d row col 424*74217cc0Sdrh set start $row.$v(plength) 425*74217cc0Sdrh $w delete $start end 426*74217cc0Sdrh $w insert end $line 427*74217cc0Sdrh $w mark set insert end 428*74217cc0Sdrh $w yview insert 429*74217cc0Sdrh} 430*74217cc0Sdrh 431*74217cc0Sdrh# Called when the mouse button is pressed at position $x,$y on 432*74217cc0Sdrh# the console widget. 433*74217cc0Sdrh# 434*74217cc0Sdrhproc sqlitecon::Button1 {w x y} { 435*74217cc0Sdrh global tkPriv 436*74217cc0Sdrh upvar #0 $w v 437*74217cc0Sdrh set v(mouseMoved) 0 438*74217cc0Sdrh set v(pressX) $x 439*74217cc0Sdrh set p [sqlitecon::nearestBoundry $w $x $y] 440*74217cc0Sdrh scan [$w index insert] %d.%d ix iy 441*74217cc0Sdrh scan $p %d.%d px py 442*74217cc0Sdrh if {$px==$ix} { 443*74217cc0Sdrh $w mark set insert $p 444*74217cc0Sdrh } 445*74217cc0Sdrh $w mark set anchor $p 446*74217cc0Sdrh focus $w 447*74217cc0Sdrh} 448*74217cc0Sdrh 449*74217cc0Sdrh# Find the boundry between characters that is nearest 450*74217cc0Sdrh# to $x,$y 451*74217cc0Sdrh# 452*74217cc0Sdrhproc sqlitecon::nearestBoundry {w x y} { 453*74217cc0Sdrh set p [$w index @$x,$y] 454*74217cc0Sdrh set bb [$w bbox $p] 455*74217cc0Sdrh if {![string compare $bb ""]} {return $p} 456*74217cc0Sdrh if {($x-[lindex $bb 0])<([lindex $bb 2]/2)} {return $p} 457*74217cc0Sdrh $w index "$p + 1 char" 458*74217cc0Sdrh} 459*74217cc0Sdrh 460*74217cc0Sdrh# This routine extends the selection to the point specified by $x,$y 461*74217cc0Sdrh# 462*74217cc0Sdrhproc sqlitecon::SelectTo {w x y} { 463*74217cc0Sdrh upvar #0 $w v 464*74217cc0Sdrh set cur [sqlitecon::nearestBoundry $w $x $y] 465*74217cc0Sdrh if {[catch {$w index anchor}]} { 466*74217cc0Sdrh $w mark set anchor $cur 467*74217cc0Sdrh } 468*74217cc0Sdrh set anchor [$w index anchor] 469*74217cc0Sdrh if {[$w compare $cur != $anchor] || (abs($v(pressX) - $x) >= 3)} { 470*74217cc0Sdrh if {$v(mouseMoved)==0} { 471*74217cc0Sdrh $w tag remove sel 0.0 end 472*74217cc0Sdrh } 473*74217cc0Sdrh set v(mouseMoved) 1 474*74217cc0Sdrh } 475*74217cc0Sdrh if {[$w compare $cur < anchor]} { 476*74217cc0Sdrh set first $cur 477*74217cc0Sdrh set last anchor 478*74217cc0Sdrh } else { 479*74217cc0Sdrh set first anchor 480*74217cc0Sdrh set last $cur 481*74217cc0Sdrh } 482*74217cc0Sdrh if {$v(mouseMoved)} { 483*74217cc0Sdrh $w tag remove sel 0.0 $first 484*74217cc0Sdrh $w tag add sel $first $last 485*74217cc0Sdrh $w tag remove sel $last end 486*74217cc0Sdrh update idletasks 487*74217cc0Sdrh } 488*74217cc0Sdrh} 489*74217cc0Sdrh 490*74217cc0Sdrh# Called whenever the mouse moves while button-1 is held down. 491*74217cc0Sdrh# 492*74217cc0Sdrhproc sqlitecon::B1Motion {w x y} { 493*74217cc0Sdrh upvar #0 $w v 494*74217cc0Sdrh set v(y) $y 495*74217cc0Sdrh set v(x) $x 496*74217cc0Sdrh sqlitecon::SelectTo $w $x $y 497*74217cc0Sdrh} 498*74217cc0Sdrh 499*74217cc0Sdrh# Called whenever the mouse leaves the boundries of the widget 500*74217cc0Sdrh# while button 1 is held down. 501*74217cc0Sdrh# 502*74217cc0Sdrhproc sqlitecon::B1Leave {w x y} { 503*74217cc0Sdrh upvar #0 $w v 504*74217cc0Sdrh set v(y) $y 505*74217cc0Sdrh set v(x) $x 506*74217cc0Sdrh sqlitecon::motor $w 507*74217cc0Sdrh} 508*74217cc0Sdrh 509*74217cc0Sdrh# This routine is called to automatically scroll the window when 510*74217cc0Sdrh# the mouse drags offscreen. 511*74217cc0Sdrh# 512*74217cc0Sdrhproc sqlitecon::motor w { 513*74217cc0Sdrh upvar #0 $w v 514*74217cc0Sdrh if {![winfo exists $w]} return 515*74217cc0Sdrh if {$v(y)>=[winfo height $w]} { 516*74217cc0Sdrh $w yview scroll 1 units 517*74217cc0Sdrh } elseif {$v(y)<0} { 518*74217cc0Sdrh $w yview scroll -1 units 519*74217cc0Sdrh } else { 520*74217cc0Sdrh return 521*74217cc0Sdrh } 522*74217cc0Sdrh sqlitecon::SelectTo $w $v(x) $v(y) 523*74217cc0Sdrh set v(timer) [after 50 sqlitecon::motor $w] 524*74217cc0Sdrh} 525*74217cc0Sdrh 526*74217cc0Sdrh# This routine cancels the scrolling motor if it is active 527*74217cc0Sdrh# 528*74217cc0Sdrhproc sqlitecon::cancelMotor w { 529*74217cc0Sdrh upvar #0 $w v 530*74217cc0Sdrh catch {after cancel $v(timer)} 531*74217cc0Sdrh catch {unset v(timer)} 532*74217cc0Sdrh} 533*74217cc0Sdrh 534*74217cc0Sdrh# Do a Copy operation on the stuff currently selected. 535*74217cc0Sdrh# 536*74217cc0Sdrhproc sqlitecon::Copy w { 537*74217cc0Sdrh if {![catch {set text [$w get sel.first sel.last]}]} { 538*74217cc0Sdrh clipboard clear -displayof $w 539*74217cc0Sdrh clipboard append -displayof $w $text 540*74217cc0Sdrh } 541*74217cc0Sdrh} 542*74217cc0Sdrh 543*74217cc0Sdrh# Return 1 if the selection exists and is contained 544*74217cc0Sdrh# entirely on the input line. Return 2 if the selection 545*74217cc0Sdrh# exists but is not entirely on the input line. Return 0 546*74217cc0Sdrh# if the selection does not exist. 547*74217cc0Sdrh# 548*74217cc0Sdrhproc sqlitecon::canCut w { 549*74217cc0Sdrh set r [catch { 550*74217cc0Sdrh scan [$w index sel.first] %d.%d s1x s1y 551*74217cc0Sdrh scan [$w index sel.last] %d.%d s2x s2y 552*74217cc0Sdrh scan [$w index insert] %d.%d ix iy 553*74217cc0Sdrh }] 554*74217cc0Sdrh if {$r==1} {return 0} 555*74217cc0Sdrh if {$s1x==$ix && $s2x==$ix} {return 1} 556*74217cc0Sdrh return 2 557*74217cc0Sdrh} 558*74217cc0Sdrh 559*74217cc0Sdrh# Do a Cut operation if possible. Cuts are only allowed 560*74217cc0Sdrh# if the current selection is entirely contained on the 561*74217cc0Sdrh# current input line. 562*74217cc0Sdrh# 563*74217cc0Sdrhproc sqlitecon::Cut w { 564*74217cc0Sdrh if {[sqlitecon::canCut $w]==1} { 565*74217cc0Sdrh sqlitecon::Copy $w 566*74217cc0Sdrh $w delete sel.first sel.last 567*74217cc0Sdrh } 568*74217cc0Sdrh} 569*74217cc0Sdrh 570*74217cc0Sdrh# Do a paste opeation. 571*74217cc0Sdrh# 572*74217cc0Sdrhproc sqlitecon::Paste w { 573*74217cc0Sdrh if {[sqlitecon::canCut $w]==1} { 574*74217cc0Sdrh $w delete sel.first sel.last 575*74217cc0Sdrh } 576*74217cc0Sdrh if {[catch {selection get -displayof $w -selection CLIPBOARD} topaste] 577*74217cc0Sdrh && [catch {selection get -displayof $w -selection PRIMARY} topaste]} { 578*74217cc0Sdrh return 579*74217cc0Sdrh } 580*74217cc0Sdrh if {[info exists ::$w]} { 581*74217cc0Sdrh set prior 0 582*74217cc0Sdrh foreach line [split $topaste \n] { 583*74217cc0Sdrh if {$prior} { 584*74217cc0Sdrh sqlitecon::Enter $w 585*74217cc0Sdrh update 586*74217cc0Sdrh } 587*74217cc0Sdrh set prior 1 588*74217cc0Sdrh $w insert insert $line 589*74217cc0Sdrh } 590*74217cc0Sdrh } else { 591*74217cc0Sdrh $w insert insert $topaste 592*74217cc0Sdrh } 593*74217cc0Sdrh} 594*74217cc0Sdrh 595*74217cc0Sdrh# Enable or disable entries in the Edit menu 596*74217cc0Sdrh# 597*74217cc0Sdrhproc sqlitecon::EnableEditMenu w { 598*74217cc0Sdrh upvar #0 $w.t v 599*74217cc0Sdrh set m $v(editmenu) 600*74217cc0Sdrh if {$m=="" || ![winfo exists $m]} return 601*74217cc0Sdrh switch [sqlitecon::canCut $w.t] { 602*74217cc0Sdrh 0 { 603*74217cc0Sdrh $m entryconf Copy -state disabled 604*74217cc0Sdrh $m entryconf Cut -state disabled 605*74217cc0Sdrh } 606*74217cc0Sdrh 1 { 607*74217cc0Sdrh $m entryconf Copy -state normal 608*74217cc0Sdrh $m entryconf Cut -state normal 609*74217cc0Sdrh } 610*74217cc0Sdrh 2 { 611*74217cc0Sdrh $m entryconf Copy -state normal 612*74217cc0Sdrh $m entryconf Cut -state disabled 613*74217cc0Sdrh } 614*74217cc0Sdrh } 615*74217cc0Sdrh} 616*74217cc0Sdrh 617*74217cc0Sdrh# Prompt the user for the name of a writable file. Then write the 618*74217cc0Sdrh# entire contents of the console screen to that file. 619*74217cc0Sdrh# 620*74217cc0Sdrhproc sqlitecon::SaveFile w { 621*74217cc0Sdrh set types { 622*74217cc0Sdrh {{Text Files} {.txt}} 623*74217cc0Sdrh {{All Files} *} 624*74217cc0Sdrh } 625*74217cc0Sdrh set f [tk_getSaveFile -filetypes $types -title "Write Screen To..."] 626*74217cc0Sdrh if {$f!=""} { 627*74217cc0Sdrh if {[catch {open $f w} fd]} { 628*74217cc0Sdrh tk_messageBox -type ok -icon error -message $fd 629*74217cc0Sdrh } else { 630*74217cc0Sdrh puts $fd [string trimright [$w get 1.0 end] \n] 631*74217cc0Sdrh close $fd 632*74217cc0Sdrh } 633*74217cc0Sdrh } 634*74217cc0Sdrh} 635*74217cc0Sdrh 636*74217cc0Sdrh# Erase everything from the console above the insertion line. 637*74217cc0Sdrh# 638*74217cc0Sdrhproc sqlitecon::Clear w { 639*74217cc0Sdrh $w delete 1.0 {insert linestart} 640*74217cc0Sdrh} 641*74217cc0Sdrh 642*74217cc0Sdrh# An in-line editor for SQL 643*74217cc0Sdrh# 644*74217cc0Sdrhproc sqlitecon::_edit {origtxt {title {}}} { 645*74217cc0Sdrh for {set i 0} {[winfo exists .ed$i]} {incr i} continue 646*74217cc0Sdrh set w .ed$i 647*74217cc0Sdrh toplevel $w 648*74217cc0Sdrh wm protocol $w WM_DELETE_WINDOW "$w.b.can invoke" 649*74217cc0Sdrh wm title $w {Inline SQL Editor} 650*74217cc0Sdrh frame $w.b 651*74217cc0Sdrh pack $w.b -side bottom -fill x 652*74217cc0Sdrh button $w.b.can -text Cancel -width 6 -command [list set ::$w 0] 653*74217cc0Sdrh button $w.b.ok -text OK -width 6 -command [list set ::$w 1] 654*74217cc0Sdrh button $w.b.cut -text Cut -width 6 -command [list ::sqlitecon::Cut $w.t] 655*74217cc0Sdrh button $w.b.copy -text Copy -width 6 -command [list ::sqlitecon::Copy $w.t] 656*74217cc0Sdrh button $w.b.paste -text Paste -width 6 -command [list ::sqlitecon::Paste $w.t] 657*74217cc0Sdrh set ::$w {} 658*74217cc0Sdrh pack $w.b.cut $w.b.copy $w.b.paste $w.b.can $w.b.ok\ 659*74217cc0Sdrh -side left -padx 5 -pady 5 -expand 1 660*74217cc0Sdrh if {$title!=""} { 661*74217cc0Sdrh label $w.title -text $title 662*74217cc0Sdrh pack $w.title -side top -padx 5 -pady 5 663*74217cc0Sdrh } 664*74217cc0Sdrh text $w.t -bg white -fg black -yscrollcommand [list $w.sb set] 665*74217cc0Sdrh pack $w.t -side left -fill both -expand 1 666*74217cc0Sdrh scrollbar $w.sb -orient vertical -command [list $w.t yview] 667*74217cc0Sdrh pack $w.sb -side left -fill y 668*74217cc0Sdrh $w.t insert end $origtxt 669*74217cc0Sdrh 670*74217cc0Sdrh vwait ::$w 671*74217cc0Sdrh 672*74217cc0Sdrh if {[set ::$w]} { 673*74217cc0Sdrh set txt [string trimright [$w.t get 1.0 end]] 674*74217cc0Sdrh } else { 675*74217cc0Sdrh set txt $origtxt 676*74217cc0Sdrh } 677*74217cc0Sdrh destroy $w 678*74217cc0Sdrh return $txt 679*74217cc0Sdrh} 680