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.
Files changed (123) hide show
  1. data/CHANGELOG +45 -0
  2. data/Makefile +1 -1
  3. data/Manifest.txt +91 -0
  4. data/NOTES +349 -2
  5. data/README.markdown +12 -0
  6. data/VERSION +1 -1
  7. data/examples/abasiclist.rb +25 -0
  8. data/examples/alpmenu.rb +42 -0
  9. data/examples/app.rb +883 -0
  10. data/examples/appcombo.rb +17 -0
  11. data/examples/appdirtree.rb +73 -0
  12. data/examples/appemail.rb +164 -0
  13. data/examples/appemaillb.rb +308 -0
  14. data/examples/appgcompose.rb +303 -0
  15. data/examples/appgmail.rb +951 -0
  16. data/examples/atree.rb +56 -0
  17. data/examples/dirtree.rb +78 -0
  18. data/examples/focusmanager.rb +31 -0
  19. data/examples/imap.rb +48 -0
  20. data/examples/menu1.rb +79 -0
  21. data/examples/multispl.rb +86 -0
  22. data/examples/rfe.rb +3 -4
  23. data/examples/rmail.rb +188 -0
  24. data/examples/s.rb +10 -0
  25. data/examples/scrollbar.rb +104 -0
  26. data/examples/splitp.rb +56 -0
  27. data/examples/table1.rb +30 -0
  28. data/examples/term.rb +48 -0
  29. data/examples/term2.rb +54 -0
  30. data/examples/test1.rb +4 -2
  31. data/examples/test2.rb +9 -9
  32. data/examples/testapp.rb +44 -0
  33. data/examples/testapp2.rb +51 -0
  34. data/examples/testcombo.rb +2 -2
  35. data/examples/testgmail.rb +46 -0
  36. data/examples/testlistbox.rb +0 -1
  37. data/examples/testmultispl.rb +199 -0
  38. data/examples/testree.rb +127 -0
  39. data/examples/testscroller.rb +0 -1
  40. data/examples/testscrolllb.rb +1 -1
  41. data/examples/testscrollp.rb +2 -1
  42. data/examples/testscrollta.rb +1 -1
  43. data/examples/testscrolltable.rb +1 -2
  44. data/examples/testsplit.rb +1 -1
  45. data/examples/testsplit2.rb +1 -1
  46. data/examples/testsplit3.rb +1 -1
  47. data/examples/testsplit3_1.rb +1 -1
  48. data/examples/testsplit3a.rb +1 -1
  49. data/examples/testsplit3b.rb +1 -1
  50. data/examples/testsplitta.rb +1 -1
  51. data/examples/testsplittv.rb +1 -1
  52. data/examples/testsplittvv.rb +1 -1
  53. data/examples/testtodo.rb +491 -488
  54. data/examples/testvimsplit.rb +111 -0
  55. data/examples/todo.db +0 -0
  56. data/examples/todocsv.csv +28 -0
  57. data/examples/viewtodo.rb +408 -403
  58. data/lib/rbcurse/action.rb +1 -0
  59. data/lib/rbcurse/app.rb +1294 -0
  60. data/lib/rbcurse/applicationheader.rb +7 -2
  61. data/lib/rbcurse/checkboxcellrenderer.rb +0 -12
  62. data/lib/rbcurse/colormap.rb +34 -8
  63. data/lib/rbcurse/comboboxcellrenderer.rb +0 -11
  64. data/lib/rbcurse/defaultlistselectionmodel.rb +23 -7
  65. data/lib/rbcurse/extras/bottomline.rb +1681 -0
  66. data/lib/rbcurse/extras/directorylist.rb +445 -0
  67. data/lib/rbcurse/extras/directorytree.rb +69 -0
  68. data/lib/rbcurse/extras/divider.rb +310 -0
  69. data/lib/rbcurse/extras/focusmanager.rb +31 -0
  70. data/lib/rbcurse/extras/listselectable.rb +222 -0
  71. data/lib/rbcurse/extras/masterdetail.rb +164 -0
  72. data/lib/rbcurse/extras/menutree.rb +63 -0
  73. data/lib/rbcurse/extras/rlink.rb +27 -0
  74. data/lib/rbcurse/extras/rmenulink.rb +21 -0
  75. data/lib/rbcurse/extras/scrollbar.rb +134 -0
  76. data/lib/rbcurse/extras/stdscrwindow.rb +247 -0
  77. data/lib/rbcurse/extras/tabular.rb +258 -0
  78. data/lib/rbcurse/extras/tabularwidget.rb +1070 -0
  79. data/lib/rbcurse/extras/viewer.rb +106 -0
  80. data/lib/rbcurse/io.rb +137 -80
  81. data/lib/rbcurse/keylabelprinter.rb +4 -0
  82. data/lib/rbcurse/listcellrenderer.rb +91 -59
  83. data/lib/rbcurse/listscrollable.rb +93 -95
  84. data/lib/rbcurse/listselectable.rb +60 -7
  85. data/lib/rbcurse/ractionevent.rb +67 -0
  86. data/lib/rbcurse/rbasiclistbox.rb +688 -0
  87. data/lib/rbcurse/rcombo.rb +5 -5
  88. data/lib/rbcurse/rcommandwindow.rb +555 -0
  89. data/lib/rbcurse/rinputdataevent.rb +12 -0
  90. data/lib/rbcurse/rlistbox.rb +305 -124
  91. data/lib/rbcurse/rmenu.rb +99 -46
  92. data/lib/rbcurse/rmessagebox.rb +13 -6
  93. data/lib/rbcurse/rmulticontainer.rb +54 -93
  94. data/lib/rbcurse/rmultisplit.rb +731 -0
  95. data/lib/rbcurse/rmultitextview.rb +3 -2
  96. data/lib/rbcurse/rpopupmenu.rb +0 -1
  97. data/lib/rbcurse/rprogress.rb +117 -0
  98. data/lib/rbcurse/rscrollpane.rb +2 -1
  99. data/lib/rbcurse/rsplitpane.rb +94 -20
  100. data/lib/rbcurse/rsplitpane2.rb +1009 -0
  101. data/lib/rbcurse/rtabbedpane.rb +3 -2
  102. data/lib/rbcurse/rtabbedwindow.rb +0 -1
  103. data/lib/rbcurse/rtable.rb +92 -64
  104. data/lib/rbcurse/rtextarea.rb +91 -57
  105. data/lib/rbcurse/rtextview.rb +223 -70
  106. data/lib/rbcurse/rtree.rb +723 -0
  107. data/lib/rbcurse/rviewport.rb +2 -1
  108. data/lib/rbcurse/rvimsplit.rb +768 -0
  109. data/lib/rbcurse/rwidget.rb +524 -325
  110. data/lib/rbcurse/table/tablecellrenderer.rb +1 -1
  111. data/lib/rbcurse/table/tabledatecellrenderer.rb +0 -1
  112. data/lib/rbcurse/tree/treecellrenderer.rb +137 -0
  113. data/lib/rbcurse/tree/treemodel.rb +428 -0
  114. data/lib/rbcurse/vieditable.rb +14 -13
  115. data/lib/ver/ncurses.rb +6 -0
  116. data/lib/ver/window.rb +67 -32
  117. metadata +99 -23
  118. data/bin/rbcurse +0 -0
  119. data/examples/rvimsplit.rb +0 -376
  120. data/examples/todo.rb +0 -1
  121. data/lib/rbcurse/rform.rb +0 -845
  122. data/lib/rbcurse/selectable.rb +0 -94
  123. 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