Class: RubyCurses::Listbox

Inherits:
Widget
  • Object
show all
Extended by:
Forwardable
Includes:
ListScrollable, ListKeys, ListSelectable
Defined in:
lib/rbcurse/extras/widgets/rlistbox.rb

Overview

A control for displaying a list of data or values. The list will be editable if @cell_editing_allowed is set to true when creating. By default, multiple selection is allowed, but may be set to :single. TODO: were we not going to force creation of datamodel and listener on startup by putting a blank :list in config, if no list or list_variable or model is there ? Or at end of constructor check, if no listdatamodel then create default. TODO : perhaps when datamodel created, attach listener to it, so we can fire to callers when they want to be informed of changes. As we did with selection listeners.

Instance Attribute Summary collapse

Attributes included from ListSelectable

#column_selection_allowed, #row_selection_allowed

Instance Method Summary collapse

Methods included from ListKeys

#install_list_keys

Methods included from ListSelectable

#add_row_selection_interval, #clear_selection, #do_next_selection, #do_prev_selection, #is_selected?, #list_selection_model, #remove_row_selection_interval, #selected_row, #selected_row_count, #selected_rows, #selected_value, #toggle_row_selection

Constructor Details

#initialize(form, config = {}, &block) ⇒ Listbox

Returns a new instance of Listbox.



447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
# File 'lib/rbcurse/extras/widgets/rlistbox.rb', line 447

def initialize form, config={}, &block
  @focusable = true
  @editable = false
  @sanitization_required = true
  @one_key_selection = false # allow vim like keys
  @row = 0
  @col = 0
  # data of listbox this is not an array, its a pointer to the  listdatamodel
  @list = nil 
  # any special attribs such as status to be printed in col1, or color (selection)
  @list_attribs = {}
  @suppress_borders = false
  @row_offset = @col_offset = 1 # for borders
  super
  @_events.push(*[:ENTER_ROW, :LEAVE_ROW, :LIST_SELECTION_EVENT, :PRESS])
  @current_index ||= 0
  @selection_mode ||= :multiple # default is multiple, anything else given becomes single
  @win = @graphic    # 2010-01-04 12:36 BUFFERED  replace form.window with graphic
  # moving down to repaint so that scrollpane can set should_buffered
  # added 2010-02-17 23:05  RFED16 so we don't need a form.

  # next 2 lines carry a redundancy
  select_default_values   
  # when the combo box has a certain row in focus, the popup should have the same row in focus

  install_keys
  install_list_keys
  init_vars
  init_actions
  # OMG What about people whove installed custom renders such as rfe.rb 2011-10-15 
  #bind(:PROPERTY_CHANGE){|e| @cell_renderer = nil } # will be recreated if anything changes 2011-09-28 V1.3.1  
  bind(:PROPERTY_CHANGE){|e| 
    # I can't delete the cell renderer, but this may not have full effect if one color is passed.
    if @cell_renderer.respond_to? e.property_name
      @cell_renderer.send(e.property_name.to_sym, e.newvalue)
    end
  } # will be recreated if anything changes 2011-09-28 V1.3.1  

  if @list && !@list.selected_index.nil? 
    set_focus_on @list.selected_index # the new version
  end
end

Instance Attribute Details

#current_indexObject

Returns the value of attribute current_index.



422
423
424
# File 'lib/rbcurse/extras/widgets/rlistbox.rb', line 422

def current_index
  @current_index
end

#one_key_selectionObject

will pressing a single key select or not



441
442
443
# File 'lib/rbcurse/extras/widgets/rlistbox.rb', line 441

def one_key_selection
  @one_key_selection
end

#toprowObject (readonly)

dsl_accessor :list # the array of data to be sent by user



415
416
417
# File 'lib/rbcurse/extras/widgets/rlistbox.rb', line 415

def toprow
  @toprow
end

Instance Method Details

#[](off0) ⇒ Object

get element at

Parameters:

  • index (Fixnum)

    for element

Returns:

  • (Object)

    element

Since:

  • 1.2.0 2010-09-06 14:33 making life easier for others.



664
665
666
# File 'lib/rbcurse/extras/widgets/rlistbox.rb', line 664

def [](off0)
  @list[off0]
end

#ask_search_backwardObject

gets string to search and calls data models find prev



888
889
890
891
892
893
894
895
896
897
# File 'lib/rbcurse/extras/widgets/rlistbox.rb', line 888

def ask_search_backward
  regex =  get_string("Enter regex to search (backward)")
  @last_regex = regex
  ix = @list.find_prev regex, @current_index
  if ix.nil?
    alert("No matching data for: #{regex}")
  else
    set_focus_on(ix)
  end
end

#ask_search_forwardObject



