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,739 @@
1
+ =begin
2
+ * Name: TextView
3
+ * Description View text in this widget.
4
+ * Author: rkumar (arunachalesha)
5
+ * file created 2009-01-08 15:23
6
+ * major change: 2010-02-10 19:43 simplifying the buffer stuff.
7
+ TODO
8
+ * border, and footer could be objects (classes) at some future stage.
9
+ --------
10
+ * License:
11
+ Same as Ruby's License (http://www.ruby-lang.org/LICENSE.txt)
12
+
13
+ =end
14
+ require 'logger'
15
+ require 'rbhex'
16
+ require 'rbhex/core/include/listscrollable'
17
+ require 'rbhex/core/include/bordertitle'
18
+ require 'forwardable'
19
+
20
+ include RubyCurses
21
+ module RubyCurses
22
+ extend self
23
+
24
+ ##
25
+ # A viewable read only box. Can scroll.
26
+ # Intention is to be able to change content dynamically - the entire list.
27
+ # Use set_content to set content, or just update the list attrib
28
+ # TODO -
29
+ # - goto line - DONE
30
+ class TextView < Widget
31
+ include ListScrollable
32
+ extend Forwardable
33
+ #dsl_accessor :height # height of viewport cmmented on 2010-01-09 19:29 since widget has method
34
+ #dsl_accessor :title # set this on top
35
+ #dsl_accessor :title_attrib # bold, reverse, normal
36
+ dsl_accessor :footer_attrib # bold, reverse, normal
37
+ dsl_accessor :list # the array of data to be sent by user
38
+ dsl_accessor :maxlen # max len to be displayed
39
+ attr_reader :toprow # the toprow in the view (offsets are 0)
40
+ # painting the footer does slow down cursor painting slightly if one is moving cursor fast
41
+ dsl_accessor :print_footer
42
+ attr_reader :current_index
43
+ dsl_accessor :sanitization_required
44
+
45
+ def initialize form = nil, config={}, &block
46
+ @focusable = true
47
+ @editable = false
48
+ @sanitization_required = true
49
+ @suppress_borders = false
50
+ @row_offset = @col_offset = 1
51
+ @row = 0
52
+ @col = 0
53
+ @show_focus = false # don't highlight row under focus
54
+ @list = []
55
+ map_keys
56
+ super
57
+ # ideally this should have been 2 to take care of borders, but that would break
58
+ # too much stuff !
59
+ @win = @graphic
60
+
61
+ @_events.push :CHANGE # thru vieditable
62
+ @_events << :PRESS # new, in case we want to use this for lists and allow ENTER
63
+ @_events << :ENTER_ROW # new, should be there in listscrollable ??
64
+ install_keys # do something about this nonsense FIXME
65
+ bordertitle_init
66
+ init_vars
67
+ init_actions
68
+ end
69
+ def init_vars #:nodoc:
70
+ @curpos = @pcol = @toprow = @current_index = 0
71
+ @repaint_all=true
72
+ @repaint_required=true
73
+ @widget_scrolled = true
74
+ ## 2010-02-10 20:20 RFED16 taking care if no border requested
75
+ @row_offset = @col_offset = 0 if @suppress_borders == true
76
+ # added 2010-02-11 15:11 RFED16 so we don't need a form.
77
+ $error_message_row ||= 23
78
+ $error_message_col ||= 1
79
+ # currently i scroll right only if current line is longer than display width, i should use
80
+ # longest line on screen.
81
+ @longest_line = 0 # the longest line printed on this page, used to determine if scrolling shd work
82
+ @internal_width = 2
83
+ @internal_width = 0 if @suppress_borders # NOTE bordertitle says 1
84
+
85
+ @search_found_ix = nil # so old searches don't get highlighted
86
+ end
87
+ def map_keys
88
+ require 'rbhex/core/include/listbindings'
89
+ bindings()
90
+ #bind_key([?\C-x, ?\C-s], :saveas)
91
+ #bind_key([?\C-x, ?e], :edit_external)
92
+ bind_key(32, 'scroll forward'){ scroll_forward() }
93
+ # have placedhere so multi-bufer can override BS to prev buffer
94
+ bind_keys([KEY_BACKSPACE,KEY_BSPACE,KEY_DELETE], :cursor_backward)
95
+ #bind_key(?r) { getstr("Enter a word: ") } if $log.debug?
96
+ #bind_key(?m, :disp_menu) if $log.debug?
97
+ end
98
+ ##
99
+ # send in a list
100
+ # e.g. set_content File.open("README.txt","r").readlines
101
+ # set wrap at time of passing :WRAP_NONE :WRAP_WORD
102
+ # XXX if we widen the textview later, as in a vimsplit that data
103
+ # will sti1ll be wrapped at this width !!
104
+ # 2011-12-3 changed wrap to hash, so we can use content_type :ansi, :tmux
105
+ def set_content list, config = {} #wrap = :WRAP_NONE
106
+ @content_type = config[:content_type]
107
+ _title = config[:title]
108
+ self.title = _title if _title
109
+ if @content_type
110
+ formatted_text list, @content_type
111
+ return
112
+ end
113
+ # please note that content type is lost once formatted text does it's work
114
+ @wrap_policy = config[:wrap]
115
+ if list.is_a? String
116
+ if @wrap_policy == :WRAP_WORD
117
+ data = wrap_text list
118
+ @list = data.split("\n")
119
+ else
120
+ @list = list.split("\n")
121
+ end
122
+ elsif list.is_a? Array
123
+ if @wrap_policy == :WRAP_WORD
124
+ data = wrap_text list.join(" ")
125
+ @list = data.split("\n")
126
+ else
127
+ @list = list
128
+ end
129
+ else
130
+ raise "set_content expects Array not #{list.class}"
131
+ end
132
+ init_vars
133
+ end
134
+ # for consistency with other objects that respect text
135
+ #alias :text :set_content
136
+ def text(*val)
137
+ if val.empty?
138
+ return @list
139
+ end
140
+ set_content(*val)
141
+ self
142
+ end
143
+ def text=(val)
144
+ return unless val # added 2010-11-17 20:11, dup will fail on nil
145
+ set_content(val)
146
+ end
147
+ def formatted_text text, fmt
148
+ require 'rbhex/core/include/chunk'
149
+ @formatted_text = text
150
+ @color_parser = fmt
151
+ remove_all
152
+ end
153
+ #def <<(line); @list << line; @widget_scrolled = true; end
154
+ def_delegators :@list, :include?, :each, :values, :size
155
+ %w[ insert clear delete_at []= << ].each { |e|
156
+ eval %{
157
+ def #{e}(*args)
158
+ @list.send(:#{e}, *args)
159
+ @widget_scrolled = true
160
+ @repaint_required = true
161
+ end
162
+ }
163
+ }
164
+ alias :append :<<
165
+
166
+ def remove_all
167
+ @list = []
168
+ init_vars
169
+ @repaint_required = true
170
+ end
171
+ ## display this row on top
172
+ def top_row(*val) #:nodoc:
173
+ if val.empty?
174
+ @toprow
175
+ else
176
+ @toprow = val[0] || 0
177
+ end
178
+ @repaint_required = true
179
+ end
180
+ ## ---- for listscrollable ---- ##
181
+ def scrollatrow #:nodoc:
182
+ if @suppress_borders
183
+ @height - 1 # should be 2 FIXME but erasing lower line. see appemail
184
+ else
185
+ @height - 3
186
+ end
187
+ end
188
+ def row_count
189
+ @list.length
190
+ end
191
+ ##
192
+ # returns row of first match of given regex (or nil if not found)
193
+ def find_first_match regex #:nodoc:
194
+ @list.each_with_index do |row, ix|
195
+ return ix if !row.match(regex).nil?
196
+ end
197
+ return nil
198
+ end
199
+ ## returns the position where cursor was to be positioned by default
200
+ # It may no longer work like that.
201
+ def rowcol #:nodoc:
202
+ return @row+@row_offset, @col+@col_offset
203
+ end
204
+ def wrap_text(txt, col = @maxlen) #:nodoc:
205
+ col ||= @width-@internal_width
206
+ #$log.debug "inside wrap text for :#{txt}"
207
+ txt.gsub(/(.{1,#{col}})( +|$\n?)|(.{1,#{col}})/,
208
+ "\\1\\3\n")
209
+ end
210
+ def print_foot #:nodoc:
211
+ @footer_attrib ||= Ncurses::A_REVERSE
212
+ footer = "R: #{@current_index+1}, C: #{@curpos+@pcol}, #{@list.length} lines "
213
+ $log.debug " print_foot calling printstring with #{@row} + #{@height} -1, #{@col}+2"
214
+ @graphic.printstring( @row + @height -1 , @col+2, footer, @color_pair || $datacolor, @footer_attrib)
215
+ @repaint_footer_required = false # 2010-01-23 22:55
216
+ end
217
+ ### FOR scrollable ###
218
+ def get_content
219
+ @list
220
+ end
221
+ def get_window #:nodoc:
222
+ @graphic
223
+ end
224
+
225
+ def repaint # textview :nodoc:
226
+ #$log.debug "TEXTVIEW repaint r c #{@row}, #{@col}, key: #{$current_key}, reqd #{@repaint_required} "
227
+
228
+ #return unless @repaint_required # 2010-02-12 19:08 TRYING - won't let footer print for col move
229
+ # TRYING OUT dangerous 2011-10-13
230
+ @repaint_required = false
231
+ @repaint_required = true if @widget_scrolled || @pcol != @old_pcol || @record_changed || @property_changed
232
+
233
+ paint if @repaint_required
234
+
235
+ @repaint_footer_required = true if @oldrow != @current_index # 2011-10-15
236
+ print_foot if @print_footer && !@suppress_borders && @repaint_footer_required
237
+ end
238
+ # this sucks and should not be used but is everywhere, should
239
+ # use text()
240
+ def getvalue
241
+ @list
242
+ end
243
+ def current_value
244
+ @list[@current_index]
245
+ end
246
+
247
+ # determine length of row since we have chunks now.
248
+ # Since chunk implements length, so not required except for the old
249
+ # cases of demos that use an array.
250
+ def row_length
251
+ case @buffer
252
+ when String
253
+ @buffer.length
254
+ when Chunks::ChunkLine
255
+ return @buffer.length
256
+ when Array
257
+ # this is for those old cases like rfe.rb which sent in an array
258
+ # (before we moved to chunks)
259
+ # line is an array of arrays
260
+ if @buffer[0].is_a? Array
261
+ result = 0
262
+ @buffer.each {|e| result += e[1].length }
263
+ return result
264
+ end
265
+ # line is one single chunk
266
+ return @buffer[1].length
267
+ end
268
+ end
269
+ # textview
270
+ # NOTE: i think this should return if list is nil or empty. No need to put
271
+ #
272
+ # stuff into buffer and continue. will trouble other classes that extend.
273
+ def handle_key ch #:nodoc:
274
+ $log.debug " textview got ch #{ch} "
275
+ @old_pcol = @pcol
276
+ @buffer = @list[@current_index]
277
+ if @buffer.nil? && row_count == 0
278
+ @list << "\r"
279
+ @buffer = @list[@current_index]
280
+ end
281
+ return if @buffer.nil?
282
+ #$log.debug " before: curpos #{@curpos} blen: #{row_length}"
283
+ if @curpos > row_length #@buffer.length
284
+ addcol((row_length-@curpos)+1)
285
+ @curpos = row_length
286
+ set_form_col
287
+ end
288
+ # We can improve later
289
+ case ch
290
+ when KEY_UP, ?k.getbyte(0)
291
+ #select_prev_row
292
+ ret = up
293
+ # next removed as very irritating, can be configured if required 2011-11-2
294
+ #get_window.ungetch(KEY_BTAB) if ret == :NO_PREVIOUS_ROW
295
+ check_curpos
296
+
297
+ when KEY_DOWN, ?j.getbyte(0)
298
+ ret = down
299
+ # This should be configurable, or only if all rows are visible
300
+ #get_window.ungetch(KEY_TAB) if ret == :NO_NEXT_ROW
301
+ check_curpos
302
+ #when KEY_LEFT, ?h.getbyte(0)
303
+ #cursor_backward
304
+ #when KEY_RIGHT, ?l.getbyte(0)
305
+ #cursor_forward
306
+ when ?\C-a.getbyte(0) #, ?0.getbyte(0)
307
+ # take care of data that exceeds maxlen by scrolling and placing cursor at start
308
+ @repaint_required = true if @pcol > 0 # tried other things but did not work
309
+ set_form_col 0
310
+ @pcol = 0
311
+ when ?\C-e.getbyte(0), ?$.getbyte(0)
312
+ # take care of data that exceeds maxlen by scrolling and placing cursor at end
313
+ # This use to actually pan the screen to actual end of line, but now somewhere
314
+ # it only goes to end of visible screen, set_form probably does a sanity check
315
+ blen = row_length # @buffer.rstrip.length FIXME
316
+ set_form_col blen
317
+ when KEY_ENTER, FFI::NCurses::KEY_ENTER
318
+ #fire_handler :PRESS, self
319
+ fire_action_event
320
+ when ?0.getbyte(0)..?9.getbyte(0)
321
+ # FIXME the assumption here was that if numbers are being entered then a 0 is a number
322
+ # not a beg-of-line command.
323
+ # However, after introducing universal_argument, we can enters numbers using C-u and then press another
324
+ # C-u to stop. In that case a 0 should act as a command, even though multiplier has been set
325
+ if ch == ?0.getbyte(0) and $multiplier == 0
326
+ # copy of C-a - start of line
327
+ @repaint_required = true if @pcol > 0 # tried other things but did not work
328
+ set_form_col 0
329
+ @pcol = 0
330
+ return 0
331
+ end
332
+ # storing digits entered so we can multiply motion actions
333
+ $multiplier *= 10 ; $multiplier += (ch-48)
334
+ return 0
335
+ when ?\C-c.getbyte(0)
336
+ $multiplier = 0
337
+ return 0
338
+ else
339
+ # check for bindings, these cannot override above keys since placed at end
340
+ begin
341
+ ret = process_key ch, self
342
+ rescue => err
343
+ $log.error " TEXTVIEW ERROR #{err} "
344
+ $log.debug(err.backtrace.join("\n"))
345
+ textdialog [err.to_s, *err.backtrace], :title => "Exception"
346
+ end
347
+ return :UNHANDLED if ret == :UNHANDLED
348
+ end
349
+ $multiplier = 0 # you must reset if you've handled a key. if unhandled, don't reset since parent could use
350
+ set_form_row
351
+ return 0 # added 2010-01-12 22:17 else down arrow was going into next field
352
+ end
353
+ # newly added to check curpos when moving up or down
354
+ def check_curpos #:nodoc:
355
+ @buffer = @list[@current_index]
356
+ # if the cursor is ahead of data in this row then move it back
357
+ if @pcol+@curpos > row_length
358
+ addcol((@pcol+row_length-@curpos)+1)
359
+ @curpos = row_length
360
+ maxlen = (@maxlen || @width-@internal_width)
361
+
362
+ # even this row is gt maxlen, i.e., scrolled right
363
+ if @curpos > maxlen
364
+ @pcol = @curpos - maxlen
365
+ @curpos = maxlen-1
366
+ else
367
+ # this row is within maxlen, make scroll 0
368
+ @pcol=0
369
+ end
370
+ set_form_col
371
+ end
372
+ end
373
+ # set cursor on correct column tview
374
+ def set_form_col col1=@curpos #:nodoc:
375
+ @cols_panned ||= 0
376
+ @pad_offset ||= 0 # added 2010-02-11 21:54 since padded widgets get an offset.
377
+ @curpos = col1
378
+ maxlen = @maxlen || @width-@internal_width
379
+ #@curpos = maxlen if @curpos > maxlen
380
+ if @curpos > maxlen
381
+ @pcol = @curpos - maxlen
382
+ @curpos = maxlen - 1
383
+ @repaint_required = true # this is required so C-e can pan screen
384
+ else
385
+ @pcol = 0
386
+ end
387
+ # the rest only determines cursor placement
388
+ win_col = 0 # 2010-02-07 23:19 new cursor stuff
389
+ col2 = win_col + @col + @col_offset + @curpos + @cols_panned + @pad_offset
390
+ $log.debug "TV SFC #{@name} setting c to #{col2} #{win_col} #{@col} #{@col_offset} #{@curpos} "
391
+ #@form.setrowcol @form.row, col
392
+ setrowcol nil, col2
393
+ @repaint_footer_required = true
394
+ end
395
+ def cursor_forward #:nodoc:
396
+ maxlen = @maxlen || @width-@internal_width
397
+ repeatm {
398
+ if @curpos < @width and @curpos < maxlen-1 # else it will do out of box
399
+ @curpos += 1
400
+ addcol 1
401
+ else
402
+ @pcol += 1 if @pcol <= row_length
403
+ end
404
+ }
405
+ set_form_col
406
+ #@repaint_required = true
407
+ @repaint_footer_required = true # 2010-01-23 22:41
408
+ end
409
+ def addcol num #:nodoc:
410
+ #@repaint_required = true
411
+ @repaint_footer_required = true # 2010-01-23 22:41
412
+ if @form
413
+ @form.addcol num
414
+ else
415
+ @parent_component.form && @parent_component.form.addcol(num)
416
+ end
417
+ end
418
+ def addrowcol row,col #:nodoc:
419
+ #@repaint_required = true
420
+ @repaint_footer_required = true # 2010-01-23 22:41
421
+ if @form
422
+ @form.addrowcol row, col
423
+ else
424
+ @parent_component.form.addrowcol num
425
+ end
426
+ end
427
+ def cursor_backward #:nodoc:
428
+ repeatm {
429
+ if @curpos > 0
430
+ @curpos -= 1
431
+ set_form_col
432
+ #addcol -1
433
+ elsif @pcol > 0
434
+ @pcol -= 1
435
+ end
436
+ }
437
+ #@repaint_required = true
438
+ @repaint_footer_required = true # 2010-01-23 22:41
439
+ end
440
+ # gives offset of next line, does not move
441
+ # @deprecated
442
+ def next_line #:nodoc:
443
+ @list[@current_index+1]
444
+ end
445
+ # @deprecated
446
+ def do_relative_row num #:nodoc:
447
+ raise "unused will be removed"
448
+ yield @list[@current_index+num]
449
+ end
450
+
451
+ # supply with a color parser, if you supplied formatted text
452
+ def color_parser f
453
+ $log.debug "XXX: parser setting color_parser to #{f} "
454
+ #@window.color_parser f
455
+ @color_parser = f
456
+ end
457
+
458
+
459
+
460
+ ## NOTE: earlier print_border was called only once in constructor, but when
461
+ ##+ a window is resized, and destroyed, then this was never called again, so the
462
+ ##+ border would not be seen in splitpane unless the width coincided exactly with
463
+ ##+ what is calculated in divider_location.
464
+ def paint #:nodoc:
465
+
466
+ $log.debug "XXX TEXTVIEW repaint HAPPENING #{@current_index} "
467
+ my_win = nil
468
+ if @form
469
+ my_win = @form.window
470
+ else
471
+ my_win = @target_window
472
+ end
473
+ @graphic = my_win unless @graphic
474
+ if @formatted_text
475
+ $log.debug "XXX: INSIDE FORMATTED TEXT "
476
+
477
+ # I don't want to do this in 20 places and then have to change
478
+ # it and retest. Let me push it to util.
479
+ l = RubyCurses::Utils.parse_formatted_text(@color_parser,
480
+ @formatted_text)
481
+
482
+ #cp = Chunks::ColorParser.new @color_parser
483
+ #l = []
484
+ #@formatted_text.each { |e| l << cp.convert_to_chunk(e) }
485
+
486
+ @old_content_type = @content_type
487
+ text(l)
488
+ @formatted_text = nil
489
+
490
+ end
491
+
492
+ print_borders if (@suppress_borders == false && @repaint_all) # do this once only, unless everything changes
493
+ rc = row_count
494
+ maxlen = @maxlen || @width-@internal_width
495
+ #$log.debug " #{@name} textview repaint width is #{@width}, height is #{@height} , maxlen #{maxlen}/ #{@maxlen}, #{@graphic.name} roff #{@row_offset} coff #{@col_offset}"
496
+ tm = get_content
497
+ tr = @toprow
498
+ acolor = get_color $datacolor
499
+ h = scrollatrow()
500
+ r,c = rowcol
501
+ @longest_line = @width-@internal_width #maxlen
502
+ 0.upto(h) do |hh|
503
+ crow = tr+hh
504
+ if crow < rc
505
+ #focussed = @current_index == crow ? true : false
506
+ #selected = is_row_selected crow
507
+ content = tm[crow]
508
+ # next call modified string. you may wanna dup the string.
509
+ # rlistbox does
510
+ # scrolling fails if you do not dup, since content gets truncated
511
+ if content.is_a? String
512
+ content = content.dup
513
+ sanitize(content) if @sanitization_required
514
+ truncate content
515
+ @graphic.printstring r+hh, c, "%-*s" % [@width-@internal_width,content],
516
+ acolor, @attr
517
+ elsif content.is_a? Chunks::ChunkLine
518
+ # clear the line first
519
+ @graphic.printstring r+hh, c, " "* (@width-@internal_width),
520
+ acolor, @attr
521
+ # move back
522
+ @graphic.wmove r+hh, c
523
+ # either we have to loop through and put in default color and attr
524
+ # or pass it to show_col
525
+ a = get_attrib @attrib
526
+ # FIXME this does not clear till the eol
527
+ @graphic.show_colored_chunks content, acolor, a, @width-@internal_width, @pcol
528
+ elsif content.is_a? Chunks::Chunk
529
+ raise "TODO chunk in textview"
530
+ elsif content.is_a? Array
531
+ # several chunks in one row - NOTE Very experimental may change
532
+ if content[0].is_a? Array
533
+ # clearing the line since colored_chunks does not yet XXX FIXME if possible
534
+ @graphic.printstring r+hh, c, " "* (@width-@internal_width),
535
+ acolor, @attr
536
+ @graphic.wmove r+hh, c
537
+ # either we have to loop through and put in default color and attr
538
+ # or pass it to show_col
539
+ a = get_attrib @attrib
540
+ # FIXME this does not clear till the eol
541
+ # # NOTE 2013-03-08 - 17:37 pls add width to avoid overflow
542
+ @graphic.show_colored_chunks content, acolor, a, @width-@internal_width
543
+ #@graphic.show_colored_chunks content, acolor, a
544
+ else
545
+ # a single row chunk - NOTE Very experimental may change
546
+ text = content[1].dup
547
+ sanitize(text) if @sanitization_required
548
+ truncate text
549
+ @graphic.printstring r+hh, c, "%-*s" % [@width-@internal_width,text],
550
+ content[0] || acolor, content[2] || @attr
551
+ end
552
+ end
553
+
554
+ # highlighting search results.
555
+ if @search_found_ix == tr+hh
556
+ if !@find_offset.nil?
557
+ # handle exceed bounds, and if scrolling
558
+ if @find_offset1 < maxlen+@pcol and @find_offset > @pcol
559
+ @graphic.mvchgat(y=r+hh, x=c+@find_offset-@pcol, @find_offset1-@find_offset, Ncurses::A_NORMAL, $reversecolor, nil)
560
+ end
561
+ end
562
+ end
563
+
564
+ else
565
+ # clear rows
566
+ @graphic.printstring r+hh, c, " " * (@width-@internal_width), acolor,@attr
567
+ end
568
+ end
569
+
570
+
571
+ @repaint_required = false
572
+ @repaint_footer_required = true
573
+ @repaint_all = false
574
+ # 2011-10-15
575
+ @widget_scrolled = false
576
+ @record_changed = false
577
+ @property_changed = false
578
+ @old_pcol = @pcol
579
+
580
+ end
581
+ # takes a block, this way anyone extending this class can just pass a block to do his job
582
+ # This modifies the string
583
+ def sanitize content #:nodoc:
584
+
585
+ if content.is_a? String
586
+ content.chomp!
587
+ # trying out since gsub giving #<ArgumentError: invalid byte sequence in UTF-8> 2011-09-11
588
+
589
+ content.replace(content.encode("ASCII-8BIT", :invalid => :replace, :undef => :replace, :replace => "?")) if content.respond_to?(:encode)
590
+ content.gsub!(/[\t\n\r]/, ' ') # don't display tab or newlines
591
+ content.gsub!(/[^[:print:]]/, '') # don't display non print characters
592
+ else
593
+ content
594
+ end
595
+ end
596
+ # returns only the visible portion of string taking into account display length
597
+ # and horizontal scrolling. MODIFIES STRING
598
+ def truncate content #:nodoc:
599
+ _maxlen = @maxlen || @width-@internal_width
600
+ _maxlen = @width-@internal_width if _maxlen > @width-@internal_width # take care of decrease in width
601
+ if !content.nil?
602
+ if content.length > _maxlen # only show maxlen
603
+ @longest_line = content.length if content.length > @longest_line
604
+ #content = content[@pcol..@pcol+maxlen-1]
605
+ content.replace(content[@pcol..@pcol+_maxlen-1] || "")
606
+ else
607
+ if @pcol > 0
608
+ content.replace(content[@pcol..-1] || "")
609
+ end
610
+ end
611
+ end
612
+ content
613
+ end
614
+ ## this is just a test of prompting user for a string
615
+ #+ as an alternative to the dialog.
616
+ def getstr prompt, maxlen=80 #:nodoc:
617
+ tabc = Proc.new {|str| Dir.glob(str +"*") }
618
+ config={}; config[:tab_completion] = tabc
619
+ config[:default] = "test"
620
+ config[:display_length] = 11
621
+ $log.debug " inside getstr before call "
622
+ ret, str = rbgetstr(@form.window, @row+@height-1, @col+1, prompt, maxlen, config)
623
+ $log.debug " rbgetstr returned #{ret} ,#{str}."
624
+ return "" if ret != 0
625
+ return str
626
+ end
627
+ ##
628
+ # dynamically load a module and execute init method.
629
+ # Hopefully, we can get behavior like this such as vieditable or multibuffers
630
+ def load_module requirename, includename
631
+ require "rbhex/#{requirename}"
632
+ extend Object.const_get("#{includename}")
633
+ send("#{requirename}_init") #if respond_to? "#{includename}_init"
634
+ end
635
+ # on pressing ENTER we send user some info, the calling program
636
+ # would bind :PRESS
637
+ #--
638
+ # FIXME we can create this once and reuse
639
+ #++
640
+ def fire_action_event
641
+ return if @list.nil? || @list.size == 0
642
+ require 'rbhex/core/include/ractionevent'
643
+ aev = TextActionEvent.new self, :PRESS, current_value().to_s, @current_index, @curpos
644
+ fire_handler :PRESS, aev
645
+ end
646
+ # called by listscrollable, used by scrollbar ENTER_ROW
647
+ def on_enter_row arow
648
+ fire_handler :ENTER_ROW, self
649
+ @repaint_required = true
650
+ end
651
+ # added 2010-09-30 18:48 so standard with other components, esp on enter
652
+ # NOTE: the on_enter repaint required causes this to be repainted 2 times
653
+ # if its the first object, once with the entire form, then with on_enter.
654
+ def on_enter
655
+ if @list.nil? || @list.size == 0
656
+ Ncurses.beep
657
+ return :UNHANDLED
658
+ end
659
+ on_enter_row @current_index
660
+ set_form_row
661
+ @repaint_required = true
662
+ super
663
+ true
664
+ end
665
+ def pipe_file
666
+ # TODO ask process name from user
667
+ output = pipe_output 'munpack', @list
668
+ if output && !output.empty?
669
+ set_content output
670
+ end
671
+ end
672
+ # returns array of lines after running command on string passed
673
+ # TODO: need to close pipe other's we'll have a process lying
674
+ # around forever.
675
+ def pipe_output (pipeto, str)
676
+ case str
677
+ when String
678
+ #str = str.split "\n"
679
+ # okay
680
+ when Array
681
+ str = str.join "\n"
682
+ end
683
+ #pipeto = '/usr/sbin/sendmail -t'
684
+ #pipeto = %q{mail -s "my title" rahul}
685
+ if pipeto != nil # i was taking pipeto from a hash, so checking
686
+ proc = IO.popen(pipeto, "w+")
687
+ proc.puts str
688
+ proc.close_write
689
+ proc.readlines
690
+ end
691
+ end
692
+ def saveas name=nil, config={}
693
+ unless name
694
+ name = rb_gets "File to save as: "
695
+ return if name.nil? || name == ""
696
+ end
697
+ exists = File.exists? name
698
+ if exists # need to prompt
699
+ return unless rb_confirm("Overwrite existing file? ")
700
+ end
701
+ l = getvalue
702
+ File.open(name, "w"){ |f|
703
+ l.each { |line| f.puts line }
704
+ #l.each { |line| f.write line.gsub(/\r/,"\n") }
705
+ }
706
+ rb_puts "#{name} written."
707
+ end
708
+
709
+ # edit content of textview in EDITOR and bring back
710
+ # NOTE: does not maintain content_type, so if you edit ansi text,
711
+ # it will come back in as normal text
712
+ def edit_external
713
+ require 'rbhex/core/include/appmethods'
714
+ require 'tempfile'
715
+ f = Tempfile.new("rbhex")
716
+ l = self.text
717
+ l.each { |line| f.puts line }
718
+ fp = f.path
719
+ f.flush
720
+
721
+ editor = ENV['EDITOR'] || 'vi'
722
+ vimp = %x[which #{editor}].chomp
723
+ ret = shell_out "#{vimp} #{fp}"
724
+ if ret
725
+ lines = File.open(f,'r').readlines
726
+ set_content(lines, :content_type => @old_content_type)
727
+ end
728
+ end
729
+ def init_actions
730
+ editor = ENV['EDITOR'] || 'vi'
731
+ am = action_manager()
732
+ am.add_action( Action.new("&Edit in #{editor} ") { edit_external } )
733
+ am.add_action( Action.new("&Saveas") { saveas() })
734
+ end
735
+
736
+
737
+ end # class textview
738
+
739
+ end # modul