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,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