rbcurse 1.1.5 → 1.2.0.pre
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +45 -0
- data/Makefile +1 -1
- data/Manifest.txt +91 -0
- data/NOTES +349 -2
- data/README.markdown +12 -0
- data/VERSION +1 -1
- data/examples/abasiclist.rb +25 -0
- data/examples/alpmenu.rb +42 -0
- data/examples/app.rb +883 -0
- data/examples/appcombo.rb +17 -0
- data/examples/appdirtree.rb +73 -0
- data/examples/appemail.rb +164 -0
- data/examples/appemaillb.rb +308 -0
- data/examples/appgcompose.rb +303 -0
- data/examples/appgmail.rb +951 -0
- data/examples/atree.rb +56 -0
- data/examples/dirtree.rb +78 -0
- data/examples/focusmanager.rb +31 -0
- data/examples/imap.rb +48 -0
- data/examples/menu1.rb +79 -0
- data/examples/multispl.rb +86 -0
- data/examples/rfe.rb +3 -4
- data/examples/rmail.rb +188 -0
- data/examples/s.rb +10 -0
- data/examples/scrollbar.rb +104 -0
- data/examples/splitp.rb +56 -0
- data/examples/table1.rb +30 -0
- data/examples/term.rb +48 -0
- data/examples/term2.rb +54 -0
- data/examples/test1.rb +4 -2
- data/examples/test2.rb +9 -9
- data/examples/testapp.rb +44 -0
- data/examples/testapp2.rb +51 -0
- data/examples/testcombo.rb +2 -2
- data/examples/testgmail.rb +46 -0
- data/examples/testlistbox.rb +0 -1
- data/examples/testmultispl.rb +199 -0
- data/examples/testree.rb +127 -0
- data/examples/testscroller.rb +0 -1
- data/examples/testscrolllb.rb +1 -1
- data/examples/testscrollp.rb +2 -1
- data/examples/testscrollta.rb +1 -1
- data/examples/testscrolltable.rb +1 -2
- data/examples/testsplit.rb +1 -1
- data/examples/testsplit2.rb +1 -1
- data/examples/testsplit3.rb +1 -1
- data/examples/testsplit3_1.rb +1 -1
- data/examples/testsplit3a.rb +1 -1
- data/examples/testsplit3b.rb +1 -1
- data/examples/testsplitta.rb +1 -1
- data/examples/testsplittv.rb +1 -1
- data/examples/testsplittvv.rb +1 -1
- data/examples/testtodo.rb +491 -488
- data/examples/testvimsplit.rb +111 -0
- data/examples/todo.db +0 -0
- data/examples/todocsv.csv +28 -0
- data/examples/viewtodo.rb +408 -403
- data/lib/rbcurse/action.rb +1 -0
- data/lib/rbcurse/app.rb +1294 -0
- data/lib/rbcurse/applicationheader.rb +7 -2
- data/lib/rbcurse/checkboxcellrenderer.rb +0 -12
- data/lib/rbcurse/colormap.rb +34 -8
- data/lib/rbcurse/comboboxcellrenderer.rb +0 -11
- data/lib/rbcurse/defaultlistselectionmodel.rb +23 -7
- data/lib/rbcurse/extras/bottomline.rb +1681 -0
- data/lib/rbcurse/extras/directorylist.rb +445 -0
- data/lib/rbcurse/extras/directorytree.rb +69 -0
- data/lib/rbcurse/extras/divider.rb +310 -0
- data/lib/rbcurse/extras/focusmanager.rb +31 -0
- data/lib/rbcurse/extras/listselectable.rb +222 -0
- data/lib/rbcurse/extras/masterdetail.rb +164 -0
- data/lib/rbcurse/extras/menutree.rb +63 -0
- data/lib/rbcurse/extras/rlink.rb +27 -0
- data/lib/rbcurse/extras/rmenulink.rb +21 -0
- data/lib/rbcurse/extras/scrollbar.rb +134 -0
- data/lib/rbcurse/extras/stdscrwindow.rb +247 -0
- data/lib/rbcurse/extras/tabular.rb +258 -0
- data/lib/rbcurse/extras/tabularwidget.rb +1070 -0
- data/lib/rbcurse/extras/viewer.rb +106 -0
- data/lib/rbcurse/io.rb +137 -80
- data/lib/rbcurse/keylabelprinter.rb +4 -0
- data/lib/rbcurse/listcellrenderer.rb +91 -59
- data/lib/rbcurse/listscrollable.rb +93 -95
- data/lib/rbcurse/listselectable.rb +60 -7
- data/lib/rbcurse/ractionevent.rb +67 -0
- data/lib/rbcurse/rbasiclistbox.rb +688 -0
- data/lib/rbcurse/rcombo.rb +5 -5
- data/lib/rbcurse/rcommandwindow.rb +555 -0
- data/lib/rbcurse/rinputdataevent.rb +12 -0
- data/lib/rbcurse/rlistbox.rb +305 -124
- data/lib/rbcurse/rmenu.rb +99 -46
- data/lib/rbcurse/rmessagebox.rb +13 -6
- data/lib/rbcurse/rmulticontainer.rb +54 -93
- data/lib/rbcurse/rmultisplit.rb +731 -0
- data/lib/rbcurse/rmultitextview.rb +3 -2
- data/lib/rbcurse/rpopupmenu.rb +0 -1
- data/lib/rbcurse/rprogress.rb +117 -0
- data/lib/rbcurse/rscrollpane.rb +2 -1
- data/lib/rbcurse/rsplitpane.rb +94 -20
- data/lib/rbcurse/rsplitpane2.rb +1009 -0
- data/lib/rbcurse/rtabbedpane.rb +3 -2
- data/lib/rbcurse/rtabbedwindow.rb +0 -1
- data/lib/rbcurse/rtable.rb +92 -64
- data/lib/rbcurse/rtextarea.rb +91 -57
- data/lib/rbcurse/rtextview.rb +223 -70
- data/lib/rbcurse/rtree.rb +723 -0
- data/lib/rbcurse/rviewport.rb +2 -1
- data/lib/rbcurse/rvimsplit.rb +768 -0
- data/lib/rbcurse/rwidget.rb +524 -325
- data/lib/rbcurse/table/tablecellrenderer.rb +1 -1
- data/lib/rbcurse/table/tabledatecellrenderer.rb +0 -1
- data/lib/rbcurse/tree/treecellrenderer.rb +137 -0
- data/lib/rbcurse/tree/treemodel.rb +428 -0
- data/lib/rbcurse/vieditable.rb +14 -13
- data/lib/ver/ncurses.rb +6 -0
- data/lib/ver/window.rb +67 -32
- metadata +99 -23
- data/bin/rbcurse +0 -0
- data/examples/rvimsplit.rb +0 -376
- data/examples/todo.rb +0 -1
- data/lib/rbcurse/rform.rb +0 -845
- data/lib/rbcurse/selectable.rb +0 -94
- data/rbcurse.gemspec +0 -188
@@ -0,0 +1,310 @@
|
|
1
|
+
require 'rbcurse/app'
|
2
|
+
include Ncurses
|
3
|
+
include RubyCurses
|
4
|
+
|
5
|
+
# TODO : We can consider making it independent of objects, or allow for a margin so it does not write
|
6
|
+
# over the object. Then it will be always visible.
|
7
|
+
# TODO: if lists and tables, can without borders actually adjust then putting this independent
|
8
|
+
# would make even more sense, since it won't eat an extra line.
|
9
|
+
#
|
10
|
+
# @example
|
11
|
+
# lb = list_box ....
|
12
|
+
# rb = Divider.new @form, :parent => lb, :side => :right
|
13
|
+
#
|
14
|
+
# At a later stage, we will integrate this with lists and tables, so it will happen automatically.
|
15
|
+
#
|
16
|
+
# @since 1.2.0
|
17
|
+
module RubyCurses
|
18
|
+
class DragEvent < Struct.new(:source, :type); end
|
19
|
+
|
20
|
+
# This is a horizontal or vertical bar (like a scrollbar), at present attached to a
|
21
|
+
# widget that is focusable, and allows user to press arrow keys.
|
22
|
+
# It highlights on focus, the caller can expand and contract components in a container
|
23
|
+
# or even screen, based on arrow movements. This allows for a visual resizing of components.
|
24
|
+
# @example
|
25
|
+
# lb = list_box ....
|
26
|
+
# rb = Divider.new @form, :parent => lb, :side => :right
|
27
|
+
#
|
28
|
+
# NOTE: since this can be deactivated, containers need to check focusable before passing
|
29
|
+
# focus in
|
30
|
+
# 2010-10-07 23:56 made focusable false by default. Add divider to
|
31
|
+
# FocusManager when creating, so F3 can be used to set focusable
|
32
|
+
# See rvimsplit.rb for example
|
33
|
+
|
34
|
+
class Divider < Widget
|
35
|
+
# row to start, same as listbox, required.
|
36
|
+
dsl_property :row
|
37
|
+
# column to start, same as listbox, required.
|
38
|
+
dsl_property :col
|
39
|
+
# how many rows is this (should be same as listboxes height, required.
|
40
|
+
dsl_property :length
|
41
|
+
# vertical or horizontal currently only VERTICAL
|
42
|
+
dsl_property :side
|
43
|
+
# initialize based on parent's values
|
44
|
+
dsl_property :parent
|
45
|
+
# which row is focussed, current_index of listbox, required.
|
46
|
+
# how many total rows of data does the list have, same as @list.length, required.
|
47
|
+
dsl_accessor :next
|
48
|
+
|
49
|
+
# TODO: if parent passed, we shold bind to ON_ENTER and get current_index, so no extra work is required.
|
50
|
+
|
51
|
+
def initialize form, config={}, &block
|
52
|
+
|
53
|
+
# setting default first or else Widget will place its BW default
|
54
|
+
#@color, @bgcolor = ColorMap.get_colors_for_pair $bottomcolor
|
55
|
+
super
|
56
|
+
@height = 1
|
57
|
+
@color_pair = get_color $datacolor, @color, @bgcolor
|
58
|
+
@scroll_pair = get_color $bottomcolor, :green, :white
|
59
|
+
#@window = form.window
|
60
|
+
@editable = false
|
61
|
+
# you can set to true upon creation, or use F3 on vimsplit to
|
62
|
+
# toggle focusable
|
63
|
+
@focusable = false
|
64
|
+
@repaint_required = true
|
65
|
+
@_events.push(:DRAG_EVENT)
|
66
|
+
map_keys
|
67
|
+
unless @parent
|
68
|
+
raise ArgumentError, "row col and length should be provided" if !@row || !@col || !@length
|
69
|
+
end
|
70
|
+
#if @parent
|
71
|
+
#@parent.bind :ENTER_ROW do |p|
|
72
|
+
## parent must implement row_count, and have a @current_index
|
73
|
+
#raise StandardError, "Parent must implement row_count" unless p.respond_to? :row_count
|
74
|
+
#self.current_index = p.current_index
|
75
|
+
#@repaint_required = true #requred otherwise at end when same value sent, prop handler
|
76
|
+
## will not be fired (due to optimization).
|
77
|
+
#end
|
78
|
+
#end
|
79
|
+
end
|
80
|
+
def map_keys
|
81
|
+
if !defined? $deactivate_dividers
|
82
|
+
$deactivate_dividers = false
|
83
|
+
end
|
84
|
+
# deactivate only this bar
|
85
|
+
bind_key(?f) {@focusable=false; }
|
86
|
+
# deactivate all bars, i've had nuff!
|
87
|
+
bind_key(?F) {deactivate_all(true)}
|
88
|
+
end
|
89
|
+
|
90
|
+
##
|
91
|
+
# repaint the scrollbar
|
92
|
+
# Taking the data from parent as late as possible in case parent resized, or
|
93
|
+
# moved around by a container.
|
94
|
+
# NOTE: sometimes if this is inside another object, the divider repaints but then
|
95
|
+
# is wiped out when that objects print_border is called. So such an obkect (e.g.
|
96
|
+
# vimsplit) should call repaint after its has done its own repaint. that does mean
|
97
|
+
# the repaint happens twice during movement
|
98
|
+
def repaint
|
99
|
+
woffset = 2
|
100
|
+
coffset = 1
|
101
|
+
if @parent
|
102
|
+
woffset = 0 if @parent.suppress_borders
|
103
|
+
@border_attrib ||= @parent.border_attrib
|
104
|
+
case @side
|
105
|
+
when :right
|
106
|
+
@row = @parent.row+1
|
107
|
+
@col = @parent.col + @parent.width - 0
|
108
|
+
@length = @parent.height - woffset
|
109
|
+
when :left
|
110
|
+
@row = @parent.row+1
|
111
|
+
@col = @parent.col+0 #+ @parent.width - 1
|
112
|
+
@length = @parent.height - woffset
|
113
|
+
when :top
|
114
|
+
@row = @parent.row+0
|
115
|
+
@col = @parent.col + @parent.col_offset #+ @parent.width - 1
|
116
|
+
@length = @parent.width - woffset
|
117
|
+
when :bottom
|
118
|
+
@row = @parent.row+@parent.height-0 #1
|
119
|
+
@col = @parent.col+@parent.col_offset #+ @parent.width - 1
|
120
|
+
@length = @parent.width - woffset
|
121
|
+
end
|
122
|
+
else
|
123
|
+
# row, col and length should be passed
|
124
|
+
end
|
125
|
+
my_win = @form ? @form.window : @target_window
|
126
|
+
@graphic = my_win unless @graphic
|
127
|
+
raise "graphic is nil in divider, perhaps form was nil when creating" unless @graphic
|
128
|
+
return unless @repaint_required
|
129
|
+
|
130
|
+
# first print a right side vertical line
|
131
|
+
#bc = $bottomcolor # dark blue
|
132
|
+
bc = $datacolor
|
133
|
+
bordercolor = @border_color || bc
|
134
|
+
borderatt = @border_attrib || Ncurses::A_REVERSE
|
135
|
+
if @focussed
|
136
|
+
bordercolor = $promptcolor || bordercolor
|
137
|
+
end
|
138
|
+
|
139
|
+
borderatt = convert_attrib_to_sym(borderatt) if borderatt.is_a? Symbol
|
140
|
+
|
141
|
+
@graphic.attron(Ncurses.COLOR_PAIR(bordercolor) | borderatt)
|
142
|
+
$log.debug " XXX DIVIDER #{@row} #{@col} #{@length} "
|
143
|
+
case @side
|
144
|
+
when :right, :left
|
145
|
+
@graphic.mvvline(@row, @col, 1, @length)
|
146
|
+
when :top, :bottom
|
147
|
+
@graphic.mvhline(@row, @col, 1, @length)
|
148
|
+
end
|
149
|
+
@graphic.attroff(Ncurses.COLOR_PAIR(bordercolor) | borderatt)
|
150
|
+
_paint_marker
|
151
|
+
#alert "divider repaint at #{row} #{col} "
|
152
|
+
|
153
|
+
@repaint_required = false
|
154
|
+
end
|
155
|
+
def convert_attrib_to_sym attr
|
156
|
+
case attr
|
157
|
+
when 'reverse'
|
158
|
+
Ncurses::A_REVERSE
|
159
|
+
when 'bold'
|
160
|
+
Ncurses::A_BOLD
|
161
|
+
when 'normal'
|
162
|
+
Ncurses::A_NORMAL
|
163
|
+
when 'blink'
|
164
|
+
Ncurses::A_BLINK
|
165
|
+
when 'underline'
|
166
|
+
Ncurses::A_UNDERLINE
|
167
|
+
else
|
168
|
+
Ncurses::A_REVERSE
|
169
|
+
end
|
170
|
+
end
|
171
|
+
# deactivate all dividers
|
172
|
+
# The application has to provide a key or button to activate all
|
173
|
+
# or just this one.
|
174
|
+
def deactivate_all tf=true
|
175
|
+
$deactivate_dividers = tf
|
176
|
+
@focusable = !tf
|
177
|
+
end
|
178
|
+
def handle_key ch
|
179
|
+
# all dividers have been deactivated
|
180
|
+
if $deactivate_dividers || !@focusable
|
181
|
+
@focusable = false
|
182
|
+
return :UNHANDLED
|
183
|
+
end
|
184
|
+
case @side
|
185
|
+
when :right, :left
|
186
|
+
case ch
|
187
|
+
when KEY_RIGHT
|
188
|
+
fire_handler :DRAG_EVENT, DragEvent.new(self, ch)
|
189
|
+
when KEY_LEFT
|
190
|
+
fire_handler :DRAG_EVENT, DragEvent.new(self, ch)
|
191
|
+
else
|
192
|
+
ret = process_key ch, self
|
193
|
+
return ret if ret == :UNHANDLED
|
194
|
+
end
|
195
|
+
set_form_col
|
196
|
+
when :top, :bottom
|
197
|
+
case ch
|
198
|
+
when KEY_UP
|
199
|
+
fire_handler :DRAG_EVENT, DragEvent.new(self, ch)
|
200
|
+
when KEY_DOWN
|
201
|
+
fire_handler :DRAG_EVENT, DragEvent.new(self, ch)
|
202
|
+
else
|
203
|
+
ret = process_key ch, self
|
204
|
+
return ret if ret == :UNHANDLED
|
205
|
+
end
|
206
|
+
set_form_col
|
207
|
+
else
|
208
|
+
end
|
209
|
+
@repaint_required = true
|
210
|
+
return 0
|
211
|
+
end
|
212
|
+
def on_enter
|
213
|
+
if $deactivate_dividers || !@focusable
|
214
|
+
@focusable = false
|
215
|
+
return :UNHANDLED
|
216
|
+
end
|
217
|
+
# since it is over border of component, we need to repaint
|
218
|
+
@focussed = true
|
219
|
+
@repaint_required = true
|
220
|
+
repaint
|
221
|
+
end
|
222
|
+
def on_leave
|
223
|
+
@focussed = false
|
224
|
+
@repaint_required = true
|
225
|
+
repaint
|
226
|
+
# TODO: we should review this since its not over the parent any longer
|
227
|
+
if @parent
|
228
|
+
# since it is over border of component, we need to clear
|
229
|
+
@parent.repaint_required
|
230
|
+
# if we don't paint now, parent paints over other possible dividers
|
231
|
+
@parent.repaint
|
232
|
+
end
|
233
|
+
end
|
234
|
+
def set_form_row
|
235
|
+
return unless @focusable
|
236
|
+
r,c = rowcol
|
237
|
+
setrowcol r, c
|
238
|
+
end
|
239
|
+
# set the cursor on first point of bar
|
240
|
+
def set_form_col
|
241
|
+
return unless @focusable
|
242
|
+
# need to set it to first point, otherwise it could be off the widget
|
243
|
+
r,c = rowcol
|
244
|
+
setrowcol r, c
|
245
|
+
end
|
246
|
+
# is this a vertical divider
|
247
|
+
def v?
|
248
|
+
@side == :top || @side == :bottom
|
249
|
+
end
|
250
|
+
# is this a horizontal divider
|
251
|
+
def h?
|
252
|
+
@side == :right || @side == :left
|
253
|
+
end
|
254
|
+
private
|
255
|
+
def _paint_marker #:nodoc:
|
256
|
+
r,c = rowcol
|
257
|
+
if @focussed
|
258
|
+
@graphic.mvwaddch r,c, Ncurses::ACS_DIAMOND
|
259
|
+
if v?
|
260
|
+
@graphic.mvwaddch r,c+1, Ncurses::ACS_UARROW
|
261
|
+
@graphic.mvwaddch r,c+2, Ncurses::ACS_DARROW
|
262
|
+
else
|
263
|
+
@graphic.mvwaddch r+1,c, Ncurses::ACS_LARROW
|
264
|
+
@graphic.mvwaddch r+2,c, Ncurses::ACS_RARROW
|
265
|
+
end
|
266
|
+
else
|
267
|
+
#@graphic.mvwaddch r,c, Ncurses::ACS_CKBOARD
|
268
|
+
end
|
269
|
+
end
|
270
|
+
##
|
271
|
+
##
|
272
|
+
# ADD HERE
|
273
|
+
end # class
|
274
|
+
end # module
|
275
|
+
if __FILE__ == $PROGRAM_NAME
|
276
|
+
App.new do
|
277
|
+
r = 5
|
278
|
+
len = 20
|
279
|
+
list = []
|
280
|
+
0.upto(100) { |v| list << "#{v} scrollable data" }
|
281
|
+
lb = list_box "A list", :list => list, :row => 2, :col => 2
|
282
|
+
#sb = Scrollbar.new @form, :row => r, :col => 20, :length => len, :list_length => 50, :current_index => 0
|
283
|
+
rb = Divider.new @form, :parent => lb, :side => :right
|
284
|
+
rb.bind :DRAG_EVENT do |e|
|
285
|
+
message "got an event #{e.type} "
|
286
|
+
case e.type
|
287
|
+
when KEY_RIGHT
|
288
|
+
lb.width += 1
|
289
|
+
when KEY_LEFT
|
290
|
+
lb.width -= 1
|
291
|
+
end
|
292
|
+
lb.repaint_required
|
293
|
+
end
|
294
|
+
rb1 = Divider.new @form, :parent => lb, :side => :bottom
|
295
|
+
rb.focusable(true)
|
296
|
+
rb1.focusable(true)
|
297
|
+
rb1.bind :DRAG_EVENT do |e|
|
298
|
+
message " 2 got an event #{e.type} "
|
299
|
+
end
|
300
|
+
#hline :width => 20, :row => len+r
|
301
|
+
#keypress do |ch|
|
302
|
+
#case ch
|
303
|
+
#when :down
|
304
|
+
#sb.current_index += 1
|
305
|
+
#when :up
|
306
|
+
#sb.current_index -= 1
|
307
|
+
#end
|
308
|
+
#end
|
309
|
+
end
|
310
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# Allow some objects to take focus when a certain key is pressed.
|
2
|
+
# This is for objects like scrollbars and grabbars. We don't want these always
|
3
|
+
# getting focus, only sometimes when we want to resize panes.
|
4
|
+
# This will not only be included by Form but by containers such as Vimsplit
|
5
|
+
# or MasterDetail.
|
6
|
+
# Usage: the idea is that when you create grabbars, you would add them to the FocusManager
|
7
|
+
# Thus they would remain non-focusable on creation. When hte user presses (say F3) then
|
8
|
+
# make_focusable is called, or toggle_focusable. Now user can press TAB and access
|
9
|
+
# these bars. When he is done he can toggle again.
|
10
|
+
# TODO: we might add a Circular class here so user can traverse only these objects
|
11
|
+
module RubyCurses
|
12
|
+
module FocusManager
|
13
|
+
extend self
|
14
|
+
attr_reader :focusables
|
15
|
+
# add a component to this list so it can be made focusable later
|
16
|
+
def add component
|
17
|
+
@focusables ||= []
|
18
|
+
@focusables << component
|
19
|
+
self
|
20
|
+
end
|
21
|
+
def make_focusable bool=true
|
22
|
+
@focusing = bool
|
23
|
+
@focusables.each { |e| e.focusable(bool) }
|
24
|
+
end
|
25
|
+
def toggle_focusable
|
26
|
+
return unless @focusables
|
27
|
+
alert "FocusManager Making #{@focusables.length} objects #{!@focusing} "
|
28
|
+
make_focusable !@focusing
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,222 @@
|
|
1
|
+
# File created: 2010-10-29 14:09
|
2
|
+
# Author : rkumar
|
3
|
+
#
|
4
|
+
# this is a new, simpler version of listselectable
|
5
|
+
# the original gets into models and has complicated operation as well
|
6
|
+
# as difficult to remember method names. This attempts to be a simple plugin.
|
7
|
+
# Currently being used by rbasiclistbox and now tabularwidget.
|
8
|
+
# TODO: of course we need to fire events so user can do something.
|
9
|
+
module RubyCurses
|
10
|
+
module NewListSelectable
|
11
|
+
|
12
|
+
# @group selection related
|
13
|
+
|
14
|
+
# change selection of current row on pressing space bar
|
15
|
+
# If mode is multiple, then other selections are cleared and this is added
|
16
|
+
# @example
|
17
|
+
# bind_key(32) { toggle_row_selection }
|
18
|
+
# current_index is not account for header_adjustment
|
19
|
+
# if current row is selected in mulitple we should deselect ?? FIXME
|
20
|
+
def toggle_row_selection crow=@current_index-@_header_adjustment
|
21
|
+
@last_clicked = crow
|
22
|
+
@repaint_required = true
|
23
|
+
case @selection_mode
|
24
|
+
when :multiple
|
25
|
+
if @selected_indices.include? crow
|
26
|
+
@selected_indices.delete crow
|
27
|
+
else
|
28
|
+
#clear_selection
|
29
|
+
#@selected_indices[0] = crow
|
30
|
+
@selected_indices << crow
|
31
|
+
end
|
32
|
+
else
|
33
|
+
if @selected_index == crow
|
34
|
+
@selected_index = nil
|
35
|
+
else
|
36
|
+
@selected_index = crow
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
#
|
41
|
+
# Only for multiple mode.
|
42
|
+
# add an item to selection, if selection mode is multiple
|
43
|
+
# if item already selected, it is deselected, else selected
|
44
|
+
# typically bound to Ctrl-Space
|
45
|
+
# @example
|
46
|
+
# bind_key(0) { add_to_selection }
|
47
|
+
def add_to_selection crow=@current_index-@_header_adjustment
|
48
|
+
@last_clicked ||= crow
|
49
|
+
min = [@last_clicked, crow].min
|
50
|
+
max = [@last_clicked, crow].max
|
51
|
+
case @selection_mode
|
52
|
+
when :multiple
|
53
|
+
if @selected_indices.include? crow
|
54
|
+
# delete from last_clicked until this one in any direction
|
55
|
+
min.upto(max){ |i| @selected_indices.delete i }
|
56
|
+
else
|
57
|
+
# add to selection from last_clicked until this one in any direction
|
58
|
+
min.upto(max){ |i| @selected_indices << i unless @selected_indices.include?(i) }
|
59
|
+
end
|
60
|
+
else
|
61
|
+
end
|
62
|
+
@repaint_required = true
|
63
|
+
end
|
64
|
+
# clears selected indices, typically called when multiple select
|
65
|
+
# Key binding is application specific
|
66
|
+
def clear_selection
|
67
|
+
@selected_indices = []
|
68
|
+
@selected_index = nil
|
69
|
+
@repaint_required = true
|
70
|
+
end
|
71
|
+
def is_row_selected crow=@current_index-@_header_adjustment
|
72
|
+
case @selection_mode
|
73
|
+
when :multiple
|
74
|
+
@selected_indices.include? crow
|
75
|
+
else
|
76
|
+
crow == @selected_index
|
77
|
+
end
|
78
|
+
end
|
79
|
+
alias :is_selected? is_row_selected
|
80
|
+
# FIXME add adjustment and test
|
81
|
+
def goto_next_selection
|
82
|
+
return if selected_rows().length == 0
|
83
|
+
row = selected_rows().sort.find { |i| i > @current_index }
|
84
|
+
row ||= @current_index
|
85
|
+
@current_index = row
|
86
|
+
@repaint_required = true # fire list_select XXX
|
87
|
+
end
|
88
|
+
# FIXME add adjustment and test
|
89
|
+
def goto_prev_selection
|
90
|
+
return if selected_rows().length == 0
|
91
|
+
row = selected_rows().sort{|a,b| b <=> a}.find { |i| i < @current_index }
|
92
|
+
row ||= @current_index
|
93
|
+
@current_index = row
|
94
|
+
@repaint_required = true # fire list_select XXX
|
95
|
+
end
|
96
|
+
# add the following range to selected items, unless already present
|
97
|
+
# should only be used if multiple selection interval
|
98
|
+
def add_selection_interval ix0, ix1
|
99
|
+
return if @selection_mode != :multiple
|
100
|
+
@anchor_selection_index = ix0
|
101
|
+
@lead_selection_index = ix1
|
102
|
+
ix0.upto(ix1) {|i| @selected_indices << i unless @selected_indices.include? i }
|
103
|
+
#lse = ListSelectionEvent.new(ix0, ix1, @parent, :INSERT)
|
104
|
+
#fire_handler :LIST_SELECTION_EVENT, lse
|
105
|
+
#$log.debug " DLSM firing LIST_SELECTION EVENT #{lse}"
|
106
|
+
end
|
107
|
+
alias :add_row_selection_interval :add_selection_interval
|
108
|
+
def remove_selection_interval ix0, ix1
|
109
|
+
@anchor_selection_index = ix0
|
110
|
+
@lead_selection_index = ix1
|
111
|
+
@selected_indices.delete_if {|x| x >= ix0 and x <= ix1}
|
112
|
+
#lse = ListSelectionEvent.new(ix0, ix1, @parent, :DELETE)
|
113
|
+
#fire_handler :LIST_SELECTION_EVENT, lse
|
114
|
+
end
|
115
|
+
alias :remove_row_selection_interval :remove_selection_interval
|
116
|
+
# convenience method to select next len rows
|
117
|
+
def insert_index_interval ix0, len
|
118
|
+
@anchor_selection_index = ix0
|
119
|
+
@lead_selection_index = ix0+len
|
120
|
+
add_selection_interval @anchor_selection_index, @lead_selection_index
|
121
|
+
end
|
122
|
+
# select all rows, you may specify starting row.
|
123
|
+
# if header row, then 1 else should be 0. Actually we should have a way to determine
|
124
|
+
# this, and the default should be zero.
|
125
|
+
def select_all start_row=0
|
126
|
+
@repaint_required = true
|
127
|
+
# don't select header row - need to make sure this works for all cases. we may
|
128
|
+
# need a variable instead of hardoded value
|
129
|
+
add_row_selection_interval start_row, row_count()
|
130
|
+
end
|
131
|
+
def invert_selection start_row=1
|
132
|
+
start_row.upto(row_count()){|i| invert_row_selection i }
|
133
|
+
end
|
134
|
+
|
135
|
+
def invert_row_selection row=@current_index-@_header_adjustment
|
136
|
+
@repaint_required = true
|
137
|
+
if is_selected? row
|
138
|
+
remove_row_selection_interval(row, row)
|
139
|
+
else
|
140
|
+
add_row_selection_interval(row, row)
|
141
|
+
end
|
142
|
+
end
|
143
|
+
# selects all rows with the values given, leaving existing selections
|
144
|
+
# intact. Typically used after accepting search criteria, and getting a list of values
|
145
|
+
# to select (such as file names). Will not work with tables (array or array)
|
146
|
+
def select_values values
|
147
|
+
return unless values
|
148
|
+
values.each do |val|
|
149
|
+
row = @list.index val
|
150
|
+
add_row_selection_interval row, row unless row.nil?
|
151
|
+
end
|
152
|
+
end
|
153
|
+
# unselects all rows with the values given, leaving all other rows intact
|
154
|
+
# You can map "-" to ask_select and call this from there.
|
155
|
+
# bind_key(?+, :ask_select) # --> calls select_values
|
156
|
+
# bind_key(?-, :ask_unselect)
|
157
|
+
def unselect_values values
|
158
|
+
return unless values
|
159
|
+
values.each do |val|
|
160
|
+
row = @list.index val
|
161
|
+
remove_row_selection_interval row, row unless row.nil?
|
162
|
+
end
|
163
|
+
end
|
164
|
+
# please override this, this is just very basic and default
|
165
|
+
# Please implement get_matching_indices(String).
|
166
|
+
def ask_select prompt="Enter selection pattern: "
|
167
|
+
ret = ask(prompt, String) {|q| yield q if block_given? }
|
168
|
+
return if ret.nil? || ret == ""
|
169
|
+
indices = get_matching_indices ret
|
170
|
+
return if indices.nil? || indices.empty?
|
171
|
+
indices.each { |e|
|
172
|
+
# will not work if single select !! FIXME
|
173
|
+
add_row_selection_interval e,e
|
174
|
+
}
|
175
|
+
@repaint_required = true
|
176
|
+
end
|
177
|
+
def get_matching_indices pattern
|
178
|
+
alert "please implement this method get_matching_indices in your class "
|
179
|
+
return []
|
180
|
+
end # mod
|
181
|
+
# Applications may call this or just copy and modify
|
182
|
+
def list_bindings
|
183
|
+
# what about users wanting 32 and ENTER to also go to next row automatically
|
184
|
+
# should make that optional, TODO
|
185
|
+
bind_key(32) { toggle_row_selection }
|
186
|
+
bind_key(0) { add_to_selection }
|
187
|
+
bind_key(?+, :ask_select) # --> calls select_values
|
188
|
+
bind_key(?-, :ask_unselect)
|
189
|
+
bind_key(?a, :select_all)
|
190
|
+
bind_key(?*, :invert_selection)
|
191
|
+
bind_key(?u, :clear_selection)
|
192
|
+
end
|
193
|
+
def list_init_vars
|
194
|
+
@selected_indices = []
|
195
|
+
@selected_index = nil
|
196
|
+
#@row_selected_symbol = ''
|
197
|
+
if @show_selector
|
198
|
+
@row_selected_symbol ||= '*'
|
199
|
+
@row_unselected_symbol ||= ' '
|
200
|
+
@left_margin ||= @row_selected_symbol.length
|
201
|
+
end
|
202
|
+
end
|
203
|
+
# paint the selector. Called from repaint, prior to printing data row
|
204
|
+
# remember to set left_margin at top of repaint method as:
|
205
|
+
# @left_margin ||= @row_selected_symbol.length
|
206
|
+
def paint_selector crow, r, c, acolor, attrib
|
207
|
+
selected = is_row_selected crow
|
208
|
+
selection_symbol = ''
|
209
|
+
if @show_selector
|
210
|
+
if selected
|
211
|
+
selection_symbol = @row_selected_symbol
|
212
|
+
else
|
213
|
+
selection_symbol = @row_unselected_symbol
|
214
|
+
end
|
215
|
+
@graphic.printstring r, c, selection_symbol, acolor,attrib
|
216
|
+
end
|
217
|
+
end
|
218
|
+
def selected_rows
|
219
|
+
@selected_indices
|
220
|
+
end
|
221
|
+
end # mod
|
222
|
+
end # mod
|