rbhex-core 1.0.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 (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