878
879
880
881
882
883
884
885
886
# File 'lib/rbcurse/extras/widgets/rlistbox.rb', line 878

def ask_search_forward
    regex =  get_string("Enter regex to search")
    ix = @list.find_match regex
    if ix.nil?
      alert("No matching data for: #{regex}")
    else
      set_focus_on(ix)
    end
end

#ask_selection_for_charObject

get a keystroke from user and go to first item starting with that key



871
872
873
874
875
876
877
# File 'lib/rbcurse/extras/widgets/rlistbox.rb', line 871

def ask_selection_for_char
  ch = @graphic.getch
  if ch < 0 || ch > 255
    return :UNHANDLED
  end
  ret = set_selection_for_char ch.chr
end

#cancel_blockObject

added on 2009-02-19 22:37



1169
1170
1171
1172
# File 'lib/rbcurse/extras/widgets/rlistbox.rb', line 1169

def cancel_block
  @first_index = @last_index = nil
  @inside_block = false
end

#cell_editor(*val) ⇒ Object

getter and setter for cell_editor



982
983
984
985
986
987
988
# File 'lib/rbcurse/extras/widgets/rlistbox.rb', line 982

def cell_editor(*val)
  if val.empty?
    @cell_editor ||= create_default_cell_editor
  else
    @cell_editor = val[0] 
  end
end

#cell_renderer(*val) ⇒ Object

getter and setter for cell_renderer



994
995
996
997
998
999
1000
# File 'lib/rbcurse/extras/widgets/rlistbox.rb', line 994

def cell_renderer(*val)
  if val.empty?
    @cell_renderer ||= create_default_cell_renderer
  else
    @cell_renderer = val[0] 
  end
end

#convert_value_to_text(value, crow) ⇒ Object

the idea here is to allow users who subclass Listbox to easily override parts of the cumbersome repaint method. This assumes your List has some data, but you print a lot more. Now you don’t need to change the data in the renderer, or keep formatted data in the list itself. e.g. @list contains file names, or File objects, and this converts to a long listing. If the renderer did that, the truncation would be on wrong data.

Since:

  • 1.2.0



1079
1080
1081
1082
1083
1084
1085
1086
# File 'lib/rbcurse/extras/widgets/rlistbox.rb', line 1079

def convert_value_to_text value, crow
  case value
  when TrueClass, FalseClass
    value
  else
    value.to_s if value
  end
end

#create_default_cell_editorObject



989
990
991
# File 'lib/rbcurse/extras/widgets/rlistbox.rb', line 989

def create_default_cell_editor
  return RubyCurses::CellEditor.new RubyCurses::Field.new nil, {"focusable"=>false, "visible"=>false, "display_length"=> @width-@internal_width-@left_margin}
end

#create_default_cell_rendererObject



1001
1002
1003
# File 'lib/rbcurse/extras/widgets/rlistbox.rb', line 1001

def create_default_cell_renderer
  return RubyCurses::ListCellRenderer.new "", {"color"=>@color, "bgcolor"=>@bgcolor, "parent" => self, "display_length"=> @width-@internal_width-@left_margin}
end

#create_default_list_selection_modelObject

create a default list selection model and set it NOTE: I am now checking if one is not already created, since a second creation would wipe out any listeners on it.



653
654
655
656
657
# File 'lib/rbcurse/extras/widgets/rlistbox.rb', line 653

def create_default_list_selection_model
  if @list_selection_model.nil?
    list_selection_model DefaultListSelectionModel.new(self)
  end
end

#current_valueObject Also known as: current_row, text

return object under cursor Note: this should not be confused with selected row/s. User may not have selected this. This is only useful since in some demos we like to change a status bar as a user scrolls down

Since:

  • 1.2.0 2010-09-06 14:33 making life easier for others.



671
672
673
# File 'lib/rbcurse/extras/widgets/rlistbox.rb', line 671

def current_value
  @list[@current_index]
end

#edit_row_at(arow) ⇒ Object



922
923
924
925
926
927
928
# File 'lib/rbcurse/extras/widgets/rlistbox.rb', line 922

def edit_row_at arow
  if @cell_editing_allowed
    #$log.debug " cell editor on enter #{arow} val of list[row]: #{@list[arow]}"
    editor = cell_editor
    prepare_editor editor, arow
  end
end

#editing_canceled(arow = @current_index) ⇒ Object



974
975
976
977
978
# File 'lib/rbcurse/extras/widgets/rlistbox.rb', line 974

def editing_canceled arow=@current_index
  return unless @cell_editing_allowed
  prepare_editor @cell_editor, arow
  @repaint_required = true
end

#editing_completed(arow) ⇒ Object



959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
# File 'lib/rbcurse/extras/widgets/rlistbox.rb', line 959

