rbcurse 1.3.0 → 1.4.0

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 (70) hide show
  1. data/CHANGELOG +33 -0
  2. data/README.markdown +7 -1
  3. data/TODO2.txt +21 -15
  4. data/VERSION +1 -1
  5. data/examples/abasiclist.rb +2 -2
  6. data/examples/alpmenu.rb +1 -1
  7. data/examples/app.rb +0 -1
  8. data/examples/appdirtree.rb +4 -2
  9. data/examples/appemail.rb +7 -3
  10. data/examples/appemaillb.rb +1 -1
  11. data/examples/appgcompose.rb +2 -2
  12. data/examples/appgmail.rb +1 -1
  13. data/examples/appmethods.rb +20 -2
  14. data/examples/atree.rb +9 -1
  15. data/examples/dbdemo.rb +460 -0
  16. data/examples/dirtree.rb +1 -1
  17. data/examples/menu1.rb +37 -5
  18. data/examples/multispl.rb +1 -1
  19. data/examples/rfe.rb +9 -2
  20. data/examples/splitp.rb +1 -1
  21. data/examples/sqlc.rb +6 -10
  22. data/examples/sqlm.rb +2 -20
  23. data/examples/sqlt.rb +408 -0
  24. data/examples/term2.rb +1 -1
  25. data/examples/test2.rb +169 -97
  26. data/examples/testapp.rb +1 -1
  27. data/examples/testapp2.rb +1 -1
  28. data/examples/testkeypress.rb +4 -2
  29. data/examples/testtable.rb +6 -0
  30. data/examples/testtpane.rb +35 -23
  31. data/examples/testvimsplit.rb +3 -2
  32. data/lib/rbcurse.rb +1 -1
  33. data/lib/rbcurse/action.rb +8 -0
  34. data/lib/rbcurse/app.rb +39 -23
  35. data/lib/rbcurse/extras/bottomline.rb +101 -13
  36. data/lib/rbcurse/extras/directorylist.rb +14 -5
  37. data/lib/rbcurse/extras/divider.rb +1 -1
  38. data/lib/rbcurse/extras/listselectable.rb +42 -8
  39. data/lib/rbcurse/extras/masterdetail.rb +2 -2
  40. data/lib/rbcurse/extras/scrollbar.rb +11 -2
  41. data/lib/rbcurse/extras/statusline.rb +56 -0
  42. data/lib/rbcurse/extras/stdscrwindow.rb +11 -0
  43. data/lib/rbcurse/extras/tabular.rb +2 -1
  44. data/lib/rbcurse/extras/tabularwidget.rb +60 -17
  45. data/lib/rbcurse/extras/viewer.rb +16 -4
  46. data/lib/rbcurse/keylabelprinter.rb +34 -4
  47. data/lib/rbcurse/listeditable.rb +5 -1
  48. data/lib/rbcurse/listkeys.rb +1 -1
  49. data/lib/rbcurse/listscrollable.rb +15 -8
  50. data/lib/rbcurse/rbasiclistbox.rb +44 -23
  51. data/lib/rbcurse/rcommandwindow.rb +8 -14
  52. data/lib/rbcurse/rdialogs.rb +187 -2
  53. data/lib/rbcurse/rlistbox.rb +38 -19
  54. data/lib/rbcurse/rmenu.rb +313 -93
  55. data/lib/rbcurse/rmessagebox.rb +3 -2
  56. data/lib/rbcurse/rmulticontainer.rb +5 -3
  57. data/lib/rbcurse/rmultisplit.rb +2 -11
  58. data/lib/rbcurse/rmultitextview.rb +4 -5
  59. data/lib/rbcurse/rtabbedpane.rb +223 -69
  60. data/lib/rbcurse/rtable.rb +6 -10
  61. data/lib/rbcurse/rtextarea.rb +57 -36
  62. data/lib/rbcurse/rtextview.rb +12 -15
  63. data/lib/rbcurse/rtree.rb +79 -22
  64. data/lib/rbcurse/rvimsplit.rb +16 -25
  65. data/lib/rbcurse/rwidget.rb +376 -523
  66. data/lib/rbcurse/tree/treecellrenderer.rb +24 -11
  67. data/lib/rbcurse/tree/treemodel.rb +1 -1
  68. data/lib/ver/window.rb +130 -66
  69. metadata +5 -3
  70. data/examples/term.rb +0 -48
data/CHANGELOG CHANGED
@@ -1,3 +1,36 @@
1
+ **2011-10-06**
2
+ ## 1.3.1 or 1.4.0
3
+
4
+ ### Major cleanup of widget class
5
+ Scrollpane support (buffering stuff all removed)
6
+
7
+ ### TabbedPane
8
+ Corrected, major changes
9
+
10
+ ### dsl_accessor and dsl_property
11
+ Now return self so we can chain.
12
+
13
+ ### return values of some keys (Class Form)
14
+ Earlier not returning anything meaningful or returning last_key
15
+ Now return NO_NEXT_FIELD or NO_PREVIOUS_FIELD
16
+
17
+ ### j and k keys used for down and up where possible
18
+ e.g. button and its children including checkbox
19
+
20
+ ### Miscellaneous
21
+
22
+ * Dynamic menuitems in menu (see menu1.tb)
23
+ * rework on menu and menubar, and keys using them
24
+ * FieldVetoException (see test2.rb)
25
+ * valid_range for Field (see test2.rb)
26
+ * fixed field's type method, earlier non-functional
27
+ * array and 4 integer constructor for Window
28
+ * StatusWindow class - 2 line window for alert, confirm and statuses as an
29
+ alternative to dialogs.
30
+ * status_dialog returning a handle so we can keep updating status then
31
+ close.
32
+ * many more minor adjustments, tweaks, issues
33
+
1
34
  **2011-09-26**
