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