def editing_completed arow
  if @cell_editing_allowed
    if !@cell_editor.nil?
  #    $log.debug " cell editor (leave) setting value row: #{arow} val: #{@cell_editor.getvalue}"
      $log.debug " cell editor #{@cell_editor.component.form.window} (leave) setting value row: #{arow} val: #{@cell_editor.getvalue}"
      @list[arow] = @cell_editor.getvalue #.dup 2009-01-10 21:42 boolean can't duplicate
    else
      $log.debug "CELL EDITOR WAS NIL, #{arow} "
    end
    if @edit_toggle
      @is_editing = false
    end
  end
  @repaint_required = true
end

#end_blockObject

sets marker for end of block added on 2009-02-19 22:37



1181
1182
1183
1184
1185
1186
1187
1188
# File 'lib/rbcurse/extras/widgets/rlistbox.rb', line 1181

def end_block #selection
  @last_index = current_index
  lower = [@first_index, @last_index].min
  higher = [@first_index, @last_index].max
  #lower.upto(higher) do |i| @list.toggle_row_selection i; end
  add_row_selection_interval(lower, higher)
  @repaint_required = true
end

#fire_action_eventObject

fire handler when user presses ENTER/RETURN listbox now traps ENTER key and fires action event to trap please bind :PRESS

Since:

  • 1.2.0



864
865
866
867
868
869
# File 'lib/rbcurse/extras/widgets/rlistbox.rb', line 864

def fire_action_event
  # this does not select the row ???? FIXME ??
  require 'rbcurse/core/include/ractionevent'
  # should have been callled :ACTION_EVENT !!!
  fire_handler :PRESS, ActionEvent.new(self, :PRESS, text)
end

#get_contentObject

START FOR scrollable ###



714
715
716
717
718
# File 'lib/rbcurse/extras/widgets/rlistbox.rb', line 714

def get_content
  #@list 2008-12-01 23:13 
  # NOTE: we never stored the listvariable, so its redundant, we used its value to set list
  @list_variable && @list_variable.value || @list 
end

#get_selected_indicesObject Also known as: selected_indices

trying to simplify usage. The Java way has made listboxes very difficult to use Returns selected indices Indices are often required since the renderer may modify the values displayed



1219
1220
1221
# File 'lib/rbcurse/extras/widgets/rlistbox.rb', line 1219

def get_selected_indices
  @list_selection_model.get_selected_indices
end

#get_selected_valuesObject Also known as: selected_values

Returns selected values



1225
1226
1227
1228
1229
# File 'lib/rbcurse/extras/widgets/rlistbox.rb', line 1225

def get_selected_values
  selected = []
  @list_selection_model.get_selected_indices.each { |i| selected << list_data_model[i] }
  return selected
end

#get_windowObject



719
720
721
# File 'lib/rbcurse/extras/widgets/rlistbox.rb', line 719

def get_window
  @graphic # 2010-01-04 12:37 BUFFERED
end

#getvalueObject

END FOR scrollable ### override widgets text returns indices of selected rows



725
726
727
# File 'lib/rbcurse/extras/widgets/rlistbox.rb', line 725

def getvalue
  selected_rows
end

#handle_key(ch) ⇒ Object

Listbox



729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
# File 'lib/rbcurse/extras/widgets/rlistbox.rb', line 729

