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,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