- add sources.
[platform/framework/web/crosswalk.git] / src / third_party / sqlite / src / contrib / sqlitecon.tcl
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 }