rbcurse-core 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
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