rbhex-core 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (108) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +20 -0
  3. data/CHANGELOG +2000 -0
  4. data/LICENSE +56 -0
  5. data/README.md +44 -0
  6. data/examples/abasiclist.rb +179 -0
  7. data/examples/alpmenu.rb +50 -0
  8. data/examples/app.sample +19 -0
  9. data/examples/atree.rb +100 -0
  10. data/examples/bline.rb +136 -0
  11. data/examples/common/file.rb +45 -0
  12. data/examples/data/README.markdown +9 -0
  13. data/examples/data/brew.txt +38 -0
  14. data/examples/data/color.2 +37 -0
  15. data/examples/data/gemlist.txt +60 -0
  16. data/examples/data/lotr.txt +12 -0
  17. data/examples/data/ports.txt +136 -0
  18. data/examples/data/table.txt +37 -0
  19. data/examples/data/tasks.csv +88 -0
  20. data/examples/data/tasks.txt +27 -0
  21. data/examples/data/todo.txt +10 -0
  22. data/examples/data/todo.txt.bak +10 -0
  23. data/examples/data/todocsv.csv +28 -0
  24. data/examples/data/unix1.txt +21 -0
  25. data/examples/data/unix2.txt +11 -0
  26. data/examples/dbdemo.rb +502 -0
  27. data/examples/dirtree.rb +94 -0
  28. data/examples/newtabbedwindow.rb +100 -0
  29. data/examples/newtesttabp.rb +92 -0
  30. data/examples/tabular.rb +146 -0
  31. data/examples/tasks.rb +178 -0
  32. data/examples/term2.rb +84 -0
  33. data/examples/testbuttons.rb +296 -0
  34. data/examples/testcombo.rb +102 -0
  35. data/examples/testfields.rb +195 -0
  36. data/examples/testkeypress.rb +72 -0
  37. data/examples/testlistbox.rb +170 -0
  38. data/examples/testmessagebox.rb +140 -0
  39. data/examples/testprogress.rb +116 -0
  40. data/examples/testree.rb +106 -0
  41. data/examples/testwsshortcuts.rb +66 -0
  42. data/examples/testwsshortcuts2.rb +128 -0
  43. data/lib/rbhex.rb +6 -0
  44. data/lib/rbhex/core/docs/index.txt +73 -0
  45. data/lib/rbhex/core/include/action.rb +80 -0
  46. data/lib/rbhex/core/include/actionmanager.rb +49 -0
  47. data/lib/rbhex/core/include/appmethods.rb +214 -0
  48. data/lib/rbhex/core/include/bordertitle.rb +48 -0
  49. data/lib/rbhex/core/include/chunk.rb +203 -0
  50. data/lib/rbhex/core/include/io.rb +553 -0
  51. data/lib/rbhex/core/include/listbindings.rb +74 -0
  52. data/lib/rbhex/core/include/listcellrenderer.rb +140 -0
  53. data/lib/rbhex/core/include/listeditable.rb +317 -0
  54. data/lib/rbhex/core/include/listscrollable.rb +663 -0
  55. data/lib/rbhex/core/include/listselectable.rb +271 -0
  56. data/lib/rbhex/core/include/multibuffer.rb +83 -0
  57. data/lib/rbhex/core/include/orderedhash.rb +77 -0
  58. data/lib/rbhex/core/include/ractionevent.rb +73 -0
  59. data/lib/rbhex/core/include/rchangeevent.rb +27 -0
  60. data/lib/rbhex/core/include/rhistory.rb +95 -0
  61. data/lib/rbhex/core/include/rinputdataevent.rb +47 -0
  62. data/lib/rbhex/core/include/vieditable.rb +172 -0
  63. data/lib/rbhex/core/include/widgetmenu.rb +66 -0
  64. data/lib/rbhex/core/system/colormap.rb +165 -0
  65. data/lib/rbhex/core/system/keyboard.rb +150 -0
  66. data/lib/rbhex/core/system/keydefs.rb +30 -0
  67. data/lib/rbhex/core/system/ncurses.rb +236 -0
  68. data/lib/rbhex/core/system/panel.rb +162 -0
  69. data/lib/rbhex/core/system/window.rb +913 -0
  70. data/lib/rbhex/core/util/ansiparser.rb +119 -0
  71. data/lib/rbhex/core/util/app.rb +1228 -0
  72. data/lib/rbhex/core/util/basestack.rb +410 -0
  73. data/lib/rbhex/core/util/bottomline.rb +1859 -0
  74. data/lib/rbhex/core/util/colorparser.rb +77 -0
  75. data/lib/rbhex/core/util/focusmanager.rb +31 -0
  76. data/lib/rbhex/core/util/padreader.rb +192 -0
  77. data/lib/rbhex/core/util/rcommandwindow.rb +604 -0
  78. data/lib/rbhex/core/util/rdialogs.rb +574 -0
  79. data/lib/rbhex/core/util/viewer.rb +149 -0
  80. data/lib/rbhex/core/util/widgetshortcuts.rb +506 -0
  81. data/lib/rbhex/core/version.rb +5 -0
  82. data/lib/rbhex/core/widgets/applicationheader.rb +103 -0
  83. data/lib/rbhex/core/widgets/box.rb +58 -0
  84. data/lib/rbhex/core/widgets/divider.rb +310 -0
  85. data/lib/rbhex/core/widgets/keylabelprinter.rb +194 -0
  86. data/lib/rbhex/core/widgets/rcombo.rb +253 -0
  87. data/lib/rbhex/core/widgets/rcontainer.rb +415 -0
  88. data/lib/rbhex/core/widgets/rlink.rb +30 -0
  89. data/lib/rbhex/core/widgets/rlist.rb +696 -0
  90. data/lib/rbhex/core/widgets/rmenu.rb +958 -0
  91. data/lib/rbhex/core/widgets/rmenulink.rb +22 -0
  92. data/lib/rbhex/core/widgets/rmessagebox.rb +387 -0
  93. data/lib/rbhex/core/widgets/rprogress.rb +118 -0
  94. data/lib/rbhex/core/widgets/rtabbedpane.rb +634 -0
  95. data/lib/rbhex/core/widgets/rtabbedwindow.rb +70 -0
  96. data/lib/rbhex/core/widgets/rtextarea.rb +960 -0
  97. data/lib/rbhex/core/widgets/rtextview.rb +739 -0
  98. data/lib/rbhex/core/widgets/rtree.rb +768 -0
  99. data/lib/rbhex/core/widgets/rwidget.rb +3277 -0
  100. data/lib/rbhex/core/widgets/scrollbar.rb +143 -0
  101. data/lib/rbhex/core/widgets/statusline.rb +113 -0
  102. data/lib/rbhex/core/widgets/tabular.rb +264 -0
  103. data/lib/rbhex/core/widgets/tabularwidget.rb +1142 -0
  104. data/lib/rbhex/core/widgets/textpad.rb +995 -0
  105. data/lib/rbhex/core/widgets/tree/treecellrenderer.rb +150 -0
  106. data/lib/rbhex/core/widgets/tree/treemodel.rb +428 -0
  107. data/rbhex-core.gemspec +32 -0
  108. metadata +172 -0
