Class: Canis::DefaultListSelectionModel

Inherits:
Object
  • Object
show all
Defined in:
lib/canis/core/include/listselectionmodel.rb

Overview

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

Instance Method Summary collapse

Constructor Details

#initialize(component) ⇒ DefaultListSelectionModel

Returns a new instance of DefaultListSelectionModel.



83
84
85
86
87
88
89
90
91
92
# 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

Instance Method Details

#add_row_selection_interval(ix0, ix1) ⇒ Object

add the following range to selected items, unless already present should only be used if multiple selection interval



220
221
222
223
224
225
226
227
228
229
230
231
# 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

#ask_select(prompt = "Enter selection pattern: ") ⇒ Object

Asks user to enter a string or pattern for selecting rows Selects rows based on pattern, leaving other selections as-is



300
301
302
303
304
305
306
307
308
309
310
# 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

#ask_unselect(prompt = "Enter selection pattern: ") ⇒ Object

Asks user to enter a string or pattern for UNselecting rows UNSelects rows based on pattern, leaving other selections as-is



326
327
328
329
330
331
332
333
334
335
# 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

#clear_selectionObject

clears selected indices, typically called when multiple select Key binding is application specific



175
176
177
178
179
180
181
182
183
184
185
186
# 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

#get_matching_indices(pattern) ⇒ Object

returns a list of matching indices using a simple regex match on given pattern returns an empty list if no match



313
314
315
316
317
318
319
320
321
322
323
# 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

#goto_next_selectionObject

after selecting, traverse selections forward



202
203
204
205
206
207
208
# 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

#goto_prev_selectionObject

after selecting, traverse selections backward



211
212
213
214
215
216
217
# 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

#insert_index_interval(ix0, len) ⇒ Object

convenience method to select next len rows



244
245
246
247
248
# 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

#invert_row_selection(row = @obj.current_index) ⇒ Object

toggles selection for given row Typically called by invert_selection



266
267
268
269
270
271
272
273
# 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

#invert_selection(start_row = 0) ⇒ Object

toggle selection of entire list Requires application specific key binding



260
261
262
# 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

#is_row_selected?(crow) ⇒ Boolean Also known as: is_selected?

returns true if given row has been selected Now that we use only the array, the multiple check is good enough

Returns:

  • (Boolean)


190
191
192
193
194
195
196
197
198
# 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

#list_bindingsObject

bindings related to selection



340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
# 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

#list_init_varsObject



359
360
361
362
363
364
365
366
367
368
369
370
371
# 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(crow = @obj.current_index) ⇒ Object

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)

Examples:


bind_key(0) { range_select }


146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
# 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_row_selection_interval(ix0, ix1) ⇒ Object

remove selected indices between given indices inclusive



234
235
236
237
238
239
240
241
242
# 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(start_row = 0) ⇒ Object

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.



252
253
254
255
256
# 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

#select_values(values) ⇒ Object

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



278
279
280
281
282
283
284
# 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

#selected_rowsObject

return the indices selected



373
374
375
# File 'lib/canis/core/include/listselectionmodel.rb', line 373

def selected_rows
  @selected_indices
end

#selected_valueObject

returns first selection, meant for convenience of single select listboxes earlier called selected_item



382
383
384
385
386
# 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

#selected_valuesObject

return the values selected



377
378
379
# File 'lib/canis/core/include/listselectionmodel.rb', line 377

def selected_values
  @obj.values_at(*@selected_indices)
end

#toggle_row_selection(crow = @obj.current_index) ⇒ Object

change selection of current row on pressing space bar (or keybinding) If mode is multiple, then this row is added to previous selections

Examples:

bind_key(32) { toggle_row_selection }


100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
# 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

#unselect_values(values) ⇒ Object

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)


290
291
292
293
294
295
296
# 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