rbhex-core 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (108) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +20 -0
  3. data/CHANGELOG +2000 -0
  4. data/LICENSE +56 -0
  5. data/README.md +44 -0
  6. data/examples/abasiclist.rb +179 -0
  7. data/examples/alpmenu.rb +50 -0
  8. data/examples/app.sample +19 -0
  9. data/examples/atree.rb +100 -0
  10. data/examples/bline.rb +136 -0
  11. data/examples/common/file.rb +45 -0
  12. data/examples/data/README.markdown +9 -0
  13. data/examples/data/brew.txt +38 -0
  14. data/examples/data/color.2 +37 -0
  15. data/examples/data/gemlist.txt +60 -0
  16. data/examples/data/lotr.txt +12 -0
  17. data/examples/data/ports.txt +136 -0
  18. data/examples/data/table.txt +37 -0
  19. data/examples/data/tasks.csv +88 -0
  20. data/examples/data/tasks.txt +27 -0
  21. data/examples/data/todo.txt +10 -0
  22. data/examples/data/todo.txt.bak +10 -0
  23. data/examples/data/todocsv.csv +28 -0
  24. data/examples/data/unix1.txt +21 -0
  25. data/examples/data/unix2.txt +11 -0
  26. data/examples/dbdemo.rb +502 -0
  27. data/examples/dirtree.rb +94 -0
  28. data/examples/newtabbedwindow.rb +100 -0
  29. data/examples/newtesttabp.rb +92 -0
  30. data/examples/tabular.rb +146 -0
  31. data/examples/tasks.rb +178 -0
  32. data/examples/term2.rb +84 -0
  33. data/examples/testbuttons.rb +296 -0
  34. data/examples/testcombo.rb +102 -0
  35. data/examples/testfields.rb +195 -0
  36. data/examples/testkeypress.rb +72 -0
  37. data/examples/testlistbox.rb +170 -0
  38. data/examples/testmessagebox.rb +140 -0
  39. data/examples/testprogress.rb +116 -0
  40. data/examples/testree.rb +106 -0
  41. data/examples/testwsshortcuts.rb +66 -0
  42. data/examples/testwsshortcuts2.rb +128 -0
  43. data/lib/rbhex.rb +6 -0
  44. data/lib/rbhex/core/docs/index.txt +73 -0
  45. data/lib/rbhex/core/include/action.rb +80 -0
  46. data/lib/rbhex/core/include/actionmanager.rb +49 -0
  47. data/lib/rbhex/core/include/appmethods.rb +214 -0
  48. data/lib/rbhex/core/include/bordertitle.rb +48 -0
  49. data/lib/rbhex/core/include/chunk.rb +203 -0
  50. data/lib/rbhex/core/include/io.rb +553 -0
  51. data/lib/rbhex/core/include/listbindings.rb +74 -0
  52. data/lib/rbhex/core/include/listcellrenderer.rb +140 -0
  53. data/lib/rbhex/core/include/listeditable.rb +317 -0
  54. data/lib/rbhex/core/include/listscrollable.rb +663 -0
  55. data/lib/rbhex/core/include/listselectable.rb +271 -0
  56. data/lib/rbhex/core/include/multibuffer.rb +83 -0
  57. data/lib/rbhex/core/include/orderedhash.rb +77 -0
  58. data/lib/rbhex/core/include/ractionevent.rb +73 -0
  59. data/lib/rbhex/core/include/rchangeevent.rb +27 -0
  60. data/lib/rbhex/core/include/rhistory.rb +95 -0
  61. data/lib/rbhex/core/include/rinputdataevent.rb +47 -0
  62. data/lib/rbhex/core/include/vieditable.rb +172 -0
  63. data/lib/rbhex/core/include/widgetmenu.rb +66 -0
  64. data/lib/rbhex/core/system/colormap.rb +165 -0
  65. data/lib/rbhex/core/system/keyboard.rb +150 -0
  66. data/lib/rbhex/core/system/keydefs.rb +30 -0
  67. data/lib/rbhex/core/system/ncurses.rb +236 -0
  68. data/lib/rbhex/core/system/panel.rb +162 -0
  69. data/lib/rbhex/core/system/window.rb +913 -0
  70. data/lib/rbhex/core/util/ansiparser.rb +119 -0
  71. data/lib/rbhex/core/util/app.rb +1228 -0
  72. data/lib/rbhex/core/util/basestack.rb +410 -0
  73. data/lib/rbhex/core/util/bottomline.rb +1859 -0
  74. data/lib/rbhex/core/util/colorparser.rb +77 -0
  75. data/lib/rbhex/core/util/focusmanager.rb +31 -0
  76. data/lib/rbhex/core/util/padreader.rb +192 -0
  77. data/lib/rbhex/core/util/rcommandwindow.rb +604 -0
  78. data/lib/rbhex/core/util/rdialogs.rb +574 -0
  79. data/lib/rbhex/core/util/viewer.rb +149 -0
  80. data/lib/rbhex/core/util/widgetshortcuts.rb +506 -0
  81. data/lib/rbhex/core/version.rb +5 -0
  82. data/lib/rbhex/core/widgets/applicationheader.rb +103 -0
  83. data/lib/rbhex/core/widgets/box.rb +58 -0
  84. data/lib/rbhex/core/widgets/divider.rb +310 -0
  85. data/lib/rbhex/core/widgets/keylabelprinter.rb +194 -0
  86. data/lib/rbhex/core/widgets/rcombo.rb +253 -0
  87. data/lib/rbhex/core/widgets/rcontainer.rb +415 -0
  88. data/lib/rbhex/core/widgets/rlink.rb +30 -0
  89. data/lib/rbhex/core/widgets/rlist.rb +696 -0
  90. data/lib/rbhex/core/widgets/rmenu.rb +958 -0
  91. data/lib/rbhex/core/widgets/rmenulink.rb +22 -0
  92. data/lib/rbhex/core/widgets/rmessagebox.rb +387 -0
  93. data/lib/rbhex/core/widgets/rprogress.rb +118 -0
  94. data/lib/rbhex/core/widgets/rtabbedpane.rb +634 -0
  95. data/lib/rbhex/core/widgets/rtabbedwindow.rb +70 -0
  96. data/lib/rbhex/core/widgets/rtextarea.rb +960 -0
  97. data/lib/rbhex/core/widgets/rtextview.rb +739 -0
  98. data/lib/rbhex/core/widgets/rtree.rb +768 -0
  99. data/lib/rbhex/core/widgets/rwidget.rb +3277 -0
  100. data/lib/rbhex/core/widgets/scrollbar.rb +143 -0
  101. data/lib/rbhex/core/widgets/statusline.rb +113 -0
  102. data/lib/rbhex/core/widgets/tabular.rb +264 -0
  103. data/lib/rbhex/core/widgets/tabularwidget.rb +1142 -0
  104. data/lib/rbhex/core/widgets/textpad.rb +995 -0
  105. data/lib/rbhex/core/widgets/tree/treecellrenderer.rb +150 -0
  106. data/lib/rbhex/core/widgets/tree/treemodel.rb +428 -0
  107. data/rbhex-core.gemspec +32 -0
  108. metadata +172 -0
