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