rbcurse-extras 0.0.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.
- data/README.md +75 -0
- data/VERSION +1 -0
- data/examples/data/list.txt +300 -0
- data/examples/data/lotr.txt +12 -0
- data/examples/data/table.txt +36 -0
- data/examples/data/tasks.txt +27 -0
- data/examples/data/unix1.txt +21 -0
- data/examples/inc/qdfilechooser.rb +70 -0
- data/examples/inc/rfe_renderer.rb +121 -0
- data/examples/newtabbedwindow.rb +100 -0
- data/examples/rfe.rb +1236 -0
- data/examples/test2.rb +670 -0
- data/examples/testeditlist.rb +78 -0
- data/examples/testtable.rb +270 -0
- data/examples/testvimsplit.rb +141 -0
- data/lib/rbcurse/extras/include/celleditor.rb +112 -0
- data/lib/rbcurse/extras/include/checkboxcellrenderer.rb +57 -0
- data/lib/rbcurse/extras/include/comboboxcellrenderer.rb +30 -0
- data/lib/rbcurse/extras/include/defaultlistselectionmodel.rb +79 -0
- data/lib/rbcurse/extras/include/listkeys.rb +37 -0
- data/lib/rbcurse/extras/include/listselectable.rb +144 -0
- data/lib/rbcurse/extras/include/tableextended.rb +40 -0
- data/lib/rbcurse/extras/widgets/horizlist.rb +203 -0
- data/lib/rbcurse/extras/widgets/menutree.rb +63 -0
- data/lib/rbcurse/extras/widgets/multilinelabel.rb +142 -0
- data/lib/rbcurse/extras/widgets/rcomboedit.rb +256 -0
- data/lib/rbcurse/extras/widgets/rlink.rb.moved +27 -0
- data/lib/rbcurse/extras/widgets/rlistbox.rb +1247 -0
- data/lib/rbcurse/extras/widgets/rmenulink.rb.moved +21 -0
- data/lib/rbcurse/extras/widgets/rmulticontainer.rb +304 -0
- data/lib/rbcurse/extras/widgets/rmultisplit.rb +722 -0
- data/lib/rbcurse/extras/widgets/rmultitextview.rb +306 -0
- data/lib/rbcurse/extras/widgets/rpopupmenu.rb +755 -0
- data/lib/rbcurse/extras/widgets/rtable.rb +1758 -0
- data/lib/rbcurse/extras/widgets/rvimsplit.rb +800 -0
- data/lib/rbcurse/extras/widgets/table/tablecellrenderer.rb +86 -0
- data/lib/rbcurse/extras/widgets/table/tabledatecellrenderer.rb +98 -0
- metadata +94 -0
@@ -0,0 +1,256 @@
|
|
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
|
+
TODO:
|
12
|
+
Simplify completely. we don't need to use popup list, use something simpler do
|
13
|
+
we can control keys.
|
14
|
+
Keys: ignore down arrow in field. Use space to popup and space to select from popup.
|
15
|
+
Or keep that as default.
|
16
|
+
That v character does not position correctly if label used.
|
17
|
+
=end
|
18
|
+
require 'rbcurse'
|
19
|
+
require 'rbcurse/extras/widgets/rlistbox'
|
20
|
+
|
21
|
+
include RubyCurses
|
22
|
+
module RubyCurses
|
23
|
+
extend self
|
24
|
+
|
25
|
+
# TODO :
|
26
|
+
# i no longer use values, i now use "list" or better "list_data_model"
|
27
|
+
# try to make it so values gets converted to list.
|
28
|
+
# NOTE: 2010-10-01 13:25 spacebar and enter will popup in addition to Alt-Down
|
29
|
+
class ComboBoxEdit < Field
|
30
|
+
include RubyCurses::EventHandler
|
31
|
+
dsl_accessor :list_config
|
32
|
+
dsl_accessor :insert_policy # NO_INSERT, INSERT_AT_TOP, INSERT_AT_BOTTOM, INSERT_AT_CURRENT
|
33
|
+
# INSERT_AFTER_CURRENT, INSERT_BEFORE_CURRENT,INSERT_ALPHABETICALLY
|
34
|
+
|
35
|
+
attr_accessor :current_index
|
36
|
+
# the symbol you want to use for combos
|
37
|
+
attr_accessor :COMBO_SYMBOL
|
38
|
+
attr_accessor :show_symbol # show that funny symbol after a combo to signify its a combo
|
39
|
+
dsl_accessor :arrow_key_policy # :IGNORE :NEXT_ROW :POPUP
|
40
|
+
|
41
|
+
def initialize form, config={}, &block
|
42
|
+
@arrow_key_policy = :ignore
|
43
|
+
@show_symbol = true
|
44
|
+
@COMBO_SYMBOL = "v".ord # trying this out
|
45
|
+
super
|
46
|
+
@current_index ||= 0
|
47
|
+
# added if check since it was overriding set_buffer in creation. 2009-01-18 00:03
|
48
|
+
set_buffer @list[@current_index].dup if @buffer.nil? or @buffer.empty?
|
49
|
+
init_vars
|
50
|
+
@_events.push(*[:CHANGE, :ENTER_ROW, :LEAVE_ROW])
|
51
|
+
end
|
52
|
+
def init_vars
|
53
|
+
super
|
54
|
+
#@show_symbol = false if @label # commented out 2011-11-13 maybe it doesn't place properly if label
|
55
|
+
@COMBO_SYMBOL ||= FFI::NCurses::ACS_DARROW #GEQUAL # now this won't work since i've set it above
|
56
|
+
bind_key(KEY_UP) { previous_row }
|
57
|
+
bind_key(KEY_DOWN) { next_row }
|
58
|
+
end
|
59
|
+
def selected_item
|
60
|
+
@list[@current_index]
|
61
|
+
end
|
62
|
+
def selected_index
|
63
|
+
@current_index
|
64
|
+
end
|
65
|
+
|
66
|
+
##
|
67
|
+
# convert given list to datamodel
|
68
|
+
def list alist=nil
|
69
|
+
return @list if alist.nil?
|
70
|
+
@list = RubyCurses::ListDataModel.new(alist)
|
71
|
+
end
|
72
|
+
##
|
73
|
+
# set given datamodel
|
74
|
+
def list_data_model ldm
|
75
|
+
raise "Expecting list_data_model" unless ldm.is_a? RubyCurses::ListDataModel
|
76
|
+
@list = ldm
|
77
|
+
end
|
78
|
+
##
|
79
|
+
# combo edit box key handling
|
80
|
+
# removed UP and DOWN and bound it, so it can be unbound
|
81
|
+
def handle_key(ch)
|
82
|
+
@current_index ||= 0
|
83
|
+
# added 2009-01-18 22:44 no point moving horiz or passing up to Field if not edit
|
84
|
+
if !@editable
|
85
|
+
if ch == KEY_LEFT or ch == KEY_RIGHT
|
86
|
+
return :UNHANDLED
|
87
|
+
end
|
88
|
+
end
|
89
|
+
case @arrow_key_policy
|
90
|
+
when :ignore
|
91
|
+
if ch == KEY_DOWN or ch == KEY_UP
|
92
|
+
return :UNHANDLED
|
93
|
+
end
|
94
|
+
when :popup
|
95
|
+
if ch == KEY_DOWN or ch == KEY_UP
|
96
|
+
popup
|
97
|
+
end
|
98
|
+
end
|
99
|
+
case ch
|
100
|
+
#when KEY_UP # show previous value
|
101
|
+
# previous_row
|
102
|
+
#when KEY_DOWN # show previous value
|
103
|
+
# next_row
|
104
|
+
# adding spacebar to popup combo, as in microemacs 2010-10-01 13:21
|
105
|
+
when 32, KEY_DOWN+ META_KEY # alt down
|
106
|
+
popup # pop up the popup
|
107
|
+
else
|
108
|
+
super
|
109
|
+
end
|
110
|
+
end
|
111
|
+
def previous_row
|
112
|
+
@current_index -= 1 if @current_index > 0
|
113
|
+
set_buffer @list[@current_index].dup
|
114
|
+
set_modified(true) ## ??? not required
|
115
|
+
fire_handler :ENTER_ROW, self
|
116
|
+
@list.on_enter_row self
|
117
|
+
end
|
118
|
+
def next_row
|
119
|
+
@current_index += 1 if @current_index < @list.length()-1
|
120
|
+
set_buffer @list[@current_index].dup
|
121
|
+
set_modified(true) ## ??? not required
|
122
|
+
fire_handler :ENTER_ROW, self
|
123
|
+
@list.on_enter_row self
|
124
|
+
end
|
125
|
+
##
|
126
|
+
# calls a popup list
|
127
|
+
# TODO: should not be positioned so that it goes off edge
|
128
|
+
# user's customizations of list should be passed in
|
129
|
+
# The dup of listconfig is due to a tricky feature/bug.
|
130
|
+
# I try to keep the config hash and instance variables in synch. So
|
131
|
+
# this config hash is sent to popuplist which updates its row col and
|
132
|
+
# next time we pop up the popup row and col are zero.
|
133
|
+
#
|
134
|
+
#
|
135
|
+
# added dup in PRESS since editing edit field mods this
|
136
|
+
# on pressing ENTER, value set back and current_index updated
|
137
|
+
def popup
|
138
|
+
listconfig = (@list_config && @list_config.dup) || {}
|
139
|
+
dm = @list
|
140
|
+
# current item in edit box will be focussed when list pops up
|
141
|
+
#$log.debug "XXX POPUP: #{dm.selected_index} = #{@current_index}, value #{@buffer}"
|
142
|
+
# we are having some problms when using this in a list. it retains earlier value
|
143
|
+
_index = dm.index @buffer
|
144
|
+
dm.selected_index = _index # @current_index
|
145
|
+
poprow = @row+0 # one row below the edit box
|
146
|
+
popcol = @col
|
147
|
+
dlength = @display_length
|
148
|
+
f = self
|
149
|
+
@popup = RubyCurses::PopupList.new do
|
150
|
+
row poprow
|
151
|
+
col popcol
|
152
|
+
width dlength
|
153
|
+
list_data_model dm
|
154
|
+
list_selection_mode 'single'
|
155
|
+
relative_to f
|
156
|
+
list_config listconfig
|
157
|
+
bind(:PRESS) do |index|
|
158
|
+
f.set_buffer dm[index].dup
|
159
|
+
f.set_modified(true) if f.current_index != index
|
160
|
+
f.current_index = index
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
# Field putc advances cursor when it gives a char so we override this
|
166
|
+
def putc c
|
167
|
+
if c >= 0 and c <= 127
|
168
|
+
ret = putch c.chr
|
169
|
+
if ret == 0
|
170
|
+
addcol 1 if @editable
|
171
|
+
set_modified
|
172
|
+
end
|
173
|
+
end
|
174
|
+
return -1 # always ??? XXX
|
175
|
+
end
|
176
|
+
##
|
177
|
+
# field does not give char to non-editable fields so we override
|
178
|
+
def putch char
|
179
|
+
@current_index ||= 0
|
180
|
+
if @editable
|
181
|
+
super
|
182
|
+
return 0
|
183
|
+
else
|
184
|
+
match = next_match(char)
|
185
|
+
set_buffer match unless match.nil?
|
186
|
+
fire_handler :ENTER_ROW, self
|
187
|
+
end
|
188
|
+
@modified = true
|
189
|
+
fire_handler :CHANGE, self # 2008-12-09 14:51 ???
|
190
|
+
0
|
191
|
+
end
|
192
|
+
##
|
193
|
+
# the sets the next match in the edit field
|
194
|
+
def next_match char
|
195
|
+
start = @current_index
|
196
|
+
start.upto(@list.length-1) do |ix|
|
197
|
+
if @list[ix][0,1].casecmp(char) == 0
|
198
|
+
return @list[ix] unless @list[ix] == @buffer
|
199
|
+
end
|
200
|
+
@current_index += 1
|
201
|
+
end
|
202
|
+
## could not find, start from zero
|
203
|
+
@current_index = 0
|
204
|
+
start = [@list.length()-1, start].min
|
205
|
+
0.upto(start) do |ix|
|
206
|
+
if @list[ix][0,1].casecmp(char) == 0
|
207
|
+
return @list[ix] unless @list[ix] == @buffer
|
208
|
+
end
|
209
|
+
@current_index += 1
|
210
|
+
end
|
211
|
+
@current_index = [@list.length()-1, @current_index].min
|
212
|
+
return nil
|
213
|
+
end
|
214
|
+
##
|
215
|
+
# on leaving the listbox, update the combo/datamodel.
|
216
|
+
# we are using methods of the datamodel. Updating our list will have
|
217
|
+
# no effect on the list, and wont trigger events.
|
218
|
+
# Do not override.
|
219
|
+
def on_leave
|
220
|
+
if !@list.include? @buffer and !@buffer.strip.empty?
|
221
|
+
_insert_policy = @insert_policy || :INSERT_AT_BOTTOM
|
222
|
+
case _insert_policy
|
223
|
+
when :INSERT_AT_BOTTOM, :INSERT_AT_END
|
224
|
+
@list.append @buffer
|
225
|
+
when :INSERT_AT_TOP
|
226
|
+
@list.insert(0, @buffer)
|
227
|
+
when :INSERT_AFTER_CURRENT
|
228
|
+
@current_index += 1
|
229
|
+
@list.insert(@current_index, @buffer)
|
230
|
+
|
231
|
+
when :INSERT_BEFORE_CURRENT
|
232
|
+
#_index = @current_index-1 if @current_index>0
|
233
|
+
_index = @current_index
|
234
|
+
@list.insert(_index, @buffer)
|
235
|
+
when :INSERT_AT_CURRENT
|
236
|
+
@list[@current_index]=@buffer
|
237
|
+
when :NO_INSERT
|
238
|
+
; # take a break
|
239
|
+
end
|
240
|
+
end
|
241
|
+
fire_handler :LEAVE, self
|
242
|
+
end
|
243
|
+
|
244
|
+
def repaint
|
245
|
+
super
|
246
|
+
c = @col + @display_length
|
247
|
+
if @show_symbol # 2009-01-11 18:47
|
248
|
+
# i have changed c +1 to c, since we have no right to print beyond display_length
|
249
|
+
@form.window.mvwaddch @row, c, @COMBO_SYMBOL # Ncurses::ACS_GEQUAL
|
250
|
+
@form.window.mvchgat(y=@row, x=c, max=1, Ncurses::A_REVERSE|Ncurses::A_UNDERLINE, $datacolor, nil)
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
end # class ComboBox
|
255
|
+
|
256
|
+
end # module
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'rbcurse'
|
2
|
+
##
|
3
|
+
module RubyCurses
|
4
|
+
class Link < Button
|
5
|
+
dsl_property :description
|
6
|
+
|
7
|
+
|
8
|
+
def initialize form, config={}, &block
|
9
|
+
super
|
10
|
+
@text_offset = 0
|
11
|
+
# haha we've never done this, pin the cursor up on 0,0
|
12
|
+
@col_offset = -1
|
13
|
+
if @mnemonic
|
14
|
+
form.bind_key(@mnemonic.downcase, self){ self.fire }
|
15
|
+
end
|
16
|
+
@display_length = config[:width]
|
17
|
+
end
|
18
|
+
def fire
|
19
|
+
super
|
20
|
+
self.focus
|
21
|
+
end
|
22
|
+
def getvalue_for_paint
|
23
|
+
getvalue()
|
24
|
+
end
|
25
|
+
##
|
26
|
+
end # class
|
27
|
+
end # module
|
@@ -0,0 +1,1247 @@
|
|
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
|
+
NOTE: listbox now traps RETURN/ENTER/13 so if you are trapping it, please use bind :PRESS
|
9
|
+
# Changes:
|
10
|
+
# Added edit_toggle_mode and edit_toggle_key so on can use vim keys for navigating
|
11
|
+
# and go into edit mode (currently using C-e). This way ENTER can still be used
|
12
|
+
# for PRESS. Esc and esc-esc closes edit mode whereas C-c or C-g reverts edit.
|
13
|
+
|
14
|
+
TODO
|
15
|
+
=end
|
16
|
+
require 'rbcurse'
|
17
|
+
require 'rbcurse/core/include/listcellrenderer'
|
18
|
+
require 'rbcurse/extras/include/listkeys'
|
19
|
+
require 'forwardable'
|
20
|
+
|
21
|
+
|
22
|
+
#include Ncurses # FFI 2011-09-8
|
23
|
+
module RubyCurses
|
24
|
+
extend self
|
25
|
+
##
|
26
|
+
# When an event is fired by Listbox, contents are changed, then this object will be passed
|
27
|
+
# to trigger
|
28
|
+
# shamelessly plugged from a legacy language best unnamed
|
29
|
+
# type is CONTENTS_CHANGED, INTERVAL_ADDED, INTERVAL_REMOVED
|
30
|
+
class ListDataEvent
|
31
|
+
attr_accessor :index0, :index1, :source, :type
|
32
|
+
def initialize index0, index1, source, type
|
33
|
+
@index0 = index0
|
34
|
+
@index1 = index1
|
35
|
+
@source = source
|
36
|
+
@type = type
|
37
|
+
end
|
38
|
+
def to_s
|
39
|
+
"#{@type.to_s}, #{@source}, #{@index0}, #{@index1}"
|
40
|
+
end
|
41
|
+
def inspect
|
42
|
+
"#{@type.to_s}, #{@source}, #{@index0}, #{@index1}"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
# http://www.java2s.com/Code/JavaAPI/javax.swing.event/ListDataEventCONTENTSCHANGED.htm
|
46
|
+
# should we extend array of will that open us to misuse
|
47
|
+
class ListDataModel
|
48
|
+
include Enumerable
|
49
|
+
include RubyCurses::EventHandler
|
50
|
+
attr_accessor :selected_index
|
51
|
+
attr_reader :last_regex # should i really keep here as public or maintain in listbox
|
52
|
+
|
53
|
+
def initialize anarray=[]
|
54
|
+
@list = anarray.dup
|
55
|
+
@_events = [:LIST_DATA_EVENT, :ENTER_ROW]
|
56
|
+
end
|
57
|
+
# changd on 2009-01-14 12:28 based on ..
|
58
|
+
# http://www.ruby-forum.com/topic/175637#769030
|
59
|
+
def each(&blk)
|
60
|
+
@list.each(&blk)
|
61
|
+
end
|
62
|
+
# not sure how to do this XXX removed on 2009-01-14 12:28
|
63
|
+
#def <=>(other)
|
64
|
+
# @list <=> other
|
65
|
+
#end
|
66
|
+
def index obj
|
67
|
+
@list.index(obj)
|
68
|
+
end
|
69
|
+
def length ; @list.length; end
|
70
|
+
alias :size :length
|
71
|
+
|
72
|
+
def insert off0, *data
|
73
|
+
@list.insert off0, *data
|
74
|
+
lde = ListDataEvent.new(off0, off0+data.length-1, self, :INTERVAL_ADDED)
|
75
|
+
fire_handler :LIST_DATA_EVENT, lde
|
76
|
+
end
|
77
|
+
def append data
|
78
|
+
@list << data
|
79
|
+
lde = ListDataEvent.new(@list.length-1, @list.length-1, self, :INTERVAL_ADDED)
|
80
|
+
fire_handler :LIST_DATA_EVENT, lde
|
81
|
+
end
|
82
|
+
def update off0, data
|
83
|
+
@list[off0] = data
|
84
|
+
lde = ListDataEvent.new(off0, off0, self, :CONTENTS_CHANGED)
|
85
|
+
fire_handler :LIST_DATA_EVENT, lde
|
86
|
+
end
|
87
|
+
def []=(off0, data)
|
88
|
+
update off0, data
|
89
|
+
end
|
90
|
+
def [](off0)
|
91
|
+
@list[off0]
|
92
|
+
end
|
93
|
+
def delete_at off0
|
94
|
+
ret=@list.delete_at off0
|
95
|
+
lde = ListDataEvent.new(off0, off0, self, :INTERVAL_REMOVED)
|
96
|
+
fire_handler :LIST_DATA_EVENT, lde
|
97
|
+
return ret
|
98
|
+
end
|
99
|
+
def remove_all
|
100
|
+
return if @list.nil? || @list.empty? # 2010-09-21 13:25
|
101
|
+
lde = ListDataEvent.new(0, @list.size, self, :INTERVAL_REMOVED)
|
102
|
+
@list = []
|
103
|
+
@current_index = 0
|
104
|
+
fire_handler :LIST_DATA_EVENT, lde
|
105
|
+
end
|
106
|
+
def delete obj
|
107
|
+
off0 = @list.index obj
|
108
|
+
return nil if off0.nil?
|
109
|
+
ret=@list.delete_at off0
|
110
|
+
lde = ListDataEvent.new(off0, off0, self, :INTERVAL_REMOVED)
|
111
|
+
fire_handler :LIST_DATA_EVENT, lde
|
112
|
+
return ret
|
113
|
+
end
|
114
|
+
def include?(obj)
|
115
|
+
return @list.include?(obj)
|
116
|
+
end
|
117
|
+
# returns a `dup()` of the list
|
118
|
+
def values
|
119
|
+
@list.dup
|
120
|
+
end
|
121
|
+
# why do we have this here in data, we should remove this
|
122
|
+
# @deprecated this was just eye candy for some demo
|
123
|
+
def on_enter_row object
|
124
|
+
$log.debug " XXX on_enter_row of list_data"
|
125
|
+
fire_handler :ENTER_ROW, object
|
126
|
+
end
|
127
|
+
# ##
|
128
|
+
# added 2009-01-14 01:00
|
129
|
+
# searches between given range of rows (def 0 and end)
|
130
|
+
# returns row index of first match of given regex (or nil if not found)
|
131
|
+
def find_match regex, ix0=0, ix1=length()
|
132
|
+
$log.debug " find_match got #{regex} #{ix0} #{ix1}"
|
133
|
+
@last_regex = regex
|
134
|
+
@search_start_ix = ix0
|
135
|
+
@search_end_ix = ix1
|
136
|
+
#@search_found_ix = nil
|
137
|
+
@list.each_with_index do |row, ix|
|
138
|
+
next if ix < ix0
|
139
|
+
break if ix > ix1
|
140
|
+
if !row.match(regex).nil?
|
141
|
+
@search_found_ix = ix
|
142
|
+
return ix
|
143
|
+
end
|
144
|
+
end
|
145
|
+
return nil
|
146
|
+
end
|
147
|
+
##
|
148
|
+
# continues previous search
|
149
|
+
def find_next
|
150
|
+
raise "No previous search" if @last_regex.nil?
|
151
|
+
start = @search_found_ix && @search_found_ix+1 || 0
|
152
|
+
return find_match @last_regex, start, @search_end_ix
|
153
|
+
end
|
154
|
+
##
|
155
|
+
# find backwards, list_data_model
|
156
|
+
# Using this to start a search or continue search
|
157
|
+
def find_prev regex=@last_regex, start = @search_found_ix
|
158
|
+
raise "No previous search" if regex.nil? # @last_regex.nil?
|
159
|
+
$log.debug " find_prev #{@search_found_ix} : #{@current_index}"
|
160
|
+
start -= 1 unless start == 0
|
161
|
+
@last_regex = regex
|
162
|
+
@search_start_ix = start
|
163
|
+
start.downto(0) do |ix|
|
164
|
+
row = @list[ix]
|
165
|
+
if !row.match(regex).nil?
|
166
|
+
@search_found_ix = ix
|
167
|
+
return ix
|
168
|
+
end
|
169
|
+
end
|
170
|
+
return nil
|
171
|
+
#return find_match @last_regex, start, @search_end_ix
|
172
|
+
end
|
173
|
+
##
|
174
|
+
# added 2010-05-23 12:10 for listeditable
|
175
|
+
def slice!(line, howmany)
|
176
|
+
ret = @list.slice!(line, howmany)
|
177
|
+
lde = ListDataEvent.new(line, line+howmany-1, self, :INTERVAL_REMOVED)
|
178
|
+
fire_handler :LIST_DATA_EVENT, lde
|
179
|
+
return ret
|
180
|
+
end
|
181
|
+
|
182
|
+
alias :to_array :values
|
183
|
+
end # class ListDataModel
|
184
|
+
##
|
185
|
+
# scrollable, selectable list of items
|
186
|
+
# TODO Add events for item add/remove and selection change
|
187
|
+
# added event LIST_COMBO_SELECT fired whenever a select/deselect is done.
|
188
|
+
# - I do not know how this works in Tk so only the name is copied..
|
189
|
+
# - @selected contains indices of selected objects.
|
190
|
+
# - currently the first argument of event is row (the row selected/deselected). Should it
|
191
|
+
# be the object.
|
192
|
+
# - this event could change when range selection is allowed.
|
193
|
+
#
|
194
|
+
|
195
|
+
##
|
196
|
+
# TODO CAN WE MOVE THIS OUT TO ANOTHER FILE as confusing me
|
197
|
+
# pops up a list of values for selection
|
198
|
+
# 2008-12-10
|
199
|
+
class PopupList
|
200
|
+
include RubyCurses::EventHandler
|
201
|
+
dsl_accessor :title
|
202
|
+
dsl_accessor :row, :col, :height, :width
|
203
|
+
dsl_accessor :layout
|
204
|
+
attr_reader :config
|
205
|
+
attr_reader :selected_index # button index selected by user
|
206
|
+
attr_reader :window # required for keyboard
|
207
|
+
dsl_accessor :list_selection_mode # true or false allow multiple selection
|
208
|
+
dsl_accessor :relative_to # a widget, if given row and col are relative to widgets windows
|
209
|
+
# layout
|
210
|
+
dsl_accessor :max_visible_items # how many to display
|
211
|
+
dsl_accessor :list_config # hash with values for the list to use
|
212
|
+
dsl_accessor :valign
|
213
|
+
attr_reader :listbox
|
214
|
+
|
215
|
+
def initialize aconfig={}, &block
|
216
|
+
@config = aconfig
|
217
|
+
@selected_index = -1
|
218
|
+
@list_config ||= {}
|
219
|
+
@config.each_pair { |k,v| instance_variable_set("@#{k}",v) }
|
220
|
+
instance_eval &block if block_given?
|
221
|
+
@list_config.each_pair { |k,v| instance_variable_set("@#{k}",v) }
|
222
|
+
@height ||= [@max_visible_items || 10, @list.length].min
|
223
|
+
$log.debug " POPUP XXX #{@max_visible_items} ll:#{@list.length} h:#{@height}"
|
224
|
+
# get widgets absolute coords
|
225
|
+
if @relative_to
|
226
|
+
layout = @relative_to.form.window.layout
|
227
|
+
@row = @row + layout[:top]
|
228
|
+
@col = @col + layout[:left]
|
229
|
+
end
|
230
|
+
if !@valign.nil?
|
231
|
+
case @valign.to_sym
|
232
|
+
when :BELOW
|
233
|
+
@row += 1
|
234
|
+
when :ABOVE
|
235
|
+
@row -= @height+1
|
236
|
+
@row = 0 if @row < 0
|
237
|
+
when :CENTER
|
238
|
+
@row -= @height/2
|
239
|
+
@row = 0 if @row < 0
|
240
|
+
else
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
layout(1+height, @width+4, @row, @col) # changed 2 to 1, 2008-12-17 13:48
|
245
|
+
@window = VER::Window.new(@layout)
|
246
|
+
@form = RubyCurses::Form.new @window
|
247
|
+
@window.bkgd(Ncurses.COLOR_PAIR($reversecolor));
|
248
|
+
@window.wrefresh
|
249
|
+
#@panel = @window.panel # useless line ?
|
250
|
+
Ncurses::Panel.update_panels
|
251
|
+
print_input # creates the listbox
|
252
|
+
@form.repaint
|
253
|
+
@window.wrefresh
|
254
|
+
handle_keys
|
255
|
+
end
|
256
|
+
# class popup
|
257
|
+
def list alist=nil
|
258
|
+
return @list if alist.nil?
|
259
|
+
@list = ListDataModel.new(alist)
|
260
|
+
@repaint_required = true
|
261
|
+
# will we need this ? listbox made each time so data should be fresh
|
262
|
+
#@list.bind(:LIST_DATA_EVENT) { |e| list_data_changed() }
|
263
|
+
end
|
264
|
+
# class popup
|
265
|
+
def list_data_model ldm
|
266
|
+
raise "Expecting list_data_model" unless ldm.is_a? RubyCurses::ListDataModel
|
267
|
+
@list = ldm
|
268
|
+
# will we need this ? listbox made each time so data should be fresh
|
269
|
+
#@list.bind(:LIST_DATA_EVENT) { |e| list_data_changed() }
|
270
|
+
end
|
271
|
+
##
|
272
|
+
def input_value
|
273
|
+
#return @listbox.getvalue if !@listbox.nil?
|
274
|
+
return @listbox.focussed_index if !@listbox.nil?
|
275
|
+
end
|
276
|
+
## popuplist
|
277
|
+
def stopping?
|
278
|
+
@stop
|
279
|
+
end
|
280
|
+
## popuplist
|
281
|
+
def handle_keys
|
282
|
+
begin
|
283
|
+
while((ch = @window.getchar()) != 999 )
|
284
|
+
case ch
|
285
|
+
when -1
|
286
|
+
next
|
287
|
+
else
|
288
|
+
press ch
|
289
|
+
break if @stop
|
290
|
+
end
|
291
|
+
end
|
292
|
+
ensure
|
293
|
+
destroy
|
294
|
+
end
|
295
|
+
return 0 #@selected_index
|
296
|
+
end
|
297
|
+
##
|
298
|
+
# TODO get next match for key
|
299
|
+
def press ch
|
300
|
+
$log.debug "popup handle_keys : #{ch}" if ch != -1
|
301
|
+
case ch
|
302
|
+
when -1
|
303
|
+
return
|
304
|
+
when KEY_F1, 27, ?\C-q # 27/ESC does not come here since gobbled by keyboard.rb
|
305
|
+
@stop = true
|
306
|
+
return
|
307
|
+
when KEY_ENTER, 10, 13
|
308
|
+
# if you press ENTER without selecting, it won't come here
|
309
|
+
# it will fire button OK's fire, if that's the default button
|
310
|
+
|
311
|
+
# returns an array of indices if multiple selection
|
312
|
+
if @listbox.selection_mode == :multiple
|
313
|
+
fire_handler :PRESS, @listbox
|
314
|
+
else
|
315
|
+
fire_handler :PRESS, @listbox.focussed_index
|
316
|
+
end
|
317
|
+
# since Listbox is handling enter, COMBO_SELECT will not be fired
|
318
|
+
# $log.debug "popup ENTER : #{field.name}" if !field.nil?
|
319
|
+
@stop = true
|
320
|
+
return
|
321
|
+
when KEY_TAB
|
322
|
+
@form.select_next_field
|
323
|
+
else
|
324
|
+
# fields must return unhandled else we will miss hotkeys.
|
325
|
+
# On messageboxes, often if no edit field, then O and C are hot.
|
326
|
+
field = @form.get_current_field
|
327
|
+
handled = field.handle_key ch
|
328
|
+
|
329
|
+
if handled == :UNHANDLED
|
330
|
+
@stop = true
|
331
|
+
return
|
332
|
+
end
|
333
|
+
end
|
334
|
+
@form.repaint
|
335
|
+
Ncurses::Panel.update_panels();
|
336
|
+
Ncurses.doupdate();
|
337
|
+
@window.wrefresh
|
338
|
+
end
|
339
|
+
def print_input
|
340
|
+
r = c = 0
|
341
|
+
width = @layout[:width]
|
342
|
+
#$log.debug " print_input POPUP ht:#{@height} lh:#{@layout[:height]} "
|
343
|
+
height = @layout[:height]
|
344
|
+
#height = @height # 2010-01-06 12:52 why was this overriding previous line. its one less than layout
|
345
|
+
# i am now using layout height since it gives a closer size to whats asked for.
|
346
|
+
parent = @relative_to
|
347
|
+
defaultvalue = @default_value || ""
|
348
|
+
list = @list
|
349
|
+
selection_mode = @list_selection_mode
|
350
|
+
default_values = @default_values
|
351
|
+
@list_config['color'] ||= 'black'
|
352
|
+
@list_config['bgcolor'] ||= 'yellow'
|
353
|
+
@listbox = RubyCurses::Listbox.new @form, @list_config do
|
354
|
+
name "input"
|
355
|
+
row r
|
356
|
+
col c
|
357
|
+
# attr 'reverse'
|
358
|
+
width width
|
359
|
+
height height
|
360
|
+
list_data_model list
|
361
|
+
# ?? XXX display_length 30
|
362
|
+
# set_buffer defaultvalue
|
363
|
+
selection_mode selection_mode
|
364
|
+
default_values default_values
|
365
|
+
is_popup true
|
366
|
+
#add_observer parent
|
367
|
+
end
|
368
|
+
end
|
369
|
+
# may need to be upgraded to new one XXX FIXME
|
370
|
+
def configure(*val , &block)
|
371
|
+
case val.size
|
372
|
+
when 1
|
373
|
+
return @config[val[0]]
|
374
|
+
when 2
|
375
|
+
@config[val[0]] = val[1]
|
376
|
+
instance_variable_set("@#{val[0]}", val[1])
|
377
|
+
end
|
378
|
+
instance_eval &block if block_given?
|
379
|
+
end
|
380
|
+
def cget param
|
381
|
+
@config[param]
|
382
|
+
end
|
383
|
+
|
384
|
+
def layout(height=0, width=0, top=0, left=0)
|
385
|
+
@layout = { :height => height, :width => width, :top => top, :left => left }
|
386
|
+
end
|
387
|
+
def destroy
|
388
|
+
@window.destroy if !@window.nil?
|
389
|
+
end
|
390
|
+
end # class PopupList
|
391
|
+
##
|
392
|
+
# A control for displaying a list of data or values.
|
393
|
+
# The list will be editable if @cell_editing_allowed
|
394
|
+
# is set to true when creating. By default, multiple selection is allowed, but may be set to :single.
|
395
|
+
# TODO: were we not going to force creation of datamodel and listener on startup by putting a blank
|
396
|
+
# :list in config, if no list or list_variable or model is there ?
|
397
|
+
# Or at end of constructor check, if no listdatamodel then create default.
|
398
|
+
# TODO : perhaps when datamodel created, attach listener to it, so we can fire to callers when
|
399
|
+
# they want to be informed of changes. As we did with selection listeners.
|
400
|
+
#
|
401
|
+
class Listbox < Widget
|
402
|
+
|
403
|
+
require 'rbcurse/core/include/listscrollable'
|
404
|
+
require 'rbcurse/extras/include/listselectable'
|
405
|
+
require 'rbcurse/extras/include/defaultlistselectionmodel'
|
406
|
+
require 'rbcurse/extras/include/celleditor'
|
407
|
+
include ListScrollable
|
408
|
+
include ListSelectable
|
409
|
+
include RubyCurses::ListKeys
|
410
|
+
extend Forwardable
|
411
|
+
dsl_accessor :height
|
412
|
+
dsl_accessor :title
|
413
|
+
dsl_property :title_attrib # bold, reverse, normal
|
414
|
+
# dsl_accessor :list # the array of data to be sent by user
|
415
|
+
attr_reader :toprow
|
416
|
+
# attr_reader :prow
|
417
|
+
# attr_reader :winrow
|
418
|
+
# dsl_accessor :selection_mode # allow multiple select or not
|
419
|
+
# dsl_accessor :list_variable # a variable values are shown from this
|
420
|
+
dsl_accessor :default_values # array of default values
|
421
|
+
dsl_accessor :is_popup # if it is in a popup and single select, selection closes
|
422
|
+
attr_accessor :current_index
|
423
|
+
#dsl_accessor :cell_renderer
|
424
|
+
dsl_accessor :selected_color, :selected_bgcolor, :selected_attr
|
425
|
+
dsl_accessor :max_visible_items # how many to display 2009-01-11 16:15
|
426
|
+
dsl_accessor :cell_editing_allowed
|
427
|
+
dsl_property :show_selector
|
428
|
+
dsl_property :row_selected_symbol # 2009-01-12 12:01 changed from selector to selected
|
429
|
+
dsl_property :row_unselected_symbol # added 2009-01-12 12:00
|
430
|
+
dsl_property :left_margin
|
431
|
+
# please set these in he constructor block. Settin them later will have no effect
|
432
|
+
# since i would have bound them to actions
|
433
|
+
dsl_accessor :KEY_ROW_SELECTOR # editable lists may want to use 0 or some other key
|
434
|
+
dsl_accessor :KEY_GOTO_TOP # this is going to go
|
435
|
+
dsl_accessor :KEY_GOTO_BOTTOM # this is going to go
|
436
|
+
dsl_accessor :KEY_CLEAR_SELECTION # this is going to go
|
437
|
+
dsl_accessor :KEY_NEXT_SELECTION # this is going to go
|
438
|
+
dsl_accessor :KEY_PREV_SELECTION # this is going to go
|
439
|
+
dsl_accessor :valign # 2009-01-17 18:32 vertical alignment used in combos
|
440
|
+
dsl_accessor :justify # 2010-09-27 12:41 used by renderer
|
441
|
+
attr_accessor :one_key_selection # will pressing a single key select or not
|
442
|
+
dsl_accessor :border_attrib, :border_color #
|
443
|
+
dsl_accessor :sanitization_required
|
444
|
+
dsl_accessor :suppress_borders #to_print_borders
|
445
|
+
|
446
|
+
|
447
|
+
def initialize form, config={}, &block
|
448
|
+
@focusable = true
|
449
|
+
@editable = false
|
450
|
+
@sanitization_required = true
|
451
|
+
@one_key_selection = false # allow vim like keys
|
452
|
+
@row = 0
|
453
|
+
@col = 0
|
454
|
+
# data of listbox this is not an array, its a pointer to the listdatamodel
|
455
|
+
@list = nil
|
456
|
+
# any special attribs such as status to be printed in col1, or color (selection)
|
457
|
+
@list_attribs = {}
|
458
|
+
@suppress_borders = false
|
459
|
+
@row_offset = @col_offset = 1 # for borders
|
460
|
+
super
|
461
|
+
@_events.push(*[:ENTER_ROW, :LEAVE_ROW, :LIST_SELECTION_EVENT, :PRESS])
|
462
|
+
@current_index ||= 0
|
463
|
+
@selection_mode ||= :multiple # default is multiple, anything else given becomes single
|
464
|
+
@win = @graphic # 2010-01-04 12:36 BUFFERED replace form.window with graphic
|
465
|
+
# moving down to repaint so that scrollpane can set should_buffered
|
466
|
+
# added 2010-02-17 23:05 RFED16 so we don't need a form.
|
467
|
+
|
468
|
+
# next 2 lines carry a redundancy
|
469
|
+
select_default_values
|
470
|
+
# when the combo box has a certain row in focus, the popup should have the same row in focus
|
471
|
+
|
472
|
+
install_keys
|
473
|
+
install_list_keys
|
474
|
+
init_vars
|
475
|
+
init_actions
|
476
|
+
# OMG What about people whove installed custom renders such as rfe.rb 2011-10-15
|
477
|
+
#bind(:PROPERTY_CHANGE){|e| @cell_renderer = nil } # will be recreated if anything changes 2011-09-28 V1.3.1
|
478
|
+
bind(:PROPERTY_CHANGE){|e|
|
479
|
+
# I can't delete the cell renderer, but this may not have full effect if one color is passed.
|
480
|
+
if @cell_renderer.respond_to? e.property_name
|
481
|
+
@cell_renderer.send(e.property_name.to_sym, e.newvalue)
|
482
|
+
end
|
483
|
+
} # will be recreated if anything changes 2011-09-28 V1.3.1
|
484
|
+
|
485
|
+
if @list && !@list.selected_index.nil?
|
486
|
+
set_focus_on @list.selected_index # the new version
|
487
|
+
end
|
488
|
+
end
|
489
|
+
# this is called several times, from constructor
|
490
|
+
# and when list data changed, so only put relevant resets here.
|
491
|
+
# why can't current_index be set to 0 here
|
492
|
+
def init_vars
|
493
|
+
@repaint_required = true
|
494
|
+
@toprow = @pcol = 0
|
495
|
+
|
496
|
+
@row_offset = @col_offset = 0 if @suppress_borders
|
497
|
+
if @show_selector
|
498
|
+
@row_selected_symbol ||= '>'
|
499
|
+
@row_unselected_symbol ||= ' '
|
500
|
+
@left_margin ||= @row_selected_symbol.length
|
501
|
+
end
|
502
|
+
@left_margin ||= 0
|
503
|
+
# we reduce internal_width from width while printing
|
504
|
+
@internal_width = 2 # taking into account borders accounting for 2 cols
|
505
|
+
@internal_width = 0 if @suppress_borders # should it be 0 ???
|
506
|
+
|
507
|
+
# toggle editable state using ENTER key
|
508
|
+
@edit_toggle = false
|
509
|
+
@edit_toggle_key = ?\C-e.getbyte(0)
|
510
|
+
# has editing started using edit_toggle
|
511
|
+
@is_editing = !@edit_toggle
|
512
|
+
map_keys unless @keys_mapped # moved here so users can remap
|
513
|
+
|
514
|
+
end
|
515
|
+
def map_keys
|
516
|
+
return if @keys_mapped
|
517
|
+
bind_key(?f, 'next row starting with ...'){ ask_selection_for_char() }
|
518
|
+
bind_key(?\M-v, 'one key toggle'){ @one_key_selection = !@one_key_selection }
|
519
|
+
bind_key(?j, 'next row'){ next_row() }
|
520
|
+
bind_key(?k, 'prev row'){ previous_row() }
|
521
|
+
bind_key(?\C-n, 'next row'){ next_row() }
|
522
|
+
bind_key(?\C-p, 'prev row'){ previous_row() }
|
523
|
+
bind_key(?\C-d, :scroll_forward)
|
524
|
+
bind_key(?\C-b, :scroll_backward)
|
525
|
+
bind_key(?G, 'goto bottom'){ goto_bottom() }
|
526
|
+
bind_key([?g,?g], 'goto top'){ goto_top() }
|
527
|
+
bind_key(?\M-<, :goto_top )
|
528
|
+
bind_key(?\M->, :goto_bottom )
|
529
|
+
bind_key(?/, 'find'){ ask_search() }
|
530
|
+
bind_key(?n, 'find next'){ find_more() }
|
531
|
+
#bind_key(32){ toggle_row_selection() } # some guys may want another selector
|
532
|
+
if @cell_editing_allowed && @KEY_ROW_SELECTOR == 32
|
533
|
+
@KEY_ROW_SELECTOR = 0 # Ctrl-Space
|
534
|
+
end
|
535
|
+
bind_key(@KEY_ROW_SELECTOR, 'select a row'){ toggle_row_selection() }
|
536
|
+
bind_key(10, 'fire action'){ fire_action_event }
|
537
|
+
# I had commented this but we need ENTER for cases like a directory browser where ENTER opens dir
|
538
|
+
bind_key(KEY_ENTER, 'fire action'){ fire_action_event }
|
539
|
+
#bind_key(13){ @is_editing = !@is_editing }
|
540
|
+
#bind_key(?\M-: , :_show_menu)
|
541
|
+
@keys_mapped = true
|
542
|
+
end
|
543
|
+
|
544
|
+
##
|
545
|
+
# getter and setter for selection_mode
|
546
|
+
# Must be called after creating model, so no duplicate. Since one may set in model directly.
|
547
|
+
def selection_mode(*val)
|
548
|
+
#raise "ListSelectionModel not yet created!" if @list_selection_model.nil?
|
549
|
+
|
550
|
+
if val.empty?
|
551
|
+
if @list_selection_model
|
552
|
+
return @list_selection_model.selection_mode
|
553
|
+
else
|
554
|
+
return @tmp_selection_mode
|
555
|
+
end
|
556
|
+
else
|
557
|
+
if @list_selection_model.nil?
|
558
|
+
@tmp_selection_mode = val[0]
|
559
|
+
else
|
560
|
+
@list_selection_model.selection_mode = val[0].to_sym
|
561
|
+
end
|
562
|
+
end
|
563
|
+
end
|
564
|
+
def row_count
|
565
|
+
return 0 if @list.nil?
|
566
|
+
@list.length
|
567
|
+
end
|
568
|
+
# added 2009-01-07 13:05 so new scrollable can use
|
569
|
+
def scrollatrow
|
570
|
+
if @suppress_borders
|
571
|
+
return @height - 1
|
572
|
+
else
|
573
|
+
return @height - 3
|
574
|
+
end
|
575
|
+
end
|
576
|
+
# provide data to List in the form of an Array or Variable or
|
577
|
+
# ListDataModel. This will create a default ListSelectionModel.
|
578
|
+
#
|
579
|
+
# CHANGE as on 2010-09-21 12:53:
|
580
|
+
# If explicit nil passed then dummy datamodel and selection model created
|
581
|
+
# From now on, constructor will call this, so this can always
|
582
|
+
# happen.
|
583
|
+
#
|
584
|
+
# NOTE: sometimes this can be added much after its painted.
|
585
|
+
# Do not expect this to be called from constructor, although that
|
586
|
+
# is the usual case. it can be dependent on some other list or tree.
|
587
|
+
# @param [Array, Variable, ListDataModel] data to populate list with
|
588
|
+
# @return [ListDataModel] just created or assigned
|
589
|
+
|
590
|
+
def list *val
|
591
|
+
return @list if val.empty?
|
592
|
+
clear_selection if @list # clear previous selections if any
|
593
|
+
@default_values = nil if @list # clear previous selections if any
|
594
|
+
alist = val[0]
|
595
|
+
case alist
|
596
|
+
when Array
|
597
|
+
if @list
|
598
|
+
@list.remove_all
|
599
|
+
@list.insert 0, *alist
|
600
|
+
@current_index = 0
|
601
|
+
else
|
602
|
+
@list = RubyCurses::ListDataModel.new(alist)
|
603
|
+
end
|
604
|
+
when NilClass
|
605
|
+
if @list
|
606
|
+
@list.remove_all
|
607
|
+
else
|
608
|
+
@list = RubyCurses::ListDataModel.new(alist)
|
609
|
+
end
|
610
|
+
when Variable
|
611
|
+
@list = RubyCurses::ListDataModel.new(alist.value)
|
612
|
+
when RubyCurses::ListDataModel
|
613
|
+
@list = alist
|
614
|
+
else
|
615
|
+
raise ArgumentError, "Listbox list(): do not know how to handle #{alist.class} "
|
616
|
+
end
|
617
|
+
# added on 2009-01-13 23:19 since updates are not automatic now
|
618
|
+
@list.bind(:LIST_DATA_EVENT) { |e| list_data_changed() }
|
619
|
+
create_default_list_selection_model
|
620
|
+
@list_selection_model.selection_mode = @tmp_selection_mode if @tmp_selection_mode
|
621
|
+
@repaint_required = true
|
622
|
+
@list
|
623
|
+
end
|
624
|
+
# populate using a Variable which should contain a list
|
625
|
+
# NOTE: This explicilty overwrites any existing datamodel such as the
|
626
|
+
# default one. You may lose any events you have bound to the listbox
|
627
|
+
# prior to this call.
|
628
|
+
def list_variable alist=nil
|
629
|
+
return @list if alist.nil?
|
630
|
+
@list = RubyCurses::ListDataModel.new(alist.value)
|
631
|
+
# added on 2009-01-13 23:19 since updates are not automatic now
|
632
|
+
@list.bind(:LIST_DATA_EVENT) { |e| list_data_changed() }
|
633
|
+
create_default_list_selection_model
|
634
|
+
end
|
635
|
+
# populate using a custom data model
|
636
|
+
# NOTE: This explicilty overwrites any existing datamodel such as the
|
637
|
+
# default one. You may lose any events you have bound to the listbox
|
638
|
+
# prior to this call.
|
639
|
+
|
640
|
+
def list_data_model ldm=nil
|
641
|
+
return @list if ldm.nil?
|
642
|
+
raise "Expecting list_data_model" unless ldm.is_a? RubyCurses::ListDataModel
|
643
|
+
@list = ldm
|
644
|
+
# added on 2009-01-13 23:19 since updates are not automatic now
|
645
|
+
@list.bind(:LIST_DATA_EVENT) { |e| list_data_changed() }
|
646
|
+
create_default_list_selection_model
|
647
|
+
end
|
648
|
+
# create a default list selection model and set it
|
649
|
+
# NOTE: I am now checking if one is not already created, since
|
650
|
+
# a second creation would wipe out any listeners on it.
|
651
|
+
# @see ListSelectable
|
652
|
+
# @see DefaultListSelectionModel
|
653
|
+
def create_default_list_selection_model
|
654
|
+
if @list_selection_model.nil?
|
655
|
+
list_selection_model DefaultListSelectionModel.new(self)
|
656
|
+
end
|
657
|
+
end
|
658
|
+
# added 2010-09-15 00:11 to make life easier
|
659
|
+
def_delegators :@list, :insert, :remove_all, :delete_at, :include?, :each, :values, :size
|
660
|
+
# get element at
|
661
|
+
# @param [Fixnum] index for element
|
662
|
+
# @return [Object] element
|
663
|
+
# @since 1.2.0 2010-09-06 14:33 making life easier for others.
|
664
|
+
def [](off0)
|
665
|
+
@list[off0]
|
666
|
+
end
|
667
|
+
# return object under cursor
|
668
|
+
# Note: this should not be confused with selected row/s. User may not have selected this.
|
669
|
+
# This is only useful since in some demos we like to change a status bar as a user scrolls down
|
670
|
+
# @since 1.2.0 2010-09-06 14:33 making life easier for others.
|
671
|
+
def current_value
|
672
|
+
@list[@current_index]
|
673
|
+
end
|
674
|
+
# avoid using "row", i'd rather stick with "index" and "value".
|
675
|
+
alias :current_row :current_value
|
676
|
+
alias :text :current_value # thanks to shoes, not sure how this will impact since widget has text.
|
677
|
+
|
678
|
+
# XXX can this not be done at repaint
|
679
|
+
def select_default_values
|
680
|
+
return if @default_values.nil?
|
681
|
+
@default_values.each do |val|
|
682
|
+
row = @list.index val
|
683
|
+
add_row_selection_interval row, row unless row.nil?
|
684
|
+
end
|
685
|
+
end
|
686
|
+
def print_borders
|
687
|
+
raise "Width not supplied" unless @width
|
688
|
+
raise "Height not supplied" unless @height
|
689
|
+
width = @width
|
690
|
+
height = @height-1 # 2010-01-04 15:30 BUFFERED HEIGHT
|
691
|
+
window = @graphic # 2010-01-04 12:37 BUFFERED
|
692
|
+
startcol = @col
|
693
|
+
startrow = @row
|
694
|
+
@color_pair = get_color($datacolor)
|
695
|
+
# bordercolor = @border_color || $datacolor # changed 2011 dts
|
696
|
+
bordercolor = @border_color || @color_pair
|
697
|
+
borderatt = @border_attrib || Ncurses::A_NORMAL
|
698
|
+
|
699
|
+
#$log.debug "rlistb #{name}: window.print_border #{startrow}, #{startcol} , h:#{height}, w:#{width} , @color_pair, @attr "
|
700
|
+
window.print_border startrow, startcol, height, width, bordercolor, borderatt
|
701
|
+
print_title
|
702
|
+
end
|
703
|
+
def print_title
|
704
|
+
@color_pair ||= get_color($datacolor)
|
705
|
+
# check title.length and truncate if exceeds width
|
706
|
+
return unless @title
|
707
|
+
_title = @title
|
708
|
+
if @title.length > @width - 2
|
709
|
+
_title = @title[0..@width-2]
|
710
|
+
end
|
711
|
+
@graphic.printstring( @row, @col+(@width-_title.length)/2, _title, @color_pair, @title_attrib) unless @title.nil?
|
712
|
+
end
|
713
|
+
### START FOR scrollable ###
|
714
|
+
def get_content
|
715
|
+
#@list 2008-12-01 23:13
|
716
|
+
# NOTE: we never stored the listvariable, so its redundant, we used its value to set list
|
717
|
+
@list_variable && @list_variable.value || @list
|
718
|
+
end
|
719
|
+
def get_window
|
720
|
+
@graphic # 2010-01-04 12:37 BUFFERED
|
721
|
+
end
|
722
|
+
### END FOR scrollable ###
|
723
|
+
# override widgets text
|
724
|
+
# returns indices of selected rows
|
725
|
+
def getvalue
|
726
|
+
selected_rows
|
727
|
+
end
|
728
|
+
# Listbox
|
729
|
+
def handle_key(ch)
|
730
|
+
@current_index ||= 0
|
731
|
+
@toprow ||= 0
|
732
|
+
h = scrollatrow()
|
733
|
+
rc = row_count
|
734
|
+
$log.debug " listbox got ch #{ch}"
|
735
|
+
# not sure we should do something like this
|
736
|
+
# If editing is happening don't use space for selection, let it be using in editing
|
737
|
+
# and make 0 (ctrl-space) the selector. If no editing, let space be selector
|
738
|
+
if ch == 32
|
739
|
+
if @KEY_ROW_SELECTOR == 32
|
740
|
+
if @is_editing
|
741
|
+
key_row_selector = 0 # Ctrl-Space
|
742
|
+
else
|
743
|
+
key_row_selector = 32
|
744
|
+
end
|
745
|
+
end
|
746
|
+
end
|
747
|
+
case ch
|
748
|
+
when KEY_UP # show previous value
|
749
|
+
return previous_row
|
750
|
+
when KEY_DOWN # show previous value
|
751
|
+
return next_row
|
752
|
+
when key_row_selector # 32
|
753
|
+
return if is_popup && @selection_mode != :multiple # not allowing select this way since there will be a difference
|
754
|
+
toggle_row_selection @current_index #, @current_index
|
755
|
+
@repaint_required = true
|
756
|
+
when @KEY_GOTO_TOP # 48, ?\C-[
|
757
|
+
# please note that C-[ gives 27, same as esc so will respond after ages
|
758
|
+
goto_top
|
759
|
+
when @KEY_GOTO_BOTTOM # ?\C-]
|
760
|
+
goto_bottom
|
761
|
+
when @KEY_NEXT_SELECTION # ?'
|
762
|
+
$log.debug "insdie next selection"
|
763
|
+
@oldrow = @current_index
|
764
|
+
do_next_selection
|
765
|
+
bounds_check
|
766
|
+
when @KEY_PREV_SELECTION # ?"
|
767
|
+
@oldrow = @current_index
|
768
|
+
$log.debug "insdie prev selection"
|
769
|
+
do_prev_selection
|
770
|
+
bounds_check
|
771
|
+
when @KEY_CLEAR_SELECTION
|
772
|
+
clear_selection
|
773
|
+
@repaint_required = true
|
774
|
+
when 3, ?\C-c.getbyte(0), ?\C-g.getbyte(0)
|
775
|
+
editing_canceled @current_index if @cell_editing_allowed
|
776
|
+
cancel_block # block
|
777
|
+
@repaint_required = true
|
778
|
+
$multiplier = 0
|
779
|
+
when 27, 2727
|
780
|
+
# ESC or ESC ESC completes edit along with toggle key
|
781
|
+
if @is_editing
|
782
|
+
editing_completed @current_index
|
783
|
+
end
|
784
|
+
@repaint_required = true
|
785
|
+
when @edit_toggle_key
|
786
|
+
# I am currently allowing this to work even if edit_toggle is false
|
787
|
+
# You can check for edit_toggle here, if you don't like this behaviour
|
788
|
+
@is_editing = !@is_editing
|
789
|
+
if !@is_editing
|
790
|
+
editing_completed @current_index
|
791
|
+
end
|
792
|
+
@repaint_required = true
|
793
|
+
when @KEY_ASK_FIND_FORWARD
|
794
|
+
# ask_search_forward
|
795
|
+
when @KEY_ASK_FIND_BACKWARD
|
796
|
+
# ask_search_backward
|
797
|
+
when @KEY_FIND_NEXT
|
798
|
+
# find_next
|
799
|
+
when @KEY_FIND_PREV
|
800
|
+
# find_prev
|
801
|
+
when @KEY_ASK_FIND
|
802
|
+
ask_search
|
803
|
+
when @KEY_FIND_MORE
|
804
|
+
find_more
|
805
|
+
when @KEY_BLOCK_SELECTOR
|
806
|
+
mark_block #selection
|
807
|
+
#when ?\C-u.getbyte(0)
|
808
|
+
# multiplier. Series is 4 16 64
|
809
|
+
# TESTING @multiplier = (@multiplier == 0 ? 4 : @multiplier *= 4)
|
810
|
+
# return 0
|
811
|
+
when ?\C-c.getbyte(0)
|
812
|
+
@multiplier = 0
|
813
|
+
return 0
|
814
|
+
else
|
815
|
+
# this has to be fixed, if compo does not handle key it has to continue into next part FIXME
|
816
|
+
ret = :UNHANDLED # changed on 2009-01-27 13:14 not going into unhandled, tab not released
|
817
|
+
if @cell_editing_allowed
|
818
|
+
#if !@edit_toggle || (@edit_toggle && @is_editing)
|
819
|
+
if @is_editing
|
820
|
+
@repaint_required = true
|
821
|
+
# hack - on_enter_row should fire when this widget gets focus. first row that is DONE
|
822
|
+
begin
|
823
|
+
ret = @cell_editor.component.handle_key(ch)
|
824
|
+
rescue
|
825
|
+
on_enter_row @current_index
|
826
|
+
ret = @cell_editor.component.handle_key(ch)
|
827
|
+
end
|
828
|
+
end
|
829
|
+
end
|
830
|
+
if ret == :UNHANDLED
|
831
|
+
# beware one-key eats up numbers. we'll be wondering why
|
832
|
+
if @one_key_selection
|
833
|
+
case ch
|
834
|
+
#when ?A.getbyte(0)..?Z.getbyte(0), ?a.getbyte(0)..?z.getbyte(0), ?0.getbyte(0)..?9.getbyte(0)
|
835
|
+
when ?A.getbyte(0)..?Z.getbyte(0), ?a.getbyte(0)..?z.getbyte(0)
|
836
|
+
# simple motion, key press defines motion
|
837
|
+
ret = set_selection_for_char ch.chr
|
838
|
+
else
|
839
|
+
ret = process_key ch, self
|
840
|
+
$log.debug "111 listbox #{@current_index} "
|
841
|
+
@multiplier = 0
|
842
|
+
return :UNHANDLED if ret == :UNHANDLED
|
843
|
+
end
|
844
|
+
else
|
845
|
+
# no motion on single key, we can freak out like in vim, pref f <char> for set_selection
|
846
|
+
case ch
|
847
|
+
when ?0.getbyte(0)..?9.getbyte(0)
|
848
|
+
$multiplier *= 10 ; $multiplier += (ch-48)
|
849
|
+
#$log.debug " setting mult to #{$multiplier} in list "
|
850
|
+
return 0
|
851
|
+
end
|
852
|
+
ret = process_key ch, self
|
853
|
+
return :UNHANDLED if ret == :UNHANDLED
|
854
|
+
end
|
855
|
+
end
|
856
|
+
end
|
857
|
+
$multiplier = 0
|
858
|
+
end
|
859
|
+
# fire handler when user presses ENTER/RETURN
|
860
|
+
# @since 1.2.0
|
861
|
+
# listbox now traps ENTER key and fires action event
|
862
|
+
# to trap please bind :PRESS
|
863
|
+
#
|
864
|
+
def fire_action_event
|
865
|
+
# this does not select the row ???? FIXME ??
|
866
|
+
require 'rbcurse/core/include/ractionevent'
|
867
|
+
# should have been callled :ACTION_EVENT !!!
|
868
|
+
fire_handler :PRESS, ActionEvent.new(self, :PRESS, text)
|
869
|
+
end
|
870
|
+
# get a keystroke from user and go to first item starting with that key
|
871
|
+
def ask_selection_for_char
|
872
|
+
ch = @graphic.getch
|
873
|
+
if ch < 0 || ch > 255
|
874
|
+
return :UNHANDLED
|
875
|
+
end
|
876
|
+
ret = set_selection_for_char ch.chr
|
877
|
+
end
|
878
|
+
def ask_search_forward
|
879
|
+
regex = get_string("Enter regex to search")
|
880
|
+
ix = @list.find_match regex
|
881
|
+
if ix.nil?
|
882
|
+
alert("No matching data for: #{regex}")
|
883
|
+
else
|
884
|
+
set_focus_on(ix)
|
885
|
+
end
|
886
|
+
end
|
887
|
+
# gets string to search and calls data models find prev
|
888
|
+
def ask_search_backward
|
889
|
+
regex = get_string("Enter regex to search (backward)")
|
890
|
+
@last_regex = regex
|
891
|
+
ix = @list.find_prev regex, @current_index
|
892
|
+
if ix.nil?
|
893
|
+
alert("No matching data for: #{regex}")
|
894
|
+
else
|
895
|
+
set_focus_on(ix)
|
896
|
+
end
|
897
|
+
end
|
898
|
+
# please check for error before proceeding
|
899
|
+
# @return [Boolean] false if no data
|
900
|
+
def on_enter
|
901
|
+
if @list.nil? || @list.size == 0
|
902
|
+
#Ncurses.beep
|
903
|
+
get_window.ungetch($current_key) # 2011-10-4 push key back so form can go next
|
904
|
+
return :UNHANDLED
|
905
|
+
end
|
906
|
+
on_enter_row @current_index
|
907
|
+
set_form_row # added 2009-01-11 23:41
|
908
|
+
#$log.debug " ONE ENTER LIST #{@current_index}, #{@form.row}"
|
909
|
+
@repaint_required = true
|
910
|
+
super
|
911
|
+
#fire_handler :ENTER, self
|
912
|
+
true
|
913
|
+
end
|
914
|
+
def on_enter_row arow
|
915
|
+
#$log.debug " Listbox #{self} ENTER_ROW with curr #{@current_index}. row: #{arow} H: #{@handler.keys}"
|
916
|
+
#fire_handler :ENTER_ROW, arow
|
917
|
+
fire_handler :ENTER_ROW, self
|
918
|
+
@list.on_enter_row self ## XXX WHY THIS ???
|
919
|
+
edit_row_at arow
|
920
|
+
@repaint_required = true
|
921
|
+
end
|
922
|
+
def edit_row_at arow
|
923
|
+
if @cell_editing_allowed
|
924
|
+
#$log.debug " cell editor on enter #{arow} val of list[row]: #{@list[arow]}"
|
925
|
+
editor = cell_editor
|
926
|
+
prepare_editor editor, arow
|
927
|
+
end
|
928
|
+
end
|
929
|
+
##
|
930
|
+
# private
|
931
|
+
def prepare_editor editor, row
|
932
|
+
r,c = rowcol
|
933
|
+
value = @list[row] # .chomp
|
934
|
+
value = value.dup rescue value # so we can cancel
|
935
|
+
row = r + (row - @toprow) # @form.row
|
936
|
+
col = c+@left_margin # @form.col
|
937
|
+
# unfortunately 2009-01-11 19:47 combo boxes editable allows changing value
|
938
|
+
editor.prepare_editor self, row, col, value
|
939
|
+
editor.component.curpos = 0 # reset it after search, if user scrols down
|
940
|
+
#editor.component.graphic = @graphic # 2010-01-05 00:36 TRYING OUT BUFFERED
|
941
|
+
## override is required if the listbox uses a buffer
|
942
|
+
#if @should_create_buffer # removed on 2011-09-29
|
943
|
+
#$log.debug " overriding editors comp with GRAPHIC #{@graphic} "
|
944
|
+
#editor.component.override_graphic(@graphic) # 2010-01-05 00:36 TRYING OUT BUFFERED
|
945
|
+
#end
|
946
|
+
set_form_col 0 #@left_margin
|
947
|
+
|
948
|
+
# set original value so we can cancel
|
949
|
+
# set row and col,
|
950
|
+
# set value and other things, color and bgcolor
|
951
|
+
end
|
952
|
+
def on_leave_row arow
|
953
|
+
#$log.debug " Listbox #{self} leave with (cr: #{@current_index}) #{arow}: list[row]:#{@list[arow]}"
|
954
|
+
#$log.debug " Listbox #{self} leave with (cr: #{@current_index}) #{arow}: "
|
955
|
+
#fire_handler :LEAVE_ROW, arow
|
956
|
+
fire_handler :LEAVE_ROW, self
|
957
|
+
editing_completed arow
|
958
|
+
end
|
959
|
+
def editing_completed arow
|
960
|
+
if @cell_editing_allowed
|
961
|
+
if !@cell_editor.nil?
|
962
|
+
# $log.debug " cell editor (leave) setting value row: #{arow} val: #{@cell_editor.getvalue}"
|
963
|
+
$log.debug " cell editor #{@cell_editor.component.form.window} (leave) setting value row: #{arow} val: #{@cell_editor.getvalue}"
|
964
|
+
@list[arow] = @cell_editor.getvalue #.dup 2009-01-10 21:42 boolean can't duplicate
|
965
|
+
else
|
966
|
+
$log.debug "CELL EDITOR WAS NIL, #{arow} "
|
967
|
+
end
|
968
|
+
if @edit_toggle
|
969
|
+
@is_editing = false
|
970
|
+
end
|
971
|
+
end
|
972
|
+
@repaint_required = true
|
973
|
+
end
|
974
|
+
def editing_canceled arow=@current_index
|
975
|
+
return unless @cell_editing_allowed
|
976
|
+
prepare_editor @cell_editor, arow
|
977
|
+
@repaint_required = true
|
978
|
+
end
|
979
|
+
|
980
|
+
##
|
981
|
+
# getter and setter for cell_editor
|
982
|
+
def cell_editor(*val)
|
983
|
+
if val.empty?
|
984
|
+
@cell_editor ||= create_default_cell_editor
|
985
|
+
else
|
986
|
+
@cell_editor = val[0]
|
987
|
+
end
|
988
|
+
end
|
989
|
+
def create_default_cell_editor
|
990
|
+
return RubyCurses::CellEditor.new RubyCurses::Field.new nil, {"focusable"=>false, "visible"=>false, "display_length"=> @width-@internal_width-@left_margin}
|
991
|
+
end
|
992
|
+
##
|
993
|
+
# getter and setter for cell_renderer
|
994
|
+
def cell_renderer(*val)
|
995
|
+
if val.empty?
|
996
|
+
@cell_renderer ||= create_default_cell_renderer
|
997
|
+
else
|
998
|
+
@cell_renderer = val[0]
|
999
|
+
end
|
1000
|
+
end
|
1001
|
+
def create_default_cell_renderer
|
1002
|
+
return RubyCurses::ListCellRenderer.new "", {"color"=>@color, "bgcolor"=>@bgcolor, "parent" => self, "display_length"=> @width-@internal_width-@left_margin}
|
1003
|
+
end
|
1004
|
+
##
|
1005
|
+
# this method chops the data to length before giving it to the
|
1006
|
+
# renderer, this can cause problems if the renderer does some
|
1007
|
+
# processing. also, it pans the data horizontally giving the renderer
|
1008
|
+
# a section of it.
|
1009
|
+
def repaint
|
1010
|
+
return unless @repaint_required
|
1011
|
+
return if (@height || 0) < 3 || @width == 0
|
1012
|
+
# not sure where to put this, once for all or repeat 2010-02-17 23:07 RFED16
|
1013
|
+
my_win = @form ? @form.window : @target_window
|
1014
|
+
@graphic = my_win unless @graphic
|
1015
|
+
raise " #{@name} neither form, nor target window given LB paint " unless my_win
|
1016
|
+
raise " #{@name} NO GRAPHIC set as yet LB paint " unless @graphic
|
1017
|
+
|
1018
|
+
#$log.debug "rlistbox repaint #{@name} r,c, #{@row} #{@col} , width: #{@width} "
|
1019
|
+
print_borders unless @suppress_borders # do this once only, unless everything changes
|
1020
|
+
#maxlen = @maxlen || @width-@internal_width
|
1021
|
+
renderer = cell_renderer()
|
1022
|
+
renderer.display_length(@width-@internal_width-@left_margin) # just in case resizing of listbox
|
1023
|
+
tm = list()
|
1024
|
+
rc = row_count
|
1025
|
+
@longest_line = @width
|
1026
|
+
$log.debug " rlistbox repaint #{row_count} #{name} "
|
1027
|
+
if rc > 0 # just added in case no data passed
|
1028
|
+
tr = @toprow
|
1029
|
+
acolor = get_color $datacolor # should be set once, if color or bgcolor changs TODO FIXME
|
1030
|
+
h = scrollatrow()
|
1031
|
+
r,c = rowcol
|
1032
|
+
0.upto(h) do |hh|
|
1033
|
+
crow = tr+hh
|
1034
|
+
if crow < rc
|
1035
|
+
_focussed = @current_index == crow ? true : false # row focussed ?
|
1036
|
+
focus_type = _focussed
|
1037
|
+
# added 2010-09-02 14:39 so inactive fields don't show a bright focussed line
|
1038
|
+
#focussed = false if focussed && !@focussed
|
1039
|
+
focus_type = :SOFT_FOCUS if _focussed && !@focussed
|
1040
|
+
selected = is_row_selected crow
|
1041
|
+
content = tm[crow] # 2009-01-17 18:37 chomp giving error in some cases says frozen
|
1042
|
+
content = convert_value_to_text content, crow # 2010-09-23 20:12
|
1043
|
+
# by now it has to be a String
|
1044
|
+
if content.is_a? String
|
1045
|
+
content = content.dup
|
1046
|
+
sanitize content if @sanitization_required
|
1047
|
+
truncate content
|
1048
|
+
end
|
1049
|
+
## set the selector symbol if requested
|
1050
|
+
selection_symbol = ''
|
1051
|
+
if @show_selector
|
1052
|
+
if selected
|
1053
|
+
selection_symbol = @row_selected_symbol
|
1054
|
+
else
|
1055
|
+
selection_symbol = @row_unselected_symbol
|
1056
|
+
end
|
1057
|
+
@graphic.printstring r+hh, c, selection_symbol, acolor,@attr
|
1058
|
+
end
|
1059
|
+
#renderer = get_default_cell_renderer_for_class content.class.to_s
|
1060
|
+
renderer.repaint @graphic, r+hh, c+@left_margin, crow, content, focus_type, selected
|
1061
|
+
else
|
1062
|
+
# clear rows
|
1063
|
+
@graphic.printstring r+hh, c, " " * (@width-@internal_width), acolor,@attr
|
1064
|
+
end
|
1065
|
+
end
|
1066
|
+
if @cell_editing_allowed && @is_editing
|
1067
|
+
@cell_editor.component.repaint unless @cell_editor.nil? or @cell_editor.component.form.nil?
|
1068
|
+
end
|
1069
|
+
end # rc == 0
|
1070
|
+
#@table_changed = false
|
1071
|
+
@repaint_required = false
|
1072
|
+
end
|
1073
|
+
# the idea here is to allow users who subclass Listbox to easily override parts of the cumbersome repaint
|
1074
|
+
# method. This assumes your List has some data, but you print a lot more. Now you don't need to
|
1075
|
+
# change the data in the renderer, or keep formatted data in the list itself.
|
1076
|
+
# e.g. @list contains file names, or File objects, and this converts to a long listing.
|
1077
|
+
# If the renderer did that, the truncation would be on wrong data.
|
1078
|
+
# @since 1.2.0
|
1079
|
+
def convert_value_to_text value, crow
|
1080
|
+
case value
|
1081
|
+
when TrueClass, FalseClass
|
1082
|
+
value
|
1083
|
+
else
|
1084
|
+
value.to_s if value
|
1085
|
+
end
|
1086
|
+
end
|
1087
|
+
# takes a block, this way anyone extending this class can just pass a block to do his job
|
1088
|
+
# This modifies the string
|
1089
|
+
def sanitize content
|
1090
|
+
if content.is_a? String
|
1091
|
+
content.chomp!
|
1092
|
+
content.gsub!(/[\t\n\r]/, ' ') # don't display tab
|
1093
|
+
content.gsub!(/[^[:print:]]/, '') # don't display non print characters
|
1094
|
+
else
|
1095
|
+
content
|
1096
|
+
end
|
1097
|
+
end
|
1098
|
+
# returns only the visible portion of string taking into account display length
|
1099
|
+
# and horizontal scrolling. MODIFIES STRING
|
1100
|
+
# if you;ve truncated the data, it could stay truncated even if lb is increased. be careful
|
1101
|
+
# FIXME if _maxlen becomes 0 then maxlen -1 will print whole string again
|
1102
|
+
def truncate content
|
1103
|
+
_maxlen = @maxlen || @width-@internal_width
|
1104
|
+
_maxlen = @width-@internal_width if _maxlen > @width-@internal_width
|
1105
|
+
#$log.debug "TRUNCATE: listbox maxlen #{@maxlen}, #{_maxlen} width #{@width}: #{content} "
|
1106
|
+
if !content.nil?
|
1107
|
+
if content.length > _maxlen # only show maxlen
|
1108
|
+
@longest_line = content.length if content.length > @longest_line
|
1109
|
+
#content = content[@pcol..@pcol+maxlen-1]
|
1110
|
+
content.replace content[@pcol..@pcol+_maxlen-1]
|
1111
|
+
else
|
1112
|
+
# can this be avoided if pcol is 0 XXX
|
1113
|
+
content.replace content[@pcol..-1] if @pcol > 0
|
1114
|
+
end
|
1115
|
+
end
|
1116
|
+
#$log.debug " content: #{content}"
|
1117
|
+
content
|
1118
|
+
end
|
1119
|
+
|
1120
|
+
def list_data_changed
|
1121
|
+
if row_count == 0 # added on 2009-02-02 17:13 so cursor not hanging on last row which could be empty
|
1122
|
+
init_vars
|
1123
|
+
@current_index = 0
|
1124
|
+
# I had placed this at some time to get cursor correct. But if this listbox is updated
|
1125
|
+
# during entry in another field, then this steals the row. e.g. test1.rb 5
|
1126
|
+
#set_form_row
|
1127
|
+
end
|
1128
|
+
@repaint_required = true
|
1129
|
+
end
|
1130
|
+
# set cursor column position
|
1131
|
+
# if i set col1 to @curpos, i can move around left right if key mapped
|
1132
|
+
def set_form_col col1=0
|
1133
|
+
# TODO BUFFERED use setrowcol @form.row, col
|
1134
|
+
# TODO BUFFERED use cols_panned
|
1135
|
+
#col1 ||= 0
|
1136
|
+
@cols_panned ||= 0 # RFED16 2010-02-17 23:40
|
1137
|
+
# editable listboxes will involve changing cursor and the form issue
|
1138
|
+
## added win_col on 2010-01-04 23:28 for embedded forms BUFFERED TRYING OUT
|
1139
|
+
#win_col=@form.window.left
|
1140
|
+
win_col = 0 # 2010-02-17 23:19 RFED16
|
1141
|
+
#col = win_col + @orig_col + @col_offset + @curpos + @form.cols_panned
|
1142
|
+
col2 = win_col + @col + @col_offset + col1 + @cols_panned + @left_margin
|
1143
|
+
$log.debug " set_form_col in rlistbox #{@col}+ left_margin #{@left_margin} ( #{col2} ) "
|
1144
|
+
#super col+@left_margin
|
1145
|
+
#@form.setrowcol @form.row, col2 # added 2009-12-29 18:50 BUFFERED
|
1146
|
+
setrowcol nil, col2 # 2010-02-17 23:19 RFED16
|
1147
|
+
end
|
1148
|
+
#def rowcol
|
1149
|
+
## $log.debug "rlistbox rowcol : #{@row+@row_offset+@winrow}, #{@col+@col_offset}"
|
1150
|
+
#win_col=@form.window.left
|
1151
|
+
#col2 = win_col + @col + @col_offset + @form.cols_panned + @left_margin
|
1152
|
+
#return @row+@row_offset, col2
|
1153
|
+
#end
|
1154
|
+
# experimental selection of multiple rows via block
|
1155
|
+
# specify a block start and then a block end
|
1156
|
+
# usage: bind mark_selection to a key. It works as a toggle.
|
1157
|
+
# C-c will cancel any selection that has begun.
|
1158
|
+
# added on 2009-02-19 22:37
|
1159
|
+
def mark_block #selection
|
1160
|
+
if @inside_block
|
1161
|
+
@inside_block = false
|
1162
|
+
end_block #selection
|
1163
|
+
else
|
1164
|
+
@inside_block = true
|
1165
|
+
start_block #selection
|
1166
|
+
end
|
1167
|
+
end
|
1168
|
+
# added on 2009-02-19 22:37
|
1169
|
+
def cancel_block
|
1170
|
+
@first_index = @last_index = nil
|
1171
|
+
@inside_block = false
|
1172
|
+
end
|
1173
|
+
# sets marker for start of block
|
1174
|
+
# added on 2009-02-19 22:37
|
1175
|
+
def start_block #selection
|
1176
|
+
@first_index = @current_index
|
1177
|
+
@last_index = nil
|
1178
|
+
end
|
1179
|
+
# sets marker for end of block
|
1180
|
+
# added on 2009-02-19 22:37
|
1181
|
+
def end_block #selection
|
1182
|
+
@last_index = current_index
|
1183
|
+
lower = [@first_index, @last_index].min
|
1184
|
+
higher = [@first_index, @last_index].max
|
1185
|
+
#lower.upto(higher) do |i| @list.toggle_row_selection i; end
|
1186
|
+
add_row_selection_interval(lower, higher)
|
1187
|
+
@repaint_required = true
|
1188
|
+
end
|
1189
|
+
# 2010-02-18 11:40
|
1190
|
+
# TRYING OUT - canceling editing if resized otherwise drawing errors can occur
|
1191
|
+
# the earlier painted edited comp in yellow keeps showing until a key is pressed
|
1192
|
+
|
1193
|
+
def set_buffering params
|
1194
|
+
$log.warn "CALLED set_buffering in LISTBOX listbox " if $log.debug?
|
1195
|
+
super # removed from widget 2011-09-29
|
1196
|
+
## Ensuring that changes to top get reflect in editing comp
|
1197
|
+
#+ otherwise it raises an exception. Still the earlier cell_edit is being
|
1198
|
+
#+ printed where it was , until a key is moved
|
1199
|
+
# FIXME - do same for col
|
1200
|
+
if @cell_editor
|
1201
|
+
r,c = rowcol
|
1202
|
+
if @cell_editor.component.row < @row_offset + @buffer_params[:screen_top]
|
1203
|
+
@cell_editor.component.row = @row_offset + @buffer_params[:screen_top]
|
1204
|
+
end
|
1205
|
+
# TODO next block to be tested by placing a listbox in right split of vertical
|
1206
|
+
if @cell_editor.component.col < @col_offset + @buffer_params[:screen_left]
|
1207
|
+
@cell_editor.component.col = @col_offset + @buffer_params[:screen_left]
|
1208
|
+
end
|
1209
|
+
#editing_canceled @current_index if @cell_editing_allowed and @cell_editor
|
1210
|
+
end
|
1211
|
+
#set_form_row
|
1212
|
+
@repaint_required = true
|
1213
|
+
end
|
1214
|
+
|
1215
|
+
# trying to simplify usage. The Java way has made listboxes very difficult to use
|
1216
|
+
# Returns selected indices
|
1217
|
+
# Indices are often required since the renderer may modify the values displayed
|
1218
|
+
#
|
1219
|
+
def get_selected_indices
|
1220
|
+
@list_selection_model.get_selected_indices
|
1221
|
+
end
|
1222
|
+
|
1223
|
+
# Returns selected values
|
1224
|
+
#
|
1225
|
+
def get_selected_values
|
1226
|
+
selected = []
|
1227
|
+
@list_selection_model.get_selected_indices.each { |i| selected << list_data_model[i] }
|
1228
|
+
return selected
|
1229
|
+
end
|
1230
|
+
alias :selected_values :get_selected_values
|
1231
|
+
alias :selected_indices :get_selected_indices
|
1232
|
+
|
1233
|
+
def init_actions
|
1234
|
+
return if @actions_added
|
1235
|
+
@actions_added = true
|
1236
|
+
am = action_manager()
|
1237
|
+
am.add_action(Action.new("&One Key Selection toggle ") { @one_key_selection = !@one_key_selection} )
|
1238
|
+
am.add_action(Action.new("&Edit Toggle") { @edit_toggle = !@edit_toggle; $status_message.value = "Edit toggle is #{@edit_toggle}" })
|
1239
|
+
|
1240
|
+
# this is a little more involved due to list selection model
|
1241
|
+
am.add_action(Action.new("&Disable selection") { @selection_mode = :none; unbind_key(32); bind_key(32, :scroll_forward); })
|
1242
|
+
end
|
1243
|
+
# ADD HERE
|
1244
|
+
end # class listb
|
1245
|
+
|
1246
|
+
|
1247
|
+
end # module
|