rbcurse 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. data/CHANGELOG +1570 -0
  2. data/History.txt +6 -0
  3. data/Manifest.txt +54 -0
  4. data/README.txt +304 -0
  5. data/Rakefile +28 -0
  6. data/examples/qdfilechooser.rb +68 -0
  7. data/examples/rfe.rb +853 -0
  8. data/examples/rfe_renderer.rb +69 -0
  9. data/examples/test1.rb +242 -0
  10. data/examples/test2.rb +498 -0
  11. data/examples/testcombo.rb +95 -0
  12. data/examples/testkeypress.rb +61 -0
  13. data/examples/testmenu.rb +105 -0
  14. data/examples/testtable.rb +266 -0
  15. data/examples/testtabp.rb +106 -0
  16. data/examples/testtodo.rb +532 -0
  17. data/examples/viewtodo.rb +512 -0
  18. data/lib/rbcurse/action.rb +31 -0
  19. data/lib/rbcurse/applicationheader.rb +57 -0
  20. data/lib/rbcurse/celleditor.rb +120 -0
  21. data/lib/rbcurse/checkboxcellrenderer.rb +69 -0
  22. data/lib/rbcurse/colormap.rb +133 -0
  23. data/lib/rbcurse/comboboxcellrenderer.rb +45 -0
  24. data/lib/rbcurse/defaultlistselectionmodel.rb +49 -0
  25. data/lib/rbcurse/keylabelprinter.rb +143 -0
  26. data/lib/rbcurse/listcellrenderer.rb +99 -0
  27. data/lib/rbcurse/listkeys.rb +33 -0
  28. data/lib/rbcurse/listscrollable.rb +216 -0
  29. data/lib/rbcurse/listselectable.rb +67 -0
  30. data/lib/rbcurse/mapper.rb +108 -0
  31. data/lib/rbcurse/orderedhash.rb +77 -0
  32. data/lib/rbcurse/rcombo.rb +243 -0
  33. data/lib/rbcurse/rdialogs.rb +183 -0
  34. data/lib/rbcurse/rform.rb +845 -0
  35. data/lib/rbcurse/rinputdataevent.rb +36 -0
  36. data/lib/rbcurse/rlistbox.rb +804 -0
  37. data/lib/rbcurse/rmenu.rb +666 -0
  38. data/lib/rbcurse/rmessagebox.rb +325 -0
  39. data/lib/rbcurse/rpopupmenu.rb +754 -0
  40. data/lib/rbcurse/rtabbedpane.rb +259 -0
  41. data/lib/rbcurse/rtable.rb +1296 -0
  42. data/lib/rbcurse/rtextarea.rb +673 -0
  43. data/lib/rbcurse/rtextview.rb +335 -0
  44. data/lib/rbcurse/rwidget.rb +1731 -0
  45. data/lib/rbcurse/scrollable.rb +301 -0
  46. data/lib/rbcurse/selectable.rb +94 -0
  47. data/lib/rbcurse/table/tablecellrenderer.rb +85 -0
  48. data/lib/rbcurse/table/tabledatecellrenderer.rb +102 -0
  49. data/lib/rbcurse.rb +7 -0
  50. data/lib/ver/keyboard.rb +150 -0
  51. data/lib/ver/keyboard2.rb +170 -0
  52. data/lib/ver/ncurses.rb +102 -0
  53. data/lib/ver/window.rb +369 -0
  54. data/test/test_rbcurse.rb +0 -0
  55. metadata +118 -0
