rbcurse 1.3.0 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
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