-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtable.tcl
183 lines (150 loc) · 4.84 KB
/
table.tcl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
package require Tk 8.5
package require snit 2.2
package require tdbc
namespace eval dbluejay {
# Megawidget for browsing a single result set in a scrollable flat multi-column
# ttk::treeview.
snit::widget rstable {
hulltype ttk::frame
delegate option * to hull
delegate method * to hull
component table ;# ttk::treeview
component vscroll ;# ttk::scrollbar -orient vertical
component hscroll ;# ttk::scrollbar -orient horizontal
# TDBC resultset object to browse.
option -rs -readonly yes -default {}
# Number of rows to fetch from the result set at once; -chunksize rows
# are read from the result set every time the scrollbar bottoms out,
# until there are no more rows to read. inf is legal and behaves as you
# would expect (fetching all rows at once at construction time).
option -chunksize -default 256
constructor {args} {
$self configurelist $args
if {[$self cget -rs] eq {}} {
return -code error "-rs is not optional"
}
install table using ttk::treeview $win.table -columns [
[$self cget -rs] columns
] -show headings
install vscroll using ttk::scrollbar $win.vscroll \
-orient vertical \
-command [mymethod Vscroll_yview]
install hscroll using ttk::scrollbar $win.hscroll \
-orient horizontal \
-command [list $table xview]
$table configure -yscrollcommand [
mymethod Vscroll_set
] -xscrollcommand [
list $hscroll set
]
foreach col [[$self cget -rs] columns] {
$table heading $col -text $col
}
grid $table $vscroll -sticky ns
grid $hscroll x -sticky ew
grid configure $table -sticky nsew
grid rowconfigure $win 0 -weight 1
grid columnconfigure $win 0 -weight 1
$self read [$self cget -chunksize]
}
# Read and display $chunksize rows from the result set.
method read {chunksize} {
for {set index 0} {
$index < $chunksize && [[$self cget -rs] nextlist row]
} {incr index} {
$table insert {} end -values $row
}
}
# Utility method for the two Vscroll_ methods below, reading -chunksize
# rows from the result set if the scrollbar seems bottomed-out-ish.
method Check_read {} {
if {[lindex [$vscroll get] 1] > 0.95} {
$self read [$self cget -chunksize]
}
}
# Assigned to the $vscroll as its -command, functions as a hook to
# ensure the [read] method is called if needed
method Vscroll_yview {args} {
$table yview {*}$args
$self Check_read
}
# Assigned to $table as its -yscrollcommand; this one functions as a
# hook to ensure the [read] method is called if needed too
method Vscroll_set {args} {
$vscroll set {*}$args
$self Check_read
}
}
# Megawidget for browsing all the result sets generated by a multi-statement
# SQL script in a cargocult::cnotebook.
snit::widgetadaptor rspager {
delegate method * to hull
delegate option * to hull
# TDBC handle on which to execute SQL scripts
option -db -default {apply {{args} {
return -code error "please supply a tdbc handle"
}}}
# SQL script to execute
option -sql -default {} -configuremethod Set_sql
method Set_sql {opt val} {
set options($opt) $val
after idle [list $self update]
}
# Bound parameters to apply to all statements in the script
option -params -default {}
# Number of rows to fetch from each result set at once, passed directly
# to dbluejay::rstable
option -chunksize -default 256
# Used to serialize dbluejay::rstable widgets
variable rs_serial 0
constructor {args} {
installhull using cargocult::cnotebook -height 300
$self configurelist $args
}
# Clear out our tabs and replace them with newer, better ones that
# reflect the current state of the database and the current state of our
# -sql script.
method update {} {
$self clear
set sql_acc {}
foreach token [tdbc::tokenize [$self cget -sql]] {
if {$token eq {;}} {
$self Execute $sql_acc
set sql_acc {}
} else {
append sql_acc $token
}
}
# There may be a trailing nonterminated statement; execute it
# if so
$self Execute $sql_acc
event generate $win <<Executed>>
}
# Clear out our tabs, and close any statements that may be open on our
# database. (Maybe don't pass database handles to this widget that you
# don't want getting randomly stripped of all their open statements.)
method clear {} {
set rs_serial 0
foreach tab [$hull tabs] {
destroy $tab
}
foreach statement [[$self cget -db] statements] {
$statement close
}
}
# Execute a single SQL statement and add a rstable widget over its
# result set to the notebook (unless it's a null statement, then smile
# tolerantly and pretend nothing happened)
method Execute {sql} {
# Some drivers dislike null statements
if {[string trim $sql] ne {}} {
set statement [[$self cget -db] prepare $sql]
$hull add [
rstable $win.[::cargocult::gensym rs] -rs [
$statement execute [$self cget -params]
] -chunksize [$self cget -chunksize]
] -text "Query [incr rs_serial]"
}
}
}
} ;# namespace eval dbluejay