canis 0.0.4

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 (134) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +45 -0
  3. data/CHANGES +52 -0
  4. data/Gemfile +4 -0
  5. data/LICENSE.txt +22 -0
  6. data/README.md +24 -0
  7. data/Rakefile +2 -0
  8. data/canis.gemspec +25 -0
  9. data/examples/alpmenu.rb +46 -0
  10. data/examples/app.sample +19 -0
  11. data/examples/appemail.rb +191 -0
  12. data/examples/atree.rb +105 -0
  13. data/examples/bline.rb +181 -0
  14. data/examples/common/devel.rb +319 -0
  15. data/examples/common/file.rb +93 -0
  16. data/examples/data/README.markdown +9 -0
  17. data/examples/data/brew.txt +38 -0
  18. data/examples/data/color.2 +37 -0
  19. data/examples/data/gemlist.txt +59 -0
  20. data/examples/data/lotr.txt +12 -0
  21. data/examples/data/ports.txt +136 -0
  22. data/examples/data/table.txt +37 -0
  23. data/examples/data/tasks.csv +88 -0
  24. data/examples/data/tasks.txt +27 -0
  25. data/examples/data/todo.txt +16 -0
  26. data/examples/data/todocsv.csv +28 -0
  27. data/examples/data/unix1.txt +21 -0
  28. data/examples/data/unix2.txt +11 -0
  29. data/examples/dbdemo.rb +506 -0
  30. data/examples/dirtree.rb +177 -0
  31. data/examples/newtabbedwindow.rb +100 -0
  32. data/examples/newtesttabp.rb +92 -0
  33. data/examples/tabular.rb +212 -0
  34. data/examples/tasks.rb +179 -0
  35. data/examples/term2.rb +88 -0
  36. data/examples/testbuttons.rb +307 -0
  37. data/examples/testcombo.rb +102 -0
  38. data/examples/testdb.rb +182 -0
  39. data/examples/testfields.rb +208 -0
  40. data/examples/testflowlayout.rb +43 -0
  41. data/examples/testkeypress.rb +98 -0
  42. data/examples/testlistbox.rb +187 -0
  43. data/examples/testlistbox1.rb +199 -0
  44. data/examples/testmessagebox.rb +144 -0
  45. data/examples/testprogress.rb +116 -0
  46. data/examples/testree.rb +107 -0
  47. data/examples/testsplitlayout.rb +53 -0
  48. data/examples/testsplitlayout1.rb +49 -0
  49. data/examples/teststacklayout.rb +48 -0
  50. data/examples/testwsshortcuts.rb +68 -0
  51. data/examples/testwsshortcuts2.rb +129 -0
  52. data/lib/canis.rb +16 -0
  53. data/lib/canis/core/docs/index.txt +104 -0
  54. data/lib/canis/core/docs/list.txt +16 -0
  55. data/lib/canis/core/docs/style_help.yml +34 -0
  56. data/lib/canis/core/docs/tabbedpane.txt +15 -0
  57. data/lib/canis/core/docs/table.txt +31 -0
  58. data/lib/canis/core/docs/textpad.txt +48 -0
  59. data/lib/canis/core/docs/tree.txt +23 -0
  60. data/lib/canis/core/include/.DS_Store +0 -0
  61. data/lib/canis/core/include/action.rb +83 -0
  62. data/lib/canis/core/include/actionmanager.rb +49 -0
  63. data/lib/canis/core/include/appmethods.rb +179 -0
  64. data/lib/canis/core/include/bordertitle.rb +49 -0
  65. data/lib/canis/core/include/canisparser.rb +100 -0
  66. data/lib/canis/core/include/colorparser.rb +437 -0
  67. data/lib/canis/core/include/defaultfilerenderer.rb +64 -0
  68. data/lib/canis/core/include/io.rb +320 -0
  69. data/lib/canis/core/include/layouts/SplitLayout.rb +161 -0
  70. data/lib/canis/core/include/layouts/abstractlayout.rb +213 -0
  71. data/lib/canis/core/include/layouts/flowlayout.rb +104 -0
  72. data/lib/canis/core/include/layouts/stacklayout.rb +109 -0
  73. data/lib/canis/core/include/listbindings.rb +89 -0
  74. data/lib/canis/core/include/listeditable.rb +319 -0
  75. data/lib/canis/core/include/listoperations.rb +61 -0
  76. data/lib/canis/core/include/listselectionmodel.rb +388 -0
  77. data/lib/canis/core/include/multibuffer.rb +173 -0
  78. data/lib/canis/core/include/ractionevent.rb +73 -0
  79. data/lib/canis/core/include/rchangeevent.rb +27 -0
  80. data/lib/canis/core/include/rhistory.rb +95 -0
  81. data/lib/canis/core/include/rinputdataevent.rb +47 -0
  82. data/lib/canis/core/include/textdocument.rb +111 -0
  83. data/lib/canis/core/include/vieditable.rb +175 -0
  84. data/lib/canis/core/include/widgetmenu.rb +66 -0
  85. data/lib/canis/core/system/colormap.rb +165 -0
  86. data/lib/canis/core/system/keydefs.rb +32 -0
  87. data/lib/canis/core/system/ncurses.rb +237 -0
  88. data/lib/canis/core/system/panel.rb +129 -0
  89. data/lib/canis/core/system/window.rb +1081 -0
  90. data/lib/canis/core/util/ansiparser.rb +119 -0
  91. data/lib/canis/core/util/app.rb +696 -0
  92. data/lib/canis/core/util/basestack.rb +412 -0
  93. data/lib/canis/core/util/defaultcolorparser.rb +84 -0
  94. data/lib/canis/core/util/extras/README +5 -0
  95. data/lib/canis/core/util/extras/bottomline.rb +1815 -0
  96. data/lib/canis/core/util/extras/padreader.rb +192 -0
  97. data/lib/canis/core/util/focusmanager.rb +31 -0
  98. data/lib/canis/core/util/helpmanager.rb +160 -0
  99. data/lib/canis/core/util/oldwidgetshortcuts.rb +304 -0
  100. data/lib/canis/core/util/promptmenu.rb +235 -0
  101. data/lib/canis/core/util/rcommandwindow.rb +933 -0
  102. data/lib/canis/core/util/rdialogs.rb +520 -0
  103. data/lib/canis/core/util/textutils.rb +74 -0
  104. data/lib/canis/core/util/viewer.rb +238 -0
  105. data/lib/canis/core/util/widgetshortcuts.rb +508 -0
  106. data/lib/canis/core/widgets/applicationheader.rb +103 -0
  107. data/lib/canis/core/widgets/box.rb +58 -0
  108. data/lib/canis/core/widgets/divider.rb +310 -0
  109. data/lib/canis/core/widgets/extras/README.md +12 -0
  110. data/lib/canis/core/widgets/extras/rtextarea.rb +960 -0
  111. data/lib/canis/core/widgets/extras/stackflow.rb +474 -0
  112. data/lib/canis/core/widgets/keylabelprinter.rb +194 -0
  113. data/lib/canis/core/widgets/listbox.rb +326 -0
  114. data/lib/canis/core/widgets/listfooter.rb +86 -0
  115. data/lib/canis/core/widgets/rcombo.rb +210 -0
  116. data/lib/canis/core/widgets/rcontainer.rb +415 -0
  117. data/lib/canis/core/widgets/rlink.rb +30 -0
  118. data/lib/canis/core/widgets/rmenu.rb +970 -0
  119. data/lib/canis/core/widgets/rmenulink.rb +30 -0
  120. data/lib/canis/core/widgets/rmessagebox.rb +400 -0
  121. data/lib/canis/core/widgets/rprogress.rb +118 -0
  122. data/lib/canis/core/widgets/rtabbedpane.rb +631 -0
  123. data/lib/canis/core/widgets/rtabbedwindow.rb +70 -0
  124. data/lib/canis/core/widgets/rwidget.rb +3634 -0
  125. data/lib/canis/core/widgets/scrollbar.rb +147 -0
  126. data/lib/canis/core/widgets/statusline.rb +113 -0
  127. data/lib/canis/core/widgets/table.rb +1072 -0
  128. data/lib/canis/core/widgets/tabular.rb +264 -0
  129. data/lib/canis/core/widgets/textpad.rb +1674 -0
  130. data/lib/canis/core/widgets/tree.rb +690 -0
  131. data/lib/canis/core/widgets/tree/treecellrenderer.rb +150 -0
  132. data/lib/canis/core/widgets/tree/treemodel.rb +432 -0
  133. data/lib/canis/version.rb +3 -0
  134. metadata +229 -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: jkepler http://github.com/mare-imbrium/canis/
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 Canis
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,696 @@
1
+ =begin
2
+ * Name: App
3
+ * Description: Experimental Application class
4
+ * Author: jkepler (ABCD)
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 'canis'
19
+ require 'canis/core/util/widgetshortcuts'
20
+
21
+ include Canis
22
+ include Canis::Utils
23
+ include Io
24
+ module Canis
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
+ # 2014-04-17 - 13:15 XXX are these used. required ???
45
+ class Widget
46
+ def changed *args, &block
47
+ bind :CHANGED, *args, &block
48
+ end
49
+ def leave *args, &block
50
+ bind :LEAVE, *args, &block
51
+ end
52
+ def enter *args, &block
53
+ bind :ENTER, *args, &block
54
+ end
55
+ # actually we already have command() for buttons
56
+ def click *args, &block
57
+ bind :PRESS, *args, &block
58
+ end
59
+ end
60
+ class CheckBox
61
+ # a little dicey XXX
62
+ def text(*val)
63
+ if val.empty?
64
+ @value ? @onvalue : @offvalue
65
+ else
66
+ super
67
+ end
68
+ end
69
+ end
70
+ # This is the Application class which does the job of setting up the
71
+ # environment, and closing it at the end.
72
+ class App
73
+ include Canis::WidgetShortcuts
74
+ attr_reader :config
75
+ attr_reader :form
76
+ attr_reader :window
77
+ attr_writer :quit_key
78
+ # the row on which to prompt user for any inputs
79
+ #attr_accessor :prompt_row # 2011-10-17 14:06:22
80
+
81
+ # TODO: i should be able to pass window coords here in config
82
+ # :title
83
+ def initialize config={}, &block
84
+ @config = config
85
+
86
+
87
+ widget_shortcuts_init
88
+ @variables = {}
89
+ # if we are creating child objects then we will not use outer form. this object will manage
90
+ @current_object = []
91
+ @_system_commands = %w{ bind_global bind_component field_help_text }
92
+
93
+ init_vars
94
+ $log.debug "XXX APP CONFIG: #{@config} " if $log.debug?
95
+ run &block
96
+ end
97
+ def init_vars
98
+ @quit_key ||= FFI::NCurses::KEY_F10
99
+ # actually this should be maintained inside ncurses pack, so not loaded 2 times.
100
+ # this way if we call an app from existing program, App won't start ncurses.
101
+ unless $ncurses_started
102
+ init_ncurses
103
+ end
104
+ $lastline = Ncurses.LINES - 1
105
+ #@message_row = Ncurses.LINES-1
106
+ #@prompt_row = @message_row # hope to use for ask etc # 2011-10-17 14:06:27
107
+ unless $log
108
+ logpath=ENV["CANIS_LOG_PATH"]
109
+ $log = create_logger(logpath || "/dev/null")
110
+ end
111
+ end
112
+ def logger; return $log; end
113
+ def close
114
+ $log.debug " INSIDE CLOSE, #{@stop_ncurses_on_close} "
115
+ @window.destroy if !@window.nil?
116
+ $log.debug " INSIDE CLOSE, #{@stop_ncurses_on_close} "
117
+ if @stop_ncurses_on_close
118
+ Canis::stop_ncurses
119
+ $log.debug " CLOSING NCURSES"
120
+ end
121
+ $log.debug " CLOSING APP"
122
+ end
123
+ # not sure, but user shuld be able to trap keystrokes if he wants
124
+ # but do i still call handle_key if he does, or give him total control.
125
+ # But loop is already called by framework
126
+ def loop &block
127
+ @form.repaint
128
+ @window.wrefresh
129
+ Ncurses::Panel.update_panels
130
+ @break_key = ?\C-q.getbyte(0)
131
+ # added this extra loop since from some places we exit using throw :close
132
+ # amd that was in a much higher place, and was getting us right out, with
133
+ # no chance of user canceling quit. This extra loop allows us to remain
134
+ # added on 2011-11-24
135
+ while true
136
+ catch :close do
137
+ while((ch = @window.getchar()) != 999 )
138
+ #break if ch == @break_key
139
+ if ch == @break_key || ch == @quit_key
140
+ #stopping = @window.fire_close_handler
141
+ #break if stopping.nil? || stopping
142
+ break
143
+ end
144
+
145
+ if @keyblock
146
+ str = keycode_tos ch
147
+ @keyblock.call(str.gsub(/-/, "_").to_sym) # not used ever
148
+ end
149
+
150
+ yield ch if block # <<<----
151
+ # this is what the user should have control ove. earlier we would put this in
152
+ # a try catch block so user could do what he wanted with the error. Now we
153
+ # need to get it to him somehow, perhaps through a block or on_error event
154
+ begin
155
+ @form.handle_key ch
156
+ rescue => err
157
+ $log.debug( "handle_key rescue reached ")
158
+ $log.debug( err.to_s)
159
+ $log.debug(err.backtrace.join("\n"))
160
+ textdialog [err.to_s, *err.backtrace], :title => "Exception"
161
+ end
162
+ #@form.repaint # was this duplicate ?? handle calls repaint not needed
163
+ @window.wrefresh
164
+ end
165
+ end # catch
166
+ stopping = @window.fire_close_handler
167
+ @window.wrefresh
168
+ break if stopping.nil? || stopping
169
+ end # while
170
+ end
171
+ # if calling loop separately better to call this, since it will shut off ncurses
172
+ # and print error on screen.
173
+ def safe_loop &block
174
+ begin
175
+ loop &block
176
+ rescue => ex
177
+ $log.debug( "APP.rb rescue reached ")
178
+ $log.debug( ex) if ex
179
+ $log.debug(ex.backtrace.join("\n")) if ex
180
+ ensure
181
+ close
182
+ # putting it here allows it to be printed on screen, otherwise it was not showing at all.
183
+ if ex
184
+ puts "========== EXCEPTION =========="
185
+ p ex
186
+ puts "==============================="
187
+ puts(ex.backtrace.join("\n"))
188
+ end
189
+ end
190
+ end
191
+ # returns a symbol of the key pressed
192
+ # e.g. :C_c for Ctrl-C
193
+ # :Space, :bs, :M_d etc
194
+ def keypress &block
195
+ @keyblock = block
196
+ end
197
+ # updates a global var with text. Calling app has to set up a Variable with that name and attach to
198
+ # a label so it can be printed.
199
+ def message text
200
+ $status_message.value = text # trying out 2011-10-9
201
+ #@message.value = text # 2011-10-17 14:07:01
202
+ end
203
+
204
+ # used only by LiveConsole, if enables in an app, usually only during testing.
205
+ def get_binding
206
+ return binding()
207
+ end
208
+ #
209
+ # suspends curses so you can play around on the shell
210
+ # or in cooked mode like Vim does. Expects a block to be passed.
211
+ # Purpose: you can print some stuff without creating a window, or
212
+ # just run shell commands without coming out.
213
+ # NOTE: if you pass clear as true, then the screen will be cleared
214
+ # and you can use puts or print to print. You may have to flush.
215
+ # However, with clear as false, the screen will not be cleared. You
216
+ # will have to print using printw, and if you expect user input
217
+ # you must do a "system /bin/stty sane"
218
+ # If you print stuff, you will have to put a getch() or system("read")
219
+ # to pause the screen.
220
+ def suspend clear=true
221
+ return unless block_given?
222
+ Ncurses.def_prog_mode
223
+ if clear
224
+ Ncurses.endwin
225
+ # NOTE: avoid false since screen remains half off
226
+ # too many issues
227
+ else
228
+ system "/bin/stty sane"
229
+ end
230
+ yield if block_given?
231
+ Ncurses.reset_prog_mode
232
+ if !clear
233
+ # Hope we don't screw your terminal up with this constantly.
234
+ Canis::stop_ncurses
235
+ Canis::start_ncurses
236
+ #@form.reset_all # not required
237
+ end
238
+ @form.repaint
239
+ @window.wrefresh
240
+ Ncurses::Panel.update_panels
241
+ end
242
+ def get_all_commands
243
+ opts = @_system_commands.dup
244
+ if respond_to? :get_commands
245
+ opts.push(*get_commands())
246
+ end
247
+ opts
248
+ end
249
+ # bind a key to a method at global (form) level
250
+ # Note that individual component may be overriding this.
251
+ def bind_global
252
+ opts = get_all_commands
253
+ cmd = rb_gets("Select a command (<tab> for choices) : ", opts)
254
+ if cmd.nil? || cmd == ""
255
+ rb_puts "Aborted."
256
+ return
257
+ end
258
+ key = []
259
+ str = ""
260
+ # the next is fine but does not allow user to enter a control or alt or function character
261
+ # since it uses Field. It is fine if you want to force alphanum input
262
+ ch = rb_getchar("Enter one or two keys. Finish with <ENTER>. Enter first key:")
263
+ unless ch
264
+ rb_puts "Aborted. <Press a key>"
265
+ return
266
+ end
267
+ key << ch
268
+ str << keycode_tos(ch)
269
+ ch = rb_getchar "Got #{str}. Enter second key or hit return:"
270
+ unless ch
271
+ rb_puts "Aborted. <Press a key>"
272
+ return
273
+ end
274
+ if ch == KEY_ENTER || ch == 13
275
+ else
276
+ key << ch
277
+ str << keycode_tos(ch)
278
+ end
279
+ if !key.empty?
280
+ rb_puts "Binding #{cmd} to #{str}. "
281
+ key = key[0] if key.size == 1
282
+ #@form.bind_key(key, cmd.to_sym) # not finding it, getting called by that comp
283
+ @form.bind_key(key){ send(cmd.to_sym) }
284
+ end
285
+ end
286
+ def bind_component
287
+ #rb_puts "Todo. ", :color_pair => get_color($promptcolor, :red, :black)
288
+ print_error_message "Todo this. "
289
+ # the idea here is to get the current component
290
+ # and bind some keys to some methods.
291
+ # however, how do we divine the methods we can map to
292
+ # and also in some cases the components itself has multiple components
293
+ end
294
+ # displays help_text associated with field. 2011-10-15
295
+ def field_help_text
296
+ f = @form.get_current_field
297
+ if f.respond_to?('help_text')
298
+ h = f.help_text
299
+ h = "No help text defined for this field.\nTry F1, or press '?' for key-bindings." unless h
300
+ textdialog "#{h}", :title => "Widget Help Text"
301
+ else
302
+ alert "Could not get field #{f} or does not respond to helptext. Try F1 or '?'"
303
+ end
304
+ end
305
+ # prompts user for a command. we need to get this back to the calling app
306
+ # or have some block stuff TODO
307
+ # Actually, this is naive, you would want to pass some values in like current data value
308
+ # or lines ??
309
+ # Also may want command completion, or help so all commands can be displayed
310
+ # NOTE: This is gonna change very soon - 2012-01-8
311
+ def get_command_from_user choices=["quit","help", "suspend", "shell_output"]
312
+ @_command_history ||= Array.new
313
+ str = rb_gets("Cmd: ", choices) { |q| q.default = @_previous_command; q.history = @_command_history }
314
+ @_command_history << str unless @_command_history.include? str
315
+ # shell the command
316
+ if str =~ /^!/
317
+ str = str[1..-1]
318
+ suspend(false) {
319
+ #system(str);
320
+ $log.debug "XXX STR #{str} " if $log.debug?
321
+
322
+ output=`#{str}`
323
+ system("echo ' ' ");
324
+ $log.debug "XXX output #{output} " if $log.debug?
325
+ system("echo '#{output}' ");
326
+ system("echo Press Enter to continue.");
327
+ system("read");
328
+ }
329
+ return nil # i think
330
+ else
331
+ # TODO
332
+ # here's where we can take internal commands
333
+ #alert "[#{str}] string did not match :!"
334
+ str = str.to_s #= str[1..-1]
335
+ cmdline = str.split
336
+ cmd = cmdline.shift #.to_sym
337
+ return unless cmd # added 2011-09-11 FFI
338
+ f = @form.get_current_field
339
+ if respond_to?(cmd, true)
340
+ if cmd == "close"
341
+ throw :close # other seg faults in del_panel window.destroy executes 2x
342
+ else
343
+ res = send cmd, *cmdline
344
+ end
345
+ elsif f.respond_to?(cmd, true)
346
+ res = f.send(cmd, *cmdline)
347
+ else
348
+ alert "App: #{self.class} does not respond to #{cmd} "
349
+ ret = false
350
+ # what is this execute_this: some kind of general routine for all apps ?
351
+ ret = execute_this(cmd, *cmdline) if respond_to?(:execute_this, true)
352
+ rb_puts("#{self.class} does not respond to #{cmd} ", :color_pair => $promptcolor) unless ret
353
+ # should be able to say in red as error
354
+ end
355
+ end
356
+ end
357
+ #
358
+ # @group methods to create widgets easily
359
+ #
360
+ # process arguments based on datatype, perhaps making configuration
361
+ # of some components easier for caller avoiding too much boiler plate code
362
+ #
363
+ #instance_eval &block if block_given?
364
+ # or
365
+ #@blk = block # for later execution using @blk.call()
366
+ #colorlabel = Label.new @form, {'text' => "Select a color:", "row" => row, "col" => col, "color"=>"cyan", "mnemonic" => 'S'}
367
+ alias :text :label
368
+
369
+ # print a title on first row -- this is so bad, not even a label
370
+ def title string, config={}
371
+ raise "don't use DELETE dead code"
372
+ ## TODO center it
373
+ @window.printstring 1, 30, string, $normalcolor, 'reverse'
374
+ end
375
+ # print a sutitle on second row, center and use a label, if this is even used.
376
+ def subtitle string, config={}
377
+ raise "don't use DELETE"
378
+ @window.printstring 2, 30, string, $datacolor, 'normal'
379
+ end
380
+ # menu bar
381
+
382
+ # displays a horizontal line
383
+ # takes col (column to start from) from current stack
384
+ # take row from app_row
385
+ #
386
+ # requires width to be passed in config, else defaults to 20
387
+ # @example
388
+ # hline :width => 55
389
+ def hline config={}
390
+ row = config[:row] || @app_row
391
+ width = config[:width] || 20
392
+ _position config
393
+ col = config[:col] || 1
394
+ @color_pair = config[:color_pair] || $datacolor
395
+ @attrib = config[:attrib] || Ncurses::A_NORMAL
396
+ @window.attron(Ncurses.COLOR_PAIR(@color_pair) | @attrib)
397
+ @window.mvwhline( row, col, FFI::NCurses::ACS_HLINE, width)
398
+ @window.attron(Ncurses.COLOR_PAIR(@color_pair) | @attrib)
399
+ @app_row += 1
400
+ end
401
+
402
+
403
+ # ADD new widget above this
404
+
405
+ # @endgroup
406
+
407
+ # @group positioning of components
408
+
409
+
410
+ private
411
+ def quit
412
+ throw(:close)
413
+ end
414
+ #def help; display_app_help; end
415
+ # i am removing above from rdialog. 2014-05-21 - 12:42 hope next
416
+ # line works UNTESTED
417
+ def help; @form.help_manager.display_help; end
418
+
419
+ # Initialize curses
420
+ def init_ncurses
421
+ Canis::start_ncurses # this is initializing colors via ColorMap.setup
422
+ #$ncurses_started = true
423
+ @stop_ncurses_on_close = true
424
+ end
425
+
426
+ # returns length of longest
427
+ def longest_in_list list #:nodoc:
428
+ longest = list.inject(0) do |memo,word|
429
+ memo >= word.length ? memo : word.length
430
+ end
431
+ longest
432
+ end
433
+ # returns longest item
434
+ # rows = list.max_by(&:length)
435
+ #
436
+ def longest_in_list2 list #:nodoc:
437
+ longest = list.inject(list[0]) do |memo,word|
438
+ memo.length >= word.length ? memo : word
439
+ end
440
+ longest
441
+ end
442
+
443
+ # if partial command entered then returns matches
444
+ def _resolve_command opts, cmd
445
+ return cmd if opts.include? cmd
446
+ matches = opts.grep Regexp.new("^#{cmd}")
447
+ end
448
+
449
+ def run &block
450
+ begin
451
+
452
+ # check if user has passed window coord in config, else root window
453
+ @window = Canis::Window.root_window
454
+ awin = @window
455
+ catch(:close) do
456
+ @form = Form.new @window
457
+ @form.bind_key([?\C-x, ?c], 'suspend') { suspend(false) do
458
+ system("tput cup 26 0")
459
+ system("tput ed")
460
+ system("echo Enter C-d to return to application")
461
+ system (ENV['PS1']='\s-\v\$ ')
462
+ system(ENV['SHELL']);
463
+ end
464
+ }
465
+ # this is a very rudimentary default command executer, it does not
466
+ # allow tab completion. App should use M-x with names of commands
467
+ # as in appgmail
468
+ # NOTE: This is gonna change very soon - 2012-01-8
469
+ @form.bind_key(?:, 'prompt') {
470
+ str = get_command_from_user
471
+ }
472
+
473
+ # this M-x stuff has to be moved out so it can be used by all. One should be able
474
+ # to add_commands properly to this, and to C-x. I am thinking how to go about this,
475
+ # and what function M-x actually serves.
476
+
477
+ @form.bind_key(?\M-x, 'M-x commands'){
478
+ # TODO previous command to be default
479
+ opts = get_all_commands()
480
+ @_command_history ||= Array.new
481
+ # previous command should be in opts, otherwise it is not in this context
482
+ cmd = rb_gets("Command: ", opts){ |q| q.default = @_previous_command; q.history = @_command_history }
483
+ if cmd.nil? || cmd == ""
484
+ else
485
+ @_command_history << cmd unless @_command_history.include? cmd
486
+ cmdline = cmd.split
487
+ cmd = cmdline.shift
488
+ # check if command is a substring of a larger command
489
+ if !opts.include?(cmd)
490
+ rcmd = _resolve_command(opts, cmd) if !opts.include?(cmd)
491
+ if rcmd.size == 1
492
+ cmd = rcmd.first
493
+ elsif !rcmd.empty?
494
+ rb_puts "Cannot resolve #{cmd}. Matches are: #{rcmd} "
495
+ end
496
+ end
497
+ if respond_to?(cmd, true)
498
+ @_previous_command = cmd
499
+ begin
500
+ send cmd, *cmdline
501
+ rescue => exc
502
+ $log.error "ERR EXC: send throwing an exception now. Duh. IMAP keeps crashing haha !! #{exc} " if $log.debug?
503
+ if exc
504
+ $log.debug( exc)
505
+ $log.debug(exc.backtrace.join("\n"))
506
+ rb_puts exc.to_s
507
+ end
508
+ end
509
+ else
510
+ rb_puts("Command [#{cmd}] not supported by #{self.class} ", :color_pair => $promptcolor)
511
+ end
512
+ end
513
+ }
514
+ #@form.bind_key(KEY_F1, 'help'){ display_app_help } # NOT REQUIRED NOW 2012-01-7 since form does it
515
+ @form.bind_key([?q,?q], 'quit' ){ throw :close } if $log.debug?
516
+
517
+ #@message = Variable.new
518
+ #@message.value = ""
519
+ $status_message ||= Variable.new # remember there are multiple levels of apps
520
+ $status_message.value = ""
521
+ #$error_message.update_command { @message.set_value($error_message.value) }
522
+ if block
523
+ begin
524
+ yield_or_eval &block if block_given? # modified 2010-11-17 20:36
525
+ # how the hell does a user trap exception if the loop is hidden from him ? FIXME
526
+ loop
527
+ rescue => ex
528
+ $log.debug( "APP.rb rescue reached ")
529
+ $log.debug( ex) if ex
530
+ $log.debug(ex.backtrace.join("\n")) if ex
531
+ ensure
532
+ close
533
+ # putting it here allows it to be printed on screen, otherwise it was not showing at all.
534
+ if ex
535
+ puts "========== EXCEPTION =========="
536
+ p ex
537
+ puts "==============================="
538
+ puts(ex.backtrace.join("\n"))
539
+ end
540
+ end
541
+ nil
542
+ else
543
+ #@close_on_terminate = true
544
+ self
545
+ end #if block
546
+ end # :close
547
+ end
548
+ end
549
+ # process args, all widgets should call this
550
+ def _process_args args, config, block_event, events #:nodoc:
551
+ args.each do |arg|
552
+ case arg
553
+ when Array
554
+ # please don't use this, keep it simple and use hash NOTE
555
+ # we can use r,c, w, h
556
+ row, col, width, height = arg
557
+ config[:row] = row
558
+ config[:col] = col
559
+ config[:width] = width if width
560
+ # width for most XXX ?
561
+ config[:height] = height if height
562
+ when Hash
563
+ config.merge!(arg)
564
+ if block_event
565
+ block_event = config.delete(:block_event){ block_event }
566
+ raise "Invalid event. Use #{events}" unless events.include? block_event
567
+ end
568
+ when String
569
+ config[:name] = arg
570
+ config[:title] = arg # some may not have title
571
+ #config[:text] = arg # some may not have title
572
+ end
573
+ end
574
+ end # _process
575
+ end # class
576
+ end # module
577
+ if $0 == __FILE__
578
+ include Canis
579
+ #app = App.new
580
+ #window = app.window
581
+ #window.printstring 2, 30, "Demo of Listbox - canis", $normalcolor, 'reverse'
582
+ #app.logger.info "beforegetch"
583
+ #window.getch
584
+ #app.close
585
+ # this was the yield example, but now we've moved to instance eval
586
+ App.new do
587
+ @window.printstring 0, 30, "Demo of Listbox - canis", $normalcolor, 'reverse'
588
+ @window.printstring 1, 30, "Hit F1 to quit", $datacolor, 'normal'
589
+ form = @form
590
+ fname = "Search"
591
+ r, c = 7, 30
592
+ c += fname.length + 1
593
+ #field1 = field( [r,c, 30], fname, :bgcolor => "cyan", :block_event => :CHANGE) do |fld|
594
+ stack :margin_top => 2, :margin => 10 do
595
+ lbl = label({:text => fname, :color=>'white',:bgcolor=>'red', :mnemonic=> 's'})
596
+ field1 = field( [r,c, 30], fname, :bgcolor => "cyan",:block_event => :CHANGE) do |fld|
597
+ message("You entered #{fld.getvalue}. To quit enter quit and tab out")
598
+ if fld.getvalue == "quit"
599
+ logger.info "you typed quit!"
600
+ throw :close
601
+ end
602
+ end
603
+ #field1.set_label Label.new @form, {:text => fname, :color=>'white',:bgcolor=>'red', :mnemonic=> 's'}
604
+ field1.set_label( lbl )
605
+ field1.enter do
606
+ message "you entered this field"
607
+ end
608
+
609
+ stack :margin_top => 2, :margin => 0 do
610
+ #label( [8, 30, 60],{:text => "A label", :color=>'white',:bgcolor=>'blue'} )
611
+ end
612
+
613
+ @bluelabel = label( [8, 30, 60],{:text => "B label", :color=>'white',:bgcolor=>'blue'} )
614
+
615
+ stack :margin_top => 2, :margin => 0 do
616
+ toggle :onvalue => " Toggle Down ", :offvalue => " Untoggle ", :mnemonic => 'T', :value => true
617
+
618
+ toggle :onvalue => " On ", :offvalue => " Off ", :value => true do |e|
619
+ alert "You pressed me #{e.state}"
620
+ end
621
+ check :text => "Check me!", :onvalue => "Checked", :offvalue => "Unchecked", :value => true do |e|
622
+ # this works but long and complicated
623
+ #@bluelabel.text = e.item.getvalue ? e.item.onvalue : e.item.offvalue
624
+ @bluelabel.text = e.item.text
625
+ end
626
+ radio :text => "red", :value => "RED", :color => "red", :group => :colors
627
+ radio :text => "green", :value => "GREEN", :color => "green", :group => :colors
628
+ flow do
629
+ button_row = 17
630
+ ok_button = button( [button_row,30], "OK", {:mnemonic => 'O'}) do
631
+ alert("About to dump data into log file!")
632
+ message "Dumped data to log file"
633
+ end
634
+
635
+ # using ampersand to set mnemonic
636
+ cancel_button = button( [button_row, 40], "&Cancel" ) do
637
+ if confirm("Do your really want to quit?")== :YES
638
+ #throw(:close);
639
+ quit
640
+ else
641
+ message "Quit aborted"
642
+ end
643
+ end # cancel
644
+ button "Don't know"
645
+ end
646
+ flow :margin_top => 2 do
647
+ button "Another"
648
+ button "Line"
649
+ end
650
+ stack :margin_top => 2, :margin => 0 do
651
+ @pbar = progress :width => 20, :bgcolor => 'white', :color => 'red'
652
+ @pbar1 = progress :width => 20, :style => :old
653
+ end
654
+ end
655
+ end # stack
656
+ # lets make another column
657
+ stack :margin_top => 2, :margin => 70 do
658
+ l = label "Column 2"
659
+ f1 = field "afield", :bgcolor => 'white', :color => 'black'
660
+ listbox "A list", :list => ["Square", "Oval", "Rectangle", "Somethinglarge"], :choose => ["Square"]
661
+ lb = listbox "Another", :list => ["Square", "Oval", "Rectangle", "Somethinglarge"] do |list|
662
+ #f1.set_buffer list.text
663
+ #f1.text list.text
664
+ f1.text = list.text
665
+ l.text = list.current_value.upcase
666
+ end
667
+ t = textarea :height => 10 do |e|
668
+ #@bluelabel.text = e.to_s.tr("\n",' ')
669
+ @bluelabel.text = e.text.gsub("\n"," ")
670
+ len = e.source.get_text.length
671
+ len = len % 20 if len > 20
672
+ $log.debug " PBAR len of text is #{len}: #{len/20.0} "
673
+ @pbar.fraction(len/20.0)
674
+ @pbar1.fraction(len/20.0)
675
+ i = ((len/20.0)*100).to_i
676
+ @pbar.text = "completed:#{i}"
677
+ end
678
+ t.leave do |c|
679
+ @bluelabel.text = c.get_text.gsub("\n"," ")
680
+ end
681
+
682
+ end
683
+
684
+ # Allow user to get the keys
685
+ keypress do |key|
686
+ if key == :C_c
687
+ message "You tried to cancel"
688
+ #throw :close
689
+ quit
690
+ else
691
+ #app.message "You pressed #{key}, #{char} "
692
+ message "You pressed #{key}"
693
+ end
694
+ end
695
+ end
696
+ end