rkumar-rbcurse 0.1.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 (52) hide show
  1. data/CHANGELOG +1577 -0
  2. data/README.txt +310 -0
  3. data/examples/qdfilechooser.rb +68 -0
  4. data/examples/rfe.rb +853 -0
  5. data/examples/rfe_renderer.rb +69 -0
  6. data/examples/test1.rb +242 -0
  7. data/examples/test2.rb +498 -0
  8. data/examples/testcombo.rb +95 -0
  9. data/examples/testkeypress.rb +61 -0
  10. data/examples/testmenu.rb +105 -0
  11. data/examples/testtable.rb +266 -0
  12. data/examples/testtabp.rb +106 -0
  13. data/examples/testtodo.rb +532 -0
  14. data/examples/viewtodo.rb +512 -0
  15. data/lib/rbcurse.rb +7 -0
  16. data/lib/rbcurse/action.rb +31 -0
  17. data/lib/rbcurse/applicationheader.rb +57 -0
  18. data/lib/rbcurse/celleditor.rb +120 -0
  19. data/lib/rbcurse/checkboxcellrenderer.rb +69 -0
  20. data/lib/rbcurse/colormap.rb +133 -0
  21. data/lib/rbcurse/comboboxcellrenderer.rb +45 -0
  22. data/lib/rbcurse/defaultlistselectionmodel.rb +49 -0
  23. data/lib/rbcurse/keylabelprinter.rb +143 -0
  24. data/lib/rbcurse/listcellrenderer.rb +99 -0
  25. data/lib/rbcurse/listkeys.rb +33 -0
  26. data/lib/rbcurse/listscrollable.rb +216 -0
  27. data/lib/rbcurse/listselectable.rb +67 -0
  28. data/lib/rbcurse/mapper.rb +108 -0
  29. data/lib/rbcurse/orderedhash.rb +77 -0
  30. data/lib/rbcurse/rcombo.rb +243 -0
  31. data/lib/rbcurse/rdialogs.rb +183 -0
  32. data/lib/rbcurse/rform.rb +845 -0
  33. data/lib/rbcurse/rinputdataevent.rb +36 -0
  34. data/lib/rbcurse/rlistbox.rb +804 -0
  35. data/lib/rbcurse/rmenu.rb +666 -0
  36. data/lib/rbcurse/rmessagebox.rb +325 -0
  37. data/lib/rbcurse/rpopupmenu.rb +754 -0
  38. data/lib/rbcurse/rtabbedpane.rb +259 -0
  39. data/lib/rbcurse/rtable.rb +1296 -0
  40. data/lib/rbcurse/rtextarea.rb +673 -0
  41. data/lib/rbcurse/rtextview.rb +335 -0
  42. data/lib/rbcurse/rwidget.rb +1731 -0
  43. data/lib/rbcurse/scrollable.rb +301 -0
  44. data/lib/rbcurse/selectable.rb +94 -0
  45. data/lib/rbcurse/table/tablecellrenderer.rb +85 -0
  46. data/lib/rbcurse/table/tabledatecellrenderer.rb +102 -0
  47. data/lib/ver/keyboard.rb +150 -0
  48. data/lib/ver/keyboard2.rb +170 -0
  49. data/lib/ver/ncurses.rb +102 -0
  50. data/lib/ver/window.rb +369 -0
  51. data/test/test_rbcurse.rb +0 -0
  52. metadata +117 -0