2
35
  ## 1.3.0
3
36
 
data/README.markdown CHANGED
@@ -2,7 +2,11 @@
2
2
 
3
3
  * Migrating from ncurses-ruby to ffi-ncurses
4
4
 
5
- I have moved rbcurse to ffi-ncurses (earlier ncurses gem).
5
+ I have moved rbcurse to ffi-ncurses (earlier ncurses gem) since 1.3.0.
6
+
7
+ Please report bugs on github/rkumar/rbcurse mentioning version. Your program dependency
8
+ should be an explicit rbcurse version since I am doing a lot of improvements, cleaning up
9
+ etc in the code base.
6
10
 
7
11
  If you are looking for the ncurses version, please goto
8
12
  branch rbcurse1.2.0. I am not likely to support that henceforth due to the difficulty in installing ncurses gem.
@@ -17,6 +21,8 @@ then do a final after some testing.
17
21
 
18
22
  * Version that works with ruby 1.9 (backward compatible with 1.8.7)
19
23
 
24
+ * 1.3.1: Rework of some classes - see CHANGELOG
25
+ * 1.3.0: ffi-ification of rbcurses with some minor bug-fixes, deprecations
20
26
  * 1.2.0: many additions (See CHANGELOG for details)
21
27
  - App class that wraps the environment and makes application development very easy
22
28
  - New controls such as:
data/TODO2.txt CHANGED
@@ -41,7 +41,6 @@
41
41
  28 [x] (A) TextArea: borders get overwritten  * chomp was required when using << (2010-01-14) (x2010-01-14)
42
42
  29 [x] what if we don't want to print borders, since that eats an extraline and col. Can we switch off and reclaim the line and col (2010-01-15) (x2010-02-18)
43
43
  31 [x] move create_buffer to repaintin TA and TV and others, so that enduser does not have to bother. scroll and split can set internally  * All create_buffer calls moved to repaint. Maybe table pending (2010-01-16) (x2010-02-18)
44
- 36 [ ] should parent set scrollatrows for lb and tv and ta ? (2010-01-17)
45
44
  37 [x] (B) test rtable in splitpane and scrollpanes  * done rtable in scrollpane (2010-01-18) (x2010-02-24)
46
45
  38 [x] (B) test various widgets inside rtabbedpane (2010-01-19) (x2010-02-22)
47
46
  41 [x] Textareas: C-a C-e does not repain which is good, but footer not updated (2010-01-23) (x2010-01-25)
@@ -69,7 +68,6 @@
69
68
  72 [ ] (F) TextView: keymaps. p 699 (2010-03-07)
70
69
  73 [ ] (F) TextView: freezing an area horiz vertically (2010-03-07)
71
70
  74 [x] General: move key bindings to bind_key so user can see what's bound (2010-03-07)
72
- 76 [ ] a Box class = would that help? (2010-03-08)
73
71
  78 [x] (A) Undo: refactor undomanager and undohandler (2010-03-08) (x2010-03-10)
74
72
  79 [x] (A) Demo: get sqlc.rb demo running (2010-03-09) (x2010-03-14)
75
73
  80 [x] firehandler: can we combine consecutive ones into one, passing an array? (2010-03-09) (x2010-03-12)
@@ -80,22 +78,18 @@
80
78
  85 [x] (F) how about getting ascii tables in here for sql stuff (2010-03-12)
81
79
  87 [x] (E) MultiTextView: list buffers to do (2010-03-12) (x2010-03-15)
82
80
  88 [x] (E) TextV+A, when printing a screenful, store actual maxlen for scrolling left (2010-03-12) (x2010-03-13)
83
- 89 [ ] (Z) Scrollpane is just wrong with textobjects, searching doesnt work properly - it doesnt show up (2010-03-13)
84
- 90 [ ] (Z) tabbedpane buttons focus sometimes mixes with table first row focuses (2010-03-14)
85
81
  91 [x] (B) tabbedpane: adding new tab dyn, not immediately shown (2010-03-14) (x2010-03-15)
86
- 93 [ ] (Z) tabbedpane: new tabs not really getting focus??? (2010-03-15)
87
82
  94 [x] (F) MultiContainer: takes n objects, fills, one can cycle, just as in MultiTextView except that any component can be put. (2010-03-15) (x2010-03-15)
88
- 95 [ ] (Z) TabbedWindow is totally off - redo ? (2010-03-16)
89
83
  97 [x] (A) Scrollform : dont we need some indicators or bars (2010-03-17) (x2010-03-18)
90
84
  98 [x] (A) SCrollFOrm: cursor not on first field to start with (2010-03-17) (x2010-03-18)
91
85
  99 [ ] scrollform demo: add larger objects like textview and test (2010-03-18)
92
86
  100 [x] (A) sqlm.rb finish this with multicontainer and table (2010-03-19) (x2010-03-19)