@@ -0,0 +1,804 @@
1
+ =begin
2
+ * Name: rlistbox: editable scrollable lists
3
+ * Description
4
+ * Author: rkumar (arunachalesha)
5
+ * Date: 2008-11-19 12:49
6
+ * License: Same as Ruby's License (http://www.ruby-lang.org/LICENSE.txt)
7
+ * This file started on 2009-01-13 22:18 (broken off rwidgets.rb)
8
+ TODO
9
+ =end
10
+ require 'rubygems'
11
+ require 'ncurses'
12
+ require 'logger'
13
+ require 'rbcurse'
14
+ require 'rbcurse/listcellrenderer'
15
+ require 'rbcurse/listkeys'
16
+
17
+
18
+ include Ncurses
19
+ module RubyCurses
20
+ extend self
21
+ ##
22
+ # When an event is fired by Listbox, contents are changed, then this object will be passed
23
+ # to trigger
24
+ # shamelessly plugged from a legacy language best unnamed
25
+ # type is CONTENTS_CHANGED, INTERVAL_ADDED, INTERVAL_REMOVED
26
+ class ListDataEvent
27
+ attr_accessor :index0, :index1, :source, :type
28
+ def initialize index0, index1, source, type
29
+ @index0 = index0
30
+ @index1 = index1
31
+ @source = source
32
+ @type = type
33
+ end
34
+ def to_s
35
+ "#{@type.to_s}, #{@source}, #{@index0}, #{@index1}"
36
+ end
37
+ def inspect
38
+ "#{@type.to_s}, #{@source}, #{@index0}, #{@index1}"
39
+ end
40
+ end
41
+ # http://www.java2s.com/Code/JavaAPI/javax.swing.event/ListDataEventCONTENTSCHANGED.htm
42
+ # should we extend array of will that open us to misuse
43
+ class ListDataModel
44
+ include Enumerable
45
+ include RubyCurses::EventHandler
46
+ attr_accessor :selected_index
47
+
48
+ def initialize anarray
49
+ @list = anarray.dup
50
+ end
51
+ # changd on 2009-01-14 12:28 based on ..
52
+ # http://www.ruby-forum.com/topic/175637#769030
53
+ def each(&blk)
54
+ @list.each(&blk)
55
+ end
56
+ #def each
57
+ # @list.each { |item| yield item }
58
+ #end
59
+ # not sure how to do this XXX removed on 2009-01-14 12:28
60
+ #def <=>(other)
61
+ # @list <=> other
62
+ #end
63
+ def index obj
64
+ @list.index(obj)
65
+ end
66
+ def length ; @list.length; end
67
+
68
+ def insert off0, *data
69
+ @list.insert off0, *data
70
+ lde = ListDataEvent.new(off0, off0+data.length-1, self, :INTERVAL_ADDED)
71
+ fire_handler :LIST_DATA_EVENT, lde
72
+ end
73
+ def append data
74
+ @list << data
75
+ lde = ListDataEvent.new(@list.length-1, @list.length-1, self, :INTERVAL_ADDED)
76
+ fire_handler :LIST_DATA_EVENT, lde
77
+ end
78
+ def update off0, data
79
+ @list[off0] = data
80
+ lde = ListDataEvent.new(off0, off0, self, :CONTENTS_CHANGED)
81
+ fire_handler :LIST_DATA_EVENT, lde
82
+ end
83
+ def []=(off0, data)
84
+ update off0, data
85
+ end
86
+ def [](off0)
87
+ @list[off0]
88
+ end
89
+ def delete_at off0
90
+ ret=@list.delete_at off0
91
+ lde = ListDataEvent.new(off0, off0, self, :INTERVAL_REMOVED)
92
+ fire_handler :LIST_DATA_EVENT, lde
93
+ return ret
94
+ end
95
+ def remove_all
96
+ lde = ListDataEvent.new(0, @list.size, self, :INTERVAL_REMOVED)
97
+ @list = []
98
+ fire_handler :LIST_DATA_EVENT, lde
99
+ end
100
+ def delete obj
101
+ off0 = @list.index obj
102
+ return nil if off0.nil?
103
+ ret=@list.delete off0
104
+ lde = ListDataEvent.new(off0, off0, self, :INTERVAL_REMOVED)
105
+ fire_handler :LIST_DATA_EVENT, lde
106
+ return ret
107
+ end
108
+ def include?(obj)
109
+ return @list.include?(obj)
110
+ end
111
+ def values
112
+ @list.dup
113
+ end
114
+ def on_enter_row object
115
+ $log.debug " XXX on_enter_row of list_data"
116
+ fire_handler :ENTER_ROW, object
117
+ end
118
+ # ##
119
+ # added 2009-01-14 01:00
120
+ # searches between given range of rows (def 0 and end)
121
+ # returns row index of first match of given regex (or nil if not found)
122
+ def find_match regex, ix0=0, ix1=length()
123
+ $log.debug " find_match got #{regex} #{ix0} #{ix1}"
124
+ @last_regex = regex
125
+ @search_start_ix = ix0
126
+ @search_end_ix = ix1
127
+ #@search_found_ix = nil
128
+ @list.each_with_index do |row, ix|
129
+ next if ix < ix0
130
+ break if ix > ix1
131
+ if !row.match(regex).nil?
132
+ @search_found_ix = ix
133
+ return ix
134
+ end
135
+ end
136
+ return nil
137
+ end
138
+ ##
139
+ # continues previous search
140
+ def find_next
141
+ raise "No previous search" if @last_regex.nil?
142
+ start = @search_found_ix && @search_found_ix+1 || 0
143
+ return find_match @last_regex, start, @search_end_ix
144
+ end
145
+ ##
146
+ # find backwards
147
+ # Using this to start a search or continue search
148
+ def find_prev regex=@last_regex, start = @search_found_ix
149
+ raise "No previous search" if @last_regex.nil?
150
+ $log.debug " find_prev #{@search_found_ix} : #{@current_index}"
151
+ start -= 1 unless start == 0
152
+ @last_regex = regex
153
+ @search_start_ix = start
154
+ start.downto(0) do |ix|
155
+ row = @list[ix]
156
+ if !row.match(regex).nil?
157
+ @search_found_ix = ix
158
+ return ix
159
+ end
160
+ end
161
+ return nil
162
+ #return find_match @last_regex, start, @search_end_ix
163
+ end
164
+
165
+ alias :to_array :values
166
+ end # class ListDataModel
167
+ ##
168
+ # scrollable, selectable list of items
169
+ # TODO Add events for item add/remove and selection change
170
+ # added event LIST_COMBO_SELECT fired whenever a select/deselect is done.
171
+ # - I do not know how this works in Tk so only the name is copied..
172
+ # - @selected contains indices of selected objects.
173
+ # - currently the first argument of event is row (the row selected/deselected). Should it
174
+ # be the object.
175
+ # - this event could change when range selection is allowed.
176
+ #
177
+
178
+ ##
179
+ # pops up a list of values for selection
180
+ # 2008-12-10
181
+ class PopupList
182
+ include DSL
183
+ include RubyCurses::EventHandler
184
+ dsl_accessor :title
185
+ dsl_accessor :row, :col, :height, :width
186
+ dsl_accessor :layout
187
+ attr_reader :config
188
+ attr_reader :selected_index # button index selected by user
189
+ attr_reader :window # required for keyboard
190
+ dsl_accessor :list_selection_mode # true or false allow multiple selection
191
+ dsl_accessor :relative_to # a widget, if given row and col are relative to widgets windows
192
+ # layout
193
+ dsl_accessor :max_visible_items # how many to display
194
+ dsl_accessor :list_config # hash with values for the list to use
195
+ dsl_accessor :valign
196
+ attr_reader :listbox
197
+
198
+ def initialize aconfig={}, &block
199
+ @config = aconfig
200
+ @selected_index = -1
201
+ @list_config ||= {}
202
+ @config.each_pair { |k,v| instance_variable_set("@#{k}",v) }
203
+ instance_eval &block if block_given?
204
+ @list_config.each_pair { |k,v| instance_variable_set("@#{k}",v) }
205
+ @height ||= [@max_visible_items || 10, @list.length].min
206
+ #$log.debug " POPUP XXX #{@max_visible_items} ll:#{@list.length} h:#{@height}"
207
+ # get widgets absolute coords
208
+ if !@relative_to.nil?
209
+ layout = @relative_to.form.window.layout
210
+ @row = @row + layout[:top]
211
+ @col = @col + layout[:left]
212
+ end
213
+ if !@valign.nil?
214
+ case @valign.to_sym
215
+ when :BELOW
216
+ @row += 1
217
+ when :ABOVE
218
+ @row -= @height+1
219
+ @row = 0 if @row < 0
220
+ when :CENTER
221
+ @row -= @height/2
222
+ @row = 0 if @row < 0
223
+ else
224
+ end
225
+ end
226
+
227
+ layout(1+height, @width+4, @row, @col) # changed 2 to 1, 2008-12-17 13:48
228
+ @window = VER::Window.new(@layout)
229
+ @form = RubyCurses::Form.new @window
230
+ @window.bkgd(Ncurses.COLOR_PAIR($reversecolor));
231
+ #@window.attron(Ncurses.COLOR_PAIR($reversecolor));
232
+ #@window.wclear
233
+ #@window.attroff(Ncurses.COLOR_PAIR($reversecolor));
234
+ @window.wrefresh
235
+ @panel = @window.panel # useless line ?
236
+ Ncurses::Panel.update_panels
237
+ # @message_row = @message_col = 2
238
+ # print_borders
239
+ # print_title
240
+ print_input # creates the listbox
241
+ @form.repaint
242
+ @window.wrefresh
243
+ handle_keys
244
+ end
245
+ def list alist=nil
246
+ return @list if alist.nil?
247
+ @list = ListDataModel.new(alist)
248
+ # will we need this ? listbox made each time so data should be fresh
249
+ #@list.bind(:LIST_DATA_EVENT) { |e| list_data_changed() }
250
+ end
251
+ def list_data_model ldm
252
+ raise "Expecting list_data_model" unless ldm.is_a? RubyCurses::ListDataModel
253
+ @list = ldm
254
+ # will we need this ? listbox made each time so data should be fresh
255
+ #@list.bind(:LIST_DATA_EVENT) { |e| list_data_changed() }
256
+ end
257
+ ##
258
+ def input_value
259
+ #return @listbox.getvalue if !@listbox.nil?
260
+ return @listbox.focussed_index if !@listbox.nil?
261
+ end
262
+ ## popuplist
263
+ def stopping?
264
+ @stop
265
+ end
266
+ ## popuplist
267
+ def handle_keys
268
+ begin
269
+ while((ch = @window.getchar()) != 999 )
270
+ case ch
271
+ when -1
272
+ next
273
+ else
274
+ press ch
275
+ break if @stop
276
+ end
277
+ end
278
+ ensure
279
+ destroy
280
+ end
281
+ return 0 #@selected_index
282
+ end
283
+ ##
284
+ # TODO get next match for key
285
+ def press ch
286
+ $log.debug "popup handle_keys : #{ch}" if ch != -1
287
+ case ch
288
+ when -1
289
+ return
290
+ when KEY_F1, 27, ?\C-q # 27/ESC does not come here since gobbled by keyboard.rb
291
+ @stop = true
292
+ return
293
+ when KEY_ENTER, 10, 13
294
+ fire_handler :PRESS, @listbox.focussed_index
295
+ # since Listbox is handling enter, COMBO_SELECT will not be fired
296
+ # $log.debug "popup ENTER : #{@selected_index} "
297
+ # $log.debug "popup ENTER : #{field.name}" if !field.nil?
298
+ @stop = true
299
+ return
300
+ when 9
301
+ @form.select_next_field
302
+ else
303
+ # fields must return unhandled else we will miss hotkeys.
304
+ # On messageboxes, often if no edit field, then O and C are hot.
305
+ field = @form.get_current_field
306
+ handled = field.handle_key ch
307
+
308
+ if handled == :UNHANDLED
309
+ @stop = true
310
+ return
311
+ end
312
+ end
313
+ @form.repaint
314
+ Ncurses::Panel.update_panels();
315
+ Ncurses.doupdate();
316
+ @window.wrefresh
317
+ end
318
+ def print_input
319
+ r = c = 0
320
+ width = @layout[:width]
321
+ height = @layout[:height]
322
+ height = @height
323
+ parent = @relative_to
324
+ defaultvalue = @default_value || ""
325
+ list = @list
326
+ selection_mode = @list_selection_mode
327
+ default_values = @default_values
328
+ @list_config['color'] ||= 'black'
329
+ @list_config['bgcolor'] ||= 'yellow'
330
+ @listbox = RubyCurses::Listbox.new @form, @list_config do
331
+ name "input"
332
+ row r
333
+ col c
334
+ # attr 'reverse'
335
+ width width
336
+ height height
337
+ list_data_model list
338
+ # ?? XXX display_length 30
339
+ # set_buffer defaultvalue
340
+ selection_mode selection_mode
341
+ default_values default_values
342
+ is_popup true
343
+ #add_observer parent
344
+
345
+ end
346
+ end
347
+ # may need to be upgraded to new one XXX FIXME
348
+ def configure(*val , &block)
349
+ case val.size
350
+ when 1
351
+ return @config[val[0]]
352
+ when 2
353
+ @config[val[0]] = val[1]
354
+ instance_variable_set("@#{val[0]}", val[1])
355
+ end
356
+ instance_eval &block if block_given?
357
+ end
358
+ def cget param
359
+ @config[param]
360
+ end
361
+
362
+ def layout(height=0, width=0, top=0, left=0)
363
+ @layout = { :height => height, :width => width, :top => top, :left => left }
364
+ end
365
+ def destroy
366
+ @window.destroy if !@window.nil?
367
+ end
368
+ end # class PopupList
369
+ ##
370
+ # this is the new LISTBOX, based on new scrollable.
371
+ #
372
+ class Listbox < Widget
373
+ require 'rbcurse/listscrollable'
374
+ require 'rbcurse/listselectable'
375
+ require 'rbcurse/defaultlistselectionmodel'
376
+ require 'rbcurse/celleditor'
377
+ include ListScrollable
378
+ include ListSelectable
379
+ include RubyCurses::ListKeys
380
+ dsl_accessor :height
381
+ dsl_accessor :title
382
+ dsl_property :title_attrib # bold, reverse, normal
383
+ # dsl_accessor :list # the array of data to be sent by user
384
+ attr_reader :toprow
385
+ # attr_reader :prow
386
+ # attr_reader :winrow
387
+ # dsl_accessor :selection_mode # allow multiple select or not
388
+ # dsl_accessor :list_variable # a variable values are shown from this
389
+ dsl_accessor :default_values # array of default values
390
+ dsl_accessor :is_popup # if it is in a popup and single select, selection closes
391
+ attr_accessor :current_index
392
+ #dsl_accessor :cell_renderer
393
+ dsl_accessor :selected_color, :selected_bgcolor, :selected_attr
394
+ dsl_accessor :max_visible_items # how many to display 2009-01-11 16:15
395
+ dsl_accessor :cell_editing_allowed
396
+ dsl_property :show_selector
397
+ dsl_property :row_selected_symbol # 2009-01-12 12:01 changed from selector to selected
398
+ dsl_property :row_unselected_symbol # added 2009-01-12 12:00
399
+ dsl_property :left_margin
400
+ # please set these in he constructor block. Settin them later will have no effect
401
+ # since i would have bound them to actions
402
+ dsl_accessor :KEY_ROW_SELECTOR
403
+ dsl_accessor :KEY_GOTO_TOP
404
+ dsl_accessor :KEY_GOTO_BOTTOM
405
+ dsl_accessor :KEY_CLEAR_SELECTION
406
+ dsl_accessor :KEY_NEXT_SELECTION
407
+ dsl_accessor :KEY_PREV_SELECTION
408
+ dsl_accessor :valign # 2009-01-17 18:32
409
+
410
+ def initialize form, config={}, &block
411
+ @focusable = true
412
+ @editable = false
413
+ @row = 0
414
+ @col = 0
415
+ # data of listbox
416
+ @list = []
417
+ # any special attribs such as status to be printed in col1, or color (selection)
418
+ @list_attribs = {}
419
+ super
420
+ @current_index ||= 0
421
+ @row_offset = @col_offset = 1
422
+ @content_rows = @list.length
423
+ @selection_mode ||= 'multiple'
424
+ @win = @form.window
425
+ print_borders unless @win.nil? # in messagebox we don;t have window as yet!
426
+ # next 2 lines carry a redundancy
427
+ select_default_values
428
+ # when the combo box has a certain row in focus, the popup should have the same row in focus
429
+
430
+ init_vars
431
+ install_list_keys
432
+
433
+ if !@list.selected_index.nil?
434
+ set_focus_on @list.selected_index # the new version
435
+ end
436
+ end
437
+ def init_vars
438
+ @to_print_borders ||= 1
439
+ @repaint_required = true
440
+ @toprow = @pcol = 0
441
+ if @show_selector
442
+ @row_selected_symbol ||= '>'
443
+ @row_unselected_symbol ||= ' '
444
+ @left_margin ||= @row_selected_symbol.length
445
+ end
446
+ @left_margin ||= 0
447
+ end
448
+ def install_bindings
449
+
450
+ end
451
+
452
+ ##
453
+ # getter and setter for selection_mode
454
+ # Must be called after creating model, so no duplicate. Since one may set in model directly.
455
+ def selection_mode(*val)
456
+ raise "ListSelectionModel not yet created!" if @list_selection_model.nil?
457
+ if val.empty?
458
+ @list_selection_model.selection_mode
459
+ else
460
+ @list_selection_model.selection_mode = val[0]
461
+ end
462
+ end
463
+ def row_count
464
+ @list.length
465
+ end
466
+ # added 2009-01-07 13:05 so new scrollable can use
467
+ def scrollatrow
468
+ @height - 2
469
+ end
470
+ def list alist=nil
471
+ return @list if alist.nil?
472
+ @list = RubyCurses::ListDataModel.new(alist)
473
+ # added on 2009-01-13 23:19 since updates are not automatic now
474
+ @list.bind(:LIST_DATA_EVENT) { |e| list_data_changed() }
475
+ create_default_list_selection_model
476
+ end
477
+ def list_variable alist=nil
478
+ return @list if alist.nil?
479
+ @list = RubyCurses::ListDataModel.new(alist.value)
480
+ # added on 2009-01-13 23:19 since updates are not automatic now
481
+ @list.bind(:LIST_DATA_EVENT) { |e| list_data_changed() }
482
+ create_default_list_selection_model
483
+ end
484
+ def list_data_model ldm=nil
485
+ return @list if ldm.nil?
486
+ raise "Expecting list_data_model" unless ldm.is_a? RubyCurses::ListDataModel
487
+ @list = ldm
488
+ # added on 2009-01-13 23:19 since updates are not automatic now
489
+ @list.bind(:LIST_DATA_EVENT) { |e| list_data_changed() }
490
+ create_default_list_selection_model
491
+ end
492
+
493
+ def select_default_values
494
+ return if @default_values.nil?
495
+ @default_values.each do |val|
496
+ row = @list.index val
497
+ #do_select(row) unless row.nil?
498
+ add_row_selection_interval row, row unless row.nil?
499
+ end
500
+ end
501
+ def print_borders
502
+ width = @width
503
+ height = @height
504
+ window = @form.window
505
+ startcol = @col
506
+ startrow = @row
507
+ @color_pair = get_color($datacolor)
508
+ window.print_border startrow, startcol, height, width, @color_pair, @attr
509
+ print_title
510
+ end
511
+ def print_title
512
+ printstring(@form.window, @row, @col+(@width-@title.length)/2, @title, @color_pair, @title_attrib) unless @title.nil?
513
+ end
514
+ ### START FOR scrollable ###
515
+ def get_content
516
+ #@list 2008-12-01 23:13
517
+ @list_variable && @list_variable.value || @list
518
+ end
519
+ def get_window
520
+ @form.window
521
+ end
522
+ ### END FOR scrollable ###
523
+ # override widgets text
524
+ def getvalue
525
+ selected_rows
526
+ end
527
+ # Listbox
528
+ def handle_key(ch)
529
+ @current_index ||= 0
530
+ @toprow ||= 0
531
+ h = scrollatrow()
532
+ rc = row_count
533
+ $log.debug " listbxo got ch #{ch}"
534
+ $log.debug " when kps #{@KEY_PREV_SELECTION} "
535
+ case ch
536
+ when KEY_UP # show previous value
537
+ previous_row
538
+ when KEY_DOWN # show previous value
539
+ next_row
540
+ when @KEY_ROW_SELECTOR # 32:
541
+ return if is_popup and @selection_mode == 'single' # not allowing select this way since there will be a difference
542
+ toggle_row_selection @current_index #, @current_index
543
+ @repaint_required = true
544
+ when @KEY_SCROLL_FORWARD # ?\C-n:
545
+ scroll_forward
546
+ when @KEY_SCROLL_BACKWARD # ?\C-p:
547
+ scroll_backward
548
+ when @KEY_GOTO_TOP # 48, ?\C-[:
549
+ # please note that C-[ gives 27, same as esc so will respond after ages
550
+ goto_top
551
+ when @KEY_GOTO_BOTTOM # ?\C-]:
552
+ goto_bottom
553
+ when @KEY_NEXT_SELECTION # ?'
554
+ $log.debug "insdie next selection"
555
+ @oldrow = @current_index
556
+ do_next_selection #if @select_mode == 'multiple'
557
+ bounds_check
558
+ when @KEY_PREV_SELECTION # ?"
559
+ @oldrow = @current_index
560
+ $log.debug "insdie prev selection"
561
+ do_prev_selection #if @select_mode == 'multiple'
562
+ bounds_check
563
+ when @KEY_CLEAR_SELECTION
564
+ clear_selection #if @select_mode == 'multiple'
565
+ @repaint_required = true
566
+ when 27, ?\C-c:
567
+ editing_canceled @current_index if @cell_editing_allowed
568
+ when @KEY_ASK_FIND_FORWARD
569
+ ask_search_forward
570
+ when @KEY_ASK_FIND_BACKWARD
571
+ ask_search_backward
572
+ when @KEY_FIND_NEXT
573
+ find_next
574
+ when @KEY_FIND_PREV
575
+ find_prev
576
+ else
577
+ # this has to be fixed, if compo does not handle key it has to continue into next part FIXME
578
+ ret = :UNHANDLED # changed on 2009-01-27 13:14 not going into unhandled, tab not released
579
+ if @cell_editing_allowed
580
+ @repaint_required = true
581
+ # hack - on_enter_row should fire when this widget gets focus. first row that is DONE
582
+ begin
583
+ ret = @cell_editor.component.handle_key(ch)
584
+ rescue
585
+ on_enter_row @current_index
586
+ ret = @cell_editor.component.handle_key(ch)
587
+ end
588
+ end
589
+ if ret == :UNHANDLED
590
+ case ch
591
+ when ?A..?Z, ?a..?z
592
+ ret = set_selection_for_char ch.chr
593
+ else
594
+ ret = process_key ch, self
595
+ return :UNHANDLED if ret == :UNHANDLED
596
+ end
597
+ end
598
+ end
599
+ end
600
+ def ask_search_forward
601
+ regex = get_string("Enter regex to search")
602
+ ix = @list.find_match regex
603
+ if ix.nil?
604
+ alert("No matching data for: #{regex}")
605
+ else
606
+ set_focus_on(ix)
607
+ end
608
+ end
609
+ def ask_search_backward
610
+ regex = get_string("Enter regex to search (backward)")
611
+ ix = @list.find_prev regex, @current_index
612
+ if ix.nil?
613
+ alert("No matching data for: #{regex}")
614
+ else
615
+ set_focus_on(ix)
616
+ end
617
+ end
618
+ def find_prev
619
+ ix = @list.find_prev
620
+ regex = @last_regex
621
+ if ix.nil?
622
+ alert("No previous matching data for: #{regex}")
623
+ else
624
+ @oldrow = @current_index
625
+ @current_index = ix
626
+ bounds_check
627
+ end
628
+ end
629
+ # table find_next
630
+ def find_next
631
+ ix = @list.find_next
632
+ regex = @last_regex
633
+ if ix.nil?
634
+ alert("No more matching data for: #{regex}")
635
+ else
636
+ set_focus_on(ix) unless ix.nil?
637
+ end
638
+ end
639
+ def on_enter
640
+ on_enter_row @current_index
641
+ set_form_row # added 2009-01-11 23:41
642
+ $log.debug " ONE ENTER LIST #{@current_index}, #{@form.row}"
643
+ @repaint_required
644
+ fire_handler :ENTER, self
645
+ end
646
+ def on_enter_row arow
647
+ $log.debug " Listbox #{self} ENTER_ROW with curr #{@current_index}. row: #{arow} H: #{@handler.keys}"
648
+ #fire_handler :ENTER_ROW, arow
649
+ fire_handler :ENTER_ROW, self
650
+ @list.on_enter_row self
651
+ edit_row_at arow
652
+ @repaint_required = true
653
+ end
654
+ def edit_row_at arow
655
+ if @cell_editing_allowed
656
+ #$log.debug " cell editor on enter #{arow} val of list[row]: #{@list[arow]}"
657
+ editor = cell_editor
658
+ prepare_editor editor, arow
659
+ end
660
+ end
661
+ ##
662
+ # private
663
+ def prepare_editor editor, row
664
+ r,c = rowcol
665
+ value = @list[row] # .chomp
666
+ value = value.dup rescue value # so we can cancel
667
+ row = r + (row - @toprow) # @form.row
668
+ col = c+@left_margin # @form.col
669
+ # unfortunately 2009-01-11 19:47 combo boxes editable allows changing value
670
+ editor.prepare_editor self, row, col, value
671
+ set_form_col @left_margin
672
+
673
+ # set original value so we can cancel
674
+ # set row and col,
675
+ # set value and other things, color and bgcolor
676
+ end
677
+ def on_leave_row arow
678
+ #$log.debug " Listbox #{self} leave with (cr: #{@current_index}) #{arow}: list[row]:#{@list[arow]}"
679
+ $log.debug " Listbox #{self} leave with (cr: #{@current_index}) #{arow}: "
680
+ #fire_handler :LEAVE_ROW, arow
681
+ fire_handler :LEAVE_ROW, self
682
+ editing_completed arow
683
+ end
684
+ def editing_completed arow
685
+ if @cell_editing_allowed
686
+ if !@cell_editor.nil?
687
+ # $log.debug " cell editor (leave) setting value row: #{arow} val: #{@cell_editor.getvalue}"
688
+ @list[arow] = @cell_editor.getvalue #.dup 2009-01-10 21:42 boolean can't duplicate
689
+ else
690
+ $log.debug "CELL EDITOR WAS NIL, #{arow} "
691
+ end
692
+ end
693
+ @repaint_required = true
694
+ end
695
+ def editing_canceled arow=@current_index
696
+ return unless @cell_editing_allowed
697
+ prepare_editor @cell_editor, arow
698
+ @repaint_required = true
699
+ end
700
+
701
+ ##
702
+ # getter and setter for cell_editor
703
+ def cell_editor(*val)
704
+ if val.empty?
705
+ @cell_editor ||= create_default_cell_editor
706
+ else
707
+ @cell_editor = val[0]
708
+ end
709
+ end
710
+ def create_default_cell_editor
711
+ return RubyCurses::CellEditor.new RubyCurses::Field.new nil, {"focusable"=>false, "visible"=>false, "display_length"=> @width-2-@left_margin}
712
+ end
713
+ ##
714
+ # getter and setter for cell_renderer
715
+ def cell_renderer(*val)
716
+ if val.empty?
717
+ @cell_renderer ||= create_default_cell_renderer
718
+ else
719
+ @cell_renderer = val[0]
720
+ end
721
+ end
722
+ def create_default_cell_renderer
723
+ return RubyCurses::ListCellRenderer.new "", {"color"=>@color, "bgcolor"=>@bgcolor, "parent" => self, "display_length"=> @width-2-@left_margin}
724
+ end
725
+ ##
726
+ # this method chops the data to length before giving it to the
727
+ # renderer, this can cause problems if the renderer does some
728
+ # processing. also, it pans the data horizontally giving the renderer
729
+ # a section of it.
730
+ def repaint
731
+ return unless @repaint_required
732
+ print_borders if @to_print_borders == 1 # do this once only, unless everything changes
733
+ rc = row_count
734
+ maxlen = @maxlen ||= @width-2
735
+ tm = list()
736
+ tr = @toprow
737
+ acolor = get_color $datacolor
738
+ h = scrollatrow()
739
+ r,c = rowcol
740
+ 0.upto(h) do |hh|
741
+ crow = tr+hh
742
+ if crow < rc
743
+ focussed = @current_index == crow ? true : false
744
+ selected = is_row_selected crow
745
+ content = tm[crow] # 2009-01-17 18:37 chomp giving error in some cases says frozen
746
+ if content.is_a? String
747
+ content = content.dup
748
+ content.chomp!
749
+ content.gsub!(/\t/, ' ') # don't display tab
750
+ content.gsub!(/[^[:print:]]/, '') # don't display non print characters
751
+ if !content.nil?
752
+ if content.length > maxlen # only show maxlen
753
+ content = content[@pcol..@pcol+maxlen-1]
754
+ else
755
+ content = content[@pcol..-1]
756
+ end
757
+ end
758
+ elsif content.is_a? TrueClass or content.is_a? FalseClass
759
+ else
760
+ content = content.to_s
761
+ end
762
+ ## set the selector symbol if requested
763
+ selection_symbol = ''
764
+ if @show_selector
765
+ if selected
766
+ selection_symbol = @row_selected_symbol
767
+ else
768
+ selection_symbol = @row_unselected_symbol
769
+ end
770
+ @form.window.printstring r+hh, c, selection_symbol, acolor,@attr
771
+ end
772
+ #renderer = get_default_cell_renderer_for_class content.class.to_s
773
+ renderer = cell_renderer()
774
+ #renderer.show_selector @show_selector
775
+ #renderer.row_selected_symbol @row_selected_symbol
776
+ #renderer.left_margin @left_margin
777
+ #renderer.repaint @form.window, r+hh, c+(colix*11), content, focussed, selected
778
+ ## added crow on 2009-02-06 23:03
779
+ # since data is being truncated and renderer may need index
780
+ renderer.repaint @form.window, r+hh, c+@left_margin, crow, content, focussed, selected
781
+ else
782
+ # clear rows
783
+ @form.window.printstring r+hh, c, " " * (@width-2), acolor,@attr
784
+ end
785
+ end
786
+ if @cell_editing_allowed
787
+ @cell_editor.component.repaint unless @cell_editor.nil? or @cell_editor.component.form.nil?
788
+ end
789
+ @table_changed = false
790
+ @repaint_required = false
791
+ end
792
+ def list_data_changed
793
+ if row_count == 0 # added on 2009-02-02 17:13 so cursor not hanging on last row which could be empty
794
+ init_vars
795
+ @current_index = 0
796
+ set_form_row
797
+ end
798
+ @repaint_required = true
799
+ end
800
+
801
+ end # class listb
802
+
803
+
804
+ end # module