rbcurse 0.1.3 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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