93
- 101 [ ] Menu: simpler rewrite ?? (2010-03-20)
94
- 102 [ ] TabbedPane: rewrite (2011-09-21)
87
+ 101 [x] Menu: simpler rewrite ?? (2010-03-20)
88
+ 102 [x] (A) TabbedPane: rewrite (2011-09-21)
95
89
  103 [ ] Messagebox: rewrite (2011-09-21)
96
- 104 [ ] (E) Gen: catching extended keys C-left, S-F10 (2011-09-21)
97
- 105 [ ] (A) Window: combine array into existing constructor (2011-09-21)
98
- 106 [ ] gen: check if that buffering nonsense is still being used? (2011-09-21)
90
+ 104 [x] (E) Gen: catching extended keys C-left, S-F10 (2011-09-21)
91
+ 105 [x] (A) Window: combine array into existing constructor (2011-09-21)
92
+ 106 [x] (A) gen: check if that buffering nonsense is still being used? (2011-09-21)
99
93
  107 [ ] (F) find_file like microemacs (2011-09-21)
100
94
  108 [ ] (F) color schemes (applying at form or app level) (2011-09-21)
101
95
  109 [ ] (E) App: stack and flow to be objects so RESIZE can happen (2011-09-21)
@@ -105,8 +99,20 @@
105
99
  110.3 [ ] (E) Text objects: execute ex commands ???  * printf '%cs/change this/to that/nwq' '%' | ex file.name (2010-03-05)
106
100
  110.4 [ ] (E) TextView etal: store path so save can be done? (2010-03-12)
107
101
  110.5 [ ] (E) table: yank one or more rows into kill ring as comma delim (2010-03-17)
108
- 110.6 [ ] (B) Table needs multiplier for resizing cols (2010-02-24)
109
- 110.7 [ ] TabbedPane: move fun stuff to Util class - dont crowd main classes (2010-03-14)
102
+ 110.6 [ ] Table needs multiplier for resizing cols (2010-02-24)
110
103
  110.8 [ ] (O) maybe we should combine set_form_row and col since we call them one after another nowadays (2010-01-16)
111
- 110.9 [ ] (O) size changes immediately trigger a buffer resize ...instead set a flag so one resize can be done in repaint. otherwise too many resizes (2010-01-16)
112
- 110.10 [ ] are we destroying these pads ?? I don;t think so. (2010-02-19)
104
+ 110.11 [ ] should parent set scrollatrows for lb and tv and ta ? (2010-01-17)
105
+ 110.12 [ ] a Box class = would that help? (2010-03-08)
106
+ 111 [ ] (B) me: arrow down and up moves vertically not to next field (2011-09-21)
107
+ 112 [ ] (E) app: add popup (2011-09-21)
108
+ 113 [ ] (F) basic html view like alpine help with link, underline (2011-09-21)
109
+ 115 [ ] (F) widgets shd add major keys to keylabel if present (2011-09-28)
110
+ 116 [ ] (F) colormap can check for colorscheme before setting colors (2011-10-01)
111
+ 117 [ ] make readme and homepage simple and clean like urwid (2011-10-01)
112
+ 118 [x] return self from dsl_property and dsl_accessor (2011-10-02)
113
+ 119 [x] PropertyVetoException to disallow change (2011-10-02)
114
+ 120 [x] StatusWindow: 2 line bottom window for alert, confirm and status (2011-10-02)
115
+ 121 [x] cleanup Widget and others, remove buffering nonsense (2011-10-02)
116
+ 122 [x] Add valid_range to Field (2011-10-02)
117
+ 123 [x] (A) Make listbox data more accessible from lb class (2011-10-02)
118
+ 124 [ ] look at sandy,ne,pygments source for ncurses syntax highlighting (2011-10-07)
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.3.0
1
+ 1.4.0
@@ -3,8 +3,8 @@ require 'rbcurse/rbasiclistbox'
3
3
 
4
4
  # just a simple test to ensure that rbasiclistbox is running inside a container.
5
5
  App.new do
6
- header = app_header "rbcurse 1.2.0", :text_center => "Basic List Demo", :text_right =>"New Improved!", :color => :black, :bgcolor => :white, :attr => :bold
7
- message "Press F1 to escape from here"
6
+ header = app_header "rbcurse #{Rbcurse::VERSION}", :text_center => "Basic List Demo", :text_right =>"New Improved!", :color => :black, :bgcolor => :white, :attr => :bold
7
+ message "Press F10 to escape from here"
8
8
 
9
9
  list = %W{ bhikshu boddisattva avalokiteswara mu mun kwan paramita prajna samadhi sutra shakyamuni }
10
10
  vimsplit :row => 1, :col => 0, :suppress_borders => false, :width => 60, :height => Ncurses.LINES-2, :weight => 0.4, :orientation => :VERTICAL do |s|
data/examples/alpmenu.rb CHANGED
@@ -3,7 +3,7 @@ require 'rbcurse/app'
3
3
  App.new do
4
4
  #title "Demo of Menu - rbcurse"
5
5
  #subtitle "Hit F1 to quit, F2 for menubar toggle"
6
- header = app_header "rbcurse 1.2.0", :text_center => "Alpine Menu Demo", :text_right =>""
6
+ header = app_header "rbcurse #{Rbcurse::VERSION}", :text_center => "Alpine Menu Demo", :text_right =>""
7
7
  message_row(27)
8
8
 