def handle_key(ch)
  @current_index ||= 0
  @toprow ||= 0
  h = scrollatrow()
  rc = row_count
  $log.debug " listbox got ch #{ch}"
  # not sure we should do something like this
  #  If editing is happening don't use space for selection, let it be using in editing
  #  and make 0 (ctrl-space) the selector. If no editing, let space be selector
  if ch == 32
    if @KEY_ROW_SELECTOR == 32
      if @is_editing
        key_row_selector = 0 # Ctrl-Space
      else
        key_row_selector = 32
      end
    end
  end
  case ch
  when KEY_UP  # show previous value
    return previous_row
  when KEY_DOWN  # show previous value
    return next_row
  when key_row_selector # 32
    return if is_popup && @selection_mode != :multiple # not allowing select this way since there will be a difference 
    toggle_row_selection @current_index #, @current_index
    @repaint_required = true
  when @KEY_GOTO_TOP # 48, ?\C-[
    # please note that C-[ gives 27, same as esc so will respond after ages
    goto_top
  when @KEY_GOTO_BOTTOM # ?\C-]
    goto_bottom
  when @KEY_NEXT_SELECTION # ?'
    $log.debug "insdie next selection"
    @oldrow = @current_index
    do_next_selection 
    bounds_check
  when @KEY_PREV_SELECTION # ?"
    @oldrow = @current_index
    $log.debug "insdie prev selection"
    do_prev_selection 
    bounds_check
  when @KEY_CLEAR_SELECTION
    clear_selection 
    @repaint_required = true
  when 3, ?\C-c.getbyte(0), ?\C-g.getbyte(0)
    editing_canceled @current_index if @cell_editing_allowed
    cancel_block # block
    @repaint_required = true
    $multiplier = 0
  when 27, 2727
    # ESC or ESC ESC completes edit along with toggle key
    if @is_editing
      editing_completed @current_index
    end
    @repaint_required = true
  when @edit_toggle_key
    # I am currently allowing this to work even if edit_toggle is false
    # You can check for edit_toggle here, if you don't like this behaviour
    @is_editing = !@is_editing
    if !@is_editing
      editing_completed @current_index
    end
    @repaint_required = true
  when @KEY_ASK_FIND_FORWARD
  # ask_search_forward
  when @KEY_ASK_FIND_BACKWARD
  # ask_search_backward
  when @KEY_FIND_NEXT
  # find_next
  when @KEY_FIND_PREV
  # find_prev
  when @KEY_ASK_FIND
    ask_search
  when @KEY_FIND_MORE
    find_more
  when @KEY_BLOCK_SELECTOR
    mark_block #selection
  #when ?\C-u.getbyte(0)
    # multiplier. Series is 4 16 64
    # TESTING @multiplier = (@multiplier == 0 ? 4 : @multiplier *= 4)
  #  return 0
  when ?\C-c.getbyte(0)
    @multiplier = 0
    return 0
  else
    # this has to be fixed, if compo does not handle key it has to continue into next part FIXME
    ret = :UNHANDLED # changed on 2009-01-27 13:14 not going into unhandled, tab not released
    if @cell_editing_allowed
      #if !@edit_toggle || (@edit_toggle && @is_editing)
      if @is_editing
        @repaint_required = true
        # hack - on_enter_row should fire when this widget gets focus. first row that is DONE
        begin
          ret = @cell_editor.component.handle_key(ch)
        rescue
          on_enter_row @current_index
          ret = @cell_editor.component.handle_key(ch)
        end
      end
    end
    if ret == :UNHANDLED
      # beware one-key eats up numbers. we'll be wondering why
      if @one_key_selection
        case ch
        #when ?A.getbyte(0)..?Z.getbyte(0), ?a.getbyte(0)..?z.getbyte(0), ?0.getbyte(0)..?9.getbyte(0)
        when ?A.getbyte(0)..?Z.getbyte(0), ?a.getbyte(0)..?z.getbyte(0)
          # simple motion, key press defines motion
          ret = set_selection_for_char ch.chr
        else
          ret = process_key ch, self
  $log.debug "111 listbox #{@current_index} "
          @multiplier = 0
          return :UNHANDLED if ret == :UNHANDLED
        end
      else
        # no motion on single key, we can freak out like in vim, pref f <char> for set_selection
        case ch
        when ?0.getbyte(0)..?9.getbyte(0)
          $multiplier *= 10 ; $multiplier += (ch-48)
          #$log.debug " setting mult to #{$multiplier} in list "
          return 0
        end
        ret = process_key ch, self
        return :UNHANDLED if ret == :UNHANDLED
      end
    end
  end
  $multiplier = 0
end

#init_actionsObject



1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
# File 'lib/rbcurse/extras/widgets/rlistbox.rb', line 1233

def init_actions
  return if @actions_added
  @actions_added = true
  am = action_manager()
  am.add_action(Action.new("&One Key Selection toggle ") { @one_key_selection = !@one_key_selection} )
  am.add_action(Action.new("&Edit Toggle") { @edit_toggle = !@edit_toggle; $status_message.value = "Edit toggle is #{@edit_toggle}" })

  # this is a little more involved due to list selection model
  am.add_action(Action.new("&Disable selection") { @selection_mode = :none; unbind_key(32); bind_key(32, :scroll_forward); })
end

#init_varsObject

this is called several times, from constructor and when list data changed, so only put relevant resets here. why can’t current_index be set to 0 here



492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
# File 'lib/rbcurse/extras/widgets/rlistbox.rb', line 492

def init_vars
  @repaint_required = true
  @toprow = @pcol = 0

  @row_offset = @col_offset = 0 if @suppress_borders
  if @show_selector
    @row_selected_symbol ||= '>'
    @row_unselected_symbol ||= ' '
    @left_margin ||= @row_selected_symbol.length
  end
  @left_margin ||= 0
  # we reduce internal_width from width while printing
  @internal_width = 2 # taking into account borders accounting for 2 cols
  @internal_width = 0 if @suppress_borders # should it be 0 ???

  # toggle editable state using ENTER key
  @edit_toggle = false
  @edit_toggle_key = ?\C-e.getbyte(0)
  # has editing started using edit_toggle
  @is_editing = !@edit_toggle
  map_keys unless @keys_mapped # moved here so users can remap