@@ -0,0 +1,183 @@
1
+ =begin
2
+ * Name: dialogs so user can do basic stuff in one line.
3
+ * Description:
4
+ * Author: rkumar
5
+
6
+ --------
7
+ * Date: 2008-12-30 12:22
8
+ * License:
9
+ Same as Ruby's License (http://www.ruby-lang.org/LICENSE.txt)
10
+
11
+ TODO:
12
+ Add one get_string(message, len, regex ...)
13
+ Add select_one (message, values, default)
14
+ =end
15
+ require 'rubygems'
16
+ require 'ncurses'
17
+ require 'logger'
18
+ require 'rbcurse/rwidget'
19
+ require 'rbcurse/rmessagebox'
20
+
21
+ ##
22
+ # pops up a modal box with a message and an OK button.
23
+ # No return value.
24
+ # Usage:
25
+ # alert("You did not enter anything!")
26
+ # alert("You did not enter anything!", "title"=>"Wake Up")
27
+ # alert("You did not enter anything!", {"title"=>"Wake Up", "bgcolor"=>"blue", "color"=>"white"})
28
+ # block currently ignored. don't know what to do, can't pass it to MB since alread sending in a block
29
+ def alert text, config={}, &block
30
+ title = config['title'] || "Alert"
31
+ #instance_eval &block if block_given?
32
+ mb = RubyCurses::MessageBox.new nil, config do
33
+ title title
34
+ message text
35
+ button_type :ok
36
+ end
37
+ end
38
+ def confirm text, config={}, &block
39
+ title = config['title'] || "Confirm"
40
+ #instance_eval &block if block_given?
41
+ mb = RubyCurses::MessageBox.new nil, config do
42
+ title title
43
+ message text
44
+ button_type :yes_no
45
+ end
46
+ return mb.selected_index == 0 ? :YES : :NO
47
+ end
48
+
49
+ ##
50
+ # allows user entry of a string.
51
+ # In config you may pass Field related properties such as chars_allowed, valid_regex, values, etc.
52
+ def get_string(message, len=20, default="", config={})
53
+ config["maxlen"]=len
54
+ title = config["title"] || "Input required"
55
+ mb = RubyCurses::MessageBox.new nil, config do
56
+ title title
57
+ message message
58
+ type :input
59
+ button_type :ok
60
+ default_value default
61
+ end
62
+ return mb.input_value
63
+ end
64
+
65
+ ##
66
+ # Added 2009-02-05 13:16
67
+ # get a string from user with some additional checkboxes and optionally supply default values
68
+ # Usage:
69
+ #sel, inp, hash = get_string_with_options("Enter a filter pattern", 20, "*", {"checkboxes" => ["case sensitive","reverse"], "checkbox_defaults"=>[true, false]})
70
+ # sel, inp, hash = get_string_with_options("Enter a filter pattern", 20, "*", {"checkboxes" => ["case sensitive","reverse"]})
71
+ # $log.debug " POPUP: #{sel}: #{inp}, #{hash['case sensitive']}, #{hash['reverse']}"
72
+ #
73
+ # @param: message to print,
74
+ # @param: length of entry field
75
+ # @param: default value of field
76
+ # @param: configuration of box or field
77
+ # checkboxes: array of strings to use as checkboxes
78
+ # checkbox_defaults : array of true/false default values for each cb
79
+ # @return: int 0 OK, 1 cancel pressed
80
+ # @return: string value entered
81
+ # @return: hash of strings and booleans for checkboxes and values
82
+ #
83
+ def get_string_with_options(message, len=20, default="", config={})
84
+ title = config["title"] || "Input required"
85
+ input_config = config["input_config"] || {}
86
+ checks = config["checkboxes"]
87
+ checkbox_defaults = config["checkbox_defaults"] || []
88
+
89
+ height = config["height"] || 1
90
+ display_length = config["display_length"] || 30
91
+
92
+ r = 3
93
+ c = 4
94
+ mform = RubyCurses::Form.new nil
95
+ message_label = RubyCurses::Label.new mform, {'text' => message, "name"=>"message_label","row" => r, "col" => c, "display_length" => display_length, "height" => height, "attr"=>"reverse"}
96
+
97
+ r += 1
98
+ input = RubyCurses::Field.new mform, input_config do
99
+ name "input"
100
+ row r
101
+ col c
102
+ display_length display_length
103
+ maxlen len
104
+ set_buffer default
105
+ end
106
+ if !checks.nil?
107
+ r += 2
108
+ checks.each_with_index do |cbtext,ix|
109
+ field = RubyCurses::CheckBox.new mform do
110
+ text cbtext
111
+ name cbtext
112
+ value checkbox_defaults[ix]||false
113
+ color 'black'
114
+ bgcolor 'white'
115
+ row r
116
+ col c
117
+ end
118
+ r += 1
119
+ end
120
+ end
121
+ radios = config["radiobuttons"]
122
+ if !radios.nil?
123
+ radio_default = config["radio_default"] || radios[0]
124
+ radio = RubyCurses::Variable.new radio_default
125
+ r += 2
126
+ radios.each_with_index do |cbtext,ix|
127
+ field = RubyCurses::RadioButton.new mform do
128
+ variable radio
129
+ text cbtext
130
+ value cbtext
131
+ color 'black'
132
+ bgcolor 'white'
133
+ row r
134
+ col c
135
+ end
136
+ r += 1
137
+ end
138
+ end
139
+ mb = RubyCurses::MessageBox.new mform do
140
+ title title
141
+ button_type :ok_cancel
142
+ default_button 0
143
+ end
144
+ hash = {}
145
+ if !checks.nil?
146
+ checks.each do |c|
147
+ hash[c] = mform.by_name[c].getvalue
148
+ end
149
+ end
150
+ hash["radio"] = radio.get_value unless radio.nil?
151
+ # returns button index (0 = OK), value of field, hash containing values of checkboxes
152
+ return mb.selected_index, mform.by_name['input'].getvalue, hash
153
+ end
154
+
155
+ =begin
156
+ http://www.kammerl.de/ascii/AsciiSignature.php
157
+ ___
158
+ |__ \
159
+ ) |
160
+ / /
161
+ |_|
162
+ (_)
163
+
164
+ _
165
+ | |
166
+ | |
167
+ | |
168
+ |_|
169
+ (_)
170
+
171
+
172
+ _____ _ _____
173
+ | __ \ | | / ____|
174
+ | |__) | _| |__ _ _ | | _ _ _ __ ___ ___ ___
175
+ | _ / | | | '_ \| | | | | | | | | | '__/ __|/ _ \/ __|
176
+ | | \ \ |_| | |_) | |_| | | |___| |_| | | \__ \ __/\__ \
177
+ |_| \_\__,_|_.__/ \__, | \_____\__,_|_| |___/\___||___/
178
+ __/ |
179
+ |___/
180
+
181
+ =end
182
+
183
+
@@ -0,0 +1,845 @@
1
+ =begin
2
+ * Name: TextView and TextArea
3
+ * $Id$
4
+ * Description Our own form with own simple field to make life easier. Ncurses forms are great, but
5
+ * honestly the sequence sucks and is a pain after a while for larger scale work.
6
+ * We need something less restrictive.
7
+ * Author: rkumar (arunachalesha)
8
+ TODO
9
+ * Field/entry
10
+ - textvariable - bding field to a var so the var is updated
11
+ *
12
+
13
+ 2008-12-24 18:01 moved menu etc to rmenu.rb
14
+ --------
15
+ * Date: 2008-11-14 23:43
16
+ * License:
17
+ Same as Ruby's License (http://www.ruby-lang.org/LICENSE.txt)
18
+
19
+ =end
20
+ require 'rubygems'
21
+ require 'ncurses'
22
+ require 'logger'
23
+ require 'rbcurse'
24
+ require 'rbcurse/scrollable'
25
+ require 'rbcurse/selectable'
26
+ require 'rbcurse/rinputdataevent'
27
+
28
+ include Ncurses
29
+ include RubyCurses
30
+ module RubyCurses
31
+ extend self
32
+
33
+ ## a multiline text editing widget
34
+ # TODO - giving data to user - adding newlines, and withog adding.
35
+ # - respect newlines for incoming data
36
+ #
37
+ class OldTextArea < Widget
38
+ include Scrollable
39
+ dsl_accessor :height
40
+ dsl_accessor :title
41
+ dsl_accessor :title_attrib # bold, reverse, normal
42
+ dsl_accessor :list # the array of data to be sent by user
43
+ dsl_accessor :maxlen # the array of data to be sent by user
44
+ attr_reader :toprow
45
+ attr_reader :prow
46
+ attr_reader :winrow
47
+ dsl_accessor :auto_scroll # boolean, keeps view at end as data is inserted.
48
+ dsl_accessor :print_footer
49
+ dsl_accessor :editable # allow editing
50
+ attr_accessor :modified # boolean, value modified or not 2009-01-08 12:29
51
+
52
+ def initialize form, config={}, &block
53
+ @focusable = true
54
+ @editable = true
55
+ @left_margin = 1
56
+ @row = 0
57
+ @col = 0
58
+ @curpos = 0
59
+ @show_focus = false
60
+ @list = []
61
+ super
62
+ @row_offset = @col_offset = 1
63
+ @orig_col = @col
64
+ # this does result in a blank line if we insert after creating. That's required at
65
+ # present if we wish to only insert
66
+ if @list.empty?
67
+ @list << "\r"
68
+ end
69
+ @scrollatrow = @height-2
70
+ @content_rows = @list.length
71
+ @win = @form.window
72
+ init_scrollable
73
+ print_borders
74
+ @maxlen ||= @width-2
75
+ end
76
+ def rowcol
77
+ # $log.debug "textarea rowcol : #{@row+@row_offset+@winrow}, #{@col+@col_offset}"
78
+ return @row+@row_offset+@winrow, @col+@col_offset
79
+ end
80
+ ##
81
+ # this avoids wrapping. Better to use the <<.
82
+ def Oinsert off0, *data
83
+ @list.insert off0, *data
84
+ # fire_handler :CHANGE, self # 2008-12-09 14:56 NOT SURE
85
+ end
86
+ # private
87
+ def wrap_text(txt, col = @maxlen)
88
+ $log.debug "inside wrap text for :#{txt}"
89
+ txt.gsub(/(.{1,#{col}})( +|$\n?)|(.{1,#{col}})/,
90
+ "\\1\\3\n")
91
+ end
92
+ def remove_all
93
+ @list = []
94
+ end
95
+ ##
96
+ # trying to wrap and insert
97
+ def insert off0, data
98
+ if data.length > @maxlen
99
+ data = wrap_text data
100
+ # $log.debug "after wrap text done :#{data}"
101
+ data = data.split("\n")
102
+ data[-1] << "\r" #XXXX
103
+ else
104
+ data << "\r" if data[-1,1] != "\r" #XXXX
105
+ end
106
+ data.each do |row|
107
+ @list.insert off0, row
108
+ off0 += 1
109
+ end
110
+ #$log.debug " AFTER INSERT: #{@list}"
111
+ end
112
+ ##
113
+ # wraps line sent in if longer than maxlen
114
+ # Typically a line is sent in. We wrap and put a hard return at end.
115
+ def << data
116
+ if data.length > @maxlen
117
+ $log.debug "wrapped append for #{data}"
118
+ data = wrap_text data
119
+ $log.debug "after wrap text for :#{data}"
120
+ data = data.split("\n")
121
+ # 2009-01-01 22:24 the \n was needed so we would put a space at time of writing.
122
+ # we need a soft return so a space can be added when pushing down.
123
+ # commented off 2008-12-28 21:59
124
+ #data.each {|line| @list << line+"\n"}
125
+ data.each {|line| @list << line}
126
+ @list[-1] << "\r" #XXXX
127
+ else
128
+ $log.debug "normal append for #{data}"
129
+ data << "\r" if data[-1,1] != "\r" #XXXX
130
+ @list << data
131
+ end
132
+ goto_end if @auto_scroll # to test out.
133
+ self
134
+ end
135
+ def wrap_para line=@prow
136
+ line ||= 0
137
+ l=[]
138
+ while true
139
+ if @list[line].nil? or @list[line]=="" or @list[line]==13 #"\r"
140
+ break
141
+ end
142
+ $log.debug "lastchar #{@list[line][-1]}, appending: #{@list[line]}]"
143
+ t = @list[line]
144
+ l << t.strip
145
+ @list.delete_at line
146
+ break if t[-1]==13 # "\r"
147
+ # line += 1
148
+ end
149
+ str=l.join(" ")
150
+ $log.debug " sending insert : #{str}."
151
+ insert line, str
152
+ end
153
+ ##
154
+ # private
155
+ def print_borders
156
+ window = @form.window
157
+ color = $datacolor
158
+ window.print_border @row, @col, @height, @width, color
159
+ print_title
160
+ =begin
161
+ hline = "+%s+" % [ "-"*(width-((1)*2)) ]
162
+ hline2 = "|%s|" % [ " "*(width-((1)*2)) ]
163
+ window.printstring( row=startrow, col=startcol, hline, color)
164
+ print_title
165
+ (startrow+1).upto(startrow+height-1) do |row|
166
+ window.printstring(row, col=startcol, hline2, color)
167
+ end
168
+ window.printstring(startrow+height, col=startcol, hline, color)
169
+ =end
170
+
171
+ end
172
+ # private
173
+ def print_title
174
+ @form.window.printstring( @row, @col+(@width-@title.length)/2, @title, $datacolor, @title_attrib) unless @title.nil?
175
+ end
176
+ # text_area print footer
177
+ def print_foot
178
+ @footer_attrib ||= Ncurses::A_REVERSE
179
+ footer = "R: #{@prow+1}, C: #{@curpos}, #{@list.length} lines "
180
+ @form.window.printstring( @row + @height, @col+2, footer, $datacolor, @footer_attrib)
181
+ end
182
+ ### FOR scrollable ###
183
+ def get_content
184
+ @list
185
+ end
186
+ def get_window
187
+ @form.window
188
+ end
189
+ ### FOR scrollable ###
190
+ def repaint # textarea
191
+ paint
192
+ print_foot if @print_footer
193
+ end
194
+ def getvalue
195
+ @list
196
+ end
197
+ # textarea
198
+
199
+ def handle_key ch
200
+ @buffer = @list[@prow]
201
+ if @buffer.nil? and @list.length == 0
202
+ @list << "\n" # changed space to newline so wrapping puts a line.
203
+ @buffer = @list[@prow]
204
+ end
205
+ return if @buffer.nil?
206
+ $log.debug "TA: before: curpos #{@curpos} blen: #{@buffer.length}"
207
+ # on any line if the cursor is ahead of buffer length, ensure its on last position
208
+ # what if the buffer is somehow gt maxlen ??
209
+ if @curpos > @buffer.length
210
+ addcol(@buffer.length-@curpos)+1
211
+ @curpos = @buffer.length
212
+ end
213
+ $log.debug "TA: after : curpos #{@curpos} blen: #{@buffer.length}, w: #{@width} max #{@maxlen}"
214
+ pre_key
215
+ case ch
216
+ when ?\C-n
217
+ scroll_forward
218
+ when ?\C-p
219
+ scroll_backward
220
+ when ?\C-[
221
+ goto_start #cursor_start of buffer
222
+ when ?\C-]
223
+ goto_end # cursor_end of buffer
224
+ when KEY_UP
225
+ #select_prev_row
226
+ ret = up
227
+ when KEY_DOWN
228
+ ret = down
229
+ when KEY_ENTER, 10, 13
230
+ insert_break
231
+ when KEY_LEFT
232
+ cursor_backward
233
+ when KEY_RIGHT
234
+ cursor_forward
235
+ when KEY_BACKSPACE, 127
236
+ if @editable # checking here means that i can programmatically bypass!!
237
+ delete_prev_char
238
+ #fire_handler :CHANGE, self # 2008-12-22 15:23
239
+ end
240
+ when 330, ?\C-d # delete char
241
+ if @editable
242
+ delete_curr_char
243
+ #fire_handler :CHANGE, self # 2008-12-22 15:23
244
+ end
245
+ when ?\C-k # delete till eol
246
+ if @editable
247
+ if @buffer == ""
248
+ delete_line
249
+ #fire_handler :CHANGE, self # 2008-12-22 15:23
250
+ else
251
+ delete_eol
252
+ #fire_handler :CHANGE, self # 2008-12-22 15:23
253
+ end
254
+ end
255
+ when ?\C-u
256
+ undo_delete
257
+ when ?\C-a
258
+ cursor_bol
259
+ when ?\C-e
260
+ cursor_eol
261
+ #set_form_col @buffer.length
262
+ else
263
+ #$log.debug(" textarea ch #{ch}")
264
+ ret = putc ch
265
+ return ret if ret == :UNHANDLED
266
+ end
267
+ post_key
268
+ set_form_row
269
+ set_form_col # testing 2008-12-26 19:37
270
+ end
271
+ def undo_delete
272
+ # added 2008-11-27 12:43 paste delete buffer into insertion point
273
+ @buffer.insert @curpos, @delete_buffer unless @delete_buffer.nil?
274
+ fire_handler :CHANGE, InputDataEvent.new(@curpos,@curpos+@delete_buffer.length, self, :INSERT, @prow, @delete_buffer) # 2008-12-24 18:34
275
+ end
276
+ def insert_break
277
+ return -1 unless @editable
278
+ # insert a blank row and append rest of this line to cursor
279
+ $log.debug "ENTER PRESSED at #{@curpos}, on row #{@prow}"
280
+ @delete_buffer = (delete_eol || "")
281
+ @list[@prow] << "\r"
282
+ $log.debug "DELETE BUFFER #{@delete_buffer}"
283
+ @list.insert @prow+1, @delete_buffer
284
+ @curpos = 0
285
+ down
286
+ @form.col = @orig_col + @col_offset
287
+ #addrowcol 1,0
288
+ @form.row = @row + 1 + @winrow
289
+ #fire_handler :CHANGE, self # 2008-12-09 14:56
290
+ fire_handler :CHANGE, InputDataEvent.new(@curpos,@curpos+@delete_buffer.length, self, :INSERT, @prow, @delete_buffer) # 2008-12-24 18:34
291
+ end
292
+ # puts cursor on correct row.
293
+ def set_form_row
294
+ @form.row = @row + 1 + @winrow
295
+ end
296
+ # set cursor on correct column
297
+ def set_form_col col1=@curpos
298
+ @curpos = col1
299
+ cursor_bounds_check
300
+ @form.col = @orig_col + @col_offset + @curpos
301
+ $log.debug "sfc: #{@orig_col}, #{@col_offset}. #{@curpos}. "
302
+ end
303
+ def cursor_bounds_check
304
+ max = buffer_len()
305
+ @curpos = max if @curpos > max # check 2008-12-27 00:02
306
+ end
307
+ def buffer_len
308
+ @list[@prow].nil? ? 0 : @list[@prow].chomp().length
309
+ end
310
+ def do_current_row # :yields current row
311
+ yield @list[@prow]
312
+ @buffer = @list[@prow]
313
+ end
314
+ def delete_eol
315
+ return -1 unless @editable
316
+ pos = @curpos-1
317
+ @delete_buffer = @buffer[@curpos..-1]
318
+ # if pos is 0, pos-1 becomes -1, end of line!
319
+ @list[@prow] = pos == -1 ? "" : @buffer[0..pos]
320
+ $log.debug "delete EOL :pos=#{pos}, #{@delete_buffer}: row: #{@list[@prow]}:"
321
+ @buffer = @list[@prow]
322
+ cursor_backward if @curpos > 0 # now cursor back goes up to prev line
323
+ #fire_handler :CHANGE, self # 2008-12-09 14:56
324
+ fire_handler :CHANGE, InputDataEvent.new(@curpos,@curpos+@delete_buffer.length, self, :DELETE, @prow, @delete_buffer) # 2008-12-24 18:34
325
+ return @delete_buffer
326
+ end
327
+ def cursor_forward num=1
328
+ $log.debug "next char cp #{@curpos}, #{@buffer.length}. wi: #{@width}"
329
+ #if @curpos < @width and @curpos < @maxlen-1 # else it will do out of box
330
+ if @curpos < buffer_len()
331
+ @curpos += 1
332
+ addcol 1
333
+ else # trying this out 2008-12-26 20:18
334
+ @curpos = 0
335
+ down
336
+ end
337
+ cursor_bounds_check
338
+ end
339
+ def addcol num
340
+ @form.addcol num
341
+ end
342
+ def addrowcol row,col
343
+ @form.addrowcol row, col
344
+ end
345
+ def cursor_backward
346
+ if @curpos > 0
347
+ @curpos -= 1
348
+ addcol -1
349
+ else # trying this out 2008-12-26 20:18
350
+ ret = up
351
+ cursor_eol if ret != -1
352
+ end
353
+ end
354
+ def delete_line line=@prow
355
+ return -1 unless @editable
356
+ $log.debug "called delete line"
357
+ @delete_buffer = @list.delete_at line
358
+ @buffer = @list[@prow]
359
+ if @buffer.nil?
360
+ up
361
+ @form.row = @row + 1 + @winrow
362
+ end
363
+ #fire_handler :CHANGE, self # 2008-12-09 14:56
364
+ fire_handler :CHANGE, InputDataEvent.new(@curpos,@curpos+@delete_buffer.length, self, :DELETE, @prow, @delete_buffer) # 2008-12-24 18:34
365
+ end
366
+ def delete_curr_char num=1
367
+ return -1 unless @editable
368
+ num.times do
369
+ delete_at
370
+ set_modified
371
+ end
372
+ end
373
+ def delete_prev_char num=1
374
+ return -1 if !@editable
375
+ num.times do
376
+ if @curpos <= 0
377
+ join_to_prev_line
378
+ return
379
+ end
380
+ @curpos -= 1 if @curpos > 0
381
+ delete_at
382
+ set_modified
383
+ addcol -1
384
+ end
385
+ end
386
+ # private
387
+ # when backspace pressed in position zero if the previous line is filled we may have to bring
388
+ # down the last word and join, rather than go up
389
+ def join_to_prev_line
390
+ return -1 unless @editable
391
+ return if @prow == 0
392
+ oldcurpos = @curpos
393
+ oldprow = @prow
394
+ prev = @list[@prow-1].chomp
395
+ prevlen = prev.length
396
+ # 2008-12-26 21:37 delete previous line if nothing there. This moves entire buffer up.
397
+ if prevlen == 0
398
+ delete_line @prow-1
399
+ up
400
+ return
401
+ end
402
+ space_left = @maxlen - prev.length
403
+ # BUG. carry full words up, or if no space then bring down last word of prev lien and join with first
404
+ carry_up = words_in_length @buffer, space_left #@buffer[0..space_left] # XXX
405
+ if carry_up.nil?
406
+ # carry down last word
407
+ prev_wd = remove_last_word @prow-1
408
+ @buffer.insert 0, prev_wd
409
+ @curpos = prev_wd.length
410
+ $log.debug " carry up nil! prev_wd (#{prev_wd}) len:#{prev_wd.length}"
411
+ fire_handler :CHANGE, InputDataEvent.new(0,prev_wd.length, self, :INSERT, oldprow, prev_wd) # 2008-12-26 23:07
412
+ else
413
+ $log.debug " carrying up #{carry_up.length} #{carry_up}, space: #{space_left}"
414
+ @list[@prow-1]=prev + carry_up
415
+ space_left2 = @buffer[(carry_up.length+1)..-1]
416
+ @list[@prow]=space_left2 #if !space_left2.nil?
417
+ @list[@prow] ||= ""
418
+ up
419
+ addrowcol -1,0
420
+ @curpos = prevlen
421
+ fire_handler :CHANGE, InputDataEvent.new(oldcurpos,carry_up.length, self, :DELETE, oldprow, carry_up) # 2008-12-24 18:34
422
+ fire_handler :CHANGE, InputDataEvent.new(prevlen,carry_up.length, self, :INSERT, oldprow-1, carry_up) # 2008-12-24 18:34
423
+ end
424
+ @form.col = @orig_col + @col_offset + @curpos
425
+
426
+ # $log.debug "carry up: nil" if carry_up.nil?
427
+ # $log.debug "listrow nil " if @list[@prow].nil?
428
+ # $log.debug "carry up: #{carry_up} prow:#{@list[@prow]}"
429
+ end
430
+ ##
431
+ # return as many words as fit into len for carrying up..
432
+ # actually there is a case of when the next char (len+1) is a white space or word boundary. XXX
433
+ def words_in_length buff, len
434
+ return nil if len == 0
435
+ str = buff[0..len]
436
+ ix = str.rindex(/\s/)
437
+ $log.debug " str #{str} len #{len} ix #{ix} , buff #{buff}~"
438
+ return nil if ix.nil?
439
+ ix = ix > 0 ? ix - 1 : ix
440
+ $log.debug " str[]:#{str[0..ix]}~ len #{len} ix #{ix} , buff #{buff}~"
441
+ return str[0..ix]
442
+ end
443
+ # push the last word from given line to next
444
+ # I have modified it to push all words that are exceeding maxlen.
445
+ # This was needed for if i push 10 chars to next line, and the last word is less then the line will
446
+ # exceed. So i must push as many words as exceed length.
447
+ def push_last_word lineno=@prow
448
+ #lastspace = @buffer.rindex(" ")
449
+ #lastspace = @list[lineno].rindex(/ \w/)
450
+ line = @list[lineno]
451
+ line = @list[lineno][0..@maxlen+1] if line.length > @maxlen
452
+ lastspace = line.rindex(/ \w/)
453
+ $log.debug " PUSH:2 #{lastspace},#{line},"
454
+ if !lastspace.nil?
455
+ lastchars = @list[lineno][lastspace+1..-1]
456
+ @list[lineno] = @list[lineno][0..lastspace]
457
+ $log.debug "PUSH_LAST:ls:#{lastspace},lw:#{lastchars},lc:#{lastchars[-1]},:#{@list[lineno]}$"
458
+ if lastchars[-1,1] == "\r" or @list[lineno+1].nil?
459
+ # open a new line and keep the 10 at the end.
460
+ append_row lineno, lastchars
461
+ else
462
+ # check for soft tab \n - NO EVEN THIS LOGIC IS WRONG.
463
+ #if lastchars[-1,1] == "\n"
464
+ if lastchars[-1,1] != ' ' and @list[lineno+1][0,1] !=' '
465
+ #@list[lineno+1].insert 0, lastchars + ' '
466
+ insert_wrap lineno+1, 0, lastchars + ' '
467
+ else
468
+ #@list[lineno+1].insert 0, lastchars
469
+ insert_wrap lineno+1, 0, lastchars
470
+ end
471
+ end
472
+ return lastchars, lastspace
473
+ end
474
+ return nil
475
+ end
476
+ ##
477
+ # this attempts to recursively insert into a row, seeing that any stuff exceeding is pushed down further.
478
+ # Yes, it should check for a para end and insert. Currently it could add to next para.
479
+ def insert_wrap lineno, pos, lastchars
480
+ @list[lineno].insert pos, lastchars
481
+ len = @list[lineno].length
482
+ if len > @maxlen
483
+ push_last_word lineno #- sometime i may push down 10 chars but the last word is less
484
+ end
485
+ end
486
+ ##
487
+ # add one char. careful, i shoved a string in yesterday.
488
+ def putch char
489
+ @buffer ||= @list[@prow]
490
+ return -1 if !@editable #or @buffer.length >= @maxlen
491
+ if @chars_allowed != nil
492
+ return if char.match(@chars_allowed).nil?
493
+ end
494
+ raise "putch expects only one char" if char.length != 1
495
+ oldcurpos = @curpos
496
+ $log.debug "putch : pr:#{@prow}, cp:#{@curpos}, char:#{char}, lc:#{@buffer[-1]}, buf:(#{@buffer})"
497
+ @buffer.insert(@curpos, char)
498
+ @curpos += 1
499
+ $log.debug "putch INS: cp:#{@curpos}, max:#{@maxlen}, buf:(#{@buffer.length})"
500
+ if @curpos-1 > @maxlen or @buffer.length()-1 > @maxlen
501
+ lastchars, lastspace = push_last_word @prow
502
+ #$log.debug "last sapce #{lastspace}, lastchars:#{lastchars},lc:#{lastchars[-1]}, #{@list[@prow]} "
503
+ ## wrap on word XX If last char is 10 then insert line
504
+ @buffer = @list[@prow]
505
+ if @curpos-1 > @maxlen or @curpos-1 > @buffer.length()-1
506
+ ret = down
507
+ # keep the cursor in the same position in the string that was pushed down.
508
+ @curpos = oldcurpos - lastspace #lastchars.length # 0
509
+ end
510
+ end
511
+ set_form_row
512
+ @buffer = @list[@prow]
513
+ set_form_col
514
+ @modified = true
515
+ #fire_handler :CHANGE, self # 2008-12-09 14:56
516
+ fire_handler :CHANGE, InputDataEvent.new(oldcurpos,@curpos, self, :INSERT, @prow, char) # 2008-12-24 18:34
517
+ 0
518
+ end
519
+ def append_row lineno=@prow, chars=""
520
+ $log.debug "append row sapce:#{chars}."
521
+ @list.insert lineno+1, chars
522
+ end
523
+ ##
524
+ # removes and returns last word in given line number, or nil if no whitespace
525
+ def remove_last_word lineno
526
+ @list[lineno].chomp!
527
+ line=@list[lineno]
528
+ lastspace = line.rindex(" ")
529
+ if !lastspace.nil?
530
+ lastchars = line[lastspace+1..-1]
531
+ @list[lineno].slice!(lastspace..-1)
532
+ $log.debug " remove_last: lastspace #{lastspace},#{lastchars},#{@list[lineno]}"
533
+ fire_handler :CHANGE, InputDataEvent.new(lastspace,lastchars.length, self, :DELETE, lineno, lastchars) # 2008-12-26 23:06
534
+ return lastchars
535
+ end
536
+ return nil
537
+ end
538
+
539
+ def putc c
540
+ if c >= 32 and c <= 126
541
+ ret = putch c.chr
542
+ if ret == 0
543
+ # addcol 1
544
+ set_modified
545
+ return 0
546
+ end
547
+ end
548
+ return :UNHANDLED
549
+ end
550
+ # DELETE func
551
+ def delete_at index=@curpos
552
+ return -1 if !@editable
553
+ $log.debug "dele : #{@prow} #{@buffer} #{index}"
554
+ char = @buffer.slice!(@curpos,1) # changed added ,1 and take char for event
555
+ # if no newline at end of this then bring up prev character/s till maxlen
556
+ # NO WE DON'T DO THIS ANYLONGER 2008-12-26 21:09 lets see
557
+ =begin
558
+ if @buffer[-1,1]!="\r"
559
+ @buffer[-1]=" " if @buffer[-1,1]=="\n"
560
+ if !next_line.nil? and next_line.length > 0
561
+ move_chars_up
562
+ end
563
+ end
564
+ =end
565
+ #@modified = true 2008-12-22 15:31
566
+ set_modified true
567
+ #fire_handler :CHANGE, self # 2008-12-09 14:56
568
+ fire_handler :CHANGE, InputDataEvent.new(@curpos,@curpos, self, :DELETE, @prow, char) # 2008-12-24 18:34
569
+ end
570
+ # move up one char from next row to current, used when deleting in a line
571
+ # should not be called if line ends in "\r"
572
+ def move_char_up
573
+ @list[@prow] << @list[@prow+1].slice!(0)
574
+ delete_line(@prow+1) if next_line().length==0
575
+ end
576
+ # tries to move up as many as possible
577
+ # should not be called if line ends in "\r"
578
+ def move_chars_up
579
+ oldprow = @prow
580
+ oldcurpos = @curpos
581
+ space_left = @maxlen - @buffer.length
582
+ can_move = [space_left, next_line.length].min
583
+ carry_up = @list[@prow+1].slice!(0, can_move)
584
+ @list[@prow] << carry_up
585
+ delete_line(@prow+1) if next_line().length==0
586
+ fire_handler :CHANGE, InputDataEvent.new(oldcurpos,oldcurpos+can_move, self, :INSERT, oldprow, carry_up) # 2008-12-24 18:34
587
+ end
588
+ ## returns next line, does not move to it,
589
+ def next_line
590
+ @list[@prow+1]
591
+ end
592
+ def current_line
593
+ @list[@prow]
594
+ end
595
+ def do_relative_row num
596
+ yield @list[@prow+num]
597
+ end
598
+ def set_modified tf=true
599
+ @modified = tf
600
+ @form.modified = true if tf
601
+ end
602
+ def cursor_eol
603
+ $log.error "ERROR !!! bufferlen gt maxlen #{@buffer.length}, #{@maxlen}" if @buffer.length > @maxlen
604
+ set_form_col current_line().chomp().length()-1
605
+ end
606
+ def cursor_bol
607
+ set_form_col 0
608
+ end
609
+ def to_s
610
+ l = getvalue
611
+ str = ""
612
+ old = " "
613
+ l.each_with_index do |line, i|
614
+ tmp = line.gsub("\n","")
615
+ tmp.gsub!("\r", "\n")
616
+ if old[-1,1] !~ /\s/ and tmp[0,1] !~ /\s/
617
+ str << " "
618
+ end
619
+ str << tmp
620
+ old = tmp
621
+ end
622
+ str
623
+ end
624
+ end # class textarea
625
+ ##
626
+ # A viewable read only box. Can scroll.
627
+ # Intention is to be able to change content dynamically - the entire list.
628
+ # Use set_content to set content, or just update the list attrib
629
+ # TODO -
630
+ # - searching, goto line - DONE
631
+ class OldTextView < Widget
632
+ include Scrollable
633
+ dsl_accessor :height # height of viewport
634
+ dsl_accessor :title # set this on top
635
+ dsl_accessor :title_attrib # bold, reverse, normal
636
+ dsl_accessor :footer_attrib # bold, reverse, normal
637
+ dsl_accessor :list # the array of data to be sent by user
638
+ dsl_accessor :maxlen # max len to be displayed
639
+ attr_reader :toprow # the toprow in the view (offsets are 0)
640
+ attr_reader :prow # the row on which cursor/focus is
641
+ attr_reader :winrow # the row in the viewport/window
642
+ dsl_accessor :print_footer
643
+
644
+ def initialize form, config={}, &block
645
+ @focusable = true
646
+ @editable = false
647
+ @left_margin = 1
648
+ @row = 0
649
+ @col = 0
650
+ @show_focus = false # don't highlight row under focus
651
+ @list = []
652
+ super
653
+ @row_offset = @col_offset = 1
654
+ @orig_col = @col
655
+ # this does result in a blank line if we insert after creating. That's required at
656
+ # present if we wish to only insert
657
+ @scrollatrow = @height-2
658
+ @content_rows = @list.length
659
+ @win = @form.window
660
+ init_scrollable
661
+ print_borders
662
+ @maxlen ||= @width-2
663
+ end
664
+ ##
665
+ # send in a list
666
+ # e.g. set_content File.open("README.txt","r").readlines
667
+ #
668
+ def set_content list
669
+ @list = list
670
+ end
671
+ ## display this row on top
672
+ def top_row(*val)
673
+ if val.empty?
674
+ @toprow
675
+ else
676
+ @toprow = val[0] || 0
677
+ @prow = val[0] || 0
678
+ end
679
+ end
680
+ ## ---- for listscrollable ---- ##
681
+ def scrollatrow
682
+ @height - 2
683
+ end
684
+ def row_count
685
+ @list.length
686
+ end
687
+ ##
688
+ # returns row of first match of given regex (or nil if not found)
689
+ def find_first_match regex
690
+ @list.each_with_index do |row, ix|
691
+ return ix if !row.match(regex).nil?
692
+ end
693
+ return nil
694
+ end
695
+ def rowcol
696
+ #$log.debug "textarea rowcol : #{@row+@row_offset+@winrow}, #{@col+@col_offset}"
697
+ return @row+@row_offset+@winrow, @col+@col_offset
698
+ end
699
+ def wrap_text(txt, col = @maxlen)
700
+ $log.debug "inside wrap text for :#{txt}"
701
+ txt.gsub(/(.{1,#{col}})( +|$\n?)|(.{1,#{col}})/,
702
+ "\\1\\3\n")
703
+ end
704
+ def print_borders
705
+ window = @form.window
706
+ color = $datacolor
707
+ window.print_border @row, @col, @height, @width, color
708
+ print_title
709
+ =begin
710
+ hline = "+%s+" % [ "-"*(width-((1)*2)) ]
711
+ hline2 = "|%s|" % [ " "*(width-((1)*2)) ]
712
+ window.printstring(row=startrow, col=startcol, hline, color)
713
+ print_title
714
+ (startrow+1).upto(startrow+height-1) do |row|
715
+ window.printstring( row, col=startcol, hline2, color)
716
+ end
717
+ window.printstring( startrow+height, col=startcol, hline, color)
718
+ =end
719
+
720
+ end
721
+ def print_title
722
+ @form.window.printstring( @row, @col+(@width-@title.length)/2, @title, $datacolor, @title_attrib) unless @title.nil?
723
+ end
724
+ def print_foot
725
+ @footer_attrib ||= Ncurses::A_REVERSE
726
+ footer = "R: #{@prow+1}, C: #{@curpos}, #{@list.length} lines "
727
+ @form.window.printstring( @row + @height, @col+2, footer, $datacolor, @footer_attrib)
728
+ end
729
+ ### FOR scrollable ###
730
+ def get_content
731
+ @list
732
+ end
733
+ def get_window
734
+ @form.window
735
+ end
736
+ ### FOR scrollable ###
737
+ def repaint # textview
738
+ paint
739
+ print_foot if @print_footer
740
+ end
741
+ def getvalue
742
+ @list
743
+ end
744
+ # textview
745
+ # [ ] scroll left right DONE
746
+ def handle_key ch
747
+ @buffer = @list[@prow]
748
+ if @buffer.nil? and @list.length == 0
749
+ @list << "\r"
750
+ @buffer = @list[@prow]
751
+ end
752
+ return if @buffer.nil?
753
+ $log.debug " before: curpos #{@curpos} blen: #{@buffer.length}"
754
+ if @curpos > @buffer.length
755
+ addcol(@buffer.length-@curpos)+1
756
+ @curpos = @buffer.length
757
+ end
758
+ $log.debug "TV after loop : curpos #{@curpos} blen: #{@buffer.length}"
759
+ pre_key
760
+ case ch
761
+ when ?\C-n
762
+ scroll_forward
763
+ when ?\C-p
764
+ scroll_backward
765
+ when ?0, ?\C-[
766
+ goto_start #start of buffer # cursor_start
767
+ when ?\C-]
768
+ goto_end # end / bottom cursor_end
769
+ when KEY_UP
770
+ #select_prev_row
771
+ ret = up
772
+ #addrowcol -1,0 if ret != -1 or @winrow != @oldwinrow # positions the cursor up
773
+ @form.row = @row + 1 + @winrow
774
+ when KEY_DOWN
775
+ ret = down
776
+ @form.row = @row + 1 + @winrow
777
+ when KEY_LEFT
778
+ cursor_backward
779
+ when KEY_RIGHT
780
+ cursor_forward
781
+ when KEY_BACKSPACE, 127
782
+ cursor_backward
783
+ when 330
784
+ cursor_backward
785
+ when ?\C-a
786
+ # take care of data that exceeds maxlen by scrolling and placing cursor at start
787
+ set_form_col 0
788
+ @pcol = 0
789
+ when ?\C-e
790
+ # take care of data that exceeds maxlen by scrolling and placing cursor at end
791
+ blen = @buffer.rstrip.length
792
+ if blen < @maxlen
793
+ set_form_col blen
794
+ else
795
+ @pcol = blen-@maxlen
796
+ set_form_col @maxlen-1
797
+ end
798
+ else
799
+ $log.debug("TEXTVIEW XXX ch #{ch}")
800
+ return :UNHANDLED
801
+ end
802
+ post_key
803
+ # XXX 2008-11-27 13:57 trying out
804
+ set_form_row
805
+ end
806
+ # puts cursor on correct row.
807
+ def set_form_row
808
+ @form.row = @row + 1 + @winrow
809
+ end
810
+ # set cursor on correct column tview
811
+ def set_form_col col=@curpos
812
+ @curpos = col
813
+ @form.col = @orig_col + @col_offset + @curpos
814
+ end
815
+ def cursor_forward
816
+ if @curpos < @width and @curpos < @maxlen-1 # else it will do out of box
817
+ @curpos += 1
818
+ addcol 1
819
+ else
820
+ # XXX 2008-11-26 23:03 trying out
821
+ @pcol += 1 if @pcol <= @buffer.length
822
+ end
823
+ end
824
+ def addcol num
825
+ @form.addcol num
826
+ end
827
+ def addrowcol row,col
828
+ @form.addrowcol row, col
829
+ end
830
+ def cursor_backward
831
+ if @curpos > 0
832
+ @curpos -= 1
833
+ addcol -1
834
+ elsif @pcol > 0 # XXX added 2008-11-26 23:05
835
+ @pcol -= 1
836
+ end
837
+ end
838
+ def next_line
839
+ @list[@prow+1]
840
+ end
841
+ def do_relative_row num
842
+ yield @list[@prow+num]
843
+ end
844
+ end # class textview
845
+ end # modul