9
9
  stack :margin_top => 10, :margin => 15 do
data/examples/app.rb CHANGED
@@ -22,7 +22,6 @@ module RubyCurses
22
22
 
23
23
  ##
24
24
  #
25
- # @since 1.1.6
26
25
  # TODO -
27
26
  # what of internal objects that don't want a form !
28
27
  # - stack and flow should be objects in Form, put in widget when creating
@@ -39,7 +39,7 @@ App.new do
39
39
  last = nodes.last
40
40
  nodes.last.add entries
41
41
  model = DefaultTreeModel.new nodes.first
42
- header = app_header "rbcurse 1.2.0", :text_center => "Yet Another File Manager", :text_right =>"Directory Lister", :color => :black, :bgcolor => :white#, :attr => Ncurses::A_BLINK
42
+ header = app_header "rbcurse #{Rbcurse::VERSION}", :text_center => "Yet Another File Manager", :text_right =>"Directory Lister", :color => :black, :bgcolor => :white#, :attr => Ncurses::A_BLINK
43
43
  message "Press Enter to expand/collapse"
44
44
 
45
45
 
@@ -69,5 +69,7 @@ App.new do
69
69
  #@l = list_box :height => ht, :border_attrib => borderattrib, :selection_mode => :multiple
70
70
  s.add dl, :SECOND
71
71
  end # vimsplit
72
- end # flow
72
+ end # stack
73
+ sl = status_line :row => Ncurses.LINES-1
74
+ sl.command { "%-20s | %-20s | v View" % [Time.now, File.absolute_path(dl.current_path)] }
73
75
  end # app
data/examples/appemail.rb CHANGED
@@ -8,8 +8,10 @@ require './rmail'
8
8
  # this will go into top namespace so will conflict with other apps!
9
9
  def testchoose
10
10
  # list filters as you type
11
- $log.debug "called test1 " if $log.debug?
12
- str = choose "*", :title => "Files", :prompt => "Choose a file: "
11
+ $log.debug "called CHOOSE " if $log.debug?
12
+ filter = "*"
13
+ filter = ENV['PWD']+"/*"
14
+ str = choose filter, :title => "Files", :prompt => "Choose a file: "
13
15
  end
14
16
  def testnumberedmenu
15
17
  list1 = %w{ ruby perl python erlang rake java lisp scheme chicken }
@@ -70,7 +72,7 @@ App.new do
70
72
  $unread_hash = {}
71
73
  @tv = nil
72
74
  borderattrib = :reverse
73
- @header = app_header "rbcurse 1.2.0", :text_center => "Yet Another Email Client that sucks", :text_right =>"", :color => :black, :bgcolor => :white#, :attr => Ncurses::A_BLINK
75
+ @header = app_header "rbcurse #{Rbcurse::VERSION}", :text_center => "Yet Another Email Client that sucks", :text_right =>"", :color => :black, :bgcolor => :white
74
76
  message "Press F10 to exit ...................................................."
75
77
 
76
78
 
@@ -186,6 +188,8 @@ App.new do
186
188
  @tv.suppress_borders true
187
189
  @tv.border_attrib = borderattrib
188
190
  end # stack
191
+ @statusline = status_line :row => Ncurses.LINES-1
192
+ #@statusline.command { }
189
193
  @form.bind_key(?\M-v) { test11() }
190
194
  @form.bind_key(?\M-V) { testme() }
191
195
  @form.bind_key(?\M-c) { test1() }
@@ -244,7 +244,7 @@ App.new do
244
244
  @messages = nil
245
245
  @tv = nil
246
246
  borderattrib = :reverse
247
- @header = app_header "rbcurse 1.2.0", :text_center => "Yet Another Email Client that sucks", :text_right =>"", :color => :black, :bgcolor => :white#, :attr => Ncurses::A_BLINK
247
+ @header = app_header "rbcurse #{Rbcurse::VERSION}", :text_center => "Yet Another Email Client that sucks", :text_right =>"", :color => :black, :bgcolor => :white#, :attr => Ncurses::A_BLINK
248
248
  message "Press F10 to exit ...................................................."
249
249
 
250
250
 
@@ -196,8 +196,8 @@ module AppgCompose
196
196
  def get_commands
197
197
  %w{ insert_file}
198
198
  end
199
- header = app.app_header "rbcurse 1.2.0", :text_center => "Compose Mail", :text_right =>"27% Stronger", :color => :black, :bgcolor => :white, :attr => :bold
200
- app.message "Press F1 to exit from here"
199
+ header = app.app_header "rbcurse #{Rbcurse::VERSION}", :text_center => "Compose Mail", :text_right =>"27% Stronger", :color => :black, :bgcolor => :white, :attr => :bold
200
+ app.message "Press F10 to exit from here"
201
201
  app.stack :margin_top => 2, :margin => 5, :width => 15 do |xxx|
202
202
  fg = :white
203
203
  bg = :blue
data/examples/appgmail.rb CHANGED
@@ -672,7 +672,7 @@ App.new do
672
672
  @default_mailbox = "INBOX"
673
673
  @gmail = nil
674
674
  borderattrib = :reverse
