rbhex-core 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +20 -0
  3. data/CHANGELOG +2000 -0
  4. data/LICENSE +56 -0
  5. data/README.md +44 -0
  6. data/examples/abasiclist.rb +179 -0
  7. data/examples/alpmenu.rb +50 -0
  8. data/examples/app.sample +19 -0
  9. data/examples/atree.rb +100 -0
  10. data/examples/bline.rb +136 -0
  11. data/examples/common/file.rb +45 -0
  12. data/examples/data/README.markdown +9 -0
  13. data/examples/data/brew.txt +38 -0
  14. data/examples/data/color.2 +37 -0
  15. data/examples/data/gemlist.txt +60 -0
  16. data/examples/data/lotr.txt +12 -0
  17. data/examples/data/ports.txt +136 -0
  18. data/examples/data/table.txt +37 -0
  19. data/examples/data/tasks.csv +88 -0
  20. data/examples/data/tasks.txt +27 -0
  21. data/examples/data/todo.txt +10 -0
  22. data/examples/data/todo.txt.bak +10 -0
  23. data/examples/data/todocsv.csv +28 -0
  24. data/examples/data/unix1.txt +21 -0
  25. data/examples/data/unix2.txt +11 -0
  26. data/examples/dbdemo.rb +502 -0
  27. data/examples/dirtree.rb +94 -0
  28. data/examples/newtabbedwindow.rb +100 -0
  29. data/examples/newtesttabp.rb +92 -0
  30. data/examples/tabular.rb +146 -0
  31. data/examples/tasks.rb +178 -0
  32. data/examples/term2.rb +84 -0
  33. data/examples/testbuttons.rb +296 -0
  34. data/examples/testcombo.rb +102 -0
  35. data/examples/testfields.rb +195 -0
  36. data/examples/testkeypress.rb +72 -0
  37. data/examples/testlistbox.rb +170 -0
  38. data/examples/testmessagebox.rb +140 -0
  39. data/examples/testprogress.rb +116 -0
  40. data/examples/testree.rb +106 -0
  41. data/examples/testwsshortcuts.rb +66 -0
  42. data/examples/testwsshortcuts2.rb +128 -0
  43. data/lib/rbhex.rb +6 -0
  44. data/lib/rbhex/core/docs/index.txt +73 -0
  45. data/lib/rbhex/core/include/action.rb +80 -0
  46. data/lib/rbhex/core/include/actionmanager.rb +49 -0
  47. data/lib/rbhex/core/include/appmethods.rb +214 -0
  48. data/lib/rbhex/core/include/bordertitle.rb +48 -0
  49. data/lib/rbhex/core/include/chunk.rb +203 -0
  50. data/lib/rbhex/core/include/io.rb +553 -0
  51. data/lib/rbhex/core/include/listbindings.rb +74 -0
  52. data/lib/rbhex/core/include/listcellrenderer.rb +140 -0
  53. data/lib/rbhex/core/include/listeditable.rb +317 -0
  54. data/lib/rbhex/core/include/listscrollable.rb +663 -0
  55. data/lib/rbhex/core/include/listselectable.rb +271 -0
  56. data/lib/rbhex/core/include/multibuffer.rb +83 -0
  57. data/lib/rbhex/core/include/orderedhash.rb +77 -0
  58. data/lib/rbhex/core/include/ractionevent.rb +73 -0
  59. data/lib/rbhex/core/include/rchangeevent.rb +27 -0
  60. data/lib/rbhex/core/include/rhistory.rb +95 -0
  61. data/lib/rbhex/core/include/rinputdataevent.rb +47 -0
  62. data/lib/rbhex/core/include/vieditable.rb +172 -0
  63. data/lib/rbhex/core/include/widgetmenu.rb +66 -0
  64. data/lib/rbhex/core/system/colormap.rb +165 -0
  65. data/lib/rbhex/core/system/keyboard.rb +150 -0
  66. data/lib/rbhex/core/system/keydefs.rb +30 -0
  67. data/lib/rbhex/core/system/ncurses.rb +236 -0
  68. data/lib/rbhex/core/system/panel.rb +162 -0
  69. data/lib/rbhex/core/system/window.rb +913 -0
  70. data/lib/rbhex/core/util/ansiparser.rb +119 -0
  71. data/lib/rbhex/core/util/app.rb +1228 -0
  72. data/lib/rbhex/core/util/basestack.rb +410 -0
  73. data/lib/rbhex/core/util/bottomline.rb +1859 -0
  74. data/lib/rbhex/core/util/colorparser.rb +77 -0
  75. data/lib/rbhex/core/util/focusmanager.rb +31 -0
  76. data/lib/rbhex/core/util/padreader.rb +192 -0
  77. data/lib/rbhex/core/util/rcommandwindow.rb +604 -0
  78. data/lib/rbhex/core/util/rdialogs.rb +574 -0
  79. data/lib/rbhex/core/util/viewer.rb +149 -0
  80. data/lib/rbhex/core/util/widgetshortcuts.rb +506 -0
  81. data/lib/rbhex/core/version.rb +5 -0
  82. data/lib/rbhex/core/widgets/applicationheader.rb +103 -0
  83. data/lib/rbhex/core/widgets/box.rb +58 -0
  84. data/lib/rbhex/core/widgets/divider.rb +310 -0
  85. data/lib/rbhex/core/widgets/keylabelprinter.rb +194 -0
  86. data/lib/rbhex/core/widgets/rcombo.rb +253 -0
  87. data/lib/rbhex/core/widgets/rcontainer.rb +415 -0
  88. data/lib/rbhex/core/widgets/rlink.rb +30 -0
  89. data/lib/rbhex/core/widgets/rlist.rb +696 -0
  90. data/lib/rbhex/core/widgets/rmenu.rb +958 -0
  91. data/lib/rbhex/core/widgets/rmenulink.rb +22 -0
  92. data/lib/rbhex/core/widgets/rmessagebox.rb +387 -0
  93. data/lib/rbhex/core/widgets/rprogress.rb +118 -0
  94. data/lib/rbhex/core/widgets/rtabbedpane.rb +634 -0
  95. data/lib/rbhex/core/widgets/rtabbedwindow.rb +70 -0
  96. data/lib/rbhex/core/widgets/rtextarea.rb +960 -0
  97. data/lib/rbhex/core/widgets/rtextview.rb +739 -0
  98. data/lib/rbhex/core/widgets/rtree.rb +768 -0
  99. data/lib/rbhex/core/widgets/rwidget.rb +3277 -0
  100. data/lib/rbhex/core/widgets/scrollbar.rb +143 -0
  101. data/lib/rbhex/core/widgets/statusline.rb +113 -0
  102. data/lib/rbhex/core/widgets/tabular.rb +264 -0
  103. data/lib/rbhex/core/widgets/tabularwidget.rb +1142 -0
  104. data/lib/rbhex/core/widgets/textpad.rb +995 -0
  105. data/lib/rbhex/core/widgets/tree/treecellrenderer.rb +150 -0
  106. data/lib/rbhex/core/widgets/tree/treemodel.rb +428 -0
  107. data/rbhex-core.gemspec +32 -0
  108. metadata +172 -0
@@ -0,0 +1,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