@@ -0,0 +1,119 @@
1
+ # ----------------------------------------------------------------------------- #
2
+ # File: colorparser.rb
3
+ # Description: Default parse for our tmux format
4
+ # The aim is to be able to specify parsers so different kinds
5
+ # of formatting or documents can be used, such as ANSI formatted
6
+ # manpages.
7
+ # Author: rkumar http://github.com/rkumar/rbcurse/
8
+ # Date: 07.11.11 - 13:17
9
+ # License: Same as Ruby's License (http://www.ruby-lang.org/LICENSE.txt)
10
+ # Last update: 2013-04-01 13:43
11
+ # ----------------------------------------------------------------------------- #
12
+ # == TODO
13
+ # - perhaps we can compile the regexp once and reuse
14
+ #
15
+
16
+ module RubyCurses
17
+ class AnsiParser
18
+
19
+ # NOTE: Experimental and minimal
20
+ # parses the formatted string and yields either an array of color, bgcolor and attrib
21
+ # or the text. This will be called by convert_to_chunk.
22
+ #
23
+ # Currently, assumes colors and attributes are correct. No error checking or fancy stuff.
24
+ # s="#[fg=green]hello there#[fg=yellow, bg=black, dim]"
25
+ # @since 1.4.1 2011-11-3 experimental, can change
26
+ # @return [nil] knows nothign about output format.
27
+
28
+ def parse_format s # yields attribs or text
29
+ ## set default colors
30
+ color = :white
31
+ bgcolor = :black
32
+ attrib = FFI::NCurses::A_NORMAL
33
+ text = ""
34
+
35
+ ## split #[...]
36
+ #a = s.split /(#\[[^\]]*\])/
37
+ a = s.split /(\x1b\[\d*(?:;\d+)*?[a-zA-Z])/
38
+ a.each { |e|
39
+ ## process color or attrib portion
40
+ #[ "", "\e[1m", "", "\e[34m", "", "\e[47m", "Showing all items...", "\e[0m", "", "\e[0m", "\n"]
41
+ if e[0,1] == "\x1b" && e[-1,1] == "m"
42
+
43
+ #e.each { |f| x=/^.\[(.*).$/.match(f)
44
+ $log.debug "XXX: ANSI e #{e} "
45
+ x=/^.\[(.*).$/.match(e)
46
+ color, bgcolor, attrib = nil, nil, nil
47
+ $log.debug "XXX: ANSI #{x} ..... #{x[1]} "
48
+ args = x[1].split ';'
49
+ ## first split on commas to separate fg, bg and attr
50
+ # http://ascii-table.com/ansi-escape-sequences.php
51
+ args.each { |att|
52
+ $log.debug "XXX: ANSI att: #{att} "
53
+ case att.to_i
54
+ when 0
55
+ color, bgcolor, attrib = nil, nil, nil
56
+ yield :reset # actually this resets all so we need an endall or clearall reset
57
+
58
+ when 1
59
+ attrib = 'bold'
60
+ when 2
61
+ attrib = 'dim'
62
+ when 4
63
+ attrib = 'underline'
64
+ when 5
65
+ attrib = 'blink'
66
+ when 7
67
+ attrib = 'reverse'
68
+ when 8
69
+ attrib = 'hidden' # XXX
70
+ when 30
71
+ color = 'black'
72
+ when 31
73
+ color = 'red'
74
+ when 32
75
+ color = 'green'
76
+ when 33
77
+ color = 'yellow'
78
+ when 34
79
+ color = 'blue'
80
+ when 35
81
+ color = 'magenta'
82
+ when 36
83
+ color = 'cyan'
84
+ when 37
85
+ color = 'white'
86
+
87
+ #Background colors
88
+ when 40
89
+ bgcolor = 'black'
90
+ when 41
91
+ bgcolor = 'red'
92
+ when 42
93
+ bgcolor = 'green'
94
+ when 43
95
+ bgcolor = 'yellow'
96
+ when 44
97
+ bgcolor = 'blue'
98
+ when 45
99
+ bgcolor = 'magenta'
100
+ when 46
101
+ bgcolor = 'cyan'
102
+ when 47
103
+ bgcolor = 'white'
104
+ else
105
+ $log.warn "XXX: WARN ANSI not used #{att} "
106
+ end
107
+ } # args.ea
108
+ #} # e.each
109
+ $log.debug "XXX: ANSI YIELDING #{color} , #{bgcolor} , #{attrib} "
110
+ yield [color,bgcolor,attrib] if block_given?
111
+ else
112
+ text = e
113
+ yield text if block_given?
114
+ end
115
+ } # a.each
116
+ end
117
+
118
+ end
119
+ end
@@ -0,0 +1,1228 @@
1
+ =begin
2
+ * Name: App
3
+ * Description: Experimental Application class
4
+ * Author: rkumar (arunachalesha)
5
+ * file created 2010-09-04 22:10
6
+ Todo:
7
+
8
+ * 1.5.0 : redo the constructors of these widgets
9
+ as per stack flow improved, and make the constructor
10
+ simpler, no need for jugglery of row col etc. let user
11
+ specify in config.
12
+ --------
13
+ * License:
14
+ Same as Ruby's License (http://www.ruby-lang.org/LICENSE.txt)
15
+
16
+ =end
17
+ require 'logger'
18
+ require 'rbhex'
19
+ require 'rbhex/core/util/widgetshortcuts'
20
+
21
+ include RubyCurses
22
+ include RubyCurses::Utils
23
+ include Io
24
+ module RubyCurses
25
+ extend self
26
+ ##
27
+ #
28
+ # @since 1.2.0
29
+ # TODO -
30
+ # / combo
31
+ # - popup
32
+ # - promptmenu
33
+ # - stack and flow should be objects in Form/App?, put in widget when creating
34
+ # - box / rect
35
+ # - para looks like a label that is more than one line, and calculates rows itself based on text
36
+ # - multicontainer
37
+ # - multitextview, multisplit
38
+ # - tabbedpane
39
+ # / table - more work regarding vim keys, also editable
40
+ # - margin - is left offset
41
+ # http://lethain.com/entry/2007/oct/15/getting-started-shoes-os-x/
42
+ #
43
+
44
+ class Widget
45
+ def changed *args, &block
46
+ bind :CHANGED, *args, &block
47
+ end
48
+ def leave *args, &block
49
+ bind :LEAVE, *args, &block
50
+ end
51
+ def enter *args, &block
52
+ bind :ENTER, *args, &block
53
+ end
54
+ # actually we already have command() for buttons
55
+ def click *args, &block
56
+ bind :PRESS, *args, &block
57
+ end
58
+ end
59
+ class CheckBox
60
+ # a little dicey XXX
61
+ def text(*val)
62
+ if val.empty?
63
+ @value ? @onvalue : @offvalue
64
+ else
65
+ super
66
+ end
67
+ end
68
+ end
69
+ # This is the Application class which does the job of setting up the
70
+ # environment, and closing it at the end.
71
+ class App
72
+ include RubyCurses::WidgetShortcuts
73
+ attr_reader :config
74
+ attr_reader :form
75
+ attr_reader :window
76
+ attr_writer :quit_key
77
+ # the row on which to prompt user for any inputs
78
+ #attr_accessor :prompt_row # 2011-10-17 14:06:22
79
+
80
+ # TODO: i should be able to pass window coords here in config
81
+ # :title
82
+ def initialize config={}, &block
83
+ #$log.debug " inside constructor of APP #{config} "
84
+ @config = config
85
+
86
+
87
+ widget_shortcuts_init
88
+ #@app_row = @app_col = 0
89
+ #@stack = [] # stack's coordinates
90
+ #@flowstack = []
91
+ @variables = {}
92
+ # if we are creating child objects then we will not use outer form. this object will manage
93
+ @current_object = []
94
+ @_system_commands = %w{ bind_global bind_component field_help_text }
95
+
96
+ init_vars
97
+ $log.debug "XXX APP CONFIG: #{@config} " if $log.debug?
98
+ run &block
99
+ end
100
+ def init_vars
101
+ @quit_key ||= FFI::NCurses::KEY_F10
102
+ # actually this should be maintained inside ncurses pack, so not loaded 2 times.
103
+ # this way if we call an app from existing program, App won't start ncurses.
104
+ unless $ncurses_started
105
+ init_ncurses
106
+ end
107
+ $lastline = Ncurses.LINES - 1
108
+ #@message_row = Ncurses.LINES-1
109
+ #@prompt_row = @message_row # hope to use for ask etc # 2011-10-17 14:06:27
110
+ unless $log
111
+ path = File.join(ENV["LOGDIR"] || "./" ,"rbc13.log")
112
+ file = File.open(path, File::WRONLY|File::TRUNC|File::CREAT)
113
+ $log = Logger.new(path)
114
+ $log.level = Logger::DEBUG # change to warn when you've tested your app.
115
+ colors = Ncurses.COLORS
116
+ $log.debug "START #{colors} colors --------- #{$0} win: #{@window} "
117
+ end
118
+ end
119
+ def logger; return $log; end
120
+ def close
121
+ raw_message_destroy
122
+ $log.debug " INSIDE CLOSE, #{@stop_ncurses_on_close} "
123
+ @window.destroy if !@window.nil?
124
+ $log.debug " INSIDE CLOSE, #{@stop_ncurses_on_close} "
125
+ if @stop_ncurses_on_close
126
+ $tt.destroy if $tt # added on 2011-10-9 since we created a window, but only hid it after use
127
+ VER::stop_ncurses
128
+ $log.debug " CLOSING NCURSES"
129
+ end
130
+ #p $error_message.value unless $error_message.value.nil?
131
+ $log.debug " CLOSING APP"
132
+ #end
133
+ end
134
+ # not sure, but user shuld be able to trap keystrokes if he wants
135
+ # but do i still call handle_key if he does, or give him total control.
136
+ # But loop is already called by framework
137
+ def loop &block
138
+ @form.repaint
139
+ @window.wrefresh
140
+ Ncurses::Panel.update_panels
141
+ @break_key = ?\C-q.getbyte(0)
142
+ # added this extra loop since from some places we exit using throw :close
143
+ # amd that was in a much higher place, and was getting us right out, with
144
+ # no chance of user canceling quit. This extra loop allows us to remain
145
+ # added on 2011-11-24
146
+ while true
147
+ catch :close do
148
+ while((ch = @window.getchar()) != 999 )
149
+ #break if ch == @break_key
150
+ if ch == @break_key || ch == @quit_key
151
+ #stopping = @window.fire_close_handler
152
+ #break if stopping.nil? || stopping
153
+ break
154
+ end
155
+
156
+ if @keyblock
157
+ str = keycode_tos ch
158
+ @keyblock.call(str.gsub(/-/, "_").to_sym) # not used ever
159
+ end
160
+
161
+ yield ch if block # <<<----
162
+ # this is what the user should have control ove. earlier we would put this in
163
+ # a try catch block so user could do what he wanted with the error. Now we
164
+ # need to get it to him somehow, perhaps through a block or on_error event
165
+ begin
166
+ @form.handle_key ch
167
+ rescue => err
168
+ $log.debug( "handle_key rescue reached ")
169
+ $log.debug( err.to_s)
170
+ $log.debug(err.backtrace.join("\n"))
171
+ textdialog [err.to_s, *err.backtrace], :title => "Exception"
172
+ end
173
+ #@form.repaint # was this duplicate ?? handle calls repaint not needed
174
+ @window.wrefresh
175
+ end
176
+ end # catch
177
+ stopping = @window.fire_close_handler
178
+ @window.wrefresh
179
+ break if stopping.nil? || stopping
180
+ end # while
181
+ end
182
+ # if calling loop separately better to call this, since it will shut off ncurses
183
+ # and print error on screen.
184
+ def safe_loop &block
185
+ begin
186
+ loop &block
187
+ rescue => ex
188
+ $log.debug( "APP.rb rescue reached ")
189
+ $log.debug( ex) if ex
190
+ $log.debug(ex.backtrace.join("\n")) if ex
191
+ ensure
192
+ close
193
+ # putting it here allows it to be printed on screen, otherwise it was not showing at all.
194
+ if ex
195
+ puts "========== EXCEPTION =========="
196
+ p ex
197
+ puts "==============================="
198
+ puts(ex.backtrace.join("\n"))
199
+ end
200
+ end
201
+ end
202
+ # returns a symbol of the key pressed
203
+ # e.g. :C_c for Ctrl-C
204
+ # :Space, :bs, :M_d etc
205
+ def keypress &block
206
+ @keyblock = block
207
+ end
208
+ # updates a global var with text. Calling app has to set up a Variable with that name and attach to
209
+ # a label so it can be printed.
210
+ def message text
211
+ $status_message.value = text # trying out 2011-10-9
212
+ #@message.value = text # 2011-10-17 14:07:01
213
+ end
214
+
215
+ # during a process, when you wish to update status, since ordinarily the thread is busy
216
+ # and form does not get control back, so the window won't refresh.
217
+ # This will only update on keystroke since it uses statusline
218
+ # @deprecated please use {#status_line} instead of a message label
219
+ def message_immediate text
220
+ $log.warn "DEPRECATED, use message(), or rb_puts or use status_window"
221
+ $status_message.value = text # trying out 2011-10-9 user needs to use in statusline command
222
+ # 2011-10-17 knocking off label, should be printed on status_line
223
+ end
224
+ # Usage: application is inside a long processing loop and wishes to print ongoing status
225
+ # NOTE: if you use this, you must use raw_message_destroy at some stage, after processing
226
+ # or on_leave of object.
227
+ # @deprecated Use say_with_pause or use rdialogs status_window, see test2.rb
228
+ def raw_message text, config={}, &blk
229
+ $raw_window ||= one_line_window last_line(), config, &blk
230
+ width = $raw_window.width == 0 ? FFI::NCurses.COLS : $raw_window.width
231
+ text = "%-*s" % [width, text]
232
+
233
+ $raw_window.attron(Ncurses.COLOR_PAIR($normalcolor) )
234
+ $raw_window.printstring 0,0,text, $normalcolor #, 'normal' if @title
235
+ $raw_window.wrefresh
236
+
237
+ end
238
+ def raw_message_destroy
239
+ if $raw_window
240
+ $raw_window.destroy
241
+ $raw_window = nil
242
+ end
243
+ end
244
+ # shows a simple progress bar on last row, using stdscr
245
+ # @param [Float, Array<Fixnum,Fixnum>] percentage, or part/total
246
+ # If Array of two numbers is given then also print part/total on left of bar
247
+ # @deprecated - don't use stdscr at all, use rdialogs status_window (see test2.rb)
248
+ def raw_progress arg
249
+ $log.warning "WARNING: don't use this method as it uses stdscr"
250
+ row = @message_label ? @message_label.row : Ncurses.LINES-1
251
+ s = nil
252
+ case arg
253
+ when Array
254
+ #calculate percentage
255
+ pc = (arg[0]*1.0)/arg[1]
256
+ # print items/total also
257
+ s = "%-10s" % "(#{arg[0]}/#{arg[1]})"
258
+ when
259
+ Float
260
+ pc = arg
261
+ end
262
+ scr = Ncurses.stdscr
263
+ endcol = Ncurses.COLS-1
264
+ startcol = endcol - 12
265
+ stext = ("=" * (pc*10).to_i)
266
+ text = "[" + "%-10s" % stext + "]"
267
+ Ncurses.mvprintw( row ,startcol-10, s) if s
268
+ Ncurses.mvprintw row ,startcol, text
269
+ #scr.refresh() # XXX FFI NW
270
+
271
+ end
272
+ # used only by LiveConsole, if enables in an app, usually only during testing.
273
+ def get_binding
274
+ return binding()
275
+ end
276
+ #
277
+ # suspends curses so you can play around on the shell
278
+ # or in cooked mode like Vim does. Expects a block to be passed.
279
+ # Purpose: you can print some stuff without creating a window, or
280
+ # just run shell commands without coming out.
281
+ # NOTE: if you pass clear as true, then the screen will be cleared
282
+ # and you can use puts or print to print. You may have to flush.
283
+ # However, with clear as false, the screen will not be cleared. You
284
+ # will have to print using printw, and if you expect user input
285
+ # you must do a "system /bin/stty sane"
286
+ # If you print stuff, you will have to put a getch() or system("read")
287
+ # to pause the screen.
288
+ def suspend clear=true
289
+ return unless block_given?
290
+ Ncurses.def_prog_mode
291
+ if clear
292
+ Ncurses.endwin
293
+ # NOTE: avoid false since screen remains half off
294
+ # too many issues
295
+ else
296
+ system "/bin/stty sane"
297
+ end
298
+ yield if block_given?
299
+ Ncurses.reset_prog_mode
300
+ if !clear
301
+ # Hope we don't screw your terminal up with this constantly.
302
+ VER::stop_ncurses
303
+ VER::start_ncurses
304
+ #@form.reset_all # not required
305
+ end
306
+ @form.repaint
307
+ @window.wrefresh
308
+ Ncurses::Panel.update_panels
309
+ end
310
+ def get_all_commands
311
+ opts = @_system_commands.dup
312
+ if respond_to? :get_commands
313
+ opts.push(*get_commands())
314
+ end
315
+ opts
316
+ end
317
+ # bind a key to a method at global (form) level
318
+ # Note that individual component may be overriding this.
319
+ # FIXME: why are we using rawmessage and then getchar when ask would suffice
320
+ def bind_global
321
+ opts = get_all_commands
322
+ cmd = rb_gets("Select a command (<tab> for choices) : ", opts)
323
+ if cmd.nil? || cmd == ""
324
+ rb_puts "Aborted."
325
+ return
326
+ end
327
+ key = []
328
+ str = ""
329
+ #raw_message "Enter one or 2 keys. Finish with ENTER. Enter first key:"
330
+ #ch = @window.getchar()
331
+ #raw_message_destroy
332
+ #if [KEY_ENTER, 10, 13, ?\C-g.getbyte(0)].include? ch
333
+ #say_with_pause "Aborted."
334
+ #return
335
+ #end
336
+ # the next is fine but does not allow user to enter a control or alt or function character
337
+ # since it uses Field. It is fine if you want to force alphanum input
338
+ ch = rb_getchar("Enter one or two keys. Finish with <ENTER>. Enter first key:")
339
+ unless ch
340
+ rb_puts "Aborted. <Press a key>"
341
+ return
342
+ end
343
+ key << ch
344
+ str << keycode_tos(ch)
345
+ ch = rb_getchar "Got #{str}. Enter second key or hit return:"
346
+ unless ch
347
+ rb_puts "Aborted. <Press a key>"
348
+ return
349
+ end
350
+ if ch == KEY_ENTER || ch == 13
351
+ else
352
+ key << ch
353
+ str << keycode_tos(ch)
354
+ end
355
+ if !key.empty?
356
+ rb_puts "Binding #{cmd} to #{str}. "
357
+ key = key[0] if key.size == 1
358
+ #@form.bind_key(key, cmd.to_sym) # not finding it, getting called by that comp
359
+ @form.bind_key(key){ send(cmd.to_sym) }
360
+ end
361
+ end
362
+ def bind_component
363
+ #rb_puts "Todo. ", :color_pair => get_color($promptcolor, :red, :black)
364
+ print_error_message "Todo this. "
365
+ # the idea here is to get the current component
366
+ # and bind some keys to some methods.
367
+ # however, how do we divine the methods we can map to
368
+ # and also in some cases the components itself has multiple components
369
+ end
370
+ # displays help_text associated with field. 2011-10-15
371
+ def field_help_text
372
+ f = @form.get_current_field
373
+ if f.respond_to?('help_text')
374
+ h = f.help_text
375
+ h = "No help text defined for this field.\nTry F1, or press '?' for key-bindings." unless h
376
+ textdialog "#{h}", :title => "Widget Help Text"
377
+ else
378
+ alert "Could not get field #{f} or does not respond to helptext. Try F1 or '?'"
379
+ end
380
+ end
381
+ # prompts user for a command. we need to get this back to the calling app
382
+ # or have some block stuff TODO
383
+ # Actually, this is naive, you would want to pass some values in like current data value
384
+ # or lines ??
385
+ # Also may want command completion, or help so all commands can be displayed
386
+ # NOTE: This is gonna change very soon - 2012-01-8
387
+ def get_command_from_user choices=["quit","help", "suspend", "shell_output"]
388
+ @_command_history ||= Array.new
389
+ str = rb_gets("Cmd: ", choices) { |q| q.default = @_previous_command; q.history = @_command_history }
390
+ @_command_history << str unless @_command_history.include? str
391
+ # shell the command
392
+ if str =~ /^!/
393
+ str = str[1..-1]
394
+ suspend(false) {
395
+ #system(str);
396
+ $log.debug "XXX STR #{str} " if $log.debug?
397
+
398
+ output=`#{str}`
399
+ system("echo ' ' ");
400
+ $log.debug "XXX output #{output} " if $log.debug?
401
+ system("echo '#{output}' ");
402
+ system("echo Press Enter to continue.");
403
+ system("read");
404
+ }
405
+ return nil # i think
406
+ else
407
+ # TODO
408
+ # here's where we can take internal commands
409
+ #alert "[#{str}] string did not match :!"
410
+ str = str.to_s #= str[1..-1]
411
+ cmdline = str.split
412
+ cmd = cmdline.shift #.to_sym
413
+ return unless cmd # added 2011-09-11 FFI
414
+ f = @form.get_current_field
415
+ if respond_to?(cmd, true)
416
+ if cmd == "close"
417
+ throw :close # other seg faults in del_panel window.destroy executes 2x
418
+ else
419
+ res = send cmd, *cmdline
420
+ end
421
+ elsif f.respond_to?(cmd, true)
422
+ res = f.send(cmd, *cmdline)
423
+ else
424
+ alert "App: #{self.class} does not respond to #{cmd} "
425
+ ret = false
426
+ # what is this execute_this: some kind of general routine for all apps ?
427
+ ret = execute_this(cmd, *cmdline) if respond_to?(:execute_this, true)
428
+ rb_puts("#{self.class} does not respond to #{cmd} ", :color_pair => $promptcolor) unless ret
429
+ # should be able to say in red as error
430
+ end
431
+ end
432
+ end
433
+ #
434
+ # @group methods to create widgets easily
435
+ #
436
+ # process arguments based on datatype, perhaps making configuration
437
+ # of some components easier for caller avoiding too much boiler plate code
438
+ #
439
+ # create a field
440
+ def OLDfield *args, &block
441
+ config = {}
442
+ events = [ :CHANGED, :LEAVE, :ENTER, :CHANGE ]
443
+ block_event = :CHANGED # LEAVE, ENTER, CHANGE
444
+
445
+ _process_args args, config, block_event, events
446
+ config.delete(:title)
447
+ _position config
448
+ # hope next line doesn't bonk anything
449
+ config[:display_length] ||= @stack.last.width if @stack.last # added here not sure 2010-11-17 18:43
450
+ field = Field.new @form, config
451
+ # shooz uses CHANGED, which is equivalent to our CHANGE. Our CHANGED means modified and exited
452
+ if block
453
+ field.bind(block_event, &block)
454
+ end
455
+ return field
456
+ end
457
+ #instance_eval &block if block_given?
458
+ # or
459
+ #@blk = block # for later execution using @blk.call()
460
+ #colorlabel = Label.new @form, {'text' => "Select a color:", "row" => row, "col" => col, "color"=>"cyan", "mnemonic" => 'S'}
461
+ #var = RubyCurses::Label.new @form, {'text_variable' => $results, "row" => r, "col" => fc}
462
+
463
+ def OLDlabel *args
464
+ events = block_event = nil
465
+ config = {}
466
+ _process_args args, config, block_event, events
467
+ config[:text] ||= config[:name]
468
+ config[:height] ||= 1
469
+ config.delete(:title)
470
+ _position(config)
471
+ label = Label.new @form, config
472
+ # shooz uses CHANGED, which is equivalent to our CHANGE. Our CHANGED means modified and exited
473
+ return label
474
+ end
475
+ alias :text :label
476
+ def OLDbutton *args, &block
477
+ config = {}
478
+ events = [ :PRESS, :LEAVE, :ENTER ]
479
+ block_event = :PRESS
480
+
481
+ _process_args args, config, block_event, events
482
+ config[:text] ||= config[:name]
483
+ config.delete(:title)
484
+ # flow gets precedence over stack
485
+ _position(config)
486
+ button = Button.new @form, config
487
+ # shooz uses CHANGED, which is equivalent to our CHANGE. Our CHANGED means modified and exited
488
+ if block
489
+ button.bind(block_event, &block)
490
+ end
491
+ return button
492
+ end
493
+ #
494
+ # create a list
495
+ # Since we are mouseless, one can traverse without selection. So we have a different
496
+ # way of selecting row/s and traversal. XXX this aspect of LB's has always troubled me hugely.
497
+ def OLDedit_list *args, &block # earlier list_box
498
+ config = {}
499
+ # TODO confirm events
500
+ # listdataevent has interval added and interval removed, due to multiple
501
+ # selection, we have to make that simple for user here.
502
+ events = [ :LEAVE, :ENTER, :ENTER_ROW, :LEAVE_ROW, :LIST_DATA_EVENT ]
503
+ # TODO how to do this so he gets selected row easily
504
+ block_event = :ENTER_ROW
505
+
506
+ _process_args args, config, block_event, events
507
+ # naive defaults, since list could be large or have very long items
508
+ # usually user will provide
509
+ if !config.has_key? :height
510
+ ll = 0
511
+ ll = config[:list].length + 2 if config.has_key? :list
512
+ config[:height] ||= ll
513
+ config[:height] = 15 if config[:height] > 20
514
+ end
515
+ if @current_object.empty?
516
+ $log.debug "1 APP LB w: #{config[:width]} ,#{config[:name]} "
517
+ config[:width] ||= @stack.last.width if @stack.last
518
+ $log.debug "2 APP LB w: #{config[:width]} "
519
+ config[:width] ||= longest_in_list(config[:list])+2
520
+ $log.debug "3 APP LB w: #{config[:width]} "
521
+ end
522
+ # if no width given, expand to flows width XXX SHOULD BE NOT EXPAND ?
523
+ #config[:width] ||= @stack.last.width if @stack.last
524
+ #if config.has_key? :choose
525
+ config[:default_values] = config.delete :choose
526
+ # we make the default single unless specified
527
+ config[:selection_mode] = :single unless config.has_key? :selection_mode
528
+ if @current_object.empty?
529
+ if @instack
530
+ # most likely you won't have row and col. should we check or just go ahead
531
+ col = @stack.last.margin
532
+ config[:row] = @app_row
533
+ config[:col] = col
534
+ @app_row += config[:height] # this needs to take into account height of prev object
535
+ end
536
+ end
537
+ useform = nil
538
+ useform = @form if @current_object.empty?
539
+ field = EditList.new useform, config # earlier ListBox
540
+ # shooz uses CHANGED, which is equivalent to our CHANGE. Our CHANGED means modified and exited
541
+ if block
542
+ # this way you can't pass params to the block
543
+ field.bind(block_event, &block)
544
+ end
545
+ return field
546
+ end
547
+
548
+ # toggle button
549
+ def OLDtoggle *args, &block
550
+ config = {}
551
+ # TODO confirm events
552
+ events = [ :PRESS, :LEAVE, :ENTER ]
553
+ block_event = :PRESS
554
+ _process_args args, config, block_event, events
555
+ config[:text] ||= longest_in_list2( [config[:onvalue], config[:offvalue]])
556
+ #config[:onvalue] # needed for flow, we need a better way FIXME
557
+ _position(config)
558
+ toggle = ToggleButton.new @form, config
559
+ if block
560
+ toggle.bind(block_event, &block)
561
+ end
562
+ return toggle
563
+ end
564
+ # check button
565
+ def OLDcheck *args, &block
566
+ config = {}
567
+ # TODO confirm events
568
+ events = [ :PRESS, :LEAVE, :ENTER ]
569
+ block_event = :PRESS
570
+ _process_args args, config, block_event, events
571
+ _position(config)
572
+ toggle = CheckBox.new @form, config
573
+ if block
574
+ toggle.bind(block_event, &block)
575
+ end
576
+ return toggle
577
+ end
578
+ # radio button
579
+ def OLDradio *args, &block
580
+ config = {}
581
+ # TODO confirm events
582
+ events = [ :PRESS, :LEAVE, :ENTER ]
583
+ block_event = :PRESS
584
+ _process_args args, config, block_event, events
585
+ a = config[:group]
586
+ # FIXME we should check if user has set a varialbe in :variable.
587
+ # we should create a variable, so he can use it if he wants.
588
+ if @variables.has_key? a
589
+ v = @variables[a]
590
+ else
591
+ v = Variable.new
592
+ @variables[a] = v
593
+ end
594
+ config[:variable] = v
595
+ config.delete(:group)
596
+ _position(config)
597
+ radio = RadioButton.new @form, config
598
+ if block
599
+ radio.bind(block_event, &block)
600
+ end
601
+ return radio
602
+ end
603
+ # editable text area
604
+ def OLDtextarea *args, &block
605
+ require 'rbhex/core/widgets/rtextarea'
606
+ config = {}
607
+ # TODO confirm events many more
608
+ events = [ :CHANGE, :LEAVE, :ENTER ]
609
+ block_event = events[0]
610
+ _process_args args, config, block_event, events
611
+ config[:width] = config[:display_length] unless config.has_key? :width
612
+ _position(config)
613
+ # if no width given, expand to flows width
614
+ config[:width] ||= @stack.last.width if @stack.last
615
+ useform = nil
616
+ useform = @form if @current_object.empty?
617
+ w = TextArea.new useform, config
618
+ if block
619
+ w.bind(block_event, &block)
620
+ end
621
+ return w
622
+ end
623
+ # similar definitions for textview and resultsettextview
624
+ # NOTE This is not allowing me to send blocks,
625
+ # so do not use for containers
626
+ {
627
+ 'rbhex/core/widgets/rtextview' => 'TextView',
628
+ 'rbhex/experimental/resultsettextview' => 'ResultsetTextView',
629
+ 'rbhex/core/widgets/rcontainer' => 'Container',
630
+ 'rbhex/extras/rcontainer2' => 'Container2',
631
+ }.each_pair {|k,p|
632
+ eval(
633
+ "def OLD#{p.downcase} *args, &block
634
+ require \"#{k}\"
635
+ config = {}
636
+ # TODO confirm events many more
637
+ events = [ :PRESS, :LEAVE, :ENTER ]
638
+ block_event = events[0]
639
+ _process_args args, config, block_event, events
640
+ config[:width] = config[:display_length] unless config.has_key? :width
641
+ _position(config)
642
+ # if no width given, expand to flows width
643
+ config[:width] ||= @stack.last.width if @stack.last
644
+ raise \"height needed for #{p.downcase}\" if !config.has_key? :height
645
+ useform = nil
646
+ useform = @form if @current_object.empty?
647
+ w = #{p}.new useform, config
648
+ if block
649
+ w.bind(block_event, &block)
650
+ end
651
+ return w
652
+ end"
653
+ )
654
+ }
655
+
656
+ # table widget
657
+ # @example
658
+ # data = [["Roger",16,"SWI"], ["Phillip",1, "DEU"]]
659
+ # colnames = ["Name", "Wins", "Place"]
660
+ # t = table :width => 40, :height => 10, :columns => colnames, :data => data, :estimate_widths => true
661
+ # other options are :column_widths => [12,4,12]
662
+ # :size_to_fit => true
663
+ def edit_table *args, &block # earlier table
664
+ require 'rbhex/extras/widgets/rtable'
665
+ config = {}
666
+ # TODO confirm events many more
667
+ events = [ :ENTER_ROW, :LEAVE, :ENTER ]
668
+ block_event = events[0]
669
+ _process_args args, config, block_event, events
670
+ # if user is leaving out width, then we don't want it in config
671
+ # else Widget will put a value of 10 as default, overriding what we've calculated
672
+ if config.has_key? :display_length
673
+ config[:width] = config[:display_length] unless config.has_key? :width
674
+ end
675
+ ext = config.delete :extended_keys
676
+
677
+ model = nil
678
+ _position(config)
679
+ # if no width given, expand to flows width
680
+ config[:width] ||= @stack.last.width if @stack.last
681
+ w = Table.new @form, config
682
+ if ext
683
+ require 'rbhex/extras/include/tableextended'
684
+ # so we can increase and decrease column width using keys
685
+ w.extend TableExtended
686
+ w.bind_key(?w){ w.next_column }
687
+ w.bind_key(?b){ w.previous_column }
688
+ w.bind_key(?+) { w.increase_column }
689
+ w.bind_key(?-) { w.decrease_column }
690
+ w.bind_key([?d, ?d]) { w.table_model.delete_at w.current_index }
691
+ w.bind_key(?u) { w.table_model.undo w.current_index}
692
+ end
693
+ if block
694
+ w.bind(block_event, &block)
695
+ end
696
+ return w
697
+ end
698
+ # print a title on first row
699
+ def title string, config={}
700
+ ## TODO center it
701
+ @window.printstring 1, 30, string, $normalcolor, 'reverse'
702
+ end
703
+ # print a sutitle on second row
704
+ def subtitle string, config={}
705
+ @window.printstring 2, 30, string, $datacolor, 'normal'
706
+ end
707
+ # menu bar
708
+
709
+ # creates a blank row
710
+ def OLDblank rows=1, config={}
711
+ @app_row += rows
712
+ end
713
+ # displays a horizontal line
714
+ # takes col (column to start from) from current stack
715
+ # take row from app_row
716
+ #
717
+ # requires width to be passed in config, else defaults to 20
718
+ # @example
719
+ # hline :width => 55
720
+ def hline config={}
721
+ row = config[:row] || @app_row
722
+ width = config[:width] || 20
723
+ _position config
724
+ col = config[:col] || 1
725
+ @color_pair = config[:color_pair] || $datacolor
726
+ @attrib = config[:attrib] || Ncurses::A_NORMAL
727
+ @window.attron(Ncurses.COLOR_PAIR(@color_pair) | @attrib)
728
+ @window.mvwhline( row, col, FFI::NCurses::ACS_HLINE, width)
729
+ @window.attron(Ncurses.COLOR_PAIR(@color_pair) | @attrib)
730
+ @app_row += 1
731
+ end
732
+
733
+ def TODOmultisplit *args, &block
734
+ require 'rbhex/extras/widgets/rmultisplit'
735
+ config = {}
736
+ events = [ :PROPERTY_CHANGE, :LEAVE, :ENTER ]
737
+ block_event = events[0]
738
+ _process_args args, config, block_event, events
739
+ _position(config)
740
+ # if no width given, expand to flows width
741
+ config[:width] ||= @stack.last.width if @stack.last
742
+ config.delete :title
743
+ useform = nil
744
+ useform = @form if @current_object.empty?
745
+
746
+ w = MultiSplit.new useform, config
747
+ #if block
748
+ #w.bind(block_event, w, &block)
749
+ #end
750
+ if block_given?
751
+ @current_object << w
752
+ #instance_eval &block if block_given?
753
+ yield w
754
+ @current_object.pop
755
+ end
756
+ return w
757
+ end
758
+ # create a readonly list
759
+ # I don't want to rename this to list, as that could lead to
760
+ # confusion, maybe rlist
761
+ def OLDlistbox *args, &block # earlier basic_list
762
+ require 'rbhex/core/widgets/rlist'
763
+ config = {}
764
+ #TODO check these
765
+ events = [ :LEAVE, :ENTER, :ENTER_ROW, :LEAVE_ROW, :LIST_DATA_EVENT ]
766
+ # TODO how to do this so he gets selected row easily
767
+ block_event = :ENTER_ROW
768
+ _process_args args, config, block_event, events
769
+ # some guesses at a sensible height for listbox
770
+ if !config.has_key? :height
771
+ ll = 0
772
+ ll = config[:list].length + 2 if config.has_key? :list
773
+ config[:height] ||= ll
774
+ config[:height] = 15 if config[:height] > 20
775
+ end
776
+ _position(config)
777
+ # if no width given, expand to flows width
778
+ config[:width] ||= @stack.last.width if @stack.last
779
+ config[:width] ||= longest_in_list(config[:list])+2
780
+ #config.delete :title
781
+ #config[:default_values] = config.delete :choose
782
+ config[:selection_mode] = :single unless config.has_key? :selection_mode
783
+ useform = nil
784
+ useform = @form if @current_object.empty?
785
+
786
+ w = List.new useform, config # NO BLOCK GIVEN
787
+ if block_given?
788
+ field.bind(block_event, &block)
789
+ end
790
+ return w
791
+ end
792
+ alias :basiclist :listbox # this alias will be removed
793
+ def TODOmaster_detail *args, &block
794
+ require 'rbhex/experimental/widgets/masterdetail'
795
+ config = {}
796
+ events = [:PROPERTY_CHANGE, :LEAVE, :ENTER ]
797
+ block_event = nil
798
+ _process_args args, config, block_event, events
799
+ #config[:height] ||= 10
800
+ _position(config)
801
+ # if no width given, expand to flows width
802
+ config[:width] ||= @stack.last.width if @stack.last
803
+ #config.delete :title
804
+ useform = nil
805
+ useform = @form if @current_object.empty?
806
+
807
+ w = MasterDetail.new useform, config # NO BLOCK GIVEN
808
+ if block_given?
809
+ @current_object << w
810
+ yield_or_eval &block
811
+ @current_object.pop
812
+ end
813
+ return w
814
+ end
815
+ # scrollbar attached to the right of a parent object
816
+ def OLDscrollbar *args, &block
817
+ require 'rbhex/core/widgets/scrollbar'
818
+ config = {}
819
+ events = [:PROPERTY_CHANGE, :LEAVE, :ENTER ] # # none really at present
820
+ block_event = nil
821
+ _process_args args, config, block_event, events
822
+ raise "parent needed for scrollbar" if !config.has_key? :parent
823
+ useform = nil
824
+ useform = @form if @current_object.empty?
825
+ sb = Scrollbar.new useform, config
826
+ end
827
+ # divider used to resize neighbouring components TOTEST XXX
828
+ def OLDdivider *args, &block
829
+ require 'rbhex/core/widgets/divider'
830
+ config = {}
831
+ events = [:PROPERTY_CHANGE, :LEAVE, :ENTER, :DRAG_EVENT ] # # none really at present
832
+ block_event = nil
833
+ _process_args args, config, block_event, events
834
+ useform = nil
835
+ useform = @form if @current_object.empty?
836
+ sb = Divider.new useform, config
837
+ end
838
+ # creates a simple readonly table, that allows users to click on rows
839
+ # and also on the header. Header clicking is for column-sorting.
840
+ def OLDcombo *args, &block
841
+ require 'rbhex/core/widgets/rcombo'
842
+ config = {}
843
+ events = [:PROPERTY_CHANGE, :LEAVE, :ENTER, :CHANGE, :ENTER_ROW, :PRESS ] # XXX
844
+ block_event = nil
845
+ _process_args args, config, block_event, events
846
+ _position(config)
847
+ # if no width given, expand to flows width
848
+ config[:width] ||= @stack.last.width if @stack.last
849
+ #config.delete :title
850
+ useform = nil
851
+ useform = @form if @current_object.empty?
852
+
853
+ w = ComboBox.new useform, config # NO BLOCK GIVEN
854
+ if block_given?
855
+ @current_object << w
856
+ yield_or_eval &block
857
+ @current_object.pop
858
+ end
859
+ return w
860
+ end
861
+
862
+ # ADD new widget above this
863
+
864
+ # @endgroup
865
+
866
+ # @group positioning of components
867
+
868
+ # line up vertically whatever comes in, ignoring r and c
869
+ # margin_top to add to margin of existing stack (if embedded) such as extra spacing
870
+ # margin to add to margin of existing stack, or window (0)
871
+ # NOTE: since these coordins are calculated at start
872
+ # therefore if window resized i can't recalculate.
873
+ #Stack = Struct.new(:margin_top, :margin, :width)
874
+ def OLDstack config={}, &block
875
+ @instack = true
876
+ mt = config[:margin_top] || 1
877
+ mr = config[:margin] || 0
878
+ # must take into account margin
879
+ defw = Ncurses.COLS - mr
880
+ config[:width] = defw if config[:width] == :EXPAND
881
+ w = config[:width] || [50, defw].min
882
+ s = Stack.new(mt, mr, w)
883
+ @app_row += mt
884
+ mr += @stack.last.margin if @stack.last
885
+ @stack << s
886
+ yield_or_eval &block if block_given?
887
+ @stack.pop
888
+ @instack = false if @stack.empty?
889
+ @app_row = 0 if @stack.empty?
890
+ end
891
+ # keep adding to right of previous and when no more space
892
+ # move down and continue fitting in.
893
+ # Useful for button positioning. Currently, we can use a second flow
894
+ # to get another row.
895
+ # TODO: move down when row filled
896
+ # TODO: align right, center
897
+ def OLDflow config={}, &block
898
+ @inflow = true
899
+ mt = config[:margin_top] || 0
900
+ @app_row += mt
901
+ col = @flowstack.last || @stack.last.margin || @app_col
902
+ col += config[:margin] || 0
903
+ @flowstack << col
904
+ @flowcol = col
905
+ yield_or_eval &block if block_given?
906
+ @flowstack.pop
907
+ @inflow = false if @flowstack.empty?
908
+ end
909
+
910
+ private
911
+ def quit
912
+ throw(:close)
913
+ end
914
+ def help; display_app_help; end
915
+ # Initialize curses
916
+ def init_ncurses
917
+ VER::start_ncurses # this is initializing colors via ColorMap.setup
918
+ #$ncurses_started = true
919
+ @stop_ncurses_on_close = true
920
+ end
921
+
922
+ # returns length of longest
923
+ def longest_in_list list #:nodoc:
924
+ longest = list.inject(0) do |memo,word|
925
+ memo >= word.length ? memo : word.length
926
+ end
927
+ longest
928
+ end
929
+ # returns longest item
930
+ # rows = list.max_by(&:length)
931
+ #
932
+ def longest_in_list2 list #:nodoc:
933
+ longest = list.inject(list[0]) do |memo,word|
934
+ memo.length >= word.length ? memo : word
935
+ end
936
+ longest
937
+ end
938
+
939
+ # if partial command entered then returns matches
940
+ def _resolve_command opts, cmd
941
+ return cmd if opts.include? cmd
942
+ matches = opts.grep Regexp.new("^#{cmd}")
943
+ end
944
+ # Now i am not creating this unless user wants it. Pls avoid it.
945
+ # Use either say_with_pause, or put $status_message in command of statusline
946
+ # @deprecated please use {#status_line} instead of a message label
947
+ def create_message_label row=Ncurses.LINES-1
948
+ @message_label = RubyCurses::Label.new @form, {:text_variable => @message, :name=>"message_label",:row => row, :col => 0, :display_length => Ncurses.COLS, :height => 1, :color => :white}
949
+ end
950
+
951
+ def run &block
952
+ begin
953
+
954
+ # check if user has passed window coord in config, else root window
955
+ @window = VER::Window.root_window
956
+ awin = @window
957
+ catch(:close) do
958
+ @form = Form.new @window
959
+ @form.bind_key([?\C-x, ?c], 'suspend') { suspend(false) do
960
+ system("tput cup 26 0")
961
+ system("tput ed")
962
+ system("echo Enter C-d to return to application")
963
+ system (ENV['PS1']='\s-\v\$ ')
964
+ system(ENV['SHELL']);
965
+ end
966
+ }
967
+ # this is a very rudimentary default command executer, it does not
968
+ # allow tab completion. App should use M-x with names of commands
969
+ # as in appgmail
970
+ # NOTE: This is gonna change very soon - 2012-01-8
971
+ @form.bind_key(?:, 'prompt') {
972
+ str = get_command_from_user
973
+ }
974
+
975
+ # this M-x stuff has to be moved out so it can be used by all. One should be able
976
+ # to add_commands properly to this, and to C-x. I am thinking how to go about this,
977
+ # and what function M-x actually serves.
978
+
979
+ @form.bind_key(?\M-x, 'M-x commands'){
980
+ # TODO previous command to be default
981
+ opts = get_all_commands()
982
+ @_command_history ||= Array.new
983
+ # previous command should be in opts, otherwise it is not in this context
984
+ cmd = rb_gets("Command: ", opts){ |q| q.default = @_previous_command; q.history = @_command_history }
985
+ if cmd.nil? || cmd == ""
986
+ else
987
+ @_command_history << cmd unless @_command_history.include? cmd
988
+ cmdline = cmd.split
989
+ cmd = cmdline.shift
990
+ # check if command is a substring of a larger command
991
+ if !opts.include?(cmd)
992
+ rcmd = _resolve_command(opts, cmd) if !opts.include?(cmd)
993
+ if rcmd.size == 1
994
+ cmd = rcmd.first
995
+ elsif !rcmd.empty?
996
+ rb_puts "Cannot resolve #{cmd}. Matches are: #{rcmd} "
997
+ end
998
+ end
999
+ if respond_to?(cmd, true)
1000
+ @_previous_command = cmd
1001
+ #raw_message "calling #{cmd} "
1002
+ begin
1003
+ send cmd, *cmdline
1004
+ rescue => exc
1005
+ $log.error "ERR EXC: send throwing an exception now. Duh. IMAP keeps crashing haha !! #{exc} " if $log.debug?
1006
+ if exc
1007
+ $log.debug( exc)
1008
+ $log.debug(exc.backtrace.join("\n"))
1009
+ rb_puts exc.to_s
1010
+ end
1011
+ end
1012
+ else
1013
+ rb_puts("Command [#{cmd}] not supported by #{self.class} ", :color_pair => $promptcolor)
1014
+ end
1015
+ end
1016
+ }
1017
+ #@form.bind_key(KEY_F1, 'help'){ display_app_help } # NOT REQUIRED NOW 2012-01-7 since form does it
1018
+ @form.bind_key([?q,?q], 'quit' ){ throw :close } if $log.debug?
1019
+
1020
+ #@message = Variable.new
1021
+ #@message.value = ""
1022
+ $status_message ||= Variable.new # remember there are multiple levels of apps
1023
+ $status_message.value = ""
1024
+ #$error_message.update_command { @message.set_value($error_message.value) }
1025
+ if block
1026
+ begin
1027
+ yield_or_eval &block if block_given? # modified 2010-11-17 20:36
1028
+ # how the hell does a user trap exception if the loop is hidden from him ? FIXME
1029
+ loop
1030
+ rescue => ex
1031
+ $log.debug( "APP.rb rescue reached ")
1032
+ $log.debug( ex) if ex
1033
+ $log.debug(ex.backtrace.join("\n")) if ex
1034
+ ensure
1035
+ close
1036
+ # putting it here allows it to be printed on screen, otherwise it was not showing at all.
1037
+ if ex
1038
+ puts "========== EXCEPTION =========="
1039
+ p ex
1040
+ puts "==============================="
1041
+ puts(ex.backtrace.join("\n"))
1042
+ end
1043
+ end
1044
+ nil
1045
+ else
1046
+ #@close_on_terminate = true
1047
+ self
1048
+ end #if block
1049
+ end # :close
1050
+ end
1051
+ end
1052
+ # TODO
1053
+ # process args, all widgets should call this
1054
+ def _process_args args, config, block_event, events #:nodoc:
1055
+ args.each do |arg|
1056
+ case arg
1057
+ when Array
1058
+ # please don't use this, keep it simple and use hash NOTE
1059
+ # we can use r,c, w, h
1060
+ row, col, display_length, height = arg
1061
+ config[:row] = row
1062
+ config[:col] = col
1063
+ config[:display_length] = display_length if display_length
1064
+ config[:width] = display_length if display_length
1065
+ # width for most XXX ?
1066
+ config[:height] = height if height
1067
+ when Hash
1068
+ config.merge!(arg)
1069
+ if block_event
1070
+ block_event = config.delete(:block_event){ block_event }
1071
+ raise "Invalid event. Use #{events}" unless events.include? block_event
1072
+ end
1073
+ when String
1074
+ config[:name] = arg
1075
+ config[:title] = arg # some may not have title
1076
+ #config[:text] = arg # some may not have title
1077
+ end
1078
+ end
1079
+ end # _process
1080
+ # position object based on whether in a flow or stack.
1081
+ # @app_row is prepared for next object based on this objects ht
1082
+ def OLD_position config #:nodoc:
1083
+ unless @current_object.empty?
1084
+ $log.debug " WWWW returning from position #{@current_object.last} "
1085
+ return
1086
+ end
1087
+ if @inflow
1088
+ #col = @flowstack.last
1089
+ config[:row] = @app_row
1090
+ config[:col] = @flowcol
1091
+ $log.debug " YYYY config #{config} "
1092
+ if config[:text]
1093
+ @flowcol += config[:text].length + 5 # 5 came from buttons
1094
+ else
1095
+ @flowcol += (config[:length] || 10) + 5 # trying out for combo
1096
+ end
1097
+ elsif @instack
1098
+ # most likely you won't have row and col. should we check or just go ahead
1099
+ # what if he has put it 2011-10-19 as in a container
1100
+ col = @stack.last.margin
1101
+ config[:row] ||= @app_row
1102
+ config[:col] ||= col
1103
+ @app_row += config[:height] || 1 #unless config[:no_advance]
1104
+ # TODO need to allow stack to have its spacing, but we don't have an object as yet.
1105
+ end
1106
+ end
1107
+ end # class
1108
+ end # module
1109
+ if $0 == __FILE__
1110
+ include RubyCurses
1111
+ #app = App.new
1112
+ #window = app.window
1113
+ #window.printstring 2, 30, "Demo of Listbox - rbhex", $normalcolor, 'reverse'
1114
+ #app.logger.info "beforegetch"
1115
+ #window.getch
1116
+ #app.close
1117
+ # this was the yield example, but now we've moved to instance eval
1118
+ App.new do
1119
+ @window.printstring 0, 30, "Demo of Listbox - rbhex", $normalcolor, 'reverse'
1120
+ @window.printstring 1, 30, "Hit F1 to quit", $datacolor, 'normal'
1121
+ form = @form
1122
+ fname = "Search"
1123
+ r, c = 7, 30
1124
+ c += fname.length + 1
1125
+ #field1 = field( [r,c, 30], fname, :bgcolor => "cyan", :block_event => :CHANGE) do |fld|
1126
+ stack :margin_top => 2, :margin => 10 do
1127
+ lbl = label({:text => fname, :color=>'white',:bgcolor=>'red', :mnemonic=> 's'})
1128
+ field1 = field( [r,c, 30], fname, :bgcolor => "cyan",:block_event => :CHANGE) do |fld|
1129
+ message("You entered #{fld.getvalue}. To quit enter quit and tab out")
1130
+ if fld.getvalue == "quit"
1131
+ logger.info "you typed quit!"
1132
+ throw :close
1133
+ end
1134
+ end
1135
+ #field1.set_label Label.new @form, {:text => fname, :color=>'white',:bgcolor=>'red', :mnemonic=> 's'}
1136
+ field1.set_label( lbl )
1137
+ field1.enter do
1138
+ message "you entered this field"
1139
+ end
1140
+
1141
+ stack :margin_top => 2, :margin => 0 do
1142
+ #label( [8, 30, 60],{:text => "A label", :color=>'white',:bgcolor=>'blue'} )
1143
+ end
1144
+
1145
+ @bluelabel = label( [8, 30, 60],{:text => "B label", :color=>'white',:bgcolor=>'blue'} )
1146
+
1147
+ stack :margin_top => 2, :margin => 0 do
1148
+ toggle :onvalue => " Toggle Down ", :offvalue => " Untoggle ", :mnemonic => 'T', :value => true
1149
+
1150
+ toggle :onvalue => " On ", :offvalue => " Off ", :value => true do |e|
1151
+ alert "You pressed me #{e.state}"
1152
+ end
1153
+ check :text => "Check me!", :onvalue => "Checked", :offvalue => "Unchecked", :value => true do |e|
1154
+ # this works but long and complicated
1155
+ #@bluelabel.text = e.item.getvalue ? e.item.onvalue : e.item.offvalue
1156
+ @bluelabel.text = e.item.text
1157
+ end
1158
+ radio :text => "red", :value => "RED", :color => "red", :group => :colors
1159
+ radio :text => "green", :value => "GREEN", :color => "green", :group => :colors
1160
+ flow do
1161
+ button_row = 17
1162
+ ok_button = button( [button_row,30], "OK", {:mnemonic => 'O'}) do
1163
+ alert("About to dump data into log file!")
1164
+ message "Dumped data to log file"
1165
+ end
1166
+
1167
+ # using ampersand to set mnemonic
1168
+ cancel_button = button( [button_row, 40], "&Cancel" ) do
1169
+ if confirm("Do your really want to quit?")== :YES
1170
+ #throw(:close);
1171
+ quit
1172
+ else
1173
+ message "Quit aborted"
1174
+ end
1175
+ end # cancel
1176
+ button "Don't know"
1177
+ end
1178
+ flow :margin_top => 2 do
1179
+ button "Another"
1180
+ button "Line"
1181
+ end
1182
+ stack :margin_top => 2, :margin => 0 do
1183
+ @pbar = progress :width => 20, :bgcolor => 'white', :color => 'red'
1184
+ @pbar1 = progress :width => 20, :style => :old
1185
+ end
1186
+ end
1187
+ end # stack
1188
+ # lets make another column
1189
+ stack :margin_top => 2, :margin => 70 do
1190
+ l = label "Column 2"
1191
+ f1 = field "afield", :bgcolor => 'white', :color => 'black'
1192
+ listbox "A list", :list => ["Square", "Oval", "Rectangle", "Somethinglarge"], :choose => ["Square"]
1193
+ lb = listbox "Another", :list => ["Square", "Oval", "Rectangle", "Somethinglarge"] do |list|
1194
+ #f1.set_buffer list.text
1195
+ #f1.text list.text
1196
+ f1.text = list.text
1197
+ l.text = list.text.upcase
1198
+ end
1199
+ t = textarea :height => 10 do |e|
1200
+ #@bluelabel.text = e.to_s.tr("\n",' ')
1201
+ @bluelabel.text = e.text.gsub("\n"," ")
1202
+ len = e.source.get_text.length
1203
+ len = len % 20 if len > 20
1204
+ $log.debug " PBAR len of text is #{len}: #{len/20.0} "
1205
+ @pbar.fraction(len/20.0)
1206
+ @pbar1.fraction(len/20.0)
1207
+ i = ((len/20.0)*100).to_i
1208
+ @pbar.text = "completed:#{i}"
1209
+ end
1210
+ t.leave do |c|
1211
+ @bluelabel.text = c.get_text.gsub("\n"," ")
1212
+ end
1213
+
1214
+ end
1215
+
1216
+ # Allow user to get the keys
1217
+ keypress do |key|
1218
+ if key == :C_c
1219
+ message "You tried to cancel"
1220
+ #throw :close
1221
+ quit
1222
+ else
1223
+ #app.message "You pressed #{key}, #{char} "
1224
+ message "You pressed #{key}"
1225
+ end
1226
+ end
1227
+ end
1228
+ end