675
- @header = app_header "rbcurse 1.2.0", :text_center => "Yet Another Gmail Client that sucks", :text_right =>"", :color => :black, :bgcolor => :white#, :attr => Ncurses::A_BLINK
675
+ @header = app_header "rbcurse #{Rbcurse::VERSION}", :text_center => "Yet Another Gmail Client that sucks", :text_right =>"", :color => :black, :bgcolor => :white#, :attr => Ncurses::A_BLINK
676
676
  message "Press F1 to exit ...................................................."
677
677
 
678
678
 
@@ -18,8 +18,8 @@ module RubyCurses
18
18
  VER::start_ncurses
19
19
  #@form.reset_all # not required
20
20
  end
21
- @form.repaint
22
- @window.wrefresh
21
+ @form.repaint if @form
22
+ @window.wrefresh if @window
23
23
  Ncurses::Panel.update_panels
24
24
  end
25
25
  def suspend
@@ -64,6 +64,24 @@ module RubyCurses
64
64
  t.attr = :reverse
65
65
  end
66
66
  end
67
+ def shell_output
68
+ cmd = get_string("Enter shell command:", 50)
69
+ if cmd && !cmd.empty?
70
+ run_command cmd
71
+ end
72
+ end
73
+ def run_command cmd
74
+ # http://whynotwiki.com/Ruby_/_Process_management#What_happens_to_standard_error_.28stderr.29.3F
75
+ require 'rbcurse/extras/viewer'
76
+ begin
77
+ res = `#{cmd} 2>&1`
78
+ rescue => ex
79
+ res = ex.to_s
80
+ res << ex.backtrace.join("\n")
81
+ end
82
+ res.gsub!("\t"," ")
83
+ RubyCurses::Viewer.view(res.split("\n"), :close_key => KEY_RETURN, :title => "<Enter> to close, M-l M-h to scroll")
84
+ end
67
85
  end # utils
68
86
  end # module RubyC
69
87
  include RubyCurses::Utils
data/examples/atree.rb CHANGED
@@ -1,8 +1,12 @@
1
1
  require 'rbcurse/app'
2
2
 
3
3
  App.new do
4
- header = app_header "rbcurse 1.2.0", :text_center => "Tree Demo", :text_right =>"New Improved!", :color => :black, :bgcolor => :white, :attr => :bold
4
+ header = app_header "rbcurse #{Rbcurse::VERSION}", :text_center => "Tree Demo", :text_right =>"New Improved!", :color => :black, :bgcolor => :white, :attr => :bold
5
5
  message "Press Enter to expand/collapse"
6
+ @form.bind_key(FFI::NCurses::KEY_F3) {
7
+ require 'rbcurse/extras/viewer'
8
+ RubyCurses::Viewer.view("rbc13.log", :close_key => KEY_RETURN, :title => "<Enter> to close")
9
+ }
6
10
 
7
11
  stack :margin_top => 2, :margin => 5, :width => 30 do
8
12
  tm = nil
@@ -17,6 +21,9 @@ App.new do
17
21
  end
18
22
  end
19
23
  end
24
+ found=atree.get_node_for_path "goodbye"
25
+ atree.set_expanded_state(atree.root, true)
26
+ atree.set_expanded_state(found,true)
20
27
 
21
28
  # using a Hash
22
29
  model = { :ruby => [ "jruby", {:mri => %W[ 1.8.6 1.8.7]}, {:yarv => %W[1.9.1 1.9.2]}, "rubinius", "macruby" ], :python => %W[ cpython jython laden-swallow ] }
@@ -52,5 +59,6 @@ App.new do
52
59
  end
53
60
 
54
61
  tree :data => model, :title => "legacy way"
62
+
55
63
  end
56
64
  end # app
