rbcurse 0.1.3 → 1.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (74) hide show
  1. data/CHANGELOG +126 -0
  2. data/Manifest.txt +53 -20
  3. data/README.markdown +423 -0
  4. data/Rakefile +3 -1
  5. data/examples/keytest.rb +177 -0
  6. data/examples/mpad2.rb +156 -0
  7. data/examples/newtesttabp.rb +121 -0
  8. data/examples/rfe.rb +48 -10
  9. data/examples/rfe_renderer.rb +4 -4
  10. data/examples/rvimsplit.rb +376 -0
  11. data/examples/sqlc.rb +97 -106
  12. data/examples/sqlm.rb +446 -0
  13. data/examples/test1.rb +4 -4
  14. data/examples/test2.rb +12 -12
  15. data/examples/testchars.rb +140 -0
  16. data/examples/testkeypress.rb +9 -4
  17. data/examples/testmulticomp.rb +72 -0
  18. data/examples/testscroller.rb +136 -0
  19. data/examples/testscrolllb.rb +86 -0
  20. data/examples/testscrollp.rb +87 -0
  21. data/examples/testscrollta.rb +80 -0
  22. data/examples/testscrolltable.rb +166 -0
  23. data/examples/testsplit.rb +87 -0
  24. data/examples/testsplit2.rb +123 -0
  25. data/examples/testsplit3.rb +215 -0
  26. data/examples/testsplit3_1.rb +244 -0
  27. data/examples/testsplit3a.rb +215 -0
  28. data/examples/testsplit3b.rb +237 -0
  29. data/examples/testsplitta.rb +148 -0
  30. data/examples/testsplittv.rb +142 -0
  31. data/examples/testsplittvv.rb +144 -0
  32. data/examples/testtable.rb +1 -1
  33. data/examples/testtabp.rb +3 -2
  34. data/examples/testtestw.rb +69 -0
  35. data/examples/testtodo.rb +5 -3
  36. data/examples/testtpane.rb +203 -0
  37. data/examples/testtpane2.rb +145 -0
  38. data/examples/testtpanetable.rb +199 -0
  39. data/examples/viewtodo.rb +5 -3
  40. data/lib/rbcurse.rb +1 -1
  41. data/lib/rbcurse/celleditor.rb +2 -2
  42. data/lib/rbcurse/colormap.rb +5 -5
  43. data/lib/rbcurse/defaultlistselectionmodel.rb +3 -3
  44. data/lib/rbcurse/io.rb +663 -0
  45. data/lib/rbcurse/listeditable.rb +306 -0
  46. data/lib/rbcurse/listkeys.rb +15 -15
  47. data/lib/rbcurse/listscrollable.rb +168 -27
  48. data/lib/rbcurse/mapper.rb +35 -13
  49. data/lib/rbcurse/rchangeevent.rb +28 -0
  50. data/lib/rbcurse/rform.rb +845 -0
  51. data/lib/rbcurse/rlistbox.rb +144 -34
  52. data/lib/rbcurse/rmessagebox.rb +10 -5
  53. data/lib/rbcurse/rmulticontainer.rb +325 -0
  54. data/lib/rbcurse/rmultitextview.rb +306 -0
  55. data/lib/rbcurse/rscrollform.rb +369 -0
  56. data/lib/rbcurse/rscrollpane.rb +511 -0
  57. data/lib/rbcurse/rsplitpane.rb +820 -0
  58. data/lib/rbcurse/rtabbedpane.rb +737 -109
  59. data/lib/rbcurse/rtabbedwindow.rb +326 -0
  60. data/lib/rbcurse/rtable.rb +220 -64
  61. data/lib/rbcurse/rtextarea.rb +340 -181
  62. data/lib/rbcurse/rtextview.rb +237 -101
  63. data/lib/rbcurse/rviewport.rb +203 -0
  64. data/lib/rbcurse/rwidget.rb +919 -95
  65. data/lib/rbcurse/scrollable.rb +7 -7
  66. data/lib/rbcurse/selectable.rb +4 -4
  67. data/lib/rbcurse/table/tablecellrenderer.rb +3 -0
  68. data/lib/rbcurse/undomanager.rb +181 -0
  69. data/lib/rbcurse/vieditable.rb +100 -0
  70. data/lib/ver/window.rb +471 -21
  71. metadata +66 -22
  72. data/README.txt +0 -312
  73. data/examples/testd.db +0 -0
  74. data/examples/todocsv.csv +0 -28
@@ -1,6 +1,12 @@
1
- ## rahul kumar, 2009
2
- # to demonstrate usage of rbcurse
3
- # Use C-q to quit
1
+ ## rkumar, 2009
2
+ # Sample demo of various widgets and their interaction.
3
+ # This is a simple sql client which allows table / column selection, construction
4
+ # of SQL queries, and multiple resultsets.
5
+ # Use C-q to quit, Alt-Tab to move out of Table to next field.
6
+ # Please see bind_key statements in this app for some key bindings in table.
7
+ # There are also key bindings in tabbedpanes and textarea's that will help alot.
8
+ # This demo uses a tabbedpane so we can have the results of many sql statements and not
9
+ # need to keep reissuing.
4
10
  #
