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 (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,216 @@
1
+ # Provides the ability to scroll content, typically an array
2
+ # widget that includes may override on_enter_row and on_leave_row
3
+ # Caller should have
4
+ # row_count()
5
+ # scrollatrow() typically @height - 2 (unless a header row, then -3)
6
+ # @current_index (row of current index, starting with 0 usually)
7
+ # @toprow : set to 0 for starters, top row to be displayed
8
+ # @pcol (used for horiz scrolling, starts at 0)
9
+ #
10
+ module ListScrollable
11
+ def previous_row
12
+ @oldrow = @current_index
13
+ @current_index -= 1 if @current_index > 0
14
+ bounds_check
15
+ end
16
+ alias :up :previous_row
17
+ def next_row
18
+ @oldrow = @current_index
19
+ rc = row_count
20
+ @current_index += 1 if @current_index < rc
21
+ bounds_check
22
+ end
23
+ alias :down :next_row
24
+ def goto_bottom
25
+ @oldrow = @current_index
26
+ rc = row_count
27
+ @current_index = rc -1
28
+ bounds_check
29
+ end
30
+ alias :goto_end :goto_bottom
31
+ def goto_top
32
+ @oldrow = @current_index
33
+ @current_index = 0
34
+ bounds_check
35
+ end
36
+ alias :goto_start :goto_top
37
+ def scroll_backward
38
+ @oldrow = @current_index
39
+ h = scrollatrow()
40
+ @current_index -= h
41
+ bounds_check
42
+ end
43
+ def scroll_forward
44
+ @oldrow = @current_index
45
+ h = scrollatrow()
46
+ rc = row_count
47
+ # more rows than box
48
+ if h < rc
49
+ @toprow += h+1 #if @current_index+h < rc
50
+ @current_index = @toprow
51
+ else
52
+ # fewer rows than box
53
+ @current_index = rc -1
54
+ end
55
+ #@current_index += h+1 #if @current_index+h < rc
56
+ bounds_check
57
+ end
58
+
59
+ ##
60
+ # please set oldrow before calling this. Store current_index as oldrow before changing. NOTE
61
+ def bounds_check
62
+ h = scrollatrow()
63
+ rc = row_count
64
+ $log.debug " PRE CURR:#{@current_index}, TR: #{@toprow} RC: #{rc} H:#{h}"
65
+ @current_index = 0 if @current_index < 0 # not lt 0
66
+ @current_index = rc-1 if @current_index >= rc # not gt rowcount
67
+ @toprow = rc-h-1 if rc > h and @toprow > rc - h - 1 # toprow shows full page if possible
68
+ # curr has gone below table, move toprow forward
69
+ if @current_index - @toprow > h
70
+ @toprow = @current_index - h
71
+ elsif @current_index < @toprow
72
+ # curr has gone above table, move toprow up
73
+ @toprow = @current_index
74
+ end
75
+ #$log.debug " POST CURR:#{@current_index}, TR: #{@toprow} RC: #{rc} H:#{h}"
76
+ if @oldrow != @current_index
77
+ $log.debug "going to call on leave and on enter"
78
+ on_leave_row @oldrow if respond_to? :on_leave_row # to be defined by widget that has included this
79
+ on_enter_row @current_index if respond_to? :on_enter_row # to be defined by widget that has included this
80
+ end
81
+ set_form_row
82
+ @repaint_required = true
83
+ end
84
+ # the cursor should be appropriately positioned
85
+ def set_form_row
86
+ r,c = rowcol
87
+ @form.row = r + (@current_index-@toprow)
88
+ end
89
+ def right
90
+ @hscrollcols ||= @cols/2
91
+ @pcol += @hscrollcols if @pcol + @hscrollcols < @padcols
92
+ # window_erase @win XXX
93
+ end
94
+ def left
95
+ @hscrollcols ||= @cols/2
96
+ @pcol -= @hscrollcols if @pcol > 0
97
+ @pcol = 0 if @pcol < 0
98
+ end
99
+ # not that saving content_rows is buggy since we add rows.
100
+ ##
101
+ # caution, this now uses winrow not prow
102
+ ## for user to know which row is being focussed on
103
+ def focussed_index
104
+ @current_index # 2009-01-07 14:35
105
+ end
106
+ # only to be used in single selection cases as focussed item FIXME.
107
+ # best not to use, as can be implementation dep, use current_index
108
+ def selected_item
109
+ get_content()[focussed_index()]
110
+ end
111
+ #alias :current_index :focussed_index
112
+ alias :selected_index :focussed_index
113
+ def OLDscrollable_handle_key ch
114
+ begin
115
+ ###pre_key # 2009-01-07 13:23
116
+ case ch
117
+ when ?\C-n
118
+ scroll_forward
119
+ when 32
120
+ scroll_forward
121
+ when ?\C-p
122
+ scroll_backward
123
+ when ?0
124
+ #goto_start
125
+ goto_top
126
+ when ?9
127
+ #goto_end
128
+ goto_bottom
129
+ when KEY_UP
130
+ #select_prev_row
131
+ #up
132
+ $log.debug " GOT KEY UP NEW SCROLL"
133
+ previous_row
134
+ when KEY_LEFT
135
+ when KEY_RIGHT
136
+ when KEY_DOWN
137
+ #down
138
+ $log.debug " GOT KEY DOWN NEW SCROLL"
139
+ next_row
140
+ when KEY_ENTER, 10, 13
141
+ if respond_to? :fire
142
+ fire
143
+ end
144
+ when ?A..?Z, ?a..?z
145
+ ret = set_selection_for_char ch.chr
146
+ else
147
+ return :UNHANDLED #if ret == -1
148
+ end
149
+ ensure
150
+ #post_key
151
+ end
152
+ end # handle_k listb
153
+ ## 2008-12-18 18:03
154
+ # finds the next match for the char pressed
155
+ # returning the index
156
+ def next_match char
157
+ data = get_content
158
+ row = focussed_index + 1
159
+ row.upto(data.length-1) do |ix|
160
+ val = data[ix].chomp
161
+ #if val[0,1] == char #and val != currval
162
+ if val[0,1].casecmp(char) == 0 #AND VAL != CURRval
163
+ return ix
164
+ end
165
+ end
166
+ row = focussed_index - 1
167
+ 0.upto(row) do |ix|
168
+ val = data[ix].chomp
169
+ #if val[0,1] == char #and val != currval
170
+ if val[0,1].casecmp(char) == 0 #and val != currval
171
+ return ix
172
+ end
173
+ end
174
+ return -1
175
+ end
176
+ ## 2008-12-18 18:03
177
+ # sets the selection to the next row starting with char
178
+ def set_selection_for_char char
179
+ @oldrow = @current_index
180
+ ix = next_match char
181
+ @current_index = ix if ix != -1
182
+ bounds_check
183
+ return ix
184
+ end
185
+
186
+ ##
187
+ # ensures that the given row is focussed
188
+ # new version of older one that was not perfect.
189
+ # 2009-01-17 13:25
190
+ def set_focus_on arow
191
+ @oldrow = @current_index
192
+ @current_index = arow
193
+ bounds_check if @oldrow != @current_index
194
+ end
195
+ ##
196
+ # 2008-12-18 18:05
197
+ # set focus on given index
198
+ def OLDset_focus_on arow
199
+ return if arow > row_count()-1 or arow < 0
200
+ @oldrow = @current_index
201
+ total = row_count()
202
+ @current_index = arow
203
+ sar = scrollatrow + 1
204
+ @toprow = (@current_index / sar) * sar
205
+
206
+ $log.debug "1 set_focus #{total}, sar #{sar}, toprow #{@toprow}, current_index #{@current_index}"
207
+ if total - @toprow < sar
208
+ @toprow = (total - sar)
209
+ end
210
+ $log.debug "2 set_focus #{total}, sar #{sar}, toprow #{@toprow}, current_index #{@current_index}"
211
+ set_form_row # 2009-01-17 12:44
212
+ @repaint_required = true
213
+ #bounds_check
214
+ end
215
+
216
+ end
@@ -0,0 +1,67 @@
1
+ # this is a companion file to defaultlistselectionmodel
2
+ # if you use that, include this to get all the methods to use it
3
+ module RubyCurses
4
+ module ListSelectable
5
+
6
+ def list_selection_model lsm
7
+ @list_selection_model = lsm
8
+ #@list_selection_model.selection_mode = @selection_mode || :MULTIPLE
9
+ end
10
+ def create_default_list_selection_model
11
+ list_selection_model DefaultListSelectionModel.new
12
+ end
13
+ def is_row_selected row
14
+ @list_selection_model.is_selected_index row
15
+ end
16
+
17
+ def add_row_selection_interval ix0, ix1
18
+ # if row_selection_allowed
19
+ @list_selection_model.add_selection_interval ix0, ix1
20
+ end
21
+ def remove_row_selection_interval ix0, ix1
22
+ @list_selection_model.remove_selection_interval ix0, ix1
23
+ end
24
+ def toggle_row_selection row=@current_index
25
+ if is_row_selected row
26
+ $log.debug " deleting row #{row}"
27
+ remove_row_selection_interval(row, row)
28
+ else
29
+ $log.debug " adding row #{row}"
30
+ add_row_selection_interval(row, row)
31
+ end
32
+ end
33
+
34
+ def clear_selection
35
+ @list_selection_model.clear_selection
36
+ end
37
+ def selected_item
38
+ # @list[@current_index]
39
+ end
40
+ def selected_rows
41
+ @list_selection_model.get_selected_rows
42
+ end
43
+ def selected_row_count
44
+ selected_rows.size
45
+ end
46
+ def selected_row
47
+ @list_selection_model.get_min_selection_index
48
+ end
49
+ def do_next_selection
50
+ return if selected_rows().length == 0
51
+ row = selected_rows().sort.find { |i| i > @current_index }
52
+ row ||= @current_index
53
+ @current_index = row
54
+ @repaint_required = true # fire list_select XXX
55
+ end
56
+ def do_prev_selection
57
+ return if selected_rows().length == 0
58
+ row = selected_rows().sort{|a,b| b <=> a}.find { |i| i < @current_index }
59
+ row ||= @current_index
60
+ @current_index = row
61
+ @repaint_required = true # fire list_select XXX
62
+ end
63
+ alias :selected_index :selected_row
64
+ attr_accessor :row_selection_allowed
65
+ attr_accessor :column_selection_allowed
66
+ end
67
+ end
@@ -0,0 +1,108 @@
1
+ require 'rbcurse/orderedhash'
2
+ class Mapper
3
+ attr_reader :keymap
4
+ attr_reader :view
5
+ attr_accessor :mode
6
+ attr_reader :keys
7
+ def initialize handler
8
+ #@handler = handler
9
+ @view = handler # caller program
10
+ @keys = {}
11
+ @mode = nil # used when defining
12
+ @pendingkeys = nil
13
+ @prevkey = nil # in case of a key sequence such as C-x C-c, will have C-x
14
+ @arg = nil # regex matched this key.
15
+ end
16
+ def let mode, &block
17
+ h = OrderedHash.new
18
+ @keys[mode] = h
19
+ @mode = mode
20
+ instance_eval(&block)
21
+ $log.debug("KEYS: #{@keys[mode].inspect}")
22
+ end
23
+ def map(*args, &block)
24
+ if block_given?
25
+ # We check for cases like C-x C-c etc. Only 2 levels.
26
+ #args = arg.split(/ +/)
27
+ if args.length == 2
28
+ @keys[@mode][args[0]] ||= OrderedHash.new
29
+ @keys[@mode][args[0]][args[1]]=block
30
+ else
31
+ # single key or control key
32
+ @keys[@mode][args[0]]=block
33
+ end
34
+ else
35
+ #no block, last arg shold be a symbol
36
+ symb = args.pop
37
+ raise "If block not passed, last arg should be a method symbol" if !symb.is_a? Symbol
38
+ if args.length == 2
39
+ @keys[@mode][args[0]] ||= OrderedHash.new
40
+ @keys[@mode][args[0]][args[1]]=symb
41
+ else
42
+ # single key or control key
43
+ @keys[@mode][args[0]]=symb
44
+ end
45
+ end
46
+ end
47
+
48
+ ## manages key pressing
49
+ # takes care of multiple key combos too
50
+ def press key
51
+ $log.debug("press Got: #{key}")
52
+ # for a double key combination such as C-x C-c this has the set of pending keys to check against
53
+ if @pendingkeys != nil
54
+ blk = @pendingkeys[key]
55
+ else
56
+ # this is the regular single key mode
57
+ #blk = @keys[@view.mode][key]
58
+ blk = match(key)
59
+ end
60
+ # this means this key expects more keys to follow such as C-x could
61
+ if blk.is_a? OrderedHash
62
+ @pendingkeys = blk
63
+ @prevkey = key
64
+ return
65
+ end
66
+ if blk.nil? # this should go up XXX
67
+ if !@pendingkeys.nil?
68
+ # this error message to be modified if using numeric keys -- need to convert to char
69
+ view.info("%p not valid in %p. Try: #{@pendingkeys.keys.join(', ')}" % [key, @prevkey]) # XXX
70
+ else
71
+ view.info("%p not valid in %p. " % [key, @view.mode])
72
+ end
73
+ return
74
+ end
75
+ # call the block or symbol - our user defined key mappings use symbols
76
+ if blk.is_a? Symbol
77
+ @view.send(blk)
78
+ else
79
+ blk.call
80
+ end
81
+ @prevkey = nil
82
+ @pendingkeys = nil
83
+ end
84
+ def match key
85
+ # $log.debug "MATCH #key "
86
+ #blk = @keys[@view.mode][key]
87
+ @keys[@view.mode].each_pair do |k,v|
88
+ # $log.debug "LOOP #{k.class}, #{k}, #{v} "
89
+ case k.class.to_s
90
+ when "String"
91
+ return v if k == key
92
+ when "Fixnum" # for keyboard2
93
+ $log.debug "FIXNUM LOOP #{k.class}, #{k}, #{v} "
94
+ return v if k == key
95
+ when "Regexp"
96
+ # $log.debug "REGEX #key , #k, #{k.match(key)}"
97
+ key = key.chr if key.is_a? Fixnum
98
+ if !k.match(key).nil?
99
+ @arg = key
100
+ return v
101
+ end
102
+ else
103
+ $log.error "MATCH: Unhandled class #{k.class} "
104
+ end
105
+ end
106
+ return nil
107
+ end
108
+ end
@@ -0,0 +1,77 @@
1
+ ## Insert order preserving hash
2
+ # Thanks to Bill Kelly, posted on http://www.ruby-forum.com/topic/166075
3
+ #
4
+ class OrderedHash
5
+ include Enumerable
6
+
7
+ def initialize(*args, &block)
8
+ @h = Hash.new(*args, &block)
9
+ @ordered_keys = []
10
+ end
11
+
12
+ def []=(key, val)
13
+ @ordered_keys << key unless @h.has_key? key
14
+ @h[key] = val
15
+ end
16
+
17
+ def each
18
+ @ordered_keys.each {|k| yield(k, @h[k])}
19
+ end
20
+ alias :each_pair :each
21
+
22
+ def each_value
23
+ @ordered_keys.each {|k| yield(@h[k])}
24
+ end
25
+
26
+ def each_key
27
+ @ordered_keys.each {|k| yield k}
28
+ end
29
+
30
+ def keys
31
+ @ordered_keys
32
+ end
33
+
34
+ def values
35
+ @ordered_keys.map {|k| @h[k]}
36
+ end
37
+
38
+ def clear
39
+ @ordered_keys.clear
40
+ @h.clear
41
+ end
42
+
43
+ def delete(k, &block)
44
+ @ordered_keys.delete k
45
+ @h.delete(k, &block)
46
+ end
47
+
48
+ def reject!
49
+ del = []
50
+ each_pair {|k,v| del << k if yield k,v}
51
+ del.each {|k| delete k}
52
+ del.empty? ? nil : self
53
+ end
54
+
55
+ def delete_if(&block)
56
+ reject!(&block)
57
+ self
58
+ end
59
+ ## added since the normal hash will give it in unordered. so debugging sucks
60
+ def inspect
61
+ out = []
62
+ each do | k,v |
63
+ out << " #{k} => #{v} "
64
+ end
65
+ res = %Q[ { #{out.join(",\n ")} } ]
66
+ end
67
+
68
+ %w(merge!).each do |name|
69
+ define_method(name) do |*args|
70
+ raise NotImplementedError, "#{name} not implemented"
71
+ end
72
+ end
73
+
74
+ def method_missing(*args)
75
+ @h.send(*args)
76
+ end
77
+ end
@@ -0,0 +1,243 @@
1
+ =begin
2
+ * Name: combo box
3
+ * Description:
4
+ * Author: rkumar
5
+
6
+ --------
7
+ * Date: 2008-12-16 22:03
8
+ * License:
9
+ Same as Ruby's License (http://www.ruby-lang.org/LICENSE.txt)
10
+
11
+ =end
12
+ require 'rubygems'
13
+ require 'ncurses'
14
+ require 'logger'
15
+ require 'rbcurse'
16
+ require 'rbcurse/rlistbox'
17
+
18
+ include Ncurses
19
+ include RubyCurses
20
+ module RubyCurses
21
+ META_KEY = 128
22
+ extend self
23
+
24
+ # TODO :
25
+ # i no longer use values, i now use "list" or better "list_data_model"
26
+ # try to make it so values gets converted to list.
27
+ class ComboBox < Field
28
+ include RubyCurses::EventHandler
29
+ dsl_accessor :list_config
30
+ dsl_accessor :insert_policy # NO_INSERT, INSERT_AT_TOP, INSERT_AT_BOTTOM, INSERT_AT_CURRENT
31
+ # INSERT_AFTER_CURRENT, INSERT_BEFORE_CURRENT,INSERT_ALPHABETICALLY
32
+
33
+ attr_accessor :current_index
34
+ # the symbol you want to use for combos
35
+ attr_accessor :COMBO_SYMBOL
36
+ attr_accessor :show_symbol # show that funny symbol after a combo to signify its a combo
37
+
38
+ def initialize form, config={}, &block
39
+ super
40
+ @current_index ||= 0
41
+ # added if check since it was overriding set_buffer in creation. 2009-01-18 00:03
42
+ set_buffer @list[@current_index].dup if @buffer.nil? or @buffer.empty?
43
+ init_vars
44
+ end
45
+ def init_vars
46
+ super
47
+ @show_symbol ||= true
48
+ @COMBO_SYMBOL ||= Ncurses::ACS_GEQUAL
49
+ bind_key(KEY_UP) { previous_row }
50
+ bind_key(KEY_DOWN) { next_row }
51
+ end
52
+ def selected_item
53
+ @list[@current_index]
54
+ end
55
+ def selected_index
56
+ @current_index
57
+ end
58
+
59
+ ##
60
+ # convert given list to datamodel
61
+ def list alist=nil
62
+ return @list if alist.nil?
63
+ @list = RubyCurses::ListDataModel.new(alist)
64
+ end
65
+ ##
66
+ # set given datamodel
67
+ def list_data_model ldm
68
+ raise "Expecting list_data_model" unless ldm.is_a? RubyCurses::ListDataModel
69
+ @list = ldm
70
+ end
71
+ ##
72
+ # combo edit box key handling
73
+ # removed UP and DOWN and bound it, so it can be unbound
74
+ def handle_key(ch)
75
+ @current_index ||= 0
76
+ # added 2009-01-18 22:44 no point moving horiz or passing up to Field if not edit
77
+ if !@editable
78
+ if ch == KEY_LEFT or ch == KEY_RIGHT
79
+ return :UNHANDLED
80
+ end
81
+ end
82
+ case ch
83
+ #when KEY_UP # show previous value
84
+ # previous_row
85
+ #when KEY_DOWN # show previous value
86
+ # next_row
87
+ when KEY_DOWN+ RubyCurses::META_KEY # alt down
88
+ popup # pop up the popup
89
+ else
90
+ super
91
+ end
92
+ end
93
+ def previous_row
94
+ @current_index -= 1 if @current_index > 0
95
+ set_buffer @list[@current_index].dup
96
+ set_modified(true) ## ??? not required
97
+ fire_handler :ENTER_ROW, self
98
+ @list.on_enter_row self
99
+ end
100
+ def next_row
101
+ @current_index += 1 if @current_index < @list.length()-1
102
+ set_buffer @list[@current_index].dup
103
+ set_modified(true) ## ??? not required
104
+ fire_handler :ENTER_ROW, self
105
+ @list.on_enter_row self
106
+ end
107
+ ##
108
+ # calls a popup list
109
+ # TODO: should not be positioned so that it goes off edge
110
+ # user's customizations of list should be passed in
111
+ # The dup of listconfig is due to a tricky feature/bug.
112
+ # I try to keep the config hash and instance variables in synch. So
113
+ # this config hash is sent to popuplist which updates its row col and
114
+ # next time we pop up the popup row and col are zero.
115
+ #
116
+ #
117
+ # added dup in PRESS since editing edit field mods this
118
+ # on pressing ENTER, value set back and current_index updated
119
+ def popup
120
+ listconfig = (@list_config && @list_config.dup) || {}
121
+ dm = @list
122
+ # current item in edit box will be focussed when list pops up
123
+ #$log.debug "XXX POPUP: #{dm.selected_index} = #{@current_index}, value #{@buffer}"
124
+ # we are having some problms when using this in a list. it retains earlier value
125
+ _index = dm.index @buffer
126
+ dm.selected_index = _index # @current_index
127
+ poprow = @row+0 # one row below the edit box
128
+ popcol = @col
129
+ dlength = @display_length
130
+ f = self
131
+ @popup = RubyCurses::PopupList.new do
132
+ row poprow
133
+ col popcol
134
+ width dlength
135
+ list_data_model dm
136
+ list_selection_mode 'single'
137
+ relative_to f
138
+ list_config listconfig
139
+ bind(:PRESS) do |index|
140
+ f.set_buffer dm[index].dup
141
+ f.set_modified(true) if f.current_index != index
142
+ f.current_index = index
143
+ end
144
+ end
145
+ end
146
+
147
+ # Field putc advances cursor when it gives a char so we override this
148
+ def putc c
149
+ if c >= 0 and c <= 127
150
+ ret = putch c.chr
151
+ if ret == 0
152
+ addcol 1 if @editable
153
+ set_modified
154
+ end
155
+ end
156
+ return -1 # always ??? XXX
157
+ end
158
+ ##
159
+ # field does not give char to non-editable fields so we override
160
+ def putch char
161
+ @current_index ||= 0
162
+ if @editable
163
+ super
164
+ return 0
165
+ else
166
+ match = next_match(char)
167
+ set_buffer match unless match.nil?
168
+ fire_handler :ENTER_ROW, self
169
+ end
170
+ @modified = true
171
+ fire_handler :CHANGE, self # 2008-12-09 14:51 ???
172
+ 0
173
+ end
174
+ ##
175
+ # the sets the next match in the edit field
176
+ def next_match char
177
+ start = @current_index
178
+ start.upto(@list.length-1) do |ix|
179
+ if @list[ix][0,1].casecmp(char) == 0
180
+ return @list[ix] unless @list[ix] == @buffer
181
+ end
182
+ @current_index += 1
183
+ end
184
+ ## could not find, start from zero
185
+ @current_index = 0
186
+ start = [@list.length()-1, start].min
187
+ 0.upto(start) do |ix|
188
+ if @list[ix][0,1].casecmp(char) == 0
189
+ return @list[ix] unless @list[ix] == @buffer
190
+ end
191
+ @current_index += 1
192
+ end
193
+ @current_index = [@list.length()-1, @current_index].min
194
+ return nil
195
+ end
196
+ ##
197
+ # on leaving the listbox, update the combo/datamodel.
198
+ # we are using methods of the datamodel. Updating our list will have
199
+ # no effect on the list, and wont trigger events.
200
+ # Do not override.
201
+ def on_leave
202
+ if !@list.include? @buffer and !@buffer.strip.empty?
203
+ _insert_policy = @insert_policy || :INSERT_AT_BOTTOM
204
+ case _insert_policy
205
+ when :INSERT_AT_BOTTOM, :INSERT_AT_END
206
+ @list.append @buffer
207
+ when :INSERT_AT_TOP
208
+ @list.insert(0, @buffer)
209
+ when :INSERT_AFTER_CURRENT
210
+ @current_index += 1
211
+ @list.insert(@current_index, @buffer)
212
+
213
+ when :INSERT_BEFORE_CURRENT
214
+ #_index = @current_index-1 if @current_index>0
215
+ _index = @current_index
216
+ @list.insert(_index, @buffer)
217
+ when :INSERT_AT_CURRENT
218
+ @list[@current_index]=@buffer
219
+ when :NO_INSERT
220
+ ; # take a break
221
+ end
222
+ end
223
+ fire_handler :LEAVE, self
224
+ end
225
+
226
+ def repaint
227
+ super
228
+ c = @col + @display_length
229
+ # @form.window.mvwvline( @row, c, ACS_VLINE, 1)
230
+ if @show_symbol # 2009-01-11 18:47
231
+ # i have changed c +1 to c, since we have no right to print beyond display_length
232
+ @form.window.mvwaddch @row, c, @COMBO_SYMBOL # Ncurses::ACS_GEQUAL
233
+ end
234
+ # @form.window.mvwvline( @row, c+2, ACS_VLINE, 1)
235
+ # @form.window.mvwaddch @row, c+2, Ncurses::ACS_S1
236
+ # @form.window.mvwaddch @row, c+3, Ncurses::ACS_S9
237
+ # @form.window.mvwaddch @row, c+4, Ncurses::ACS_LRCORNER
238
+ # @form.window.mvwhline( @row, c+5, ACS_HLINE, 2)
239
+ end
240
+
241
+ end # class ComboBox
242
+
243
+ end # module