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