@@ -0,0 +1,663 @@
1
+ # Provides the ability to scroll content, typically an array
2
+ # widget that includes may override on_enter_row and on_leave_row
3
+ # Caller should have
4
+ # row_count()
5
+ # scrollatrow() typically @height - 2 (unless a header row, then -3)
6
+ # @current_index (row of current index, starting with 0 usually)
7
+ # @toprow : set to 0 for starters, top row to be displayed
8
+ # @pcol (used for horiz scrolling, starts at 0)
9
+ #
10
+ module RubyCurses
11
+ module ListScrollable
12
+ attr_reader :search_found_ix, :find_offset, :find_offset1
13
+ attr_accessor :show_caret # 2010-01-23 23:06 our own fake insertion point
14
+ def previous_row num=(($multiplier.nil? or $multiplier == 0) ? 1 : $multiplier)
15
+ #return :UNHANDLED if @current_index == 0 # EVIL
16
+ return :NO_PREVIOUS_ROW if @current_index == 0
17
+ @oldrow = @current_index
18
+ # NOTE that putting a multiplier inside, prevents an event from being triggered for each row's
19
+ # on leave and on enter
20
+ num.times {
21
+ @current_index -= 1 if @current_index > 0
22
+ }
23
+ bounds_check
24
+ $multiplier = 0
25
+ end
26
+ alias :up :previous_row
27
+ def next_row num=(($multiplier.nil? or $multiplier == 0) ? 1 : $multiplier)
28
+ rc = row_count
29
+ # returning unhandled was clever .. when user hits down arrow on last row the focus goes to
30
+ # next field. however, in long lists when user scrolls the sudden jumping to next is very annoying.
31
+ # In combos, if focus was on last row, the combo closed which is not accceptable.
32
+ #return :UNHANDLED if @current_index == rc-1 # EVIL !!!
33
+ return :NO_NEXT_ROW if @current_index == rc-1 # changed 2011-10-5 so process can do something
34
+ @oldrow = @current_index
35
+ @current_index += 1*num if @current_index < rc
36
+ bounds_check
37
+ $multiplier = 0
38
+ end
39
+ alias :down :next_row
40
+ def goto_bottom
41
+ @oldrow = @current_index
42
+ rc = row_count
43
+ @current_index = rc -1
44
+ bounds_check
45
+ end
46
+ alias :goto_end :goto_bottom
47
+ def goto_top
48
+ @oldrow = @current_index
49
+ @current_index = 0
50
+ bounds_check
51
+ end
52
+ alias :goto_start :goto_top
53
+ def scroll_backward
54
+ @oldrow = @current_index
55
+ h = scrollatrow()
56
+ m = $multiplier == 0? 1 : $multiplier
57
+ @current_index -= h * m
58
+ bounds_check
59
+ $multiplier = 0
60
+ end
61
+ def scroll_forward
62
+ @oldrow = @current_index
63
+ h = scrollatrow()
64
+ rc = row_count
65
+ m = $multiplier == 0? 1 : $multiplier
66
+ # more rows than box
67
+ if h * m < rc
68
+ # next 2 lines were preventing widget_scrolled from being set to true,
69
+ # so i've modified it slightly as per scroll_down 2011-11-1
70
+ #@toprow += h+1 #if @current_index+h < rc
71
+ #@current_index = @toprow
72
+ @current_index += h+1
73
+ else
74
+ # fewer rows than box
75
+ @current_index = rc -1
76
+ end
77
+ #@current_index += h+1 #if @current_index+h < rc
78
+ bounds_check
79
+ end
80
+
81
+ ##
82
+ # please set oldrow before calling this. Store current_index as oldrow before changing. NOTE
83
+ def bounds_check
84
+ h = scrollatrow()
85
+ rc = row_count
86
+ @old_toprow = @toprow
87
+
88
+ @_header_adjustment ||= 0
89
+ @current_index = 0 if @current_index < 0 # not lt 0
90
+ @current_index = rc-1 if @current_index >= rc && rc>0 # not gt rowcount
91
+ @toprow = rc-h-1 if rc > h && @toprow > rc - h - 1 # toprow shows full page if possible
92
+ # curr has gone below table, move toprow forward
93
+ if @current_index - @toprow > h
94
+ @toprow = @current_index - h
95
+ elsif @current_index < @toprow
96
+ # curr has gone above table, move toprow up
97
+ # sometimes current row gets hidden below header line
98
+ @toprow = @current_index - (@_header_adjustment ||0)
99
+ # prev line can make top row -1, however, if we are going back, lets
100
+ # put it at start of page, so first or second row is not hidden
101
+ @toprow = 0 if @toprow < h
102
+ end
103
+
104
+ @row_changed = false
105
+ if @oldrow != @current_index
106
+ on_leave_row @oldrow if respond_to? :on_leave_row # to be defined by widget that has included this
107
+ on_enter_row @current_index if respond_to? :on_enter_row # to be defined by widget that has included this
108
+ set_form_row
109
+ @row_changed = true
110
+ end
111
+ #set_form_row # 2011-10-13
112
+
113
+ if @old_toprow != @toprow # only if scrolling has happened should we repaint
114
+ @repaint_required = true #if @old_toprow != @toprow # only if scrolling has happened should we repaint
115
+ @widget_scrolled = true
116
+ end
117
+ end
118
+ # the cursor should be appropriately positioned
119
+ def set_form_row
120
+ r,c = rowcol
121
+ @rows_panned ||= 0
122
+
123
+ # when the toprow is set externally then cursor can be mispositioned since
124
+ # bounds_check has not been called
125
+ if @current_index < @toprow
126
+ # cursor is outside table
127
+ @current_index = @toprow # ??? only if toprow 2010-10-19 12:56
128
+ end
129
+
130
+ row = r + (@current_index-@toprow) + @rows_panned
131
+ # row should not be < r or greater than r+height TODO FIXME
132
+
133
+ setrowcol row, nil
134
+
135
+ end
136
+ ## In many situations like placing a textarea or textview inside a splitpane
137
+ ##+ or scrollpane there have been issues getting the cursor at the right point,
138
+ ##+ since there are multiple buffers. Finally in tabbedpanes, i am pretty
139
+ ##+ lost getting the correct position, and i feel we should set the cursor
140
+ ##+ internally once and for all. So here's an attempt
141
+
142
+ # paint the cursor ourselves on the widget, rather than rely on getting to the top window with
143
+ # the correct coordinates. I do need to erase cursor too. Can be dicey, but is worth the attempt.
144
+ # This works perfectly, except for when placed in a Tabbedpane since that prints the form with a row offset
145
+ #+ of 2 and the widget does not know of the offset. cursor gets it correct since the form has an add_row.
146
+ def show_caret_func
147
+ return unless @show_caret
148
+ # trying highlighting cursor 2010-01-23 19:07 TABBEDPANE TRYING
149
+ # TODO take into account rows_panned etc ? I don't think so.
150
+ @rows_panned ||= 0
151
+ r,c = rowcol
152
+ yy = r + @current_index - @toprow - @win_top
153
+ #xx = @form.col # how do we know what value has been set earlier ?
154
+ yy = r + @current_index - @toprow #- @win_top
155
+ yy = @row_offset + @current_index - @toprow #- @win_top
156
+ xx = @col_offset + @curpos || 0
157
+ #yy = @row_offset if yy < @row_offset # sometimes r is 0, we are missing something in tabbedpane+scroll
158
+ #xx = @col_offset if xx < @col_offset
159
+ #xx = 0 if xx < 0
160
+
161
+ $log.debug " #{@name} printing CARET at #{yy},#{xx}: fwt:- #{@win_top} r:#{@row} tr:-#{@toprow}+ci:#{@current_index},+r #{r} "
162
+ if !@oldcursorrow.nil?
163
+ @graphic.mvchgat(y=@oldcursorrow, x=@oldcursorcol, 1, Ncurses::A_NORMAL, $datacolor, NIL)
164
+ end
165
+ @oldcursorrow = yy
166
+ @oldcursorcol = xx
167
+ @graphic.mvchgat(y=yy, x=xx, 1, Ncurses::A_NORMAL, $reversecolor, nil)
168
+ @buffer_modified = true
169
+ end
170
+ def scroll_right
171
+ $log.debug " inside scroll_right "
172
+ hscrollcols = $multiplier > 0 ? $multiplier : @width/2
173
+ #hscrollcols = $multiplier > 0 ? $multiplier : 1 # for testing out
174
+ $log.debug " scroll_right mult:#{$multiplier} , hscrollcols #{hscrollcols}, pcol #{@pcol} w: #{@width} ll:#{@longest_line} "
175
+ #blen = @buffer.rstrip.length
176
+ blen = @longest_line
177
+ if @pcol + @width < blen
178
+ @pcol += hscrollcols if @pcol + @width < blen
179
+ else
180
+ # due to some change somewhere, sometimes width = longest which is not true
181
+ hscrollcols = $multiplier > 0 ? $multiplier : 1
182
+ @pcol += hscrollcols
183
+ end
184
+ @repaint_required = true
185
+ end
186
+ def scroll_left
187
+ hscrollcols = $multiplier > 0 ? $multiplier : @width/2
188
+ @pcol -= hscrollcols if @pcol > 0
189
+ @pcol = 0 if @pcol < 0
190
+ @repaint_required = true
191
+ end
192
+ ## returns cursor to last row (if moving columns in same row, won't work)
193
+ # Useful after a large move such as 12j, 20 C-n etc, Mapped to '' in textview
194
+ def goto_last_position
195
+ return unless @oldrow
196
+ @current_index = @oldrow
197
+ bounds_check
198
+ end
199
+ # not that saving content_rows is buggy since we add rows.
200
+ ##
201
+ # caution, this now uses winrow not prow
202
+ ## for user to know which row is being focussed on
203
+ def focussed_index
204
+ @current_index # 2009-01-07 14:35
205
+ end
206
+ # only to be used in single selection cases as focussed item FIXME.
207
+ # best not to use, as can be implementation dep, use current_index
208
+ def selected_item
209
+ get_content()[focussed_index()]
210
+ end
211
+ #alias :current_index :focussed_index
212
+ alias :selected_index :focussed_index
213
+
214
+ # finds the next match for the char pressed
215
+ # returning the index
216
+ # If we are only checking first char, then why chomp ?
217
+ # Please note that this is used now by tree, and list can have non-strings, so use to_s
218
+ def next_match char
219
+ data = get_content
220
+ row = focussed_index + 1
221
+ row.upto(data.length-1) do |ix|
222
+ #val = data[ix].chomp rescue return # 2010-01-05 15:28 crashed on trueclass
223
+ val = data[ix].to_s rescue return # 2010-01-05 15:28 crashed on trueclass
224
+ #if val[0,1] == char #and val != currval
225
+ if val[0,1].casecmp(char) == 0 #AND VAL != CURRval
226
+ return ix
227
+ end
228
+ end
229
+ row = focussed_index - 1
230
+ 0.upto(row) do |ix|
231
+ #val = data[ix].chomp
232
+ val = data[ix].to_s
233
+ #if val[0,1] == char #and val != currval
234
+ if val[0,1].casecmp(char) == 0 #and val != currval
235
+ return ix
236
+ end
237
+ end
238
+ return -1
239
+ end
240
+ ## 2008-12-18 18:03
241
+ # sets the selection to the next row starting with char
242
+ def set_selection_for_char char
243
+ @oldrow = @current_index
244
+ @last_regex = "^#{char}"
245
+ ix = next_match char
246
+ @current_index = ix if ix && ix != -1
247
+ @search_found_ix = @current_index
248
+ bounds_check
249
+ return ix
250
+ end
251
+
252
+ ##
253
+ # ensures that the given row is focussed
254
+ # new version of older one that was not perfect.
255
+ # 2009-01-17 13:25
256
+ def set_focus_on arow
257
+ @oldrow = @current_index
258
+ # the next line fixed cursor positioning, but when wraparound then it messed up
259
+ # matching line would get hidden
260
+ @current_index = arow
261
+ bounds_check if @oldrow != @current_index
262
+ end
263
+ ##
264
+ def install_keys
265
+ =begin
266
+ @KEY_ASK_FIND_FORWARD ||= ?\M-f.getbyte(0)
267
+ @KEY_ASK_FIND_BACKWARD ||= ?\M-F.getbyte(0)
268
+ @KEY_FIND_NEXT ||= ?\M-g.getbyte(0)
269
+ @KEY_FIND_PREV ||= ?\M-G.getbyte(0)
270
+ =end
271
+ @KEY_ASK_FIND ||= ?\M-f.getbyte(0)
272
+ @KEY_FIND_MORE ||= ?\M-g.getbyte(0)
273
+ end
274
+ def ask_search
275
+ options = ["Search backwards", "case insensitive", "Wrap around"]
276
+ defaults = [@search_direction_prev, @search_case, @search_wrap]
277
+ regex = @last_regex || ""
278
+ # persist search history so one can popup and see
279
+ # How does user know that there is some history here.
280
+ $list_search_history ||= []
281
+ $list_search_history << @last_regex if @last_regex && !$list_search_history.include?(@last_regex)
282
+
283
+ mb = MessageBox.new :title => "Search" , :width => 70 do
284
+ fld = Field.new :label => 'Enter regex to search', :name => "patt", :display_length => 30,
285
+ :bgcolor => :cyan #, :text => regex
286
+ require 'rbhex/core/include/rhistory'
287
+ fld.extend(FieldHistory)
288
+ fld.history($list_search_history)
289
+ add fld
290
+ add CheckBox.new :text => options[0], :value => defaults[0], :name => "0"
291
+ add CheckBox.new :text => options[1], :value => defaults[1], :name => "1"
292
+ add CheckBox.new :text => options[2], :value => defaults[2], :name => "2"
293
+
294
+ button_type :ok_cancel
295
+ end
296
+ index = mb.run
297
+ return if index != 0
298
+ regex = mb.widget("patt").text || regex # if user enters nothing use existing regex
299
+ regex = @last_regex if regex == ""
300
+ return if regex.nil? || regex == ""
301
+ #$list_search_history << @last_regex if @last_regex
302
+ @search_direction_prev = mb.widget("0").value
303
+ @search_case = mb.widget("1").value
304
+ @search_wrap = mb.widget("2").value
305
+ if @search_direction_prev == true
306
+ ix = _find_prev regex, @current_index - (@_header_adjustment || 0), true
307
+ else
308
+ ix = _find_next regex, @current_index - (@_header_adjustment || 0), true
309
+ end
310
+ if ix.nil?
311
+ alert("No matching data for: #{regex}")
312
+ else
313
+ set_focus_on(ix)
314
+ set_form_col @find_offset1
315
+ @cell_editor.component.curpos = (@find_offset||0) if @cell_editing_allowed
316
+ end
317
+ @last_regex = regex
318
+ end
319
+ def find_more
320
+ if @search_direction_prev
321
+ find_prev
322
+ else
323
+ find_next
324
+ end
325
+ end
326
+ # find forwards
327
+ # Using this to start a search or continue search
328
+ def _find_next regex=@last_regex, start = @search_found_ix, first_time = false
329
+ #raise "No previous search" if regex.nil?
330
+ warn "No previous search" and return if regex.nil?
331
+ #$log.debug " _find_next #{@search_found_ix} : #{@current_index}"
332
+ extra = 1 # first time search on current line, next time skip current line or else we get stuck.
333
+ extra = 0 if first_time
334
+ start ||= 0
335
+ fend = @list.size-1
336
+ if start != fend
337
+ start += extra unless start == fend # used to be +=1 so we don't get stuck in same row
338
+ @last_regex = regex
339
+ @search_start_ix = start
340
+ regex = Regexp.new(regex, Regexp::IGNORECASE) if @search_case
341
+ start.upto(fend) do |ix|
342
+ row1 = @list[ix].to_s
343
+
344
+ # 2011-09-29 crashing on a character F3 in log file
345
+ # 2013-03-20 - 18:25 187compat
346
+ if row1.respond_to? :encode
347
+ row = row1.encode("ASCII-8BIT", :invalid => :replace, :undef => :replace, :replace => "?")
348
+ else
349
+ row = row1
350
+ end
351
+
352
+ m=row.match(regex)
353
+ if !m.nil?
354
+ @find_offset = m.offset(0)[0]
355
+ @find_offset1 = m.offset(0)[1]
356
+ ix += (@_header_adjustment || 0)
357
+ @search_found_ix = ix
358
+ return ix
359
+ end
360
+ end
361
+ end
362
+ fend = start-1
363
+ start = 0
364
+ if @search_wrap
365
+ start.upto(fend) do |ix|
366
+ row = @list[ix].to_s
367
+ m=row.match(regex)
368
+ if !m.nil?
369
+ @find_offset = m.offset(0)[0]
370
+ @find_offset1 = m.offset(0)[1]
371
+ ix += (@_header_adjustment || 0)
372
+ @search_found_ix = ix
373
+ return ix
374
+ end
375
+ end
376
+ end
377
+ return nil
378
+ end
379
+ def find_next
380
+ unless @last_regex
381
+ alert("No previous search. Search first.")
382
+ return
383
+ end
384
+ ix = _find_next
385
+ regex = @last_regex
386
+ if ix.nil?
387
+ alert("No more matching data for: #{regex}")
388
+ else
389
+ set_focus_on(ix)
390
+ set_form_col @find_offset1
391
+ @cell_editor.component.curpos = (@find_offset||0) if @cell_editing_allowed
392
+ end
393
+ end
394
+ def find_prev
395
+ unless @last_regex
396
+ alert("No previous search. Search first.")
397
+ return
398
+ end
399
+ ix = _find_prev
400
+ regex = @last_regex
401
+ if ix.nil?
402
+ alert("No previous matching data for: #{regex}")
403
+ else
404
+ set_focus_on(ix)
405
+ set_form_col @find_offset
406
+ @cell_editor.component.curpos = (@find_offset||0) if @cell_editing_allowed
407
+ end
408
+ end
409
+ ##
410
+ # find backwards
411
+ # Using this to start a search or continue search
412
+ def _find_prev regex=@last_regex, start = @search_found_ix, first_time = false
413
+ #TODO the firsttime part, see find_next
414
+ #raise "No previous search" if regex.nil?
415
+ warn "No previous search" and return if regex.nil?
416
+ #$log.debug " _find_prev #{@search_found_ix} : #{@current_index}"
417
+ if start != 0
418
+ start -= 1 unless start == 0
419
+ @last_regex = regex
420
+ @search_start_ix = start
421
+ regex = Regexp.new(regex, Regexp::IGNORECASE) if @search_case
422
+ start.downto(0) do |ix|
423
+ row = @list[ix].to_s
424
+ m=row.match(regex)
425
+ if !m.nil?
426
+ @find_offset = m.offset(0)[0]
427
+ @find_offset1 = m.offset(0)[1]
428
+ ix += (@_header_adjustment || 0)
429
+ @search_found_ix = ix
430
+ return ix
431
+ end
432
+ end
433
+ end
434
+ fend = start-1
435
+ start = @list.size-1
436
+ if @search_wrap
437
+ start.downto(fend) do |ix|
438
+ row = @list[ix].to_s
439
+ m=row.match(regex)
440
+ if !m.nil?
441
+ @find_offset = m.offset(0)[0]
442
+ @find_offset1 = m.offset(0)[1]
443
+ ix += (@_header_adjustment || 0)
444
+ @search_found_ix = ix
445
+ return ix
446
+ end
447
+ end
448
+ end
449
+ return nil
450
+ end
451
+ ##
452
+ # goes to start of next word (or n words) - vi's w
453
+ # NOTE: will not work if the list has different data from what is displayed
454
+ # Nothing i can do about it.
455
+ # Also does not work as expected if consecutive spaces FIXME
456
+ # Will not scroll list, if reaches end, jist goes on and vanshes FIXME
457
+ #
458
+ def forward_word
459
+ $multiplier = 1 if !$multiplier || $multiplier == 0
460
+ line = @current_index
461
+ buff = @list[line].to_s
462
+ return unless buff
463
+ pos = @curpos || 0 # list does not have curpos
464
+ $multiplier.times {
465
+ #found = buff.index(/[[:punct:][:space:]]+/, pos)
466
+ # 2013-03-04 - 17:35 modified so it skips spaces and puncts
467
+ found = buff.index(/[[:punct:][:space:]]\w/, pos)
468
+ if !found
469
+ # if not found, we've lost a counter
470
+ if line+1 < @list.length
471
+ line += 1
472
+ else
473
+ return
474
+ end
475
+ buff = @list[line].to_s
476
+ pos = 0
477
+ else
478
+ pos = found + 1
479
+ end
480
+ $log.debug " forward_word: pos #{pos} line #{line} buff: #{buff}"
481
+ }
482
+ @current_index = line
483
+ @curpos = pos
484
+ @buffer = @list[@current_index].to_s
485
+ set_form_row
486
+ set_form_col pos
487
+ @repaint_required = true
488
+ end
489
+
490
+ ##
491
+ # go to beginning of previous word somewhat similar to vim's 'b' movement.
492
+ # Introduced on 2013-03-25 - 00:49 and has some errors but mostly quite
493
+ # helpful - i think i've fixed the error of getting stuck on a line
494
+ def backward_word
495
+ $multiplier = 1 if !$multiplier || $multiplier == 0
496
+ line = @current_index
497
+ buff = @list[line].to_s
498
+ return unless buff
499
+ pos = @curpos || 0 # list does not have curpos
500
+ $multiplier.times {
501
+ #found = buff.index(/[[:punct:][:space:]]+/, pos)
502
+ # 2013-03-04 - 17:35 modified so it skips spaces and puncts
503
+ # ouch pos becomes -2 and remains in same line !
504
+
505
+ spos = pos -2
506
+ spos = 0 if spos < 0
507
+ found = buff.rindex(/[[:punct:][:space:]]\w/, spos)
508
+ if !found || found == 0
509
+ # if not found, we've lost a counter/multiplier
510
+ if pos > 0
511
+ pos = 0
512
+ elsif line > 0
513
+ line -= 1
514
+ buff = @list[line].to_s
515
+ pos = buff.size - 1
516
+ else
517
+ return
518
+ end
519
+ else
520
+ pos = found + 1
521
+ end
522
+ $log.debug "BBB backward_word: pos #{pos} line #{line} buff: #{buff}, found #{found}"
523
+ }
524
+ @current_index = line
525
+ @curpos = pos
526
+ @buffer = @list[@current_index].to_s
527
+ set_form_row
528
+ set_form_col pos
529
+ @repaint_required = true
530
+ end
531
+ ##
532
+ # goes to next occurence of <char> (or nth occurence)
533
+ # Actually, we can club this with forward_word so no duplication
534
+ # Or call one from the other
535
+ #
536
+ def forward_char char=nil
537
+ if char.nil?
538
+ $log.debug " XXX acceptng char"
539
+ ch = @graphic.getchar
540
+ return -1 if ch < 0 or ch > 255 # or 127 ???
541
+ char = ch.chr
542
+ end
543
+ $log.debug " forward_char char:#{char}:"
544
+ $multiplier = 1 if !$multiplier or $multiplier == 0
545
+ line = @current_index
546
+ buff = @list[line].to_s
547
+ pos = @curpos
548
+ $multiplier.times {
549
+ found = false
550
+ while !found
551
+ found = buff.index(char, pos)
552
+ if !found
553
+ line += 1 # unless eof
554
+ buff = @list[line].to_s
555
+ pos = 0
556
+ else
557
+ pos = found + 1
558
+ end
559
+ break if line >= @list.size
560
+ $log.debug " #{found} forward_word: pos #{pos} line #{line} buff: #{buff}"
561
+ end
562
+ }
563
+ @current_index = line
564
+ @curpos = pos
565
+ @buffer = @list[@current_index].to_s
566
+ set_form_row
567
+ set_form_col pos
568
+ @repaint_required = true
569
+ end
570
+ # takes a block, this way anyone extending this class can just pass a block to do his job
571
+ # This modifies the string
572
+ def sanitize content #:nodoc:
573
+ if content.is_a? String
574
+ content.chomp!
575
+ # 2013-03-20 - 18:29 187compat
576
+ if content.respond_to? :encode
577
+ content.replace(content.encode("ASCII-8BIT", :invalid => :replace, :undef => :replace, :replace => "?"))
578
+ end
579
+ content.gsub!(/[\t\r\n]/, ' ') # don't display tab, newline
580
+ content.gsub!(/\n/, ' ') # don't display tab, newline
581
+ content.gsub!(/[^[:print:]]/, '') # don't display non print characters
582
+ else
583
+ content
584
+ end
585
+ end
586
+ # returns only the visible portion of string taking into account display length
587
+ # and horizontal scrolling. MODIFIES STRING
588
+ # NOTE truncate does not take into account left_margin that some widgets might use
589
+ def truncate content #:nodoc:
590
+ #maxlen = @maxlen || @width-2
591
+ _maxlen = @maxlen || @width-@internal_width
592
+ _maxlen = @width-@internal_width if _maxlen > @width-@internal_width
593
+ if !content.nil?
594
+ if content.length > _maxlen # only show maxlen
595
+ @longest_line = content.length if content.length > @longest_line
596
+ #content = content[@pcol..@pcol+_maxlen-1]
597
+ content.replace content[@pcol..@pcol+_maxlen-1]
598
+ else
599
+ # can this be avoided if pcol is 0 XXX
600
+ content.replace content[@pcol..-1] if @pcol > 0
601
+ end
602
+ end
603
+ content
604
+ end
605
+ #
606
+ # Is the given index in the visible area
607
+ # UNTESTED XXX
608
+ def is_visible? index
609
+ #(0..scrollatrow()).include? index - @toprow
610
+ j = index - @toprow
611
+ j >= 0 && j <= scrollatrow()
612
+ end
613
+ # offset in widget like 0..scrollatrow
614
+ # NOTE can return nil, if user scrolls forward or backward
615
+ # @private
616
+ def _convert_index_to_visible_row index=@current_index
617
+ pos = index - @toprow
618
+ return nil if pos < 0 || pos > scrollatrow()
619
+ return pos
620
+ end
621
+ # actual position to write on window
622
+ # NOTE can return nil, if user scrolls forward or backward
623
+ def _convert_index_to_printable_row index=@current_index
624
+ r,c = rowcol
625
+ pos = _convert_index_to_visible_row(index)
626
+ return nil unless pos
627
+ pos = r + pos
628
+ return pos
629
+ end
630
+
631
+ # highlights the focussed (current) and unfocussed (oldrow)
632
+ # NOTE: when multiselecting ... it will remove selection
633
+ # so be careful when calling.
634
+ def highlight_focussed_row type, r=nil, c=nil, acolor=nil
635
+ return unless @should_show_focus
636
+ case type
637
+ when :FOCUSSED
638
+ ix = @current_index
639
+ return if is_row_selected ix
640
+ r = _convert_index_to_printable_row() unless r
641
+ return unless r # row is not longer visible 2013-04-10 - 16:37
642
+ attrib = @focussed_attrib || 'bold'
643
+
644
+ when :UNFOCUSSED
645
+ return if @oldrow.nil? || @oldrow == @current_index
646
+ ix = @oldrow
647
+ return if is_row_selected ix
648
+ r = _convert_index_to_printable_row(@oldrow) unless r
649
+ return unless r # row is not longer visible
650
+ attrib = @attr
651
+ end
652
+ unless c
653
+ _r, c = rowcol
654
+ end
655
+ # this optimization now overrides any coloring that listbox may have done per row XXX
656
+ acolor ||= get_color $datacolor
657
+ att = get_attrib(attrib) #if @focussed_attrib
658
+ @graphic.mvchgat(y=r, x=c, @width-@internal_width, att , acolor , nil)
659
+ end
660
+
661
+
662
+ end
663
+ end