class Canis::DefaultListSelectionModel
Object
that takes care of selection of rows. This may be replace with a custom object at time of instantiation of list Note that there are only two selection modes: single and multiple. Multiple refers to multiple intervals. There is also a multiple row selection mode, single interval, which only allows one range to be selected, much like a text object, i.e. any text editor.
I am copying this from listselectable. that was a module so was included and shared variables
but now this is a class, and cannot access state as directly
Public Class Methods
# File lib/canis/core/include/listselectionmodel.rb, line 83 def initialize component raise "Components passed to DefaultListSelectionModel is nil" unless component @obj = component @selected_indices = @obj.selected_indices # in this case since it is called immediately upon extend, user cannot change this # Need a method to let user change after extending @selection_mode = @obj.selection_mode list_bindings end
Public Instance Methods
add the following range to selected items, unless already present should only be used if multiple selection interval
# File lib/canis/core/include/listselectionmodel.rb, line 220 def add_row_selection_interval ix0, ix1 return if @obj.selection_mode != :multiple @anchor_selection_index = ix0 @lead_selection_index = ix1 ix0.upto(ix1) {|i| @selected_indices << i unless @selected_indices.include? i @obj.fire_row_changed i } lse = ListSelectionEvent.new(ix0, ix1, @obj, :INSERT) @obj.fire_handler :LIST_SELECTION_EVENT, lse #$log.debug " DLSM firing LIST_SELECTION EVENT #{lse}" end
Asks user to enter a string or pattern for selecting rows Selects rows based on pattern, leaving other selections as-is
# File lib/canis/core/include/listselectionmodel.rb, line 300 def ask_select prompt="Enter selection pattern: " ret = get_string prompt return if ret.nil? || ret == "" indices = get_matching_indices ret #$log.debug "listselectionmodel: ask_select got matches#{@indices} " return if indices.nil? || indices.empty? indices.each { |e| # will not work if single select !! FIXME add_row_selection_interval e,e } end
Asks user to enter a string or pattern for UNselecting rows UNSelects rows based on pattern, leaving other selections as-is
# File lib/canis/core/include/listselectionmodel.rb, line 326 def ask_unselect prompt="Enter selection pattern: " ret = get_string prompt return if ret.nil? || ret == "" indices = get_matching_indices ret return if indices.nil? || indices.empty? indices.each { |e| # will not work if single select !! FIXME remove_row_selection_interval e,e } end
clears selected indices, typically called when multiple select Key binding is application specific
# File lib/canis/core/include/listselectionmodel.rb, line 175 def clear_selection return if @selected_indices.nil? || @selected_indices.empty? arr = @selected_indices.dup # to un highlight @selected_indices.clear arr.each {|i| @obj.fire_row_changed(i) } @selected_index = nil @old_selected_index = nil # User should ignore first two params lse = ListSelectionEvent.new(0, arr.size, @obj, :CLEAR) @obj.fire_handler :LIST_SELECTION_EVENT, lse arr = nil end
returns a list of matching indices using a simple regex match on given pattern returns an empty list if no match
# File lib/canis/core/include/listselectionmodel.rb, line 313 def get_matching_indices pattern matches = [] @obj.content.each_with_index { |e,i| # convert to string for tables e = e.to_s unless e.is_a? String if e =~ /#{pattern}/ matches << i end } return matches end
after selecting, traverse selections forward
# File lib/canis/core/include/listselectionmodel.rb, line 202 def goto_next_selection return if selected_rows().length == 0 row = selected_rows().sort.find { |i| i > @obj.current_index } row ||= @obj.current_index #@obj.current_index = row @obj.goto_line row end
after selecting, traverse selections backward
# File lib/canis/core/include/listselectionmodel.rb, line 211 def goto_prev_selection return if selected_rows().length == 0 row = selected_rows().sort{|a,b| b <=> a}.find { |i| i < @obj.current_index } row ||= @obj.current_index #@obj.current_index = row @obj.goto_line row end
convenience method to select next len rows
# File lib/canis/core/include/listselectionmodel.rb, line 244 def insert_index_interval ix0, len @anchor_selection_index = ix0 @lead_selection_index = ix0+len add_row_selection_interval @anchor_selection_index, @lead_selection_index end
toggles selection for given row Typically called by invert_selection
# File lib/canis/core/include/listselectionmodel.rb, line 266 def invert_row_selection row=@obj.current_index @repaint_required = true if is_selected? row remove_row_selection_interval(row, row) else add_row_selection_interval(row, row) end end
toggle selection of entire list Requires application specific key binding
# File lib/canis/core/include/listselectionmodel.rb, line 260 def invert_selection start_row=0 #+@_header_adjustment start_row.upto(@obj.list.count()-1){|i| invert_row_selection i } end
returns true
if given row has been selected Now that we use only the array, the multiple check is good enough
# File lib/canis/core/include/listselectionmodel.rb, line 190 def is_row_selected? crow case @obj.selection_mode when :multiple @selected_indices.include? crow else @selected_index = @selected_indices[0] crow == @selected_index end end
bindings related to selection
# File lib/canis/core/include/listselectionmodel.rb, line 340 def list_bindings # freeing space for paging, now trying out 'v' as selector. 2014-04-14 - 18:57 @obj.bind_key($row_selector || 'v'.ord, 'toggle selection') { toggle_row_selection } # the mode may be set to single after the constructor, so this would have taken effect. if @obj.selection_mode == :multiple # freeing ctrl_space for back paging, now trying out 'V' as selector. 2014-04-14 - 18:57 @obj.bind_key($range_selector || 'V'.ord, 'range select') { range_select } @obj.bind_key(?+, 'ask_select') { ask_select } @obj.bind_key(?-, 'ask_unselect') { ask_unselect } @obj.bind_key(?a, 'select_all') {select_all} @obj.bind_key(?*, 'invert_selection') { invert_selection } @obj.bind_key(?u, 'clear_selection') { clear_selection } @obj.bind_key([?g,?n], 'goto next selection'){ goto_next_selection } # mapping double keys like vim @obj.bind_key([?g,?p], 'goto prev selection'){ goto_prev_selection } # mapping double keys like vim end @_header_adjustment ||= 0 # incase caller does not use #@obj._events << :LIST_SELECTION_EVENT unless @obj._events.include? :LIST_SELECTION_EVENT end
# File lib/canis/core/include/listselectionmodel.rb, line 359 def list_init_vars # uncommenting since link with obj will be broken #@selected_indices = [] @selected_index = nil @old_selected_index = nil #@row_selected_symbol = '' ## FIXME we are not doing selectors at present. should we, else remove this if @show_selector @row_selected_symbol ||= '*' @row_unselected_symbol ||= ' ' @left_margin ||= @row_selected_symbol.length end end
Range select. Only for multiple mode. Uses the last row clicked on, till the current one. If user clicks inside a selcted range, then deselect from last click till current (remove from earlier) If user clicks outside selected range, then select from last click till current (add to earlier) typically bound to Ctrl-Space (0)
@example
bind_key(0) { range_select }
# File lib/canis/core/include/listselectionmodel.rb, line 146 def range_select crow=@obj.current_index #alert "add to selection fired #{@last_clicked}" @last_clicked ||= crow min = [@last_clicked, crow].min max = [@last_clicked, crow].max case @obj.selection_mode when :multiple if @selected_indices.include? crow # delete from last_clicked until this one in any direction min.upto(max){ |i| @selected_indices.delete i @obj.fire_row_changed i } lse = ListSelectionEvent.new(min, max, @obj, :DELETE) @obj.fire_handler :LIST_SELECTION_EVENT, lse else # add to selection from last_clicked until this one in any direction min.upto(max){ |i| @selected_indices << i unless @selected_indices.include?(i) @obj.fire_row_changed i } lse = ListSelectionEvent.new(min, max, @obj, :INSERT) @obj.fire_handler :LIST_SELECTION_EVENT, lse end else end @last_clicked = crow # 2014-04-08 - 01:21 this was missing, i think it is required self end
remove selected indices between given indices inclusive
# File lib/canis/core/include/listselectionmodel.rb, line 234 def remove_row_selection_interval ix0, ix1 @anchor_selection_index = ix0 @lead_selection_index = ix1 arr = @selected_indices.dup # to un highlight @selected_indices.delete_if {|x| x >= ix0 and x <= ix1 } arr.each {|i| @obj.fire_row_changed(i) } lse = ListSelectionEvent.new(ix0, ix1, @obj, :DELETE) @obj.fire_handler :LIST_SELECTION_EVENT, lse end
select all rows, you may specify starting row. if header row, then 1 else should be 0. Actually we should have a way to determine this, and the default should be zero.
# File lib/canis/core/include/listselectionmodel.rb, line 252 def select_all start_row=0 #+@_header_adjustment # don't select header row - need to make sure this works for all cases. we may # need a variable instead of hardoded value add_row_selection_interval start_row, @obj.list.count()-1 end
selects all rows with the values given, leaving existing selections intact. Typically used after accepting search criteria, and getting a list of values to select (such as file names). Will not work with tables (array or array) TODO is this even needed, scrap
# File lib/canis/core/include/listselectionmodel.rb, line 278 def select_values values return unless values values.each do |val| row = @list.index val add_row_selection_interval row, row unless row.nil? end end
return the indices selected
# File lib/canis/core/include/listselectionmodel.rb, line 373 def selected_rows @selected_indices end
returns first selection, meant for convenience of single select listboxes earlier called selected_item
# File lib/canis/core/include/listselectionmodel.rb, line 382 def selected_value return nil if @selected_indices.empty? @obj[@selected_indices.first] #selected_values.first end
return the values selected
# File lib/canis/core/include/listselectionmodel.rb, line 377 def selected_values @obj.values_at(*@selected_indices) end
change selection of current row on pressing space bar (or keybinding) If mode is multiple, then this row is added to previous selections @example
bind_key(32) { toggle_row_selection }
# File lib/canis/core/include/listselectionmodel.rb, line 100 def toggle_row_selection crow=@obj.current_index @last_clicked = crow @repaint_required = true case @obj.selection_mode when :multiple if @selected_indices.include? crow @selected_indices.delete crow lse = ListSelectionEvent.new(crow, crow, @obj, :DELETE) @obj.fire_handler :LIST_SELECTION_EVENT, lse else @selected_indices << crow lse = ListSelectionEvent.new(crow, crow, @obj, :INSERT) @obj.fire_handler :LIST_SELECTION_EVENT, lse end else # single - now change to use array only @selected_index = @selected_indices[0] if @selected_index == crow @old_selected_index = @selected_index # 2011-10-15 so we can unhighlight @selected_index = nil @selected_indices.clear lse = ListSelectionEvent.new(crow, crow, @obj, :DELETE) @obj.fire_handler :LIST_SELECTION_EVENT, lse else @selected_indices[0] = crow @obj.fire_row_changed(@old_selected_index) if @old_selected_index @old_selected_index = crow # 2011-10-15 so we can unhighlight lse = ListSelectionEvent.new(crow, crow, @obj, :INSERT) @obj.fire_handler :LIST_SELECTION_EVENT, lse end end @obj.fire_row_changed crow #alert "toggling #{@selected_indices.join(',')}" end
TODO is this even needed, scrap unselects all rows with the values given, leaving all other rows intact You can map “-” to ask_select
and call this from there.
bind_key(?+, :ask_select) # --> calls select_values bind_key(?-, :ask_unselect)
# File lib/canis/core/include/listselectionmodel.rb, line 290 def unselect_values values return unless values values.each do |val| row = @list.index val remove_row_selection_interval row, row unless row.nil? end end