@@ -0,0 +1,460 @@
1
+ require 'rbcurse/app'
2
+ require 'sqlite3'
3
+ require 'rbcurse/undomanager'
4
+
5
+ # @return array of table names from selected db file
6
+ def get_table_names
7
+ raise "No database file selected." unless $current_db
8
+
9
+ $tables = get_data "select name from sqlite_master"
10
+ $tables.collect!{|x| x[0] } ## 1.9 hack, but will it run on 1.8 ??
11
+ $tables
12
+ end
13
+ def get_column_names tbname
14
+ get_metadata tbname
15
+ end
16
+ def connect dbname
17
+ $current_db = dbname
18
+ $db = SQLite3::Database.new(dbname)
19
+
20
+ return $db
21
+ end
22
+ def get_data sql
23
+ $log.debug "SQL: #{sql} "
24
+ $columns, *rows = $db.execute2(sql)
25
+ $log.debug "XXX COLUMNS #{sql} "
26
+ content = rows
27
+ return nil if content.nil? or content[0].nil?
28
+ $datatypes = content[0].types #if @datatypes.nil?
29
+ return content
30
+ end
31
+ def get_metadata table
32
+ get_data "select * from #{table} limit 1"
33
+ #$columns.collect!{|x| x[0] } ## 1.9 hack, but will it run on 1.8 ??
34
+ return $columns
35
+ end
36
+ #
37
+ # creates a popup for selection given the data, and executes given block with
38
+ # following return value.
39
+ # @return [String] if mode is :single
40
+ # @return [Array] if mode is :multiple
41
+ #
42
+ def create_popup array, selection_mode=:single, &blk
43
+ require 'rbcurse/rlistbox'
44
+ #raise "no block given " unless block_given?
45
+ listconfig = {'bgcolor' => 'blue', 'color' => 'white'}
46
+ url_list= RubyCurses::ListDataModel.new(array)
47
+ ht = 16
48
+ if array.length < 16
49
+ ht = array.length+1
50
+ end
51
+ pl = RubyCurses::PopupList.new do
52
+ row 4
53
+ col 10
54
+ width 30
55
+ height ht
56
+ #list url_list
57
+ list_data_model url_list
58
+ list_selection_mode selection_mode
59
+ #relative_to f
60
+ list_config listconfig
61
+ bind :PRESS do |lb|
62
+ #field.set_buffer url_list[index]
63
+ #blk.call(url_list[index]) #if &blk
64
+ #selected = []; indices.each{|i| selected << url_list[i] }
65
+ #blk.call(selected.join(", "))
66
+ if selection_mode == :single
67
+ blk.call(url_list[lb]) #if &blk
68
+ else
69
+ blk.call(lb.selected_values)
70
+ end
71
+
72
+ end
73
+ end
74
+ end
75
+
76
+ def view_data fields="*", name
77
+ stmt = "select #{fields} from #{name}"
78
+ stmt << $where_string if $where_string
79
+ stmt << $order_string if $order_string
80
+ @form.by_name['tarea'] << stmt
81
+ view_sql stmt
82
+ end
83
+ def view_sql stmt
84
+ begin
85
+ content = get_data stmt
86
+ if content.nil?
87
+ else
88
+ require 'rbcurse/extras/tabular'
89
+ t = Tabular.new do |t|
90
+ t.headings = $columns
91
+ t.data=content
92
+ end
93
+ view t.render
94
+ end
95
+ rescue => err
96
+ $log.error err.to_s
97
+ $log.error(err.backtrace.join("\n"))
98
+ alert err.to_s
99
+ end
100
+ end
101
+
102
+ App.new do
103
+ header = app_header "rbcurse #{Rbcurse::VERSION}", :text_center => "Database Demo", :text_right =>"enabled"
104
+ form = @form
105
+ mylabel = "a field"
106
+ $catch_alt_digits = true # use M-1..9 in textarea
107
+ $current_table = nil
108
+ $current_db = nil # "testd.db"
109
+ connect $current_db if $current_db
110
+
111
+ def help_text
112
+ <<-eos
113
+ APPEMAIL HELP
114
+
115
+ This is some help text for appemail.
116
+ We are testing out this feature.
117
+
118
+ Alt-d - Select a database
119
+ <Enter> on a table, view data (q to close window)
120
+ <Space> on a table, display columns in lower list
121
+
122
+ COLUMN LIST KEYS
123
+ <Space> on a column for multiple select
124
+ <Ctrl-Space> on a column for range select/deselect from previous selection
125
+ <Enter> on column table to view data for selected columns
126
+ u unselect all
127
+ a select all
128
+ * invert selection
129
+ F4 View data for selected table (or columns if selected)
130
+
131
+ q or C-q Close the data window that comes on Enter or F4
132
+
133
+ Alt-x - Command mode (<tab> to see commands and select)
134
+ : - Command mode
135
+ F10 - Quit application
136
+
137
+
138
+
139
+ -----------------------------------------------------------------------
140
+ Hope you enjoyed this help.
141
+ eos
142
+ end
143
+ def ask_databases
144
+ names = Dir.glob("*.{sqlite,db}")
145
+ if names
146
+ create_popup( names,:single) {|value| connect(value);
147
+ @form.by_name["tlist"].list(get_table_names)
148
+ }
149
+ else
150
+ alert "Can't find a .db or .sqlite file"
151
+ end
152
+ end
153
+ # TODO accelerators and
154
+ # getting a handle for later use
155
+ mb = menubar do
156
+ keep_visible true
157
+ #@toggle_key=KEY_F2
158
+ menu "File" do
159
+ item "Open", "O" do
160
+ accelerator "Ctrl-O"
161
+ command do
162
+ alert "HA!! you wanted to open a file?"
163
+ end
164
+ end
165
+ menu "Database" do
166
+ item_list do
167
+ Dir.glob("**/*.{sqlite,db}")
168
+ end
169
+ command do |menuitem, text|
170
+ connect text
171
+ form.by_name["tlist"].list(get_table_names)
172
+ end
173
+ end
174
+ menu "Tables" do
175
+ item_list do
176
+ if $current_db
177
+ get_table_names
178
+ end
179
+ end
180
+ command do |menuitem, text|
181
+ $current_table = text
182
+ #alert(get_column_names(text).join(", "))
183
+ create_popup(get_column_names(text), :multiple) { |value| view_data( value.join(","), text) }
184
+ end
185
+ end
186
+ item "New", "N"
187
+ separator
188
+ item "Exit", "x" do
189
+ command do
190
+ throw(:close)
191
+ end
192
+ end
193
+ item "Cancel Menu" do
194
+ accelerator "Ctrl-g"
195
+ end
196
+
197
+ end # menu
198
+ menu "Window" do
199
+ item "Tile", "T"
200
+ menu "Find" do
201
+ item "More", "M"
202
+ $x = item "Less", "L" do
203
+ #accelerator "Ctrl-X"
204
+ command do
205
+ alert "You clickses on Less"
206
+ end
207
+ end
208
+ menu "Size" do
209
+ item "Zoom", "Z"
210
+ item "Maximize", "X"
211
+ item "Minimize", "N"
212
+ end
213
+ end
214
+ end
215
+ menu "Others" do
216
+ require './appmethods.rb'
217
+ item "Shell Output" do
218
+ command { shell_output }
219
+ end
220
+ item "Suspend" do
221
+ command { suspend }
222
+ end
223
+ end
224
+ end # menubar
225
+ mb.toggle_key = FFI::NCurses::KEY_F2
226
+ mb.color = :white
227
+ mb.bgcolor = :blue
228
+ @form.set_menu_bar mb
229
+ stack :margin => 0 do
230
+ text = "No tables"
231
+ if !$current_db
232
+ text = "Select DB first. Press Alt-D"
233
+ end
234
+ tlist = basiclist :name => "tlist", :list => [text], :title => "Tables", :height => 10
235
+ tlist.bind(:PRESS) do |eve|
236
+ if $current_db
237
+ # get data of table
238
+ view_data eve.text
239
+ else
240
+ ask_databases
241
+ end
242
+ end
243
+ tlist.bind(:ENTER_ROW) do |eve|
244
+ # too much confusion between selected and focussed row
245
+ #$current_table = eve.text if $db
246
+ end
247
+ clist = basiclist :name => "clist", :list => ["No columns"], :title => "Columns", :height => 14,
248
+ :selection_mode => :multiple
249
+ tlist.bind(:LIST_SELECTION_EVENT) do |eve|
250
+ $selected_table = eve.source[eve.firstrow]
251
+ $current_table = $selected_table
252
+ clist.data = get_column_names $selected_table
253
+ end
254
+ clist.bind(:PRESS) do |eve|
255
+ # get data of table
256
+ if $selected_table
257
+ cols = "*"
258
+ c = clist.get_selected_values
259
+ unless c.empty?
260
+ cols = c.join(",")
261
+ end
262
+ view_data cols, $selected_table
263
+ else
264
+ alert "Select a table first."
265
+ end
266
+ end
267
+ clist.bind_key('w') {
268
+ c = clist.current_value
269
+ $where_columns ||= []
270
+ hist = ["#{c} = "]
271
+ w = ask("(UP arrow to edit) where "){ |q| q.default = "#{c} = "; q.history = hist }
272
+ $where_columns << w if w
273
+ message "where: #{$where_columns.last}. Press F4 when done"
274
+ $log.debug "XXX: WHERE: #{$where_columns} "
275
+ }
276
+ clist.bind_key('o') {
277
+ c = clist.current_value
278
+ $order_columns ||= []
279
+ $order_columns << c if c
280
+ message "order (asc): #{$order_columns.last}. Press F4 when done"
281
+ $log.debug "XXX: ORDER: #{$order_columns} "
282
+ }
283
+ clist.bind_key('O') {
284
+ c = clist.current_value
285
+ $order_columns ||= []
286
+ $order_columns << " #{c} desc " if c
287
+ message "order: #{$order_columns.last}"
288
+ $log.debug "XXX: ORDER: #{$order_columns}. Press F4 when done"
289
+ }
290
+ @statusline = status_line
291
+ @statusline.command {
292
+ # trying this out. If you want a persistent message that remains till the next on
293
+ # then send it in as $status_message
294
+ text = $status_message.value || ""
295
+ if !$current_db
296
+ "%-20s [%-s] %s" % [ Time.now, "Select a Database", text]
297
+ elsif !$current_table
298
+ "%-20s [DB: %-s | %-s ] %s" % [ Time.now, $current_db || "None", $current_table || "Select a table", text]
299
+ else
300
+ "%-20s [DB: %-s | %-s ] %s" % [ Time.now, $current_db || "None", $current_table || "----", text]
301
+ end
302
+ }
303
+ @adock = nil
304
+ keyarray = [
305
+ ["F1" , "Help"], ["F10" , "Exit"],
306
+ ["F2", "Menu"], ["F4", "View"],
307
+ ["M-d", "Datebase"], ["M-t", "Table"],
308
+ ["M-x", "Command"], nil
309
+ ]
310
+ tlist_keyarray = keyarray + [ ["Sp", "Select"], nil, ["Enter","View"] ]
311
+
312
+ clist_keyarray = keyarray + [ ["Sp", "Select"], ["C-sp", "Range Sel"],
313
+ ["Enter","View"], ['w', 'where'],
314
+ ["o","order by"], ['O', 'order desc']
315
+ ]
316
+ tarea_keyarray = keyarray + [ ["M-z", "Commands"], nil ]
317
+ #tarea_sub_keyarray = [ ["r", "Run"], ["c", "clear"], ["w","Save"], ["a", "Append next"],
318
+ #["y", "Yank"], ["Y", "yank pop"] ]
319
+ tarea_sub_keyarray = [ ["r", "Run"], ["c", "clear"], ["w","Kill Ring Save (M-w)"], ["a", "Append Next"],
320
+ ["y", "Yank (C-y)"], ["Y", "yank pop (M-y)"],
321
+ ["u", "Undo (C-_)"], ["R", "Redo (C-r)"],
322
+ ]
323
+
324
+ gw = get_color($reversecolor, 'green', 'black')
325
+ @adock = dock keyarray, { :row => Ncurses.LINES-2, :footer_color_pair => $datacolor,
326
+ :footer_mnemonic_color_pair => gw }
327
+ @adock.set_key_labels tlist_keyarray, :tables
328
+ @adock.set_key_labels clist_keyarray, :columns
329
+ @adock.set_key_labels tarea_sub_keyarray, :tarea_sub
330
+ @adock.set_key_labels tarea_keyarray, :tarea
331
+ tlist.bind(:ENTER) { @adock.mode :tables }
332
+ clist.bind(:ENTER) { @adock.mode :columns }
333
+
334
+ reduce = lambda { |obj|
335
+ obj.height -= 1 if obj.height > 3
336
+ }
337
+ increase = lambda { |obj|
338
+ obj.height += 1 if obj.height + obj.row < Ncurses.LINES-2
339
+ }
340
+ _lower = lambda { |obj|
341
+ obj.row += 1 if obj.height + obj.row < Ncurses.LINES-2
342
+ }
343
+ _raise = lambda { |obj|
344
+ obj.row -= 1 if obj.row > 2
345
+ }
346
+ [clist, tlist].each do |o|
347
+ o.bind_key([?\C-x, ?-]){ |o| reduce.call(o) }
348
+ o.bind_key([?\C-x, ?+]){ |o| increase.call(o) }
349
+ o.bind_key([?\C-x, ?v]){ |o| _lower.call(o) }
350
+ o.bind_key([?\C-x, ?6]){ |o| _raise.call(o) }
351
+ end
352
+
353
+
354
+ @form.bind_key([?q,?q]) { throw :close }
355
+ @form.bind_key(?\M-t) do
356
+ if $current_db.nil?
357
+ alert "Please select database first"
358
+ else
359
+ create_popup( get_table_names,:single) {|value| $selected_table = $current_table = value}
360
+ end
361
+ end
362
+ @form.bind_key(?\M-d) do
363
+ ask_databases
364
+ end
365
+ @form.bind_key(Ncurses::KEY_F4) do
366
+ $where_string = nil
367
+ $order_string = nil
368
+ if $where_columns
369
+ $where_string = " where " + $where_columns.join(" and ")
370
+ end
371
+ if $order_columns
372
+ $order_string = " order by " + $order_columns.join(" , ")
373
+ end
374
+ # mismatch between current and selected table
375
+ if $current_table
376
+ cols = "*"
377
+ c = clist.get_selected_values
378
+ unless c.empty?
379
+ cols = c.join(",")
380
+ end
381
+ view_data cols, $current_table
382
+ else
383
+ alert "Select a table first."
384
+ end
385
+ end
386
+ end # stack
387
+ stack :margin => 50, :width => :EXPAND do
388
+ tarea = textarea :name => 'tarea', :height => 20, :title => 'Sql Statement'
389
+ undom = SimpleUndo.new tarea
390
+ tarea.bind_key(Ncurses::KEY_F4) do
391
+ text = tarea.get_text
392
+ if text == ""
393
+ alert "Please enter a query and then hit F4. Or press F4 over column list"
394
+ else
395
+ view_sql tarea.get_text
396
+ end
397
+ end
398
+ tarea.bind(:ENTER) { @adock.mode :tarea }
399
+ tarea.bind_key(?\M-z){
400
+
401
+ hash = { 'c' => lambda{ tarea.remove_all },
402
+ 'w' => lambda{ tarea.kill_ring_save },
403
+ 'a' => lambda{ tarea.append_next_kill },
404
+ 'y' => lambda{ tarea.yank },
405
+ 'Y' => lambda{ tarea.yank_pop },
406
+ 'r' => lambda{ view_sql tarea.get_text },
407
+ 'u' => lambda{ tarea.undo },
408
+ 'R' => lambda{ tarea.redo },
409
+ }
410
+
411
+
412
+ @adock.mode :tarea_sub
413
+ @adock.repaint
414
+ keys = @adock.get_current_keys
415
+ while((ch = @window.getchar()) != ?\C-c.getbyte(0) )
416
+ if ch < 33 || ch > 126
417
+ Ncurses.beep
418
+ elsif !keys.include?(ch.chr)
419
+ Ncurses.beep
420
+ else
421
+ hash.fetch(ch.chr).call
422
+ #opt_file ch.chr
423
+ break
424
+ end
425
+ end
426
+ @adock.mode :normal
427
+ } # M-z
428
+ flow do
429
+ button_row = 17
430
+ button "Save" do
431
+ @cmd_history ||= []
432
+ filename = ask("File to append contents to: ") { |q| q.default = @oldfilename; q.history = @cmd_history }
433
+
434
+ if filename
435
+ str = tarea.get_text
436
+ File.open(filename, 'a') {|f| f.write(str) }
437
+ @oldfilename = filename
438
+ @cmd_history << filename unless @cmd_history.include? filename
439
+
440
+ message "Appended data to #{filename}"
441
+ else
442
+ message "Aborted operation"
443
+ end
444
+ hide_bottomline
445
+ end
446
+ button "Read" do
447
+ filter = "*"
448
+ str = choose filter, :title => "Files", :prompt => "Choose a file: "
449
+ begin
450
+ tarea.set_content(str)
451
+ message "Read content from #{str} "
452
+ rescue => err
453
+ say_with_pause "No file named: #{str}: #{err.to_s} "
454
+ end
455
+ end
456
+ #ok_button = button( [button_row,30], "OK", {:mnemonic => 'O'}) do
457
+ #end
458
+ end
459
+ end
460
+ end # app