5
11
  require 'rubygems'
6
12
  require 'ncurses'
@@ -11,10 +17,11 @@ require 'rbcurse/rcombo'
11
17
  require 'rbcurse/rtextarea'
12
18
  require 'rbcurse/rtable'
13
19
  #require 'rbcurse/table/tablecellrenderer'
14
- require 'rbcurse/comboboxcellrenderer'
15
- require 'rbcurse/keylabelprinter'
20
+ #require 'rbcurse/comboboxcellrenderer'
21
+ #require 'rbcurse/keylabelprinter'
16
22
  require 'rbcurse/applicationheader'
17
- require 'rbcurse/action'
23
+ require 'rbcurse/action' # not used here
24
+ require 'rbcurse/rtabbedpane'
18
25
 
19
26
  # pls get testd.db from
20
27
  # http://www.benegal.org/files/screen/testd.db
@@ -137,10 +144,10 @@ end
137
144
  class Sqlc
138
145
  def initialize
139
146
  @window = VER::Window.root_window
147
+ $catch_alt_digits = false # we want to use Alt-1, 2 for tabs.
140
148
  @form = Form.new @window
149
+ @tab_ctr = 0
141
150
 
142
- #@todo = Sql.new "todo.yml"
143
- #@todo.load
144
151
  @db = Datasource.new
145
152
  @db.connect "testd.db"
146
153
  end
@@ -170,8 +177,6 @@ class Sqlc
170
177
  end
171
178
  sqlarea << "select * from contacts"
172
179
  buttrow = r+ta_ht+1 #Ncurses.LINES-4
173
- #create_table_actions atable, todo, data, categ.getvalue
174
- #save_cmd = @save_cmd
175
180
  b_run = Button.new @form do
176
181
  text "&Run"
177
182
  row buttrow
@@ -179,14 +184,12 @@ class Sqlc
179
184
  help_text "Run query"
180
185
  end
181
186
  ## We use Action to create a button: to test out ampersand with MI and Button
182
- #clear_act = @clear_act
183
187
  b_clear = Button.new @form do
184
188
  #action new_act
185
189
  text "&Clear"
186
190
  row buttrow
187
191
  col c+10
188
192
  help_text "Clear query entry box "
189
- #bind(:ENTER) { status_row.text "New button adds a new row below current " }
190
193
  end
191
194
  b_clear.command {
192
195
  sqlarea.remove_all
@@ -199,31 +202,15 @@ class Sqlc
199
202
  text "Constr&uct"
200
203
  row buttrow
201
204
  col c+25
202
- #bind(:ENTER) { status_row.text "Deletes focussed row" }
203
205
  help_text "Select a table, select columns and press this to construct an SQL"
204
206
  end
205
207
 
206
208
  Button.button_layout [b_run, b_clear, b_construct], buttrow, startcol=5, cols=Ncurses.COLS-1, gap=5
207
209
 
208
- table_ht = 15
209
- atable = Table.new @form do
210
- name "sqltable"
211
- row buttrow+1
212
- col c
213
- width t_width
214
- height table_ht
215
- #title "A Table"
216
- #title_attrib (Ncurses::A_REVERSE | Ncurses::A_BOLD)
217
- #set_data data, colnames
218
- #cell_editing_allowed true
219
- #editing_policy :EDITING_AUTO
220
- help_text "M-Tab for next field"
221
- end
222
- @atable = atable
210
+ @tp = create_tabbed_pane @form, buttrow, t_width, c
211
+ @tp.show
223
212
  @data = data
224
- #atable.table_model.data = data
225
213
 
226
- tcm = atable.get_table_column_model
227
214
  b_run.command {
228
215
  query = sqlarea.get_text
229
216
  run_query query
@@ -232,45 +219,52 @@ class Sqlc
232
219
  ## key bindings fo atable
233
220
  # column widths
234
221
  app = self
235
- atable.configure() do
236
- #bind_key(330) { atable.remove_column(tcm.column(atable.focussed_col)) rescue "" }
237
- bind_key(?+) {
238
- acolumn = atable.column atable.focussed_col()
239
- w = acolumn.width + 1
240
- acolumn.width w
241
- #atable.table_structure_changed
242
- }
243
- bind_key(?-) {
244
- acolumn = atable.column atable.focussed_col()
245
- w = acolumn.width - 1
246
- if w > 3
247
- acolumn.width w
248
- #atable.table_structure_changed
249
- end
250
- }
251
- bind_key(?>) {
252
- colcount = tcm.column_count-1
253
- #atable.move_column sel_col.value, sel_col.value+1 unless sel_col.value == colcount
254
- col = atable.focussed_col
255
- atable.move_column col, col+1 unless col == colcount
256
- }
257
- bind_key(?<) {
258
- col = atable.focussed_col
259
- atable.move_column col, col-1 unless col == 0
260
- #atable.move_column sel_col.value, sel_col.value-1 unless sel_col.value == 0
261
- }
262
- bind_key(?\M-h, app) {|tab,td| $log.debug " BIND... #{tab.class}, #{td.class}"; app.make_popup atable}
263
- end
222
+ #atable.configure() do
223
+ ##bind_key(330) { atable.remove_column(tcm.column(atable.focussed_col)) rescue "" }
224
+ #bind_key(?+) {
225
+ #acolumn = atable.column atable.focussed_col()
226
+ #w = acolumn.width + 1
227
+ #acolumn.width w
228
+ ##atable.table_structure_changed
229
+ #}
230
+ #bind_key(?-) {
231
+ #acolumn = atable.column atable.focussed_col()
232
+ #w = acolumn.width - 1
233
+ #if w > 3
234
+ #acolumn.width w
235
+ ##atable.table_structure_changed
236
+ #end
237
+ #}
238
+ ## added new method on 2009-10-08 00:47
239
+ #bind_key(?=) {
240
+ #atable.size_columns_to_fit
241
+ #}
242
+ #bind_key(?>) {
243
+ #tcm = atable.get_table_column_model
244
+ #colcount = tcm.column_count-1
245
+ ##atable.move_column sel_col.value, sel_col.value+1 unless sel_col.value == colcount
246
+ #col = atable.focussed_col
247
+ #atable.move_column col, col+1 unless col == colcount
248
+ #}
249
+ #bind_key(?<) {
250
+ #col = atable.focussed_col
251
+ #atable.move_column col, col-1 unless col == 0
252
+ ##atable.move_column sel_col.value, sel_col.value-1 unless sel_col.value == 0
253
+ #}
254
+ ## TODO popup and key labels
255
+ #bind_key(?\M-h, app) {|tab,td| $log.debug " BIND... #{tab.class}, #{td.class}"; app.make_popup atable}
256
+ #end
264
257
  #keylabel = RubyCurses::Label.new @form, {'text' => "", "row" => r+table_ht+3, "col" => c, "color" => "yellow", "bgcolor"=>"blue", "display_length"=>60, "height"=>2}
265
258
  #eventlabel = RubyCurses::Label.new @form, {'text' => "Events:", "row" => r+table_ht+6, "col" => c, "color" => "white", "bgcolor"=>"blue", "display_length"=>60, "height"=>2}
266
259
 
267
260
  # report some events
268
261
  #atable.table_model.bind(:TABLE_MODEL_EVENT){|e| #eventlabel.text = "Event: #{e}"}
269
262
  #atable.get_table_column_model.bind(:TABLE_COLUMN_MODEL_EVENT){|e| eventlabel.text = "Event: #{e}"}
270
- atable.bind(:TABLE_TRAVERSAL_EVENT){|e| @header.text_right "Row #{e.newrow+1} of #{atable.row_count}" }
271
263
 
272
264
  tablist_ht = 6
273
265
  mylist = @db.get_data "select name from sqlite_master"
266
+ # mylist is an Array of SQLite3::ResultSet::ArrayWithTypesAndFields
267
+ mylist.collect!{|x| x[0] } ## 1.9 hack, but will it run on 1.8 ??
274
268
  $listdata = Variable.new mylist
275
269
  tablelist = Listbox.new @form do
276
270
  name "tablelist"
@@ -305,29 +299,35 @@ class Sqlc
305
299
  title_attrib 'reverse'
306
300
  help_text "Press ENTER to append columns to sqlarea, Space to select"
307
301
  end
302
+ ## pressing SPACE on a table populates column list with its columns so they can be selected
308
303
  tablelist.bind_key(32) {
309
304
  @status_row.text = "Selected #{tablelist.get_content()[tablelist.current_index]}"
310
305
  table = "#{tablelist.get_content()[tablelist.current_index]}"
306
+ ##table = table[0] if table.class==Array ## 1.9 ???
311
307
  columnlist.list_data_model.remove_all
312
308
  columnlist.list_data_model.insert 0, *@db.get_metadata(table)
313
309
  }
310
+ ## pressing ENTER on a table runs a query on it, no need to type and SQL
314
311
  tablelist.bind_key(13) {
315
312
  @status_row.text = "Selected #{tablelist.get_content()[tablelist.current_index]}"
316
313
  table = "#{tablelist.get_content()[tablelist.current_index]}"
314
+ ##table = table[0] if table.class==Array ## 1.9 ???
317
315
  run_query "select * from #{table}"
318
316
  }
319
317
  columnlist.bind_key(13) {
320
- # append column name to sqlarea if ENTER pressed
318
+ ## append column name to sqlarea if ENTER pressed
321
319
  column = "#{columnlist.get_content()[columnlist.current_index]}"
322
320
  sqlarea << "#{column},"
323
321
  }
324
322
  columnlist.bind_key(32) {
325
- # select row
323
+ ## select row - later can press Construct button
326
324
  columnlist.toggle_row_selection
327
325
  column = "#{columnlist.get_content()[columnlist.current_index]}"
328
326
  }
327
+ ## construct an SQL after selecting some columns in the column list
329
328
  b_construct.command {
330
329
  table = "#{tablelist.get_content()[tablelist.current_index]}"
330
+ #table = table[0] if table.class==Array ## 1.9 ???
331
331
  indexes = columnlist.selected_rows()
332
332
  columns=[]
333
333
  indexes.each do |i|
@@ -342,10 +342,9 @@ class Sqlc
342
342
  @window.wrefresh
343
343
  Ncurses::Panel.update_panels
344
344
  begin
345
- while((ch = @window.getchar()) != ?\C-q )
346
- #colcount = tcm.column_count-1
345
+ while((ch = @window.getchar()) != ?\C-q.getbyte(0) )
347
346
  s = keycode_tos ch
348
- #status_row.text = "Pressed #{ch} , #{s}"
347
+ status_row.text = "Pressed #{ch} , #{s}. Press C-q to quit, Alt-Tab for exiting table "
349
348
  @form.handle_key(ch)
350
349
 
351
350
  @form.repaint
@@ -355,6 +354,8 @@ class Sqlc
355
354
  @window.destroy if !@window.nil?
356
355
  end
357
356
  end
357
+ ## execute the query in the textarea
358
+ # @param [String] sql string
358
359
  def run_query sql
359
360
  #query = sqlarea.get_text
360
361
  query = sql
@@ -365,53 +366,43 @@ class Sqlc
365
366
  return
366
367
  end
367
368
  #cw = @db.estimate_column_widths @atable.width, @db.columns
368
- @atable.set_data @content, @db.columns
369
- cw = @atable.estimate_column_widths @db.columns, @db.datatypes
370
- @atable.set_column_widths cw
369
+ atable = create_table @tp, @tab_ctr #, buttrow, t_width, c
370
+ atable.set_data @content, @db.columns
371
+ cw = atable.estimate_column_widths @db.columns, @db.datatypes
372
+ atable.set_column_widths cw
371
373
  rescue => exc
374
+ $log.debug(exc.backtrace.join("\n"))
372
375
  alert exc.to_s
373
376
  return
374
377
  end
375
378
  @status_row.text = "#{@content.size} rows retrieved"
376
- @atable.repaint
379
+ atable.repaint
377
380
  end
378
- def create_table_actions atable, todo, data, categ
379
- #@new_act = Action.new("New Row", "mnemonic"=>"N") {
380
- @new_act = Action.new("&New Row") {
381
- mod = nil
382
- cc = atable.get_table_column_model.column_count
383
- if atable.row_count < 1
384
- frow = 0
385
- else
386
- frow = atable.focussed_row
387
- #frow += 1 # why ?
388
- mod = atable.get_value_at(frow,0) unless frow.nil?
389
- end
390
- tmp = [mod, 5, "", "TODO", Time.now]
391
- tm = atable.table_model
392
- tm.insert frow, tmp
393
- atable.set_focus_on frow
394
- @status_row.text = "Added a row. Please press Save before changing Category."
395
- alert("Added a row before current one. Use C-k to clear task.")
396
- }
397
- @new_act.accelerator "Alt-N"
398
- @save_cmd = lambda {
399
- todo.set_tasks_for_category categ, data
400
- todo.dump
401
- alert("Rewritten yaml file")
402
- }
403
- @del_cmd = lambda {
404
- row = atable.focussed_row
405
- if !row.nil?
406
- if confirm("Do your really want to delete row #{row+1}?")== :YES
407
- tm = atable.table_model
408
- tm.delete_at row
409
- else
410
- @status_row.text = "Delete cancelled"
411
- end
381
+ ## create a Table component for populating with data
382
+ def create_table tp, counter #, buttrow, t_width, c
383
+ table_ht = 15
384
+ atable = Table.new do
385
+ name "sqltable#{counter}"
386
+ #cell_editing_allowed true
387
+ #editing_policy :EDITING_AUTO
388
+ #help_text "M-Tab for next field, M-8 amd M-7 for horiz scroll, + to resize, C-q quit"
389
+ help_text "M-Tab for next field, C-q quit"
390
+ end
391
+ atable.bind(:TABLE_TRAVERSAL_EVENT){|e| @header.text_right "Row #{e.newrow+1} of #{atable.row_count}" }
392
+ @tab_ctr += 1
393
+ tab1 = tp.add_tab "Tab&#{@tab_ctr}" , atable
394
+ return atable
395
+ end
396
+ ## create the single tabbedpane for populating with resultsets
397
+ def create_tabbed_pane form, buttrow, t_width, c
398
+ tp = RubyCurses::TabbedPane.new form do
399
+ height 16
400
+ width t_width
401
+ row buttrow +1
402
+ col c
403
+ button_type :ok
412
404
  end
413
- }
414
-
405
+ return tp
415
406
  end
416
407
  end
417
408
  if $0 == __FILE__
@@ -425,7 +416,7 @@ if $0 == __FILE__
425
416
  $log.level = Logger::DEBUG
426
417
 
427
418
  colors = Ncurses.COLORS
428
- $log.debug "START #{colors} colors ---------"
419
+ $log.debug "START #{colors} colors SQLC demo "
429
420
 
430
421
  catch(:close) do
431
422
  t = Sqlc.new
@@ -0,0 +1,446 @@
1
+ ## rkumar, 2009
2
+ # Sample demo of various widgets and their interaction.
3
+ # This is a simple sql client which allows table / column selection, construction
4
+ # of SQL queries, and multiple resultsets.
5
+ # Use C-q to quit, Alt-Tab to move out of Table to next field.
6
+ # Please see bind_key statements in this app for some key bindings in table.
7
+ # This is an offshoot of sqlc.rb -- this demo uses a multicontainer for tables
8
+ # instead of a tabbed panes.
9
+ #
10
+ require 'rubygems'
11
+ require 'ncurses'
12
+ require 'logger'
13
+ require 'sqlite3'
14
+ require 'rbcurse'
15
+ require 'rbcurse/rcombo'
16
+ require 'rbcurse/rtextarea'
17
+ require 'rbcurse/rtable'
18
+ #require 'rbcurse/table/tablecellrenderer'
19
+ #require 'rbcurse/comboboxcellrenderer'
20
+ #require 'rbcurse/keylabelprinter'
21
+ require 'rbcurse/applicationheader'
22
+ #require 'rbcurse/action' # not used here
23
+ #require 'rbcurse/rtabbedpane'
24
+ require 'rbcurse/rmulticontainer'
25
+
26
+ # pls get testd.db from
27
+ # http://www.benegal.org/files/screen/testd.db
28
+ # or put some other sqlite3 db name there.
29
+
30
+ ## must give me @content, @columns, @datatypes (opt)
31
+ class Datasource
32
+ # attr_reader :field_length # specified by user, length of row in display table
33
+ attr_accessor :columns # names of columns in array
34
+ attr_accessor :datatypes # array of datatyps of columns required to align: int, real, float, smallint
35
+ attr_accessor :content # 2 dim data
36
+ attr_accessor :user_columns # columnnames provided by user, overrides what is generated for display
37
+ # attr_reader :sqlstring # specified by user
38
+
39
+ # constructor
40
+ def initialize(config={}, &block)
41
+ @content = []
42
+ @columns = nil # actual db columnnames -- needed to figure out datatypes
43
+ @user_columns = nil # user specified db columnnames, overrides what may be provided
44
+ @datatypes = nil
45
+ # @rows = nil
46
+ # @sqlstring = nil
47
+ # @command = nil
48
+
49
+ instance_eval(&block) if block_given?
50
+ end
51
+ def connect dbname
52
+ @db = SQLite3::Database.new(dbname)
53
+ end
54
+ # get columns and datatypes, prefetch
55
+ def get_data command
56
+ @columns, *rows = @db.execute2(command)
57
+ @content = rows
58
+ return nil if @content.nil? or @content[0].nil?
59
+ @datatypes = @content[0].types #if @datatypes.nil?
60
+ @command = command
61
+ return @content
62
+ end
63
+ def get_metadata table
64
+ get_data "select * from #{table} limit 1"
65
+ return @columns
66
+ end
67
+ ##
68
+ # returns columns_widths, and updates that variable
69
+ def estimate_column_widths tablewidth, columns
70
+ colwidths = {}
71
+ min_column_width = (tablewidth/columns.length) -1
72
+ $log.debug("min: #{min_column_width}, #{tablewidth}")
73
+ @content.each_with_index do |row, cix|
74
+ break if cix >= 20
75
+ row.each_index do |ix|
76
+ col = row[ix]
77
+ colwidths[ix] ||= 0
78
+ colwidths[ix] = [colwidths[ix], col.length].max
79
+ end
80
+ end
81
+ total = 0
82
+ colwidths.each_pair do |k,v|
83
+ name = columns[k.to_i]
84
+ colwidths[name] = v
85
+ total += v
86
+ end
87
+ colwidths["__TOTAL__"] = total
88
+ column_widths = colwidths
89
+ @max_data_widths = column_widths.dup
90
+
91
+ columns.each_with_index do | col, i|
92
+ if @datatypes[i].match(/(real|int)/) != nil
93
+ wid = column_widths[i]
94
+ # cw = [column_widths[i], [8,min_column_width].min].max
95
+ $log.debug("XXX #{wid}. #{columns[i].length}")
96
+ cw = [wid, columns[i].length].max
97
+ $log.debug("int #{col} #{column_widths[i]}, #{cw}")
98
+ elsif @datatypes[i].match(/(date)/) != nil
99
+ cw = [column_widths[i], [12,min_column_width].min].max
100
+ #cw = [12,min_column_width].min
101
+ $log.debug("date #{col} #{column_widths[i]}, #{cw}")
102
+ else
103
+ cw = [column_widths[i], min_column_width].max
104
+ if column_widths[i] <= col.length and col.length <= min_column_width
105
+ cw = col.length
106
+ end
107
+ $log.debug("else #{col} #{column_widths[i]}, #{col.length} #{cw}")
108
+ end
109
+ column_widths[i] = cw
110
+ total += cw
111
+ end
112
+ column_widths["__TOTAL__"] = total
113
+ $log.debug("Estimated col widths: #{column_widths.inspect}")
114
+ @column_widths = column_widths
115
+ return column_widths
116
+ end
117
+
118
+ # added to enable query form to allow movement into table only if
119
+ # there is data 2008-10-08 17:46
120
+ # returns number of rows fetched
121
+ def data_length
122
+ return @content.length
123
+ end
124
+
125
+ end
126
+ def get_key_labels
127
+ key_labels = [
128
+ ['C-q', 'Exit'], nil,
129
+ ['M-s', 'Save'], ['M-m', 'Move']
130
+ ]
131
+ return key_labels
132
+ end
133
+ def get_key_labels_table
134
+ key_labels = [
135
+ ['M-n','NewRow'], ['M-d','DelRow'],
136
+ ['C-x','Select'], nil,
137
+ ['M-0', 'Top'], ['M-9', 'End'],
138
+ ['C-p', 'PgUp'], ['C-n', 'PgDn'],
139
+ ['M-Tab','Nxt Fld'], ['Tab','Nxt Col'],
140
+ ['+','Widen'], ['-','Narrow']
141
+ ]
142
+ return key_labels
143
+ end
144
+ class Sqlc
145
+ def initialize
146
+ @window = VER::Window.root_window
147
+ $catch_alt_digits = false # we want to use Alt-1, 2 for tabs.
148
+ @form = Form.new @window
149
+ @tab_ctr = 0
150
+
151
+ @db = Datasource.new
152
+ @db.connect "testd.db"
153
+ end
154
+ def run
155
+ title = "rbcurse"
156
+ @header = ApplicationHeader.new @form, title, {:text2=>"Demo", :text_center=>"SQL Client"}
157
+ status_row = RubyCurses::Label.new @form, {'text' => "", :row => Ncurses.LINES-4, :col => 0, :display_length=>70}
158
+ @status_row = status_row
159
+ # setting ENTER across all objects on a form
160
+ @form.bind(:ENTER) {|f| status_row.text = f.help_text unless f.help_text.nil? }
161
+ r = 1; c = 1;
162
+ @data = [ ["No data"] ]
163
+ data = @data
164
+ colnames = %w[ Result ]
165
+
166
+ ta_ht = 5
167
+ t_width = 78
168
+ sqlarea = TextArea.new @form do
169
+ name "sqlarea"
170
+ row r
171
+ col c
172
+ width t_width
173
+ height ta_ht
174
+ title "Sql Query"
175
+ title_attrib (Ncurses::A_REVERSE | Ncurses::A_BOLD)
176
+ help_text "Enter query and press Run or Meta-r"
177
+ end
178
+ sqlarea << "select * from contacts"
179
+ buttrow = r+ta_ht+1 #Ncurses.LINES-4
180
+ b_run = Button.new @form do
181
+ text "&Run"
182
+ row buttrow
183
+ col c
184
+ help_text "Run query"
185
+ end
186
+ ## We use Action to create a button: to test out ampersand with MI and Button
187
+ b_clear = Button.new @form do
188
+ #action new_act
189
+ text "&Clear"
190
+ row buttrow
191
+ col c+10
192
+ help_text "Clear query entry box "
193
+ end
194
+ b_clear.command {
195
+ sqlarea.remove_all
196
+ sqlarea.focus
197
+ }
198
+
199
+ # using ampersand to set mnemonic
200
+
201
+ b_construct = Button.new @form do
202
+ text "Constr&uct"
203
+ row buttrow
204
+ col c+25
205
+ help_text "Select a table, select columns and press this to construct an SQL"
206
+ end
207
+
208
+ Button.button_layout [b_run, b_clear, b_construct], buttrow, startcol=5, cols=Ncurses.COLS-1, gap=5
209
+
210
+ @tp = create_tabbed_pane @form, buttrow, t_width, c
211
+ @tp.show
212
+ @data = data
213
+
214
+ b_run.command {
215
+ query = sqlarea.get_text
216
+ run_query query
217
+ }
218
+ #
219
+ ## key bindings fo atable
220
+ # column widths
221
+ app = self
222
+ #atable.configure() do
223
+ ##bind_key(330) { atable.remove_column(tcm.column(atable.focussed_col)) rescue "" }
224
+ #bind_key(?+) {
225
+ #acolumn = atable.column atable.focussed_col()
226
+ #w = acolumn.width + 1
227
+ #acolumn.width w
228
+ ##atable.table_structure_changed
229
+ #}
230
+ #bind_key(?-) {
231
+ #acolumn = atable.column atable.focussed_col()
232
+ #w = acolumn.width - 1
233
+ #if w > 3
234
+ #acolumn.width w
235
+ ##atable.table_structure_changed
236
+ #end
237
+ #}
238
+ ## added new method on 2009-10-08 00:47
239
+ #bind_key(?=) {
240
+ #atable.size_columns_to_fit
241
+ #}
242
+ #bind_key(?>) {
243
+ #tcm = atable.get_table_column_model
244
+ #colcount = tcm.column_count-1
245
+ ##atable.move_column sel_col.value, sel_col.value+1 unless sel_col.value == colcount
246
+ #col = atable.focussed_col
247
+ #atable.move_column col, col+1 unless col == colcount
248
+ #}
249
+ #bind_key(?<) {
250
+ #col = atable.focussed_col
251
+ #atable.move_column col, col-1 unless col == 0
252
+ ##atable.move_column sel_col.value, sel_col.value-1 unless sel_col.value == 0
253
+ #}
254
+ ## TODO popup and key labels
255
+ #bind_key(?\M-h, app) {|tab,td| $log.debug " BIND... #{tab.class}, #{td.class}"; app.make_popup atable}
256
+ #end
257
+ #keylabel = RubyCurses::Label.new @form, {'text' => "", "row" => r+table_ht+3, "col" => c, "color" => "yellow", "bgcolor"=>"blue", "display_length"=>60, "height"=>2}
258
+ #eventlabel = RubyCurses::Label.new @form, {'text' => "Events:", "row" => r+table_ht+6, "col" => c, "color" => "white", "bgcolor"=>"blue", "display_length"=>60, "height"=>2}
259
+
260
+ # report some events
261
+ #atable.table_model.bind(:TABLE_MODEL_EVENT){|e| #eventlabel.text = "Event: #{e}"}
262
+ #atable.get_table_column_model.bind(:TABLE_COLUMN_MODEL_EVENT){|e| eventlabel.text = "Event: #{e}"}
263
+
264
+ tablist_ht = 6
265
+ mylist = @db.get_data "select name from sqlite_master"
266
+ # mylist is an Array of SQLite3::ResultSet::ArrayWithTypesAndFields
267
+ mylist.collect!{|x| x[0] } ## 1.9 hack, but will it run on 1.8 ??
268
+ $listdata = Variable.new mylist
269
+ tablelist = Listbox.new @form do
270
+ name "tablelist"
271
+ row 1
272
+ col t_width+2
273
+ width 20
274
+ height tablist_ht
275
+ # list mylist
276
+ list_variable $listdata
277
+ #selection_mode :SINGLE
278
+ #show_selector true
279
+ title "Tables"
280
+ title_attrib 'reverse'
281
+ help_text "Press ENTER to run * query, Space to select columns"
282
+ end
283
+ #tablelist.bind(:PRESS) { |alist| @status_row.text = "Selected #{alist.current_index}" }
284
+ tablelist.list_selection_model().bind(:LIST_SELECTION_EVENT,tablelist) { |lsm, alist| @status_row.text = "Selected #{alist.current_index}" }
285
+
286
+ collist = []
287
+ $coldata = Variable.new collist
288
+ columnlist = Listbox.new @form do
289
+ name "columnlist"
290
+ row tablist_ht+2
291
+ col t_width+2
292
+ width 20
293
+ height 15
294
+ # list mylist
295
+ list_variable $coldata
296
+ #selection_mode :SINGLE
297
+ #show_selector true
298
+ title "Columns"
299
+ title_attrib 'reverse'
300
+ help_text "Press ENTER to append columns to sqlarea, Space to select"
301
+ end
302
+ ## pressing SPACE on a table populates column list with its columns so they can be selected
303
+ tablelist.bind_key(32) {
304
+ @status_row.text = "Selected #{tablelist.get_content()[tablelist.current_index]}"
305
+ table = "#{tablelist.get_content()[tablelist.current_index]}"
306
+ ##table = table[0] if table.class==Array ## 1.9 ???
307
+ columnlist.list_data_model.remove_all
308
+ columnlist.list_data_model.insert 0, *@db.get_metadata(table)
309
+ }
310
+ ## pressing ENTER on a table runs a query on it, no need to type and SQL
311
+ tablelist.bind_key(13) {
312
+ @status_row.text = "Selected #{tablelist.get_content()[tablelist.current_index]}"
313
+ table = "#{tablelist.get_content()[tablelist.current_index]}"
314
+ ##table = table[0] if table.class==Array ## 1.9 ???
315
+ run_query "select * from #{table}"
316
+ }
317
+ columnlist.bind_key(13) {
318
+ ## append column name to sqlarea if ENTER pressed
319
+ column = "#{columnlist.get_content()[columnlist.current_index]}"
320
+ sqlarea << "#{column},"
321
+ }
322
+ columnlist.bind_key(32) {
323
+ ## select row - later can press Construct button
324
+ columnlist.toggle_row_selection
325
+ column = "#{columnlist.get_content()[columnlist.current_index]}"
326
+ }
327
+ ## construct an SQL after selecting some columns in the column list
328
+ b_construct.command {
329
+ table = "#{tablelist.get_content()[tablelist.current_index]}"
330
+ #table = table[0] if table.class==Array ## 1.9 ???
331
+ indexes = columnlist.selected_rows()
332
+ columns=[]
333
+ indexes.each do |i|
334
+ columns << columnlist.get_content()[i]
335
+ end
336
+ sql = "select #{columns.join(',')} from #{table}"
337
+ sqlarea << sql
338
+ }
339
+
340
+
341
+ @form.repaint
342
+ @window.wrefresh
343
+ Ncurses::Panel.update_panels
344
+ begin
345
+ while((ch = @window.getchar()) != ?\C-q.getbyte(0) )
346
+ s = keycode_tos ch
347
+ status_row.text = "Pressed #{ch} , #{s}. Press C-q to quit, Alt-Tab for exiting table "
348
+ @form.handle_key(ch)
349
+
350
+ @form.repaint
351
+ @window.wrefresh
352
+ end
353
+ ensure
354
+ @window.destroy if !@window.nil?
355
+ end
356
+ end
357
+ ## execute the query in the textarea
358
+ # @param [String] sql string
359
+ def run_query sql
360
+ #query = sqlarea.get_text
361
+ query = sql
362
+ begin
363
+ @content = @db.get_data query
364
+ if @content.nil?
365
+ @status_row.text = "0 rows retrieved"
366
+ return
367
+ end
368
+ #cw = @db.estimate_column_widths @atable.width, @db.columns
369
+ atable = create_table @tp, @tab_ctr #, buttrow, t_width, c
370
+ atable.set_data @content, @db.columns
371
+ cw = atable.estimate_column_widths @db.columns, @db.datatypes
372
+ atable.set_column_widths cw
373
+ rescue => exc
374
+ $log.debug(exc.backtrace.join("\n"))
375
+ alert exc.to_s
376
+ return
377
+ end
378
+ @status_row.text = "#{@content.size} rows retrieved"
379
+ atable.repaint
380
+ end
381
+ ## create a Table component for populating with data
382
+ def create_table tp, counter #, buttrow, t_width, c
383
+ table_ht = 15
384
+ atable = Table.new do
385
+ name "sqltable#{counter}"
386
+ #cell_editing_allowed true
387
+ #editing_policy :EDITING_AUTO
388
+ #help_text "M-Tab for next field, M-8 amd M-7 for horiz scroll, + to resize, C-q quit"
389
+ help_text "M-Tab for next field, C-q quit"
390
+ end
391
+ atable.bind(:TABLE_TRAVERSAL_EVENT){|e| @header.text_right "Row #{e.newrow+1} of #{atable.row_count}" }
392
+ @tab_ctr += 1
393
+ #tab1 = tp.add_tab "Tab&#{@tab_ctr}" , atable
394
+ tab1 = tp.add atable, "Tab&#{@tab_ctr}"
395
+ return atable
396
+ end
397
+ ## create the single tabbedpane for populating with resultsets
398
+ def create_tabbed_pane form, buttrow, t_width, c
399
+ tp = MultiContainer.new @form do
400
+ name "multic"
401
+ height 16
402
+ width t_width
403
+ row buttrow +1
404
+ col c
405
+ #row r
406
+ #col c
407
+ #width 60
408
+ #height 15
409
+ title "Results"
410
+ end
411
+ #tp = RubyCurses::TabbedPane.new form do
412
+ #height 16
413
+ #width t_width
414
+ #row buttrow +1
415
+ #col c
416
+ #button_type :ok
417
+ #end
418
+ return tp
419
+ end
420
+ end
421
+ if $0 == __FILE__
422
+ include RubyCurses
423
+ include RubyCurses::Utils
424
+
425
+ begin
426
+ # Initialize curses
427
+ VER::start_ncurses # this is initializing colors via ColorMap.setup
428
+ $log = Logger.new("view.log")
429
+ $log.level = Logger::DEBUG
430
+
431
+ colors = Ncurses.COLORS
432
+ $log.debug "START #{colors} colors SQLC demo "
433
+
434
+ catch(:close) do
435
+ t = Sqlc.new
436
+ t.run
437
+ end
438
+ rescue => ex
439
+ ensure
440
+ VER::stop_ncurses
441
+ p ex if ex
442
+ p(ex.backtrace.join("\n")) if ex
443
+ $log.debug( ex) if ex
444
+ $log.debug(ex.backtrace.join("\n")) if ex
445
+ end
446
+ end