end

#list(*val) ⇒ ListDataModel

provide data to List in the form of an Array or Variable or ListDataModel. This will create a default ListSelectionModel.

CHANGE as on 2010-09-21 12:53: If explicit nil passed then dummy datamodel and selection model created From now on, constructor will call this, so this can always happen.

NOTE: sometimes this can be added much after its painted. Do not expect this to be called from constructor, although that is the usual case. it can be dependent on some other list or tree.

Parameters:

  • data (Array, Variable, ListDataModel)

    to populate list with

Returns:



590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
# File 'lib/rbcurse/extras/widgets/rlistbox.rb', line 590

def list *val
  return @list if val.empty?
  clear_selection if @list # clear previous selections if any
  @default_values = nil if @list # clear previous selections if any
  alist = val[0]
  case alist
  when Array
    if @list
      @list.remove_all
      @list.insert 0, *alist
      @current_index = 0
    else
      @list = RubyCurses::ListDataModel.new(alist)
    end
  when NilClass
    if @list
      @list.remove_all
    else
      @list = RubyCurses::ListDataModel.new(alist)
    end
  when Variable
    @list = RubyCurses::ListDataModel.new(alist.value)
  when RubyCurses::ListDataModel
    @list = alist
  else
    raise ArgumentError, "Listbox list(): do not know how to handle #{alist.class} " 
  end
  # added on 2009-01-13 23:19 since updates are not automatic now
  @list.bind(:LIST_DATA_EVENT) { |e| list_data_changed() }
  create_default_list_selection_model
  @list_selection_model.selection_mode = @tmp_selection_mode if @tmp_selection_mode
  @repaint_required = true
  @list
end

#list_data_changedObject



1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
# File 'lib/rbcurse/extras/widgets/rlistbox.rb', line 1120

def list_data_changed
  if row_count == 0 # added on 2009-02-02 17:13 so cursor not hanging on last row which could be empty
    init_vars
    @current_index = 0
    # I had placed this at some time to get cursor correct. But if this listbox is updated
    # during entry in another field, then this steals the row. e.g. test1.rb 5
    #set_form_row
  end
  @repaint_required = true
end

#list_data_model(ldm = nil) ⇒ Object

populate using a custom data model NOTE: This explicilty overwrites any existing datamodel such as the default one. You may lose any events you have bound to the listbox prior to this call.



640
641
642
643
644
645
646
647
# File 'lib/rbcurse/extras/widgets/rlistbox.rb', line 640

def list_data_model ldm=nil
  return @list if ldm.nil?
  raise "Expecting list_data_model" unless ldm.is_a? RubyCurses::ListDataModel
  @list = ldm
  # added on 2009-01-13 23:19 since updates are not automatic now
  @list.bind(:LIST_DATA_EVENT) { |e| list_data_changed() }
  create_default_list_selection_model
end

#list_variable(alist = nil) ⇒ Object

populate using a Variable which should contain a list NOTE: This explicilty overwrites any existing datamodel such as the default one. You may lose any events you have bound to the listbox prior to this call.



628
629
630
631
632
633
634
# File 'lib/rbcurse/extras/widgets/rlistbox.rb', line 628

def list_variable alist=nil
  return @list if alist.nil?
  @list = RubyCurses::ListDataModel.new(alist.value)
  # added on 2009-01-13 23:19 since updates are not automatic now
  @list.bind(:LIST_DATA_EVENT) { |e| list_data_changed() }
  create_default_list_selection_model
end

#map_keysObject



515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
# File 'lib/rbcurse/extras/widgets/rlistbox.rb', line 515

def map_keys
  return if @keys_mapped
  bind_key(?f, 'next row starting with ...'){ ask_selection_for_char() }
  bind_key(?\M-v, 'one key toggle'){ @one_key_selection = !@one_key_selection }
  bind_key(?j, 'next row'){ next_row() }
  bind_key(?k, 'prev row'){ previous_row() }
  bind_key(?\C-n, 'next row'){ next_row() }
  bind_key(?\C-p, 'prev row'){ previous_row() }
  bind_key(?\C-d, :scroll_forward)
  bind_key(?\C-b, :scroll_backward)
  bind_key(?G, 'goto bottom'){ goto_bottom() }
  bind_key([?g,?g], 'goto top'){ goto_top() }
  bind_key(?\M-<, :goto_top )
  bind_key(?\M->, :goto_bottom )
  bind_key(?/, 'find'){ ask_search() }
  bind_key(?n, 'find next'){ find_more() }
  #bind_key(32){ toggle_row_selection() } # some guys may want another selector
  if @cell_editing_allowed && @KEY_ROW_SELECTOR == 32
    @KEY_ROW_SELECTOR = 0 # Ctrl-Space
  end
  bind_key(@KEY_ROW_SELECTOR, 'select a row'){ toggle_row_selection() }
  bind_key(10, 'fire action'){ fire_action_event }
  # I had commented this but we need ENTER for cases like a directory browser where ENTER opens dir
  bind_key(KEY_ENTER, 'fire action'){ fire_action_event }
  #bind_key(13){ @is_editing = !@is_editing }
  #bind_key(?\M-: ,  :_show_menu)
  @keys_mapped = true
end

#mark_blockObject

end experimental selection of multiple rows via block specify a block start and then a block end usage: bind mark_selection to a key. It works as a toggle. C-c will cancel any selection that has begun. added on 2009-02-19 22:37



1159
1160
1161
1162
1163
1164
1165
1166
1167
# File 'lib/rbcurse/extras/widgets/rlistbox.rb', line 1159

def mark_block #selection
  if @inside_block
    @inside_block = false
    end_block #selection
  else
    @inside_block = true
    start_block #selection
  end
end

#on_enterBoolean

please check for error before proceeding

Returns:

  • (Boolean)

    false if no data



900
901
902
903
904
905
906
907
908
909
910
911
912
913
# File 'lib/rbcurse/extras/widgets/rlistbox.rb', line 900

def on_enter
  if @list.nil? || @list.size == 0
    #Ncurses.beep
    get_window.ungetch($current_key) # 2011-10-4 push key back so form can go next
    return :UNHANDLED
  end
  on_enter_row @current_index
  set_form_row # added 2009-01-11 23:41 
  #$log.debug " ONE ENTER LIST #{@current_index}, #{@form.row}"
  @repaint_required = true
  super
  #fire_handler :ENTER, self
  true
end

#on_enter_row(arow) ⇒ Object



914
915
916
917
918
919
920
921
# File 'lib/rbcurse/extras/widgets/rlistbox.rb', line 914

def on_enter_row arow
  #$log.debug " Listbox #{self} ENTER_ROW with curr #{@current_index}. row: #{arow} H: #{@handler.keys}"
  #fire_handler :ENTER_ROW, arow
  fire_handler :ENTER_ROW, self
  @list.on_enter_row self  ## XXX WHY THIS ???
  edit_row_at arow
  @repaint_required = true
end

#on_leave_row(arow) ⇒ Object

set original value so we can cancel set row and col, set value and other things, color and bgcolor



952
953
954
955
956
957
958
# File 'lib/rbcurse/extras/widgets/rlistbox.rb', line 952

def on_leave_row arow
  #$log.debug " Listbox #{self} leave with (cr: #{@current_index}) #{arow}: list[row]:#{@list[arow]}"
  #$log.debug " Listbox #{self} leave with (cr: #{@current_index}) #{arow}: "
  #fire_handler :LEAVE_ROW, arow
  fire_handler :LEAVE_ROW, self
  editing_completed arow
end

#prepare_editor(editor, row) ⇒ Object

private



931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
# File 'lib/rbcurse/extras/widgets/rlistbox.rb', line 931

def prepare_editor editor, row
  r,c = rowcol
  value =  @list[row] # .chomp
  value = value.dup rescue value # so we can cancel
  row = r + (row - @toprow) #  @form.row
  col = c+@left_margin # @form.col
  # unfortunately 2009-01-11 19:47 combo boxes editable allows changing value
  editor.prepare_editor self, row, col, value
  editor.component.curpos = 0 # reset it after search, if user scrols down
  #editor.component.graphic = @graphic #  2010-01-05 00:36 TRYING OUT BUFFERED
  ## override is required if the listbox uses a buffer
  #if @should_create_buffer # removed on 2011-09-29 
    #$log.debug " overriding editors comp with GRAPHIC #{@graphic} "
    #editor.component.override_graphic(@graphic) #  2010-01-05 00:36 TRYING OUT BUFFERED
  #end
  set_form_col 0 #@left_margin

  # set original value so we can cancel
  # set row and col,
  # set value and other things, color and bgcolor
end


686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
# File 'lib/rbcurse/extras/widgets/rlistbox.rb', line 686

def print_borders
  raise "Width not supplied" unless @width
  raise "Height not supplied" unless @height
  width = @width
  height = @height-1 # 2010-01-04 15:30 BUFFERED HEIGHT
  window = @graphic  # 2010-01-04 12:37 BUFFERED
  startcol = @col 
  startrow = @row 
  @color_pair = get_color($datacolor)
#      bordercolor = @border_color || $datacolor # changed 2011 dts  
  bordercolor = @border_color || @color_pair
  borderatt = @border_attrib || Ncurses::A_NORMAL

  #$log.debug "rlistb #{name}: window.print_border #{startrow}, #{startcol} , h:#{height}, w:#{width} , @color_pair, @attr "
  window.print_border startrow, startcol, height, width, bordercolor, borderatt
  print_title
end


703
704
705
706
707
708
709
710
711
712
# File 'lib/rbcurse/extras/widgets/rlistbox.rb', line 703

def print_title
  @color_pair ||= get_color($datacolor)
  # check title.length and truncate if exceeds width
  return unless @title
  _title = @title
  if @title.length > @width - 2
    _title = @title[0..@width-2]
  end
  @graphic.printstring( @row, @col+(@width-_title.length)/2, _title, @color_pair, @title_attrib) unless @title.nil?
end

#repaintObject

this method chops the data to length before giving it to the renderer, this can cause problems if the renderer does some processing. also, it pans the data horizontally giving the renderer a section of it.



1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
# File 'lib/rbcurse/extras/widgets/rlistbox.rb', line 1009

def repaint
  return unless @repaint_required
  return if (@height || 0) < 3 || @width == 0
  # not sure where to put this, once for all or repeat 2010-02-17 23:07 RFED16
  my_win = @form ? @form.window : @target_window
  @graphic = my_win unless @graphic
  raise " #{@name} neither form, nor target window given LB paint " unless my_win
  raise " #{@name} NO GRAPHIC set as yet                 LB paint " unless @graphic

  #$log.debug "rlistbox repaint  #{@name} r,c, #{@row} #{@col} , width: #{@width}  "
  print_borders unless @suppress_borders # do this once only, unless everything changes
  #maxlen = @maxlen || @width-@internal_width
  renderer = cell_renderer()
  renderer.display_length(@width-@internal_width-@left_margin) # just in case resizing of listbox
  tm = list()
  rc = row_count
  @longest_line = @width
  $log.debug " rlistbox repaint #{row_count} #{name} "
  if rc > 0     # just added in case no data passed
    tr = @toprow
    acolor = get_color $datacolor # should be set once, if color or bgcolor changs TODO FIXME
    h = scrollatrow()
    r,c = rowcol
    0.upto(h) do |hh|
      crow = tr+hh
      if crow < rc
        _focussed = @current_index == crow ? true : false  # row focussed ?
        focus_type = _focussed 
        # added 2010-09-02 14:39 so inactive fields don't show a bright focussed line
        #focussed = false if focussed && !@focussed
        focus_type = :SOFT_FOCUS if _focussed && !@focussed
        selected = is_row_selected crow
        content = tm[crow]   # 2009-01-17 18:37 chomp giving error in some cases says frozen
        content = convert_value_to_text content, crow # 2010-09-23 20:12 
        # by now it has to be a String
        if content.is_a? String
          content = content.dup
          sanitize content if @sanitization_required
          truncate content
        end
        ## set the selector symbol if requested
        selection_symbol = ''
        if @show_selector
          if selected
            selection_symbol = @row_selected_symbol
          else
            selection_symbol =  @row_unselected_symbol
          end
          @graphic.printstring r+hh, c, selection_symbol, acolor,@attr
        end
        #renderer = get_default_cell_renderer_for_class content.class.to_s
        renderer.repaint @graphic, r+hh, c+@left_margin, crow, content, focus_type, selected
      else
        # clear rows
        @graphic.printstring r+hh, c, " " * (@width-@internal_width), acolor,@attr
      end
    end
    if @cell_editing_allowed && @is_editing
      @cell_editor.component.repaint unless @cell_editor.nil? or @cell_editor.component.form.nil?
    end
  end # rc == 0
  #@table_changed = false
  @repaint_required = false
end

#row_countObject



564
565
566
567
# File 'lib/rbcurse/extras/widgets/rlistbox.rb', line 564

def row_count
  return 0 if @list.nil?
  @list.length
end

#sanitize(content) ⇒ Object

takes a block, this way anyone extending this class can just pass a block to do his job This modifies the string



1089
1090
1091
1092
1093
1094
1095
1096
1097
# File 'lib/rbcurse/extras/widgets/rlistbox.rb', line 1089

def sanitize content
  if content.is_a? String
    content.chomp!
    content.gsub!(/[\t\n\r]/, '  ') # don't display tab
    content.gsub!(/[^[:print:]]/, '')  # don't display non print characters
  else
    content
  end
end

#scrollatrowObject

added 2009-01-07 13:05 so new scrollable can use



569
570
571
572
573
574
575
# File 'lib/rbcurse/extras/widgets/rlistbox.rb', line 569

def scrollatrow
  if @suppress_borders
    return @height - 1
  else
    return @height - 3
  end
end

#select_default_valuesObject

XXX can this not be done at repaint



679
680
681
682
683
684
685
# File 'lib/rbcurse/extras/widgets/rlistbox.rb', line 679

def select_default_values
  return if @default_values.nil?
  @default_values.each do |val|
    row = @list.index val
    add_row_selection_interval row, row unless row.nil?
  end
end

#selection_mode(*val) ⇒ Object

getter and setter for selection_mode Must be called after creating model, so no duplicate. Since one may set in model directly.



547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
# File 'lib/rbcurse/extras/widgets/rlistbox.rb', line 547

def selection_mode(*val)
  #raise "ListSelectionModel not yet created!" if @list_selection_model.nil?

  if val.empty?
    if @list_selection_model
      return @list_selection_model.selection_mode
    else
      return @tmp_selection_mode
    end
  else
    if @list_selection_model.nil?
      @tmp_selection_mode = val[0] 
    else
      @list_selection_model.selection_mode = val[0].to_sym
    end
  end
end

#set_buffering(params) ⇒ Object

2010-02-18 11:40 TRYING OUT - canceling editing if resized otherwise drawing errors can occur the earlier painted edited comp in yellow keeps showing until a key is pressed



1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
# File 'lib/rbcurse/extras/widgets/rlistbox.rb', line 1193

def set_buffering params
  $log.warn "CALLED set_buffering in LISTBOX listbox " if $log.debug? 
  super # removed from widget 2011-09-29 
  ## Ensuring that changes to top get reflect in editing comp
  #+ otherwise it raises an exception. Still the earlier cell_edit is being
  #+ printed where it was , until a key is moved
  # FIXME - do same for col
  if @cell_editor
    r,c = rowcol
    if @cell_editor.component.row < @row_offset + @buffer_params[:screen_top]
      @cell_editor.component.row = @row_offset +  @buffer_params[:screen_top]
    end
    # TODO next block to be tested by placing a listbox in right split of vertical
    if @cell_editor.component.col < @col_offset + @buffer_params[:screen_left]
      @cell_editor.component.col = @col_offset +  @buffer_params[:screen_left]
    end
    #editing_canceled @current_index if @cell_editing_allowed and @cell_editor
  end
  #set_form_row
  @repaint_required = true
end

#set_form_col(col1 = 0) ⇒ Object

set cursor column position if i set col1 to @curpos, i can move around left right if key mapped



1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
# File 'lib/rbcurse/extras/widgets/rlistbox.rb', line 1132

def set_form_col col1=0
  # TODO BUFFERED use setrowcol @form.row, col
  # TODO BUFFERED use cols_panned
  #col1 ||= 0
  @cols_panned ||= 0 # RFED16 2010-02-17 23:40 
  # editable listboxes will involve changing cursor and the form issue
  ## added win_col on 2010-01-04 23:28 for embedded forms BUFFERED TRYING OUT
  #[email protected]
  win_col = 0 # 2010-02-17 23:19 RFED16
  #col = win_col + @orig_col + @col_offset + @curpos + @form.cols_panned
  col2 = win_col + @col + @col_offset + col1 + @cols_panned + @left_margin
  $log.debug " set_form_col in rlistbox #{@col}+ left_margin #{@left_margin} ( #{col2} ) "
  #super col+@left_margin
  #@form.setrowcol @form.row, col2   # added 2009-12-29 18:50 BUFFERED
  setrowcol nil, col2 # 2010-02-17 23:19 RFED16
end

#start_blockObject

sets marker for start of block added on 2009-02-19 22:37



1175
1176
1177
1178
# File 'lib/rbcurse/extras/widgets/rlistbox.rb', line 1175

def start_block #selection
  @first_index = @current_index
  @last_index = nil
end

#truncate(content) ⇒ Object

returns only the visible portion of string taking into account display length and horizontal scrolling. MODIFIES STRING if you;ve truncated the data, it could stay truncated even if lb is increased. be careful FIXME if _maxlen becomes 0 then maxlen -1 will print whole string again



1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
# File 'lib/rbcurse/extras/widgets/rlistbox.rb', line 1102

def truncate content
  _maxlen = @maxlen || @width-@internal_width
  _maxlen = @width-@internal_width if _maxlen > @width-@internal_width
  #$log.debug "TRUNCATE: listbox maxlen #{@maxlen}, #{_maxlen} width #{@width}: #{content} "
  if !content.nil? 
    if content.length > _maxlen # only show maxlen
      @longest_line = content.length if content.length > @longest_line
      #content = content[@pcol..@pcol+maxlen-1] 
      content.replace content[@pcol..@pcol+_maxlen-1] 
    else
      # can this be avoided if pcol is 0 XXX
      content.replace content[@pcol..-1] if @pcol > 0
    end
  end
  #$log.debug " content: #{content}" 
  content
end