rbcurse 1.4.1 → 1.5.0.pre1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (192) hide show
  1. data/CHANGELOG +31 -0
  2. data/README.markdown +69 -11
  3. data/VERSION +1 -1
  4. data/lib/rbcurse.rb +5 -5
  5. metadata +10 -198
  6. data/Makefile +0 -21
  7. data/Manifest.txt +0 -91
  8. data/TODO +0 -372
  9. data/TODO2.txt +0 -118
  10. data/examples/README.txt +0 -67
  11. data/examples/abasiclist.rb +0 -33
  12. data/examples/alpmenu.rb +0 -42
  13. data/examples/app.rb +0 -859
  14. data/examples/app.sample +0 -10
  15. data/examples/appdirtree.rb +0 -75
  16. data/examples/appemail.rb +0 -191
  17. data/examples/appemaillb.rb +0 -308
  18. data/examples/appgcompose.rb +0 -315
  19. data/examples/atree.rb +0 -64
  20. data/examples/common/file.rb +0 -40
  21. data/examples/common/rmail.rb +0 -257
  22. data/examples/data.txt +0 -683
  23. data/examples/data/README.markdown +0 -9
  24. data/examples/data/brew.txt +0 -38
  25. data/examples/data/color.2 +0 -37
  26. data/examples/data/gemlist.txt +0 -60
  27. data/examples/data/lotr.txt +0 -12
  28. data/examples/data/ports.txt +0 -136
  29. data/examples/data/tasks.txt +0 -27
  30. data/examples/data/todocsv.csv +0 -28
  31. data/examples/data/unix1.txt +0 -21
  32. data/examples/data/unix2.txt +0 -11
  33. data/examples/dbdemo.rb +0 -495
  34. data/examples/deprecated/appgmail.rb +0 -952
  35. data/examples/deprecated/splitp.rb +0 -56
  36. data/examples/deprecated/testscrolllb.rb +0 -86
  37. data/examples/deprecated/testscrollp.rb +0 -88
  38. data/examples/deprecated/testscrollta.rb +0 -80
  39. data/examples/deprecated/testscrolltable.rb +0 -165
  40. data/examples/deprecated/testsplit.rb +0 -87
  41. data/examples/deprecated/testsplit2.rb +0 -123
  42. data/examples/deprecated/testsplit3.rb +0 -215
  43. data/examples/deprecated/testsplit3_1.rb +0 -244
  44. data/examples/deprecated/testsplit3a.rb +0 -215
  45. data/examples/deprecated/testsplit3b.rb +0 -237
  46. data/examples/deprecated/testsplitta.rb +0 -148
  47. data/examples/deprecated/testsplittv.rb +0 -142
  48. data/examples/deprecated/testsplittvv.rb +0 -144
  49. data/examples/deprecated/testtpane.rb +0 -215
  50. data/examples/deprecated/testtpane2.rb +0 -145
  51. data/examples/deprecated/testtpanetable.rb +0 -203
  52. data/examples/dirtree.rb +0 -88
  53. data/examples/experimental/resultsetdemo.rb +0 -280
  54. data/examples/experimental/testmform.rb +0 -35
  55. data/examples/experimental/testscroller.rb +0 -117
  56. data/examples/experimental/teststackflow.rb +0 -111
  57. data/examples/menu1.rb +0 -112
  58. data/examples/multispl.rb +0 -86
  59. data/examples/newmessagebox.rb +0 -130
  60. data/examples/newtabbedwindow.rb +0 -100
  61. data/examples/newtesttabp.rb +0 -121
  62. data/examples/qdfilechooser.rb +0 -68
  63. data/examples/rfe.rb +0 -1195
  64. data/examples/rfe_renderer.rb +0 -121
  65. data/examples/sqlc.rb +0 -454
  66. data/examples/sqlm.rb +0 -437
  67. data/examples/sqlt.rb +0 -408
  68. data/examples/status.txt +0 -68
  69. data/examples/table1.rb +0 -24
  70. data/examples/term2.rb +0 -84
  71. data/examples/test1.rb +0 -239
  72. data/examples/test2.rb +0 -674
  73. data/examples/testapp.rb +0 -44
  74. data/examples/testapp2.rb +0 -58
  75. data/examples/testchars.rb +0 -137
  76. data/examples/testcombo.rb +0 -91
  77. data/examples/testkeypress.rb +0 -66
  78. data/examples/testlistbox.rb +0 -113
  79. data/examples/testmenu.rb +0 -101
  80. data/examples/testmulticomp.rb +0 -70
  81. data/examples/testmulticontainer.rb +0 -94
  82. data/examples/testmultispl.rb +0 -199
  83. data/examples/testree.rb +0 -106
  84. data/examples/testtable.rb +0 -263
  85. data/examples/testtabp.rb +0 -107
  86. data/examples/testtodo.rb +0 -584
  87. data/examples/testvimsplit.rb +0 -112
  88. data/examples/testwsshortcuts.rb +0 -64
  89. data/examples/testwsshortcuts2.rb +0 -126
  90. data/examples/todo.db +0 -0
  91. data/examples/todo.yml +0 -191
  92. data/examples/viewtodo.rb +0 -574
  93. data/lib/rbcurse/action.rb +0 -40
  94. data/lib/rbcurse/app.rb +0 -1374
  95. data/lib/rbcurse/applicationheader.rb +0 -102
  96. data/lib/rbcurse/celleditor.rb +0 -112
  97. data/lib/rbcurse/checkboxcellrenderer.rb +0 -57
  98. data/lib/rbcurse/colormap.rb +0 -159
  99. data/lib/rbcurse/comboboxcellrenderer.rb +0 -30
  100. data/lib/rbcurse/common/ansiparser.rb +0 -117
  101. data/lib/rbcurse/common/appmethods.rb +0 -112
  102. data/lib/rbcurse/common/basestack.rb +0 -407
  103. data/lib/rbcurse/common/bordertitle.rb +0 -41
  104. data/lib/rbcurse/common/chunk.rb +0 -177
  105. data/lib/rbcurse/common/colorparser.rb +0 -71
  106. data/lib/rbcurse/common/keydefs.rb +0 -30
  107. data/lib/rbcurse/common/widgetshortcuts.rb +0 -302
  108. data/lib/rbcurse/defaultlistselectionmodel.rb +0 -79
  109. data/lib/rbcurse/deprecated/README.markdown +0 -12
  110. data/lib/rbcurse/deprecated/rscrollpane.rb +0 -512
  111. data/lib/rbcurse/deprecated/rsplitpane.rb +0 -894
  112. data/lib/rbcurse/deprecated/rsplitpane2.rb +0 -1009
  113. data/lib/rbcurse/deprecated/rviewport.rb +0 -204
  114. data/lib/rbcurse/experimental/README.markdown +0 -14
  115. data/lib/rbcurse/experimental/resultsettextview.rb +0 -585
  116. data/lib/rbcurse/experimental/stackflow.rb +0 -478
  117. data/lib/rbcurse/extras/bottomline.rb +0 -1850
  118. data/lib/rbcurse/extras/box.rb +0 -58
  119. data/lib/rbcurse/extras/directorylist.rb +0 -467
  120. data/lib/rbcurse/extras/directorytree.rb +0 -69
  121. data/lib/rbcurse/extras/divider.rb +0 -310
  122. data/lib/rbcurse/extras/focusmanager.rb +0 -31
  123. data/lib/rbcurse/extras/horizlist.rb +0 -203
  124. data/lib/rbcurse/extras/listselectable.rb +0 -264
  125. data/lib/rbcurse/extras/masterdetail.rb +0 -166
  126. data/lib/rbcurse/extras/menutree.rb +0 -63
  127. data/lib/rbcurse/extras/multiform.rb +0 -330
  128. data/lib/rbcurse/extras/multilinelabel.rb +0 -142
  129. data/lib/rbcurse/extras/newmessagebox.rb +0 -328
  130. data/lib/rbcurse/extras/newtabbedpane.rb +0 -612
  131. data/lib/rbcurse/extras/newtabbedwindow.rb +0 -68
  132. data/lib/rbcurse/extras/padreader.rb +0 -189
  133. data/lib/rbcurse/extras/rcomboedit.rb +0 -256
  134. data/lib/rbcurse/extras/resultsetbrowser.rb +0 -281
  135. data/lib/rbcurse/extras/rlink.rb +0 -27
  136. data/lib/rbcurse/extras/rmenulink.rb +0 -21
  137. data/lib/rbcurse/extras/scrollbar.rb +0 -143
  138. data/lib/rbcurse/extras/statusline.rb +0 -94
  139. data/lib/rbcurse/extras/stdscrwindow.rb +0 -309
  140. data/lib/rbcurse/extras/tableextended.rb +0 -40
  141. data/lib/rbcurse/extras/tabular.rb +0 -264
  142. data/lib/rbcurse/extras/tabularwidget.rb +0 -1150
  143. data/lib/rbcurse/extras/textpad.rb +0 -516
  144. data/lib/rbcurse/extras/viewer.rb +0 -136
  145. data/lib/rbcurse/io.rb +0 -850
  146. data/lib/rbcurse/keylabelprinter.rb +0 -178
  147. data/lib/rbcurse/listcellrenderer.rb +0 -140
  148. data/lib/rbcurse/listeditable.rb +0 -310
  149. data/lib/rbcurse/listkeys.rb +0 -37
  150. data/lib/rbcurse/listscrollable.rb +0 -564
  151. data/lib/rbcurse/listselectable.rb +0 -142
  152. data/lib/rbcurse/mapper.rb +0 -130
  153. data/lib/rbcurse/orderedhash.rb +0 -77
  154. data/lib/rbcurse/ractionevent.rb +0 -67
  155. data/lib/rbcurse/rbasiclistbox.rb +0 -768
  156. data/lib/rbcurse/rchangeevent.rb +0 -27
  157. data/lib/rbcurse/rcombo.rb +0 -238
  158. data/lib/rbcurse/rcommandwindow.rb +0 -587
  159. data/lib/rbcurse/rcontainer.rb +0 -415
  160. data/lib/rbcurse/rdialogs.rb +0 -451
  161. data/lib/rbcurse/rinputdataevent.rb +0 -47
  162. data/lib/rbcurse/rlistbox.rb +0 -1196
  163. data/lib/rbcurse/rmenu.rb +0 -939
  164. data/lib/rbcurse/rmessagebox.rb +0 -348
  165. data/lib/rbcurse/rmulticontainer.rb +0 -304
  166. data/lib/rbcurse/rmultisplit.rb +0 -722
  167. data/lib/rbcurse/rmultitextview.rb +0 -306
  168. data/lib/rbcurse/rpopupmenu.rb +0 -755
  169. data/lib/rbcurse/rprogress.rb +0 -118
  170. data/lib/rbcurse/rscrollform.rb +0 -418
  171. data/lib/rbcurse/rtabbedpane.rb +0 -1158
  172. data/lib/rbcurse/rtabbedwindow.rb +0 -167
  173. data/lib/rbcurse/rtable.rb +0 -1718
  174. data/lib/rbcurse/rtextarea.rb +0 -920
  175. data/lib/rbcurse/rtextview.rb +0 -761
  176. data/lib/rbcurse/rtree.rb +0 -780
  177. data/lib/rbcurse/rvimsplit.rb +0 -763
  178. data/lib/rbcurse/rwidget.rb +0 -2915
  179. data/lib/rbcurse/scrollable.rb +0 -301
  180. data/lib/rbcurse/table/tablecellrenderer.rb +0 -86
  181. data/lib/rbcurse/table/tabledatecellrenderer.rb +0 -98
  182. data/lib/rbcurse/tree/treecellrenderer.rb +0 -150
  183. data/lib/rbcurse/tree/treemodel.rb +0 -428
  184. data/lib/rbcurse/undomanager.rb +0 -188
  185. data/lib/rbcurse/vieditable.rb +0 -144
  186. data/lib/ver/keyboard.rb +0 -150
  187. data/lib/ver/keyboard2.rb +0 -170
  188. data/lib/ver/ncurses.rb +0 -218
  189. data/lib/ver/panel.rb +0 -162
  190. data/lib/ver/rpad.rb +0 -375
  191. data/lib/ver/window.rb +0 -882
  192. data/test/test_rbcurse.rb +0 -0
@@ -1,1850 +0,0 @@
1
- require "date"
2
- require "erb"
3
- require 'pathname'
4
- =begin
5
- * Name : bottomline.rb
6
- * Description : routines for input at bottom of screen like vim, or anyother line
7
- * :
8
- * Author : rkumar
9
- * Date : 2010-10-25 12:45
10
- * License :
11
- Same as Ruby's License (http://www.ruby-lang.org/LICENSE.txt)
12
-
13
- The character input routines are from io.rb, however, the user-interface to the input
14
- is copied from the Highline project (James Earl Gray) with permission.
15
-
16
- May later use a Label and Field.
17
-
18
- NOTE : Pls avoid directly using this class. I am trying to redo this so ask, agree and say
19
- can create their own window and be done with it. The hurdle in that is that ask calls
20
- say, so when to close the window is not clear within say. Some shakeup is expected by
21
- 1.4.0 or so.
22
-
23
- =end
24
- module RubyCurses
25
-
26
- # just so the program does not bomb due to a tiny feature
27
- # I do not raise error on nil array, i create a dummy array
28
- # which you likely will not be able to use, in any case it will have only one value
29
- class History < Struct.new(:array, :current_index)
30
- attr_reader :last_index
31
- attr_reader :current_index
32
- attr_reader :array
33
- def initialize a=nil, c=0
34
- #raise "Array passed to History cannot be nil" unless a
35
- #@max_index = a.size
36
- @array = a || []
37
- @current_index = c
38
- @last_index = c
39
- end
40
- def last
41
- @current_index = max_index
42
- @array.last
43
- end
44
- def first
45
- @current_index = 0
46
- @array.first
47
- end
48
- def max_index
49
- @array.size - 1
50
- end
51
- def up
52
- item = @array[@current_index]
53
- previous
54
- return item
55
- end
56
- def next
57
- @last_index = @current_index
58
- if @current_index + 1 > max_index
59
- @current_index = 0
60
- else
61
- @current_index += 1
62
- end
63
- @array[@current_index]
64
- end
65
- def previous
66
- @last_index = @current_index
67
- if @current_index - 1 < 0
68
- @current_index = max_index()
69
- else
70
- @current_index -= 1
71
- end
72
- @array[@current_index]
73
- end
74
- def is_last?
75
- @current_index == max_index()
76
- end
77
- def push item
78
- $log.debug " XXX history push #{item} " if $log.debug?
79
- @array.push item
80
- @current_index = max_index
81
- end
82
- end # class
83
- # some variables are polluting space of including app,
84
- # we should make this a class.
85
- class Bottomline
86
- attr_accessor :window
87
- attr_accessor :message_row
88
- attr_accessor :name # for debugging
89
- def initialize win=nil, row=nil
90
- @window = win
91
- #@window.wrefresh
92
- #Ncurses::Panel.update_panels
93
- #@message_row = row
94
- @message_row = 0 # 2011-10-8
95
- end
96
- #
97
- # create a window at bottom and show and hide it.
98
- # Causing a stack overflow since Window creates a bottomline too !
99
- #
100
- def _create_footer_window h = 1 , w = Ncurses.COLS, t = Ncurses.LINES-1, l = 0
101
- ewin = VER::Window.new(h, w , t, l)
102
- #ewin.bkgd(Ncurses.COLOR_PAIR($promptcolor));
103
- @window = ewin
104
- return ewin
105
- end
106
-
107
- class QuestionError < StandardError
108
- # do nothing, just creating a unique error type
109
- end
110
- class Question
111
- # An internal HighLine error. User code does not need to trap this.
112
- class NoAutoCompleteMatch < StandardError
113
- # do nothing, just creating a unique error type
114
- end
115
-
116
- #
117
- # Create an instance of HighLine::Question. Expects a _question_ to ask
118
- # (can be <tt>""</tt>) and an _answer_type_ to convert the answer to.
119
- # The _answer_type_ parameter must be a type recognized by
120
- # Question.convert(). If given, a block is yeilded the new Question
121
- # object to allow custom initializaion.
122
- #
123
- def initialize( question, answer_type )
124
- # initialize instance data
125
- @question = question
126
- @answer_type = answer_type
127
-
128
- @character = nil
129
- @limit = nil
130
- @echo = true
131
- @readline = false
132
- @whitespace = :strip
133
- @_case = nil
134
- @default = nil
135
- @validate = nil
136
- @above = nil
137
- @below = nil
138
- @in = nil
139
- @confirm = nil
140
- @gather = false
141
- @first_answer = nil
142
- @directory = Pathname.new(File.expand_path(File.dirname($0)))
143
- @glob = "*"
144
- @responses = Hash.new
145
- @overwrite = false
146
- @history = nil
147
-
148
- # allow block to override settings
149
- yield self if block_given?
150
-
151
- #$log.debug " XXX default #{@default}" if $log.debug?
152
- #$log.debug " XXX history #{@history}" if $log.debug?
153
-
154
- # finalize responses based on settings
155
- build_responses
156
- end
157
-
158
- # The ERb template of the question to be asked.
159
- attr_accessor :question
160
- # The type that will be used to convert this answer.
161
- attr_accessor :answer_type
162
- #
163
- # Can be set to +true+ to use HighLine's cross-platform character reader
164
- # instead of fetching an entire line of input. (Note: HighLine's character
165
- # reader *ONLY* supports STDIN on Windows and Unix.) Can also be set to
166
- # <tt>:getc</tt> to use that method on the input stream.
167
- #
168
- # *WARNING*: The _echo_ and _overwrite_ attributes for a question are
169
- # ignored when using the <tt>:getc</tt> method.
170
- #
171
- attr_accessor :character
172
- #
173
- # Allows you to set a character limit for input.
174
- #
175
- # If not set, a default of 100 is used
176
- #
177
- attr_accessor :limit
178
- #
179
- # Can be set to +true+ or +false+ to control whether or not input will
180
- # be echoed back to the user. A setting of +true+ will cause echo to
181
- # match input, but any other true value will be treated as to String to
182
- # echo for each character typed.
183
- #
184
- # This requires HighLine's character reader. See the _character_
185
- # attribute for details.
186
- #
187
- # *Note*: When using HighLine to manage echo on Unix based systems, we
188
- # recommend installing the termios gem. Without it, it's possible to type
189
- # fast enough to have letters still show up (when reading character by
190
- # character only).
191
- #
192
- attr_accessor :echo
193
- #
194
- # Use the Readline library to fetch input. This allows input editing as
195
- # well as keeping a history. In addition, tab will auto-complete
196
- # within an Array of choices or a file listing.
197
- #
198
- # *WARNING*: This option is incompatible with all of HighLine's
199
- # character reading modes and it causes HighLine to ignore the
200
- # specified _input_ stream.
201
- #
202
- # this messes up in ncurses RK 2010-10-24 12:23
203
- attr_accessor :readline
204
- #
205
- # Used to control whitespace processing for the answer to this question.
206
- # See HighLine::Question.remove_whitespace() for acceptable settings.
207
- #
208
- attr_accessor :whitespace
209
- #
210
- # Used to control character case processing for the answer to this question.
211
- # See HighLine::Question.change_case() for acceptable settings.
212
- #
213
- attr_accessor :_case
214
- # Used to provide a default answer to this question.
215
- attr_accessor :default
216
- #
217
- # If set to a Regexp, the answer must match (before type conversion).
218
- # Can also be set to a Proc which will be called with the provided
219
- # answer to validate with a +true+ or +false+ return.
220
- #
221
- attr_accessor :validate
222
- # Used to control range checks for answer.
223
- attr_accessor :above, :below
224
- # If set, answer must pass an include?() check on this object.
225
- attr_accessor :in
226
- #
227
- # Asks a yes or no confirmation question, to ensure a user knows what
228
- # they have just agreed to. If set to +true+ the question will be,
229
- # "Are you sure? " Any other true value for this attribute is assumed
230
- # to be the question to ask. When +false+ or +nil+ (the default),
231
- # answers are not confirmed.
232
- #
233
- attr_accessor :confirm
234
- #
235
- # When set, the user will be prompted for multiple answers which will
236
- # be collected into an Array or Hash and returned as the final answer.
237
- #
238
- # You can set _gather_ to an Integer to have an Array of exactly that
239
- # many answers collected, or a String/Regexp to match an end input which
240
- # will not be returned in the Array.
241
- #
242
- # Optionally _gather_ can be set to a Hash. In this case, the question
243
- # will be asked once for each key and the answers will be returned in a
244
- # Hash, mapped by key. The <tt>@key</tt> variable is set before each
245
- # question is evaluated, so you can use it in your question.
246
- #
247
- attr_accessor :gather
248
- #
249
- # When set to a non *nil* value, this will be tried as an answer to the
250
- # question. If this answer passes validations, it will become the result
251
- # without the user ever being prompted. Otherwise this value is discarded,
252
- # and this Question is resolved as a normal call to HighLine.ask().
253
- #
254
- attr_writer :first_answer
255
- #
256
- # The directory from which a user will be allowed to select files, when
257
- # File or Pathname is specified as an _answer_type_. Initially set to
258
- # <tt>Pathname.new(File.expand_path(File.dirname($0)))</tt>.
259
- #
260
- attr_accessor :directory
261
- #
262
- # The glob pattern used to limit file selection when File or Pathname is
263
- # specified as an _answer_type_. Initially set to <tt>"*"</tt>.
264
- #
265
- attr_accessor :glob
266
- #
267
- # A Hash that stores the various responses used by HighLine to notify
268
- # the user. The currently used responses and their purpose are as
269
- # follows:
270
- #
271
- # <tt>:ambiguous_completion</tt>:: Used to notify the user of an
272
- # ambiguous answer the auto-completion
273
- # system cannot resolve.
274
- # <tt>:ask_on_error</tt>:: This is the question that will be
275
- # redisplayed to the user in the event
276
- # of an error. Can be set to
277
- # <tt>:question</tt> to repeat the
278
- # original question.
279
- # <tt>:invalid_type</tt>:: The error message shown when a type
280
- # conversion fails.
281
- # <tt>:no_completion</tt>:: Used to notify the user that their
282
- # selection does not have a valid
283
- # auto-completion match.
284
- # <tt>:not_in_range</tt>:: Used to notify the user that a
285
- # provided answer did not satisfy
286
- # the range requirement tests.
287
- # <tt>:not_valid</tt>:: The error message shown when
288
- # validation checks fail.
289
- #
290
- attr_reader :responses
291
- #
292
- # When set to +true+ the question is asked, but output does not progress to
293
- # the next line. The Cursor is moved back to the beginning of the question
294
- # line and it is cleared so that all the contents of the line disappear from
295
- # the screen.
296
- #
297
- attr_accessor :overwrite
298
-
299
- #
300
- # If the user presses tab in ask(), then this proc is used to fill in
301
- # values. Typically, for files. e.g.
302
- #
303
- # q.completion_proc = Proc.new {|str| Dir.glob(str +"*") }
304
- #
305
- attr_accessor :completion_proc
306
-
307
- #
308
- # Called when any character is pressed with the string.
309
- #
310
- # q.change_proc = Proc.new {|str| Dir.glob(str +"*") }
311
- #
312
- attr_accessor :change_proc
313
- #
314
- # Called when any control-key is pressed, one that we are not handling
315
- #
316
- # q.key_handler_proc = Proc.new {|ch| xxxx) }
317
- #
318
- attr_accessor :key_handler_proc
319
-
320
- #
321
- # text to be shown if user presses M-h
322
- #
323
- attr_accessor :helptext
324
- attr_accessor :color_pair
325
- attr_accessor :history
326
-
327
- #
328
- # Returns the provided _answer_string_ or the default answer for this
329
- # Question if a default was set and the answer is empty.
330
- # NOTE: in our case, the user actually edits this value (in highline it
331
- # is used if user enters blank)
332
- #
333
- def answer_or_default( answer_string )
334
- if answer_string.length == 0 and not @default.nil?
335
- @default
336
- else
337
- answer_string
338
- end
339
- end
340
-
341
- #
342
- # Called late in the initialization process to build intelligent
343
- # responses based on the details of this Question object.
344
- #
345
- def build_responses( )
346
- ### WARNING: This code is quasi-duplicated in ###
347
- ### Menu.update_responses(). Check there too when ###
348
- ### making changes! ###
349
- append_default unless default.nil?
350
- @responses = { :ambiguous_completion =>
351
- "Ambiguous choice. " +
352
- "Please choose one of #{@answer_type.inspect}.",
353
- :ask_on_error =>
354
- "? ",
355
- :invalid_type =>
356
- "You must enter a valid #{@answer_type}.",
357
- :no_completion =>
358
- "You must choose one of " +
359
- "#{@answer_type.inspect}.",
360
- :not_in_range =>
361
- "Your answer isn't within the expected range " +
362
- "(#{expected_range}).",
363
- :not_valid =>
364
- "Your answer isn't valid (must match " +
365
- "#{@validate.inspect})." }.merge(@responses)
366
- ### WARNING: This code is quasi-duplicated in ###
367
- ### Menu.update_responses(). Check there too when ###
368
- ### making changes! ###
369
- end
370
-
371
- #
372
- # Returns the provided _answer_string_ after changing character case by
373
- # the rules of this Question. Valid settings for whitespace are:
374
- #
375
- # +nil+:: Do not alter character case.
376
- # (Default.)
377
- # <tt>:up</tt>:: Calls upcase().
378
- # <tt>:upcase</tt>:: Calls upcase().
379
- # <tt>:down</tt>:: Calls downcase().
380
- # <tt>:downcase</tt>:: Calls downcase().
381
- # <tt>:capitalize</tt>:: Calls capitalize().
382
- #
383
- # An unrecognized choice (like <tt>:none</tt>) is treated as +nil+.
384
- #
385
- def change_case( answer_string )
386
- if [:up, :upcase].include?(@_case)
387
- answer_string.upcase
388
- elsif [:down, :downcase].include?(@_case)
389
- answer_string.downcase
390
- elsif @_case == :capitalize
391
- answer_string.capitalize
392
- else
393
- answer_string
394
- end
395
- end
396
-
397
- #
398
- # Transforms the given _answer_string_ into the expected type for this
399
- # Question. Currently supported conversions are:
400
- #
401
- # <tt>[...]</tt>:: Answer must be a member of the passed Array.
402
- # Auto-completion is used to expand partial
403
- # answers.
404
- # <tt>lambda {...}</tt>:: Answer is passed to lambda for conversion.
405
- # Date:: Date.parse() is called with answer.
406
- # DateTime:: DateTime.parse() is called with answer.
407
- # File:: The entered file name is auto-completed in
408
- # terms of _directory_ + _glob_, opened, and
409
- # returned.
410
- # Float:: Answer is converted with Kernel.Float().
411
- # Integer:: Answer is converted with Kernel.Integer().
412
- # +nil+:: Answer is left in String format. (Default.)
413
- # Pathname:: Same as File, save that a Pathname object is
414
- # returned.
415
- # String:: Answer is converted with Kernel.String().
416
- # Regexp:: Answer is fed to Regexp.new().
417
- # Symbol:: The method to_sym() is called on answer and
418
- # the result returned.
419
- # <i>any other Class</i>:: The answer is passed on to
420
- # <tt>Class.parse()</tt>.
421
- #
422
- # This method throws ArgumentError, if the conversion cannot be
423
- # completed for any reason.
424
- #
425
- def convert( answer_string )
426
- if @answer_type.nil?
427
- answer_string
428
- elsif [Float, Integer, String].include?(@answer_type)
429
- Kernel.send(@answer_type.to_s.to_sym, answer_string)
430
- elsif @answer_type == Symbol
431
- answer_string.to_sym
432
- elsif @answer_type == Regexp
433
- Regexp.new(answer_string)
434
- elsif @answer_type.is_a?(Array) or [File, Pathname].include?(@answer_type)
435
- # cheating, using OptionParser's Completion module
436
- choices = selection
437
- #choices.extend(OptionParser::Completion)
438
- #answer = choices.complete(answer_string)
439
- answer = choices # bug in completion of optparse
440
- if answer.nil?
441
- raise NoAutoCompleteMatch
442
- end
443
- if @answer_type.is_a?(Array)
444
- #answer.last # we don't need this anylonger
445
- answer_string # we have already selected
446
- elsif @answer_type == File
447
- File.open(File.join(@directory.to_s, answer_string))
448
- else
449
- #Pathname.new(File.join(@directory.to_s, answer.last))
450
- Pathname.new(File.join(@directory.to_s, answer_string))
451
- end
452
- elsif [Date, DateTime].include?(@answer_type) or @answer_type.is_a?(Class)
453
- @answer_type.parse(answer_string)
454
- elsif @answer_type.is_a?(Proc)
455
- @answer_type[answer_string]
456
- end
457
- end
458
-
459
- # Returns a english explination of the current range settings.
460
- def expected_range( )
461
- expected = [ ]
462
-
463
- expected << "above #{@above}" unless @above.nil?
464
- expected << "below #{@below}" unless @below.nil?
465
- expected << "included in #{@in.inspect}" unless @in.nil?
466
-
467
- case expected.size
468
- when 0 then ""
469
- when 1 then expected.first
470
- when 2 then expected.join(" and ")
471
- else expected[0..-2].join(", ") + ", and #{expected.last}"
472
- end
473
- end
474
-
475
- # Returns _first_answer_, which will be unset following this call.
476
- def first_answer( )
477
- @first_answer
478
- ensure
479
- @first_answer = nil
480
- end
481
-
482
- # Returns true if _first_answer_ is set.
483
- def first_answer?( )
484
- not @first_answer.nil?
485
- end
486
-
487
- #
488
- # Returns +true+ if the _answer_object_ is greater than the _above_
489
- # attribute, less than the _below_ attribute and included?()ed in the
490
- # _in_ attribute. Otherwise, +false+ is returned. Any +nil+ attributes
491
- # are not checked.
492
- #
493
- def in_range?( answer_object )
494
- (@above.nil? or answer_object > @above) and
495
- (@below.nil? or answer_object < @below) and
496
- (@in.nil? or @in.include?(answer_object))
497
- end
498
-
499
- #
500
- # Returns the provided _answer_string_ after processing whitespace by
501
- # the rules of this Question. Valid settings for whitespace are:
502
- #
503
- # +nil+:: Do not alter whitespace.
504
- # <tt>:strip</tt>:: Calls strip(). (Default.)
505
- # <tt>:chomp</tt>:: Calls chomp().
506
- # <tt>:collapse</tt>:: Collapses all whitspace runs to a
507
- # single space.
508
- # <tt>:strip_and_collapse</tt>:: Calls strip(), then collapses all
509
- # whitspace runs to a single space.
510
- # <tt>:chomp_and_collapse</tt>:: Calls chomp(), then collapses all
511
- # whitspace runs to a single space.
512
- # <tt>:remove</tt>:: Removes all whitespace.
513
- #
514
- # An unrecognized choice (like <tt>:none</tt>) is treated as +nil+.
515
- #
516
- # This process is skipped, for single character input.
517
- #
518
- def remove_whitespace( answer_string )
519
- if @whitespace.nil?
520
- answer_string
521
- elsif [:strip, :chomp].include?(@whitespace)
522
- answer_string.send(@whitespace)
523
- elsif @whitespace == :collapse
524
- answer_string.gsub(/\s+/, " ")
525
- elsif [:strip_and_collapse, :chomp_and_collapse].include?(@whitespace)
526
- result = answer_string.send(@whitespace.to_s[/^[a-z]+/])
527
- result.gsub(/\s+/, " ")
528
- elsif @whitespace == :remove
529
- answer_string.gsub(/\s+/, "")
530
- else
531
- answer_string
532
- end
533
- end
534
-
535
- #
536
- # Returns an Array of valid answers to this question. These answers are
537
- # only known when _answer_type_ is set to an Array of choices, File, or
538
- # Pathname. Any other time, this method will return an empty Array.
539
- #
540
- def selection( )
541
- if @answer_type.is_a?(Array)
542
- @answer_type
543
- elsif [File, Pathname].include?(@answer_type)
544
- Dir[File.join(@directory.to_s, @glob)].map do |file|
545
- File.basename(file)
546
- end
547
- else
548
- [ ]
549
- end
550
- end
551
-
552
- # Stringifies the question to be asked.
553
- def to_str( )
554
- @question
555
- end
556
-
557
- #
558
- # Returns +true+ if the provided _answer_string_ is accepted by the
559
- # _validate_ attribute or +false+ if it's not.
560
- #
561
- # It's important to realize that an answer is validated after whitespace
562
- # and case handling.
563
- #
564
- def valid_answer?( answer_string )
565
- @validate.nil? or
566
- (@validate.is_a?(Regexp) and answer_string =~ @validate) or
567
- (@validate.is_a?(Proc) and @validate[answer_string])
568
- end
569
-
570
- private
571
-
572
- #
573
- # Adds the default choice to the end of question between <tt>|...|</tt>.
574
- # Trailing whitespace is preserved so the function of HighLine.say() is
575
- # not affected.
576
- #
577
- def append_default( )
578
- if @question =~ /([\t ]+)\Z/
579
- @question << "|#{@default}|#{$1}"
580
- elsif @question == ""
581
- @question << "|#{@default}| "
582
- elsif @question[-1, 1] == "\n"
583
- @question[-2, 0] = " |#{@default}|"
584
- else
585
- @question << " |#{@default}|"
586
- end
587
- end
588
- end # class
589
-
590
- # Menu objects encapsulate all the details of a call to HighLine.choose().
591
- # Using the accessors and Menu.choice() and Menu.choices(), the block passed
592
- # to HighLine.choose() can detail all aspects of menu display and control.
593
- #
594
- class Menu < Question
595
- #
596
- # Create an instance of HighLine::Menu. All customization is done
597
- # through the passed block, which should call accessors and choice() and
598
- # choices() as needed to define the Menu. Note that Menus are also
599
- # Questions, so all that functionality is available to the block as
600
- # well.
601
- #
602
- def initialize( )
603
- #
604
- # Initialize Question objects with ignored values, we'll
605
- # adjust ours as needed.
606
- #
607
- super("Ignored", [ ], &nil) # avoiding passing the block along
608
-
609
- @items = [ ]
610
- @hidden_items = [ ]
611
- @help = Hash.new("There's no help for that topic.")
612
-
613
- @index = :number
614
- @index_suffix = ". "
615
- @select_by = :index_or_name
616
- @flow = :rows
617
- @list_option = nil
618
- @header = nil
619
- @prompt = "? "
620
- @layout = :list
621
- @shell = false
622
- @nil_on_handled = false
623
-
624
- # Override Questions responses, we'll set our own.
625
- @responses = { }
626
- # Context for action code.
627
- @highline = nil
628
-
629
- yield self if block_given?
630
-
631
- init_help if @shell and not @help.empty?
632
- end
633
-
634
- #
635
- # An _index_ to append to each menu item in display. See
636
- # Menu.index=() for details.
637
- #
638
- attr_reader :index
639
- #
640
- # The String placed between an _index_ and a menu item. Defaults to
641
- # ". ". Switches to " ", when _index_ is set to a String (like "-").
642
- #
643
- attr_accessor :index_suffix
644
- #
645
- # The _select_by_ attribute controls how the user is allowed to pick a
646
- # menu item. The available choices are:
647
- #
648
- # <tt>:index</tt>:: The user is allowed to type the numerical
649
- # or alphetical index for their selection.
650
- # <tt>:index_or_name</tt>:: Allows both methods from the
651
- # <tt>:index</tt> option and the
652
- # <tt>:name</tt> option.
653
- # <tt>:name</tt>:: Menu items are selected by typing a portion
654
- # of the item name that will be
655
- # auto-completed.
656
- #
657
- attr_accessor :select_by
658
- #
659
- # This attribute is passed directly on as the mode to HighLine.list() by
660
- # all the preset layouts. See that method for appropriate settings.
661
- #
662
- attr_accessor :flow
663
- #
664
- # This setting is passed on as the third parameter to HighLine.list()
665
- # by all the preset layouts. See that method for details of its
666
- # effects. Defaults to +nil+.
667
- #
668
- attr_accessor :list_option
669
- #
670
- # Used by all the preset layouts to display title and/or introductory
671
- # information, when set. Defaults to +nil+.
672
- #
673
- attr_accessor :header
674
- #
675
- # Used by all the preset layouts to ask the actual question to fetch a
676
- # menu selection from the user. Defaults to "? ".
677
- #
678
- attr_accessor :prompt
679
- #
680
- # An ERb _layout_ to use when displaying this Menu object. See
681
- # Menu.layout=() for details.
682
- #
683
- attr_reader :layout
684
- #
685
- # When set to +true+, responses are allowed to be an entire line of
686
- # input, including details beyond the command itself. Only the first
687
- # "word" of input will be matched against the menu choices, but both the
688
- # command selected and the rest of the line will be passed to provided
689
- # action blocks. Defaults to +false+.
690
- #
691
- attr_accessor :shell
692
- #
693
- # When +true+, any selected item handled by provided action code, will
694
- # return +nil+, instead of the results to the action code. This may
695
- # prove handy when dealing with mixed menus where only the names of
696
- # items without any code (and +nil+, of course) will be returned.
697
- # Defaults to +false+.
698
- #
699
- attr_accessor :nil_on_handled
700
-
701
- #
702
- # Adds _name_ to the list of available menu items. Menu items will be
703
- # displayed in the order they are added.
704
- #
705
- # An optional _action_ can be associated with this name and if provided,
706
- # it will be called if the item is selected. The result of the method
707
- # will be returned, unless _nil_on_handled_ is set (when you would get
708
- # +nil+ instead). In _shell_ mode, a provided block will be passed the
709
- # command chosen and any details that followed the command. Otherwise,
710
- # just the command is passed. The <tt>@highline</tt> variable is set to
711
- # the current HighLine context before the action code is called and can
712
- # thus be used for adding output and the like.
713
- #
714
- def choice( name, help = nil, &action )
715
- @items << [name, action]
716
-
717
- @help[name.to_s.downcase] = help unless help.nil?
718
- update_responses # rebuild responses based on our settings
719
- end
720
-
721
- #
722
- # A shortcut for multiple calls to the sister method choice(). <b>Be
723
- # warned:</b> An _action_ set here will apply to *all* provided
724
- # _names_. This is considered to be a feature, so you can easily
725
- # hand-off interface processing to a different chunk of code.
726
- #
727
- def choices( *names, &action )
728
- names.each { |n| choice(n, &action) }
729
- end
730
-
731
- # Identical to choice(), but the item will not be listed for the user.
732
- def hidden( name, help = nil, &action )
733
- @hidden_items << [name, action]
734
-
735
- @help[name.to_s.downcase] = help unless help.nil?
736
- end
737
-
738
- #
739
- # Sets the indexing style for this Menu object. Indexes are appended to
740
- # menu items, when displayed in list form. The available settings are:
741
- #
742
- # <tt>:number</tt>:: Menu items will be indexed numerically, starting
743
- # with 1. This is the default method of indexing.
744
- # <tt>:letter</tt>:: Items will be indexed alphabetically, starting
745
- # with a.
746
- # <tt>:none</tt>:: No index will be appended to menu items.
747
- # <i>any String</i>:: Will be used as the literal _index_.
748
- #
749
- # Setting the _index_ to <tt>:none</tt> a literal String, also adjusts
750
- # _index_suffix_ to a single space and _select_by_ to <tt>:none</tt>.
751
- # Because of this, you should make a habit of setting the _index_ first.
752
- #
753
- def index=( style )
754
- @index = style
755
-
756
- # Default settings.
757
- if @index == :none or @index.is_a?(String)
758
- @index_suffix = " "
759
- @select_by = :name
760
- end
761
- end
762
-
763
- #
764
- # Initializes the help system by adding a <tt>:help</tt> choice, some
765
- # action code, and the default help listing.
766
- #
767
- def init_help( )
768
- return if @items.include?(:help)
769
-
770
- topics = @help.keys.sort
771
- help_help = @help.include?("help") ? @help["help"] :
772
- "This command will display helpful messages about " +
773
- "functionality, like this one. To see the help for " +
774
- "a specific topic enter:\n\thelp [TOPIC]\nTry asking " +
775
- "for help on any of the following:\n\n" +
776
- "<%= list(#{topics.inspect}, :columns_across) %>"
777
- choice(:help, help_help) do |command, topic|
778
- topic.strip!
779
- topic.downcase!
780
- if topic.empty?
781
- @highline.say(@help["help"])
782
- else
783
- @highline.say("= #{topic}\n\n#{@help[topic]}")
784
- end
785
- end
786
- end
787
-
788
- #
789
- # Used to set help for arbitrary topics. Use the topic <tt>"help"</tt>
790
- # to override the default message.
791
- #
792
- def help( topic, help )
793
- @help[topic] = help
794
- end
795
-
796
- #
797
- # Setting a _layout_ with this method also adjusts some other attributes
798
- # of the Menu object, to ideal defaults for the chosen _layout_. To
799
- # account for that, you probably want to set a _layout_ first in your
800
- # configuration block, if needed.
801
- #
802
- # Accepted settings for _layout_ are:
803
- #
804
- # <tt>:list</tt>:: The default _layout_. The _header_ if set
805
- # will appear at the top on its own line with
806
- # a trailing colon. Then the list of menu
807
- # items will follow. Finally, the _prompt_
808
- # will be used as the ask()-like question.
809
- # <tt>:one_line</tt>:: A shorter _layout_ that fits on one line.
810
- # The _header_ comes first followed by a
811
- # colon and spaces, then the _prompt_ with menu
812
- # items between trailing parenthesis.
813
- # <tt>:menu_only</tt>:: Just the menu items, followed up by a likely
814
- # short _prompt_.
815
- # <i>any ERb String</i>:: Will be taken as the literal _layout_. This
816
- # String can access <tt>@header</tt>,
817
- # <tt>@menu</tt> and <tt>@prompt</tt>, but is
818
- # otherwise evaluated in the typical HighLine
819
- # context, to provide access to utilities like
820
- # HighLine.list() primarily.
821
- #
822
- # If set to either <tt>:one_line</tt>, or <tt>:menu_only</tt>, _index_
823
- # will default to <tt>:none</tt> and _flow_ will default to
824
- # <tt>:inline</tt>.
825
- #
826
- def layout=( new_layout )
827
- @layout = new_layout
828
-
829
- # Default settings.
830
- case @layout
831
- when :one_line, :menu_only
832
- self.index = :none
833
- @flow = :inline
834
- end
835
- end
836
-
837
- #
838
- # This method returns all possible options for auto-completion, based
839
- # on the settings of _index_ and _select_by_.
840
- #
841
- def options( )
842
- # add in any hidden menu commands
843
- @items.concat(@hidden_items)
844
-
845
- by_index = if @index == :letter
846
- l_index = "`"
847
- @items.map { "#{l_index.succ!}" }
848
- else
849
- (1 .. @items.size).collect { |s| String(s) }
850
- end
851
- by_name = @items.collect { |c| c.first }
852
-
853
- case @select_by
854
- when :index then
855
- by_index
856
- when :name
857
- by_name
858
- else
859
- by_index + by_name
860
- end
861
- ensure
862
- # make sure the hidden items are removed, before we return
863
- @items.slice!(@items.size - @hidden_items.size, @hidden_items.size)
864
- end
865
-
866
- #
867
- # This method processes the auto-completed user selection, based on the
868
- # rules for this Menu object. If an action was provided for the
869
- # selection, it will be executed as described in Menu.choice().
870
- #
871
- def select( highline_context, selection, details = nil )
872
- # add in any hidden menu commands
873
- @items.concat(@hidden_items)
874
-
875
- # Find the selected action.
876
- name, action = if selection =~ /^\d+$/
877
- @items[selection.to_i - 1]
878
- else
879
- l_index = "`"
880
- index = @items.map { "#{l_index.succ!}" }.index(selection)
881
- $log.debug "iindex #{index}, #{@items} " if $log.debug?
882
- @items.find { |c| c.first == selection } or @items[index]
883
- end
884
-
885
- # Run or return it.
886
- if not @nil_on_handled and not action.nil?
887
- @highline = highline_context
888
- if @shell
889
- action.call(name, details)
890
- else
891
- action.call(name)
892
- end
893
- elsif action.nil?
894
- name
895
- else
896
- nil
897
- end
898
- ensure
899
- # make sure the hidden items are removed, before we return
900
- @items.slice!(@items.size - @hidden_items.size, @hidden_items.size)
901
- end
902
-
903
- #
904
- # Allows Menu objects to pass as Arrays, for use with HighLine.list().
905
- # This method returns all menu items to be displayed, complete with
906
- # indexes.
907
- #
908
- def to_ary( )
909
- case @index
910
- when :number
911
- @items.map { |c| "#{@items.index(c) + 1}#{@index_suffix}#{c.first}" }
912
- when :letter
913
- l_index = "`"
914
- @items.map { |c| "#{l_index.succ!}#{@index_suffix}#{c.first}" }
915
- when :none
916
- @items.map { |c| "#{c.first}" }
917
- else
918
- @items.map { |c| "#{index}#{@index_suffix}#{c.first}" }
919
- end
920
- end
921
-
922
- #
923
- # Allows Menu to behave as a String, just like Question. Returns the
924
- # _layout_ to be rendered, which is used by HighLine.say().
925
- #
926
- def to_str( )
927
- case @layout
928
- when :list
929
- '<%= if @header.nil? then '' else "#{@header}:\n" end %>' +
930
- "<%= list( @menu, #{@flow.inspect},
931
- #{@list_option.inspect} ) %>" +
932
- "<%= @prompt %>"
933
- when :one_line
934
- '<%= if @header.nil? then '' else "#{@header}: " end %>' +
935
- "<%= @prompt %>" +
936
- "(<%= list( @menu, #{@flow.inspect},
937
- #{@list_option.inspect} ) %>)" +
938
- "<%= @prompt[/\s*$/] %>"
939
- when :menu_only
940
- "<%= list( @menu, #{@flow.inspect},
941
- #{@list_option.inspect} ) %><%= @prompt %>"
942
- else
943
- @layout
944
- end
945
- end
946
-
947
- #
948
- # This method will update the intelligent responses to account for
949
- # Menu specific differences. This overrides the work done by
950
- # Question.build_responses().
951
- #
952
- def update_responses( )
953
- append_default unless default.nil?
954
- @responses = @responses.merge(
955
- :ambiguous_completion =>
956
- "Ambiguous choice. " +
957
- "Please choose one of #{options.inspect}.",
958
- :ask_on_error =>
959
- "? ",
960
- :invalid_type =>
961
- "You must enter a valid #{options}.",
962
- :no_completion =>
963
- "You must choose one of " +
964
- "#{options.inspect}.",
965
- :not_in_range =>
966
- "Your answer isn't within the expected range " +
967
- "(#{expected_range}).",
968
- :not_valid =>
969
- "Your answer isn't valid (must match " +
970
- "#{@validate.inspect})."
971
- )
972
- end
973
- end
974
- def ask(question, answer_type=String, &details)
975
- $log.debug "XXXX inside ask win #{@window} "
976
- @window ||= _create_footer_window
977
- #@window.show #unless @window.visible?
978
-
979
- @question ||= Question.new(question, answer_type, &details)
980
- say(@question) #unless @question.echo == true
981
-
982
- @completion_proc = @question.completion_proc
983
- @change_proc = @question.change_proc
984
- @key_handler_proc = @question.key_handler_proc
985
- @default = @question.default
986
- $log.debug "XXX: ASK RBGETS got default: #{@default} "
987
- @helptext = @question.helptext
988
- @answer_type = @question.answer_type
989
- if @question.answer_type.is_a? Array
990
- @completion_proc = Proc.new{|str| @answer_type.dup.grep Regexp.new("^#{str}") }
991
- end
992
-
993
- begin
994
- # FIXME a C-c still returns default to user !
995
- @answer = @question.answer_or_default(get_response)
996
- unless @question.valid_answer?(@answer)
997
- explain_error(:not_valid)
998
- raise QuestionError
999
- end
1000
-
1001
- @answer = @question.convert(@answer)
1002
-
1003
- if @question.in_range?(@answer)
1004
- if @question.confirm
1005
- # need to add a layer of scope to ask a question inside a
1006
- # question, without destroying instance data
1007
- context_change = self.class.new(@input, @output, @wrap_at, @page_at)
1008
- if @question.confirm == true
1009
- confirm_question = "Are you sure? "
1010
- else
1011
- # evaluate ERb under initial scope, so it will have
1012
- # access to @question and @answer
1013
- template = ERB.new(@question.confirm, nil, "%")
1014
- confirm_question = template.result(binding)
1015
- end
1016
- unless context_change.agree(confirm_question)
1017
- explain_error(nil)
1018
- raise QuestionError
1019
- end
1020
- end
1021
-
1022
- @answer
1023
- else
1024
- explain_error(:not_in_range)
1025
- raise QuestionError
1026
- end
1027
- rescue QuestionError
1028
- retry
1029
- rescue ArgumentError, NameError => error
1030
- #raise
1031
- raise if error.is_a?(NoMethodError)
1032
- if error.message =~ /ambiguous/
1033
- # the assumption here is that OptionParser::Completion#complete
1034
- # (used for ambiguity resolution) throws exceptions containing
1035
- # the word 'ambiguous' whenever resolution fails
1036
- explain_error(:ambiguous_completion)
1037
- else
1038
- explain_error(:invalid_type)
1039
- end
1040
- retry
1041
- rescue Question::NoAutoCompleteMatch
1042
- explain_error(:no_completion)
1043
- retry
1044
- rescue Interrupt
1045
- $log.warn "User interrupted ask() get_response does not want operation to proceed"
1046
- return nil
1047
- ensure
1048
- @question = nil # Reset Question object.
1049
- $log.debug "XXX: HIDE B AT ENSURE OF ASK"
1050
- hide_bottomline # assuming this method made it visible, not sure if this is called.
1051
- end
1052
- end
1053
- #
1054
- # bottomline user has to hide window if he called say().
1055
- # Call this if you find the window persists after using some method from here
1056
- # usually say or ask.
1057
- #
1058
- # NOTE: after callign this you must call window.show. Otherwise, next time
1059
- # you call this, it will not hide.
1060
- #
1061
- # @param [int, float] time to sleep before hiding window.
1062
- #
1063
- def hide wait=nil
1064
- if @window
1065
- $log.debug "XXX: HIDE BOTTOMLINE INSIDE"
1066
- sleep(wait) if wait
1067
- #if @window.visible?
1068
- #@window.hide # THIS HAS SUDDENLY STOPPED WORKING
1069
- @window.destroy
1070
- @window = nil
1071
- #@window.wrefresh
1072
- #Ncurses::Panel.update_panels
1073
- #end
1074
- end
1075
- end
1076
- alias :hide_bottomline :hide
1077
- #
1078
- # destroy window, to be called by app when shutting down
1079
- # since we are normally hiding the window only.
1080
- def destroy
1081
- $log.debug "bottomline destroy... #{@window} "
1082
- @window.destroy if @window
1083
- @window = nil
1084
- end
1085
-
1086
- #
1087
- # The basic output method for HighLine objects.
1088
- #
1089
- # The _statement_ parameter is processed as an ERb template, supporting
1090
- # embedded Ruby code. The template is evaluated with a binding inside
1091
- # the HighLine instance.
1092
- # NOTE: modified from original highline, does not care about space at end of
1093
- # question. Also, ansi color constants will not work. Be careful what ruby code
1094
- # you pass in.
1095
- #
1096
- # NOTE: This uses a window, so it will persist in the last row. You must call
1097
- # hide_bottomline to remove the window. It is preferable to call say_with_pause
1098
- # from user programs
1099
- #
1100
- def say statement, config={}
1101
- @window ||= _create_footer_window
1102
- #@window.show #unless @window.visible?
1103
- $log.debug "XXX: inside say win #{@window} !"
1104
- case statement
1105
- when Question
1106
-
1107
- if config.has_key? :color_pair
1108
- $log.debug "INSIDE QUESTION 2 " if $log.debug?
1109
- else
1110
- $log.debug "XXXX SAY using colorpair: #{statement.color_pair} " if $log.debug?
1111
- config[:color_pair] = statement.color_pair
1112
- end
1113
- else
1114
- $log.debug "XXX INSDIE SAY #{statement.class} " if $log.debug?
1115
- end
1116
- statement = statement.to_str
1117
- template = ERB.new(statement, nil, "%")
1118
- statement = template.result(binding)
1119
-
1120
- @prompt_length = statement.length # required by ask since it prints after
1121
- @statement = statement #
1122
- clear_line
1123
- print_str statement, config
1124
- end
1125
- #
1126
- # display some text at bottom and wait for a key before hiding window
1127
- #
1128
- def say_with_pause statement, config={}
1129
- @window ||= _create_footer_window
1130
- #@window.show #unless @window.visible? # 2011-10-14 23:52:52
1131
- say statement, config
1132
- @window.wrefresh
1133
- Ncurses::Panel.update_panels
1134
- ch=@window.getchar()
1135
- hide_bottomline
1136
- end
1137
- # since say does not leave the screen, it is not exactly recommended
1138
- # as it will hide what's below. It's better to call pause, or this, which
1139
- # will quickly go off. If the message is not important enough to ask for a pause,
1140
- # the will flicker on screen, but not for too long.
1141
- def say_with_wait statement, config={}
1142
- @window ||= _create_footer_window
1143
- #@window.show #unless @window.visible? # 2011-10-14 23:52:59
1144
- say statement, config
1145
- @window.wrefresh
1146
- Ncurses::Panel.update_panels
1147
- sleep 0.5
1148
- hide_bottomline
1149
- end
1150
- # A helper method for sending the output stream and error and repeat
1151
- # of the question.
1152
- #
1153
- # FIXME: since we write on one line in say, this often gets overidden
1154
- # by next say or ask
1155
- def explain_error( error )
1156
- say_with_pause(@question.responses[error]) unless error.nil?
1157
- if @question.responses[:ask_on_error] == :question
1158
- say(@question)
1159
- elsif @question.responses[:ask_on_error]
1160
- say(@question.responses[:ask_on_error])
1161
- end
1162
- end
1163
-
1164
- #
1165
- # Internal method for printing a string
1166
- #
1167
- def print_str(text, config={})
1168
- win = config.fetch(:window, @window) # assuming its in App
1169
- x = config.fetch :x, 0 # @message_row # Ncurses.LINES-1, 0 since one line window 2011-10-8
1170
- y = config.fetch :y, 0
1171
- $log.debug "XXX: print_str #{win} with text : #{text} at #{x} #{y} "
1172
- color = config[:color_pair] || $datacolor
1173
- raise "no window for ask print in #{self.class} name: #{name} " unless win
1174
- color=Ncurses.COLOR_PAIR(color);
1175
- win.attron(color);
1176
- #win.mvprintw(x, y, "%-40s" % text);
1177
- win.mvprintw(x, y, "%s" % text);
1178
- win.attroff(color);
1179
- win.refresh # FFI NW 2011-09-9 , added back gets overwritten
1180
- end
1181
-
1182
- # actual input routine, gets each character from user, taking care of echo, limit,
1183
- # completion proc, and some control characters such as C-a, C-e, C-k
1184
- # Taken from io.rb, has some improvements to it. However, does not print the prompt
1185
- # any longer
1186
- # Completion proc is vim style, on pressing tab it cycles through options
1187
- def rbgetstr
1188
- r = @message_row
1189
- c = 0
1190
- win = @window
1191
- @limit = @question.limit
1192
- @history = @question.history
1193
- @history_list = History.new(@history)
1194
- maxlen = @limit || 100 # fixme
1195
-
1196
-
1197
- raise "rbgetstr got no window. bottomline.rb" if win.nil?
1198
- ins_mode = false
1199
- oldstr = nil # for tab completion, origal word entered by user
1200
- default = @default || ""
1201
- $log.debug "XXX: RBGETS got default: #{@default} "
1202
- if @default && @history
1203
- if !@history.include?(default)
1204
- @history_list.push default
1205
- end
1206
- end
1207
-
1208
- len = @prompt_length
1209
-
1210
- # clear the area of len+maxlen
1211
- color = $datacolor
1212
- str = ""
1213
- #str = default
1214
- cpentries = nil
1215
- #clear_line len+maxlen+1
1216
- #print_str(prompt+str)
1217
- print_str(str, :y => @prompt_length+0) if @default
1218
- len = @prompt_length + str.length
1219
- begin
1220
- Ncurses.noecho();
1221
- curpos = str.length
1222
- prevchar = 0
1223
- entries = nil
1224
- while true
1225
- ch=win.getchar()
1226
- $log.debug " XXXX FFI rbgetstr got ch:#{ch}, str:#{str}. "
1227
- case ch
1228
- when 3 # -1 # C-c # sometimes this causes an interrupt and crash
1229
- return -1, nil
1230
- when ?\C-g.getbyte(0) # ABORT, emacs style
1231
- return -1, nil
1232
- when 10, 13 # hits ENTER, complete entry and return
1233
- @history_list.push str
1234
- break
1235
- when ?\C-h.getbyte(0), ?\C-?.getbyte(0), KEY_BSPACE, 263 # delete previous character/backspace
1236
- # C-h is giving 263 i/o 8. 2011-09-19
1237
- len -= 1 if len > @prompt_length
1238
- curpos -= 1 if curpos > 0
1239
- str.slice!(curpos)
1240
- clear_line len+maxlen+1, @prompt_length
1241
- when 330 # delete character on cursor
1242
- str.slice!(curpos) #rescue next
1243
- clear_line len+maxlen+1, @prompt_length
1244
- when ?\M-h.getbyte(0) # HELP KEY
1245
- helptext = @helptext || "No help provided"
1246
- print_help(helptext)
1247
- clear_line len+maxlen+1
1248
- print_str @statement # UGH
1249
- #return 7, nil
1250
- #next
1251
- when KEY_LEFT
1252
- curpos -= 1 if curpos > 0
1253
- len -= 1 if len > @prompt_length
1254
- win.move r, c+len # since getchar is not going back on del and bs wmove to move FFIWINDOW
1255
- win.wrefresh
1256
- next
1257
- when KEY_RIGHT
1258
- if curpos < str.length
1259
- curpos += 1 #if curpos < str.length
1260
- len += 1
1261
- win.move r, c+len # since getchar is not going back on del and bs
1262
- win.wrefresh
1263
- end
1264
- next
1265
- when ?\C-a.getbyte(0)
1266
- #olen = str.length
1267
- clear_line len+maxlen+1, @prompt_length
1268
- len -= curpos
1269
- curpos = 0
1270
- win.move r, c+len # since getchar is not going back on del and bs
1271
- when ?\C-e.getbyte(0)
1272
- olen = str.length
1273
- len += (olen - curpos)
1274
- curpos = olen
1275
- clear_line len+maxlen+1, @prompt_length
1276
- win.move r, c+len # since getchar is not going back on del and bs
1277
-
1278
- when ?\M-i.getbyte(0)
1279
- ins_mode = !ins_mode
1280
- next
1281
- when ?\C-k.getbyte(0) # delete forward
1282
- @delete_buffer = str.slice!(curpos..-1) #rescue next
1283
- clear_line len+maxlen+1, @prompt_length
1284
- when ?\C-u.getbyte(0) # delete to the left of cursor till start of line
1285
- @delete_buffer = str.slice!(0..curpos-1) #rescue next
1286
- curpos = 0
1287
- clear_line len+maxlen+1, @prompt_length
1288
- len = @prompt_length
1289
- when ?\C-y.getbyte(0) # paste what's in delete buffer
1290
- if @delete_buffer
1291
- olen = str.length
1292
- str << @delete_buffer if @delete_buffer
1293
- curpos = str.length
1294
- len += str.length - olen
1295
- end
1296
- when KEY_TAB # TAB
1297
- if !@completion_proc.nil?
1298
- # place cursor at end of completion
1299
- # after all completions, what user entered should come back so he can edit it
1300
- if prevchar == 9
1301
- if !entries.nil? and !entries.empty?
1302
- olen = str.length
1303
- str = entries.delete_at(0)
1304
- str = str.to_s.dup
1305
- #str = entries[@current_index].dup
1306
- #@current_index += 1
1307
- #@current_index = 0 if @current_index == entries.length
1308
- curpos = str.length
1309
- len += str.length - olen
1310
- clear_line len+maxlen+1, @prompt_length
1311
- else
1312
- olen = str.length
1313
- str = oldstr if oldstr
1314
- curpos = str.length
1315
- len += str.length - olen
1316
- clear_line len+maxlen+1, @prompt_length
1317
- prevchar = ch = nil # so it can start again completing
1318
- end
1319
- else
1320
- #@current_index = 0
1321
- tabc = @completion_proc unless tabc
1322
- next unless tabc
1323
- oldstr = str.dup
1324
- olen = str.length
1325
- entries = tabc.call(str).dup
1326
- $log.debug "XXX tab [#{str}] got #{entries} "
1327
- str = entries.delete_at(0) unless entries.nil? or entries.empty?
1328
- #str = entries[@current_index].dup unless entries.nil? or entries.empty?
1329
- #@current_index += 1
1330
- #@current_index = 0 if @current_index == entries.length
1331
- str = str.to_s.dup
1332
- if str
1333
- curpos = str.length
1334
- len += str.length - olen
1335
- else
1336
- alert "NO MORE 2"
1337
- end
1338
- end
1339
- else
1340
- # there's another type of completion that bash does, which is irritating
1341
- # compared to what vim does, it does partial completion
1342
- if cpentries
1343
- olen = str.length
1344
- if cpentries.size == 1
1345
- str = cpentries.first.dup
1346
- elsif cpentries.size > 1
1347
- str = shortest_match(cpentries).dup
1348
- end
1349
- curpos = str.length
1350
- len += str.length - olen
1351
- end
1352
- end
1353
- when ?\C-a.getbyte(0) .. ?\C-z.getbyte(0)
1354
- # here' swhere i wish i could pass stuff back without closing
1355
- # I'd like the user to be able to scroll list or do something based on
1356
- # control or other keys
1357
- if @key_handler_proc # added 2011-11-3 7:38 PM
1358
- @key_handler_proc.call(ch)
1359
- next
1360
- else
1361
- Ncurses.beep
1362
- end
1363
- when KEY_UP
1364
- if @history && !@history.empty?
1365
- olen = str.length
1366
- str = if prevchar == KEY_UP
1367
- @history_list.previous
1368
- elsif prevchar == KEY_DOWN
1369
- @history_list.previous
1370
- else
1371
- @history_list.last
1372
- end
1373
- str = str.dup
1374
- curpos = str.length
1375
- len += str.length - olen
1376
- clear_line len+maxlen+1, @prompt_length
1377
- else # try to pick up default, seems we don't get it 2011-10-14
1378
- if @default
1379
- olen = str.length
1380
- str = @default
1381
- str = str.dup
1382
- curpos = str.length
1383
- len += str.length - olen
1384
- clear_line len+maxlen+1, @prompt_length
1385
- end
1386
- end
1387
- when KEY_DOWN
1388
- if @history && !@history.empty?
1389
- olen = str.length
1390
- str = if prevchar == KEY_UP
1391
- @history_list.next
1392
- elsif prevchar == KEY_DOWN
1393
- @history_list.next
1394
- else
1395
- @history_list.first
1396
- end
1397
- str = str.dup
1398
- curpos = str.length
1399
- len += str.length - olen
1400
- clear_line len+maxlen+1, @prompt_length
1401
- end
1402
-
1403
- else
1404
- if ch < 0 || ch > 255
1405
- Ncurses.beep
1406
- next
1407
- end
1408
- # if control char, beep
1409
- if ch.chr =~ /[[:cntrl:]]/
1410
- Ncurses.beep
1411
- next
1412
- end
1413
- # we need to trap KEY_LEFT and RIGHT and what of UP for history ?
1414
- if ins_mode
1415
- str[curpos] = ch.chr
1416
- else
1417
- str.insert(curpos, ch.chr) # FIXME index out of range due to changeproc
1418
- end
1419
- len += 1
1420
- curpos += 1
1421
- break if str.length >= maxlen
1422
- end
1423
- case @question.echo
1424
- when true
1425
- begin
1426
- cpentries = @change_proc.call(str) if @change_proc # added 2010-11-09 23:28
1427
- rescue => exc
1428
- $log.error "bottomline: change_proc EXC #{exc} " if $log.debug?
1429
- $log.error( exc) if exc
1430
- $log.error(exc.backtrace.join("\n")) if exc
1431
- Ncurses.error
1432
- end
1433
- print_str(str, :y => @prompt_length+0)
1434
- when false
1435
- # noop, no echoing what is typed
1436
- else
1437
- print_str(@question.echo * str.length, :y => @prompt_length+0)
1438
- end
1439
- win.move r, c+len # more for arrow keys, curpos may not be end
1440
- win.wrefresh # 2011-10-10
1441
- prevchar = ch
1442
- end
1443
- $log.debug "XXXW bottomline: after while loop"
1444
-
1445
- str = default if str == ""
1446
- ensure
1447
- Ncurses.noecho();
1448
- end
1449
- return 0, str
1450
- end
1451
-
1452
- # compares entries in array and returns longest common starting string
1453
- # as happens in bash when pressing tab
1454
- # abc abd abe will return ab
1455
- def shortest_match a
1456
- #return "" if a.nil? || a.empty? # should not be called in such situations
1457
- raise "shortest_match should not be called with nil or empty array" if a.nil? || a.empty? # should not be called in such situations as caller program will err.
1458
-
1459
- l = a.inject do |memo,word|
1460
- str = ""
1461
- 0.upto(memo.size) do |i|
1462
- if memo[0..i] == word[0..i]
1463
- str = memo[0..i]
1464
- else
1465
- break
1466
- end
1467
- end
1468
- str
1469
- end
1470
- end
1471
- # clears line from 0, not okay in some cases
1472
- def clear_line len=100, from=0
1473
- print_str("%-*s" % [len," "], :y => from)
1474
- end
1475
-
1476
- def print_help(helptext)
1477
- # best to popup a window and hsow that with ENTER to dispell
1478
- print_str("%-*s" % [helptext.length+2," "])
1479
- print_str("%s" % helptext)
1480
- sleep(5)
1481
- end
1482
- def get_response
1483
- return @question.first_answer if @question.first_answer?
1484
- # we always use character reader, so user's value does not matter
1485
-
1486
- #if @question.character.nil?
1487
- # if @question.echo == true #and @question.limit.nil?
1488
- $log.debug "XXX: before RBGETS got default: #{@default} "
1489
- ret, str = rbgetstr
1490
- if ret == 0
1491
- return @question.change_case(@question.remove_whitespace(str))
1492
- end
1493
- if ret == -1
1494
- raise Interrupt
1495
- end
1496
- return ""
1497
- end
1498
- def agree( yes_or_no_question, character = nil )
1499
- ask(yes_or_no_question, lambda { |yn| yn.downcase[0] == ?y}) do |q|
1500
- q.validate = /\Ay(?:es)?|no?\Z/i
1501
- q.responses[:not_valid] = 'Please enter "yes" or "no".'
1502
- q.responses[:ask_on_error] = :question
1503
- q.character = character
1504
- q.limit = 1 if character
1505
-
1506
- yield q if block_given?
1507
- end
1508
- end
1509
-
1510
- # presents given list in numbered format in a window above last line
1511
- # and accepts input on last line
1512
- # The list is a list of strings. e.g.
1513
- # %w{ ruby perl python haskell }
1514
- # Multiple levels can be given as:
1515
- # list = %w{ ruby perl python haskell }
1516
- # list[0] = %w{ ruby ruby1.9 ruby 1.8 rubinius jruby }
1517
- # In this case, "ruby" is the first level option. The others are used
1518
- # in the second level. This might make it clearer. first3 has 2 choices under it.
1519
- # [ "first1" , "first2", ["first3", "second1", "second2"], "first4"]
1520
- #
1521
- # Currently, we return an array containing each selected level
1522
- #
1523
- # @return [Array] selected option/s from list
1524
- def numbered_menu list1, config={}
1525
- if list1.nil? || list1.empty?
1526
- say_with_pause "empty list passed to numbered_menu"
1527
- return nil
1528
- end
1529
- prompt = config[:prompt] || "Select one: "
1530
- require 'rbcurse/rcommandwindow'
1531
- layout = { :height => 5, :width => Ncurses.COLS-1, :top => Ncurses.LINES-6, :left => 0 }
1532
- rc = CommandWindow.new nil, :layout => layout, :box => true, :title => config[:title]
1533
- w = rc.window
1534
- # should we yield rc, so user can bind keys or whatever
1535
- # attempt a loop so we do levels.
1536
- retval = []
1537
- begin
1538
- while true
1539
- rc.display_menu list1, :indexing => :number
1540
- ret = ask(prompt, Integer ) { |q| q.in = 1..list1.size }
1541
- val = list1[ret-1]
1542
- if val.is_a? Array
1543
- retval << val[0]
1544
- $log.debug "NL: #{retval} "
1545
- list1 = val[1..-1]
1546
- rc.clear
1547
- else
1548
- retval << val
1549
- $log.debug "NL1: #{retval} "
1550
- break
1551
- end
1552
- end
1553
- ensure
1554
- rc.destroy
1555
- rc = nil
1556
- end
1557
- #list1[ret-1]
1558
- $log.debug "NL2: #{retval} , #{retval.class} "
1559
- retval
1560
- end
1561
- # Allows a selection in which options are shown over prompt. As user types
1562
- # options are narrowed down.
1563
- # NOTE: For a directory we are not showing a slash, so currently you
1564
- # have to enter the slash manually when searching.
1565
- # FIXME we can put remarks in fron as in memacs such as [No matches] or [single completion]
1566
- # @param [Array] a list of items to select from
1567
- # NOTE: if you use this please copy it to your app. This does not conform to highline's
1568
- # choose, and I'd like to somehow get it to be identical.
1569
- #
1570
- def choose list1, config={}
1571
- dirlist = true
1572
- start = 0
1573
- case list1
1574
- when NilClass
1575
- #list1 = Dir.glob("*")
1576
- list1 = Dir.glob("*").collect { |f| File.directory?(f) ? f+"/" : f }
1577
- when String
1578
- list1 = Dir.glob(list1).collect { |f| File.directory?(f) ? f+"/" : f }
1579
- when Array
1580
- dirlist = false
1581
- # let it be, that's how it should come
1582
- else
1583
- # Dir listing as default
1584
- #list1 = Dir.glob("*")
1585
- list1 = Dir.glob("*").collect { |f| File.directory?(f) ? f+"/" : f }
1586
- end
1587
- require 'rbcurse/rcommandwindow'
1588
- prompt = config[:prompt] || "Choose: "
1589
- layout = { :height => 5, :width => Ncurses.COLS-1, :top => Ncurses.LINES-6, :left => 0 }
1590
- rc = CommandWindow.new nil, :layout => layout, :box => true, :title => config[:title]
1591
- begin
1592
- w = rc.window
1593
- rc.display_menu list1
1594
- # earlier wmove bombed, now move is (window.rb 121)
1595
- str = ask(prompt) { |q| q.change_proc = Proc.new { |str| w.wmove(1,1) ; w.wclrtobot;
1596
-
1597
- l = list1.select{|e| e.index(str)==0} ; # select those starting with str
1598
-
1599
- if (l.size == 0 || str[-1]=='/') && dirlist
1600
- # used to help complete directories so we can drill up and down
1601
- #l = Dir.glob(str+"*")
1602
- l = Dir.glob(str +"*").collect { |f| File.directory?(f) ? f+"/" : f }
1603
- end
1604
- rc.display_menu l;
1605
- l
1606
- }
1607
- q.key_handler_proc = Proc.new { |ch|
1608
- # this is not very good since it does not respect above list which is filtered
1609
- # # need to clear the screen before printing - FIXME
1610
- case ch
1611
- when ?\C-n.getbyte(0)
1612
- start += 2 if start < list1.length - 2
1613
-
1614
- w.wmove(1,1) ; w.wclrtobot;
1615
- rc.display_menu list1, :startindex => start
1616
- when ?\C-p.getbyte(0)
1617
- start -= 2 if start > 2
1618
- w.wmove(1,1) ; w.wclrtobot;
1619
- rc.display_menu list1, :startindex => start
1620
- else
1621
- alert "unhalderlind by jey "
1622
- end
1623
-
1624
- }
1625
- }
1626
- # need some validation here that its in the list TODO
1627
- ensure
1628
- rc.destroy
1629
- rc = nil
1630
- $log.debug "XXX: HIDE B IN ENSURE"
1631
- hide_bottomline # since we called ask() we need to close bottomline
1632
- end
1633
- $log.debug "XXX: HIDE B AT END OF ASK"
1634
- #hide_bottomline # since we called ask() we need to close bottomline
1635
- return str
1636
- end
1637
- def display_text_interactive text, config={}
1638
- require 'rbcurse/rcommandwindow'
1639
- ht = config[:height] || 15
1640
- layout = { :height => ht, :width => Ncurses.COLS-1, :top => Ncurses.LINES-ht+1, :left => 0 }
1641
- rc = CommandWindow.new nil, :layout => layout, :box => true, :title => config[:title]
1642
- w = rc.window
1643
- #rc.text "There was a quick brown fox who ran over the lazy dog and then went over the moon over and over again and again"
1644
- rc.display_interactive(text) { |l|
1645
- l.focussed_attrib = 'bold' # Ncurses::A_UNDERLINE
1646
- l.focussed_symbol = '>'
1647
- }
1648
- rc = nil
1649
- end
1650
- #def display_list_interactive text, config={}
1651
- # returns a ListObject since you may not know what the list itself contained
1652
- # You can do ret.list[ret.current_index] to get value
1653
- def display_list text, config={}
1654
- require 'rbcurse/rcommandwindow'
1655
- ht = config[:height] || 15
1656
- layout = { :height => ht, :width => Ncurses.COLS-1, :top => Ncurses.LINES-ht+1, :left => 0 }
1657
- rc = CommandWindow.new nil, :layout => layout, :box => true, :title => config[:title]
1658
- w = rc.window
1659
- ret = rc.display_interactive text
1660
- rc = nil
1661
- ret
1662
- end
1663
- #
1664
- # This method is HighLine's menu handler. For simple usage, you can just
1665
- # pass all the menu items you wish to display. At that point, choose() will
1666
- # build and display a menu, walk the user through selection, and return
1667
- # their choice amoung the provided items. You might use this in a case
1668
- # statement for quick and dirty menus.
1669
- #
1670
- # However, choose() is capable of much more. If provided, a block will be
1671
- # passed a HighLine::Menu object to configure. Using this method, you can
1672
- # customize all the details of menu handling from index display, to building
1673
- # a complete shell-like menuing system. See HighLine::Menu for all the
1674
- # methods it responds to.
1675
- #
1676
- # Raises EOFError if input is exhausted.
1677
- #
1678
- def XXXchoose( *items, &details )
1679
- @menu = @question = Menu.new(&details)
1680
- @menu.choices(*items) unless items.empty?
1681
-
1682
- # Set _answer_type_ so we can double as the Question for ask().
1683
- @menu.answer_type = if @menu.shell
1684
- lambda do |command| # shell-style selection
1685
- first_word = command.to_s.split.first || ""
1686
-
1687
- options = @menu.options
1688
- options.extend(OptionParser::Completion)
1689
- answer = options.complete(first_word)
1690
-
1691
- if answer.nil?
1692
- raise Question::NoAutoCompleteMatch
1693
- end
1694
-
1695
- [answer.last, command.sub(/^\s*#{first_word}\s*/, "")]
1696
- end
1697
- else
1698
- @menu.options # normal menu selection, by index or name
1699
- end
1700
-
1701
- # Provide hooks for ERb layouts.
1702
- @header = @menu.header
1703
- @prompt = @menu.prompt
1704
-
1705
- if @menu.shell
1706
- selected = ask("Ignored", @menu.answer_type)
1707
- @menu.select(self, *selected)
1708
- else
1709
- selected = ask("Ignored", @menu.answer_type)
1710
- @menu.select(self, selected)
1711
- end
1712
- end
1713
-
1714
- # Each member of the _items_ Array is passed through ERb and thus can contain
1715
- # their own expansions. Color escape expansions do not contribute to the
1716
- # final field width.
1717
- #
1718
- def list( items, mode = :rows, option = nil )
1719
- items = items.to_ary.map do |item|
1720
- ERB.new(item, nil, "%").result(binding)
1721
- end
1722
-
1723
- case mode
1724
- when :inline
1725
- option = " or " if option.nil?
1726
-
1727
- case items.size
1728
- when 0
1729
- ""
1730
- when 1
1731
- items.first
1732
- when 2
1733
- "#{items.first}#{option}#{items.last}"
1734
- else
1735
- items[0..-2].join(", ") + "#{option}#{items.last}"
1736
- end
1737
- when :columns_across, :columns_down
1738
- max_length = actual_length(
1739
- items.max { |a, b| actual_length(a) <=> actual_length(b) }
1740
- )
1741
-
1742
- if option.nil?
1743
- limit = @wrap_at || 80
1744
- option = (limit + 2) / (max_length + 2)
1745
- end
1746
-
1747
- items = items.map do |item|
1748
- pad = max_length + (item.length - actual_length(item))
1749
- "%-#{pad}s" % item
1750
- end
1751
- row_count = (items.size / option.to_f).ceil
1752
-
1753
- if mode == :columns_across
1754
- rows = Array.new(row_count) { Array.new }
1755
- items.each_with_index do |item, index|
1756
- rows[index / option] << item
1757
- end
1758
-
1759
- rows.map { |row| row.join(" ") + "\n" }.join
1760
- else
1761
- columns = Array.new(option) { Array.new }
1762
- items.each_with_index do |item, index|
1763
- columns[index / row_count] << item
1764
- end
1765
-
1766
- list = ""
1767
- columns.first.size.times do |index|
1768
- list << columns.map { |column| column[index] }.
1769
- compact.join(" ") + "\n"
1770
- end
1771
- list
1772
- end
1773
- else
1774
- items.map { |i| "#{i}\n" }.join
1775
- end
1776
- end
1777
- end # module
1778
- end # module
1779
- if __FILE__ == $PROGRAM_NAME
1780
-
1781
- #tabc = Proc.new {|str| Dir.glob(str +"*") }
1782
- require 'rbcurse/app'
1783
- require 'forwardable'
1784
- #include Bottomline
1785
-
1786
- #$tt = Bottomline.new
1787
- #module Kernel
1788
- #extend Forwardable
1789
- #def_delegators :$tt, :ask, :say, :agree, :choose, :numbered_menu
1790
- #end
1791
- App.new do
1792
- header = app_header "rbcurse 1.2.0", :text_center => "**** Demo", :text_right =>"New Improved!", :color => :black, :bgcolor => :white, :attr => :bold
1793
- message "Press F1 to exit from here"
1794
-
1795
- #stack :margin_top => 2, :margin => 5, :width => 30 do
1796
- #end # stack
1797
- #-----------------#------------------
1798
-
1799
- #choose do |menu|
1800
- #menu.prompt = "Please choose your favorite programming language? "
1801
- ##menu.layout = :one_line
1802
- #
1803
- #menu.choice :ruby do say("Good choice!") end
1804
- #menu.choice(:python) do say("python Not from around here, are you?") end
1805
- #menu.choice(:perl) do say("perl Not from around here, are you?") end
1806
- #menu.choice(:rake) do say("rake Not from around here, are you?") end
1807
- #end
1808
- entry = {}
1809
- entry[:file] = ask("File? ", Pathname) do |q|
1810
- q.completion_proc = Proc.new {|str| Dir.glob(str +"*") }
1811
- q.helptext = "Enter start of filename and tab to get completion"
1812
- end
1813
- alert "file: #{entry[:file]} "
1814
- $log.debug "FILE: #{entry[:file]} "
1815
- entry[:command] = ask("Command? ", %w{archive delete read refresh delete!})
1816
- exit unless agree("Wish to continue? ", false)
1817
- entry[:address] = ask("Address? ") { |q| q.color_pair = $promptcolor }
1818
- entry[:company] = ask("Company? ") { |q| q.default = "none" }
1819
- entry[:password] = ask("password? ") { |q|
1820
- q.echo = '*'
1821
- q.limit = 4
1822
- }
1823
- =begin
1824
- entry[:state] = ask("State? ") do |q|
1825
- q._case = :up
1826
- q.validate = /\A[A-Z]{2}\Z/
1827
- q.helptext = "Enter 2 characters for your state"
1828
- end
1829
- entry[:zip] = ask("Zip? ") do |q|
1830
- q.validate = /\A\d{5}(?:-?\d{4})?\Z/
1831
- end
1832
- entry[:phone] = ask( "Phone? ",
1833
- lambda { |p| p.delete("^0-9").
1834
- sub(/\A(\d{3})/, '(\1) ').
1835
- sub(/(\d{4})\Z/, '-\1') } ) do |q|
1836
- q.validate = lambda { |p| p.delete("^0-9").length == 10 }
1837
- q.responses[:not_valid] = "Enter a phone numer with area code."
1838
- end
1839
- entry[:age] = ask("Age? ", Integer) { |q| q.in = 0..105 }
1840
- entry[:birthday] = ask("Birthday? ", Date)
1841
- entry[:interests] = ask( "Interests? (comma separated list) ",
1842
- lambda { |str| str.split(/,\s*/) } )
1843
- entry[:description] = ask("Enter a description for this contact.") do |q|
1844
- q.whitespace = :strip_and_collapse
1845
- end
1846
- =end
1847
- $log.debug "ENTRY: #{entry} " if $log.debug?
1848
- #puts entry
1849
- end # app
1850
- end # FILE