Class: RubyCurses::TableWidget

Inherits:
TextPad
  • Object
show all
Defined in:
lib/rbcurse/experimental/widgets/tablewidget.rb

Overview

If we make a pad of the whole thing then the columns will also go out when scrolling So then there’s no point storing columns separately. Might as well keep in content so scrolling works fine, otherwise textpad will have issues scrolling. Making a pad of the content but not column header complicates stuff, do we make a pad of that, or print it like the old thing.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

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

Returns a new instance of TableWidget.



369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
# File 'lib/rbcurse/experimental/widgets/tablewidget.rb', line 369

def initialize form = nil, config={}, &block

  # hash of column info objects, for some reason a hash and not an array
  @chash = []
  # chash should be an array which is basically the order of rows to be printed
  #  it contains index, which is the offset of the row in the data @content
  #  When printing we should loop through chash and get the index in data
  #
  # should be zero here, but then we won't get textpad correct
  @_header_adjustment = 0 #1
  @col_min_width = 3

  super
  bind_key(?w, "next column") { self.next_column }
  bind_key(?b, "prev column") { self.prev_column }
  bind_key(?-, "contract column") { self.contract_column }
  bind_key(?+, "expand column") { self.expand_column }
  bind_key(?=, "expand column to width") { self.expand_column_to_width }
  bind_key(?\M-=, "expand column to width") { self.expand_column_to_max_width }
end

Instance Attribute Details

#table_row_sorterObject

attr_reader :columns



367
368
369
# File 'lib/rbcurse/experimental/widgets/tablewidget.rb', line 367

def table_row_sorter
  @table_row_sorter
end

Instance Method Details

#_calculate_column_offsetsObject

This calculates and stores the offset at which each column starts. Used when going to next column or doing a find for a string in the table. TODO store this inside the hash so it’s not calculated again in renderer



427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
# File 'lib/rbcurse/experimental/widgets/tablewidget.rb', line 427

def _calculate_column_offsets
  @coffsets = []
  total = 0

  #@chash.each_pair { |i, c| 
  #@chash.each_with_index { |c, i| 
    #next if c.hidden
  each_column {|c,i|
    w = c.width
    @coffsets[i] = total
    c.offset = total
    # if you use prepare_format then use w+2 due to separator symbol
    total += w + 1
  }
end

#_convert_curpos_to_columnFixnum

Convert current cursor position to a table column calculate column based on curpos since user may not have user w and b keys (:next_column)

Returns:

  • (Fixnum)

    column index base 0



446
447
448
449
450
451
452
453
454
455
456
457
458
# File 'lib/rbcurse/experimental/widgets/tablewidget.rb', line 446

def _convert_curpos_to_column  #:nodoc:
  _calculate_column_offsets unless @coffsets
  x = 0
  @coffsets.each_with_index { |i, ix| 
    if @curpos < i 
      break
    else 
      x += 1
    end
  }
  x -= 1 # since we start offsets with 0, so first auto becoming 1
  return x
end

#_init_model(array) ⇒ Object

size each column based on widths of this row of data. Only changed width if no width for that column



554
555
556
557
558
559
560
561
562
# File 'lib/rbcurse/experimental/widgets/tablewidget.rb', line 554

def _init_model array
  array.each_with_index { |c,i| 
    # if columns added later we could be overwriting the width
    c = get_column(i)
    c.width ||= 10
  }
  # maintains index in current pointer and gives next or prev
  @column_pointer = Circular.new array.size()-1
end

#_invalidate_width_cacheObject



637
638
639
# File 'lib/rbcurse/experimental/widgets/tablewidget.rb', line 637

def _invalidate_width_cache    #:nodoc:
  @coffsets = nil
end

#add(array) ⇒ Object Also known as: <<

add a row to the table



599
600
601
602
603
604
605
606
607
608
# File 'lib/rbcurse/experimental/widgets/tablewidget.rb', line 599

def add array
  unless @content
    # columns were not added, this most likely is the title
    @content ||= []
    _init_model array
  end
  @content << array
  fire_dimension_changed
  self
end

#add_column(tc) ⇒ Object

tmce = TableColumnModelEvent.new(ix, newix, self, :MOVE) fire_handler :TABLE_COLUMN_MODEL_EVENT, tmce



650
651
652
653
# File 'lib/rbcurse/experimental/widgets/tablewidget.rb', line 650

def add_column tc
  raise "to figure out add_column"
  _invalidate_width_cache
end

#calculate_column_width(col, maxrows = 99) ⇒ Object



658
659
660
661
662
663
664
665
666
667
668
669
670
671
# File 'lib/rbcurse/experimental/widgets/tablewidget.rb', line 658

def calculate_column_width col, maxrows=99
  ret = 3
  ctr = 0
  @content.each_with_index { |r, i| 
    #next if i < @toprow # this is also a possibility, it checks visible rows
    break if ctr > maxrows
    ctr += 1
    #next if r == :separator
    c = r[col]
    x = c.to_s.length
    ret = x if x > ret
  }
  ret
end

#clear_matchesObject

return @indices



767
768
769
770
771
772
773
774
# File 'lib/rbcurse/experimental/widgets/tablewidget.rb', line 767

def clear_matches
  # clear previous match so all data can show again
  if @indices && @indices.count > 0
    fire_dimension_changed
    init_vars
  end
  @indices = nil
end

#column_align(colindex, align) ⇒ Object

convenience method to set alignment of a column

Parameters:

  • index

    of column

  • align
    • :right (any other value is taken to be left)



626
627
628
# File 'lib/rbcurse/experimental/widgets/tablewidget.rb', line 626

def column_align colindex, align
  get_column(colindex).align = align
end

#column_hidden(colindex, hidden) ⇒ Object

convenience method to hide or unhide a column Provided since column offsets need to be recalculated in the case of a width change or visibility change



632
633
634
635
# File 'lib/rbcurse/experimental/widgets/tablewidget.rb', line 632

def column_hidden colindex, hidden
  get_column(colindex).hidden = hidden
  _invalidate_width_cache
end

#column_modelObject

returns collection of ColumnInfo objects



404
405
406
# File 'lib/rbcurse/experimental/widgets/tablewidget.rb', line 404

def column_model
  @chash
end

#column_width(colindex, width) ⇒ Object

convenience method to set width of a column For setting other attributes, use get_column(index)

Parameters:

  • index

    of column

  • width


619
620
621
622
# File 'lib/rbcurse/experimental/widgets/tablewidget.rb', line 619

def column_width colindex, width
  get_column(colindex).width = width
  _invalidate_width_cache
end

#columnsObject

returns array of column names as Strings



548
549
550
# File 'lib/rbcurse/experimental/widgets/tablewidget.rb', line 548

def columns
  @content[0]
end

#columns=(array) ⇒ Object Also known as: headings=

Set column titles with given array of strings. NOTE: This is only required to be called if first row of file or content does not contain titles. In that case, this should be called before setting the data as the array passed is appended into the content array.



531
532
533
534
535
536
537
538
539
540
541
542
543
544
# File 'lib/rbcurse/experimental/widgets/tablewidget.rb', line 531

def columns=(array)
  @_header_adjustment = 1
  # I am eschewing using a separate field for columns. This is simpler for textpad.
  # We always assume first row is columns.
  #@columns = array
  # should we just clear column, otherwise there's no way to set the whole thing with new data
  # but then if we need to change columns what do it do, on moving or hiding a column ?
  # Maybe we need a separate clear method or remove_all TODO
  @content ||= []
  @content << array
  # This needs to go elsewhere since this method will not be called if file contains
  # column titles as first row.
  _init_model array
end

#content_colsObject

calculate pad width based on widths of columns



409
410
411
412
413
414
415
416
417
418
419
420
# File 'lib/rbcurse/experimental/widgets/tablewidget.rb', line 409

def content_cols
  total = 0
  #@chash.each_pair { |i, c| 
  #@chash.each_with_index { |c, i| 
    #next if c.hidden
  each_column {|c,i|
    w = c.width
    # if you use prepare_format then use w+2 due to separator symbol
    total += w + 1
  }
  return total
end

#contract_columnObject



506
507
508
509
510
511
512
513
# File 'lib/rbcurse/experimental/widgets/tablewidget.rb', line 506

def contract_column
  x = _convert_curpos_to_column
  w = get_column(x).width 
  return if w <= @col_min_width
  column_width x, w-1 if w
  @coffsets = nil
  fire_dimension_changed
end

#create_default_sorterObject



690
691
692
693
# File 'lib/rbcurse/experimental/widgets/tablewidget.rb', line 690

def create_default_sorter
  raise "Data not sent in." unless @content
  @table_row_sorter = DefaultTableRowSorter.new @content
end

#delete_at(ix) ⇒ Object



609
610
611
612
613
# File 'lib/rbcurse/experimental/widgets/tablewidget.rb', line 609

def delete_at ix
  return unless @content
  fire_dimension_changed
  @content.delete_at ix
end

#each_columnObject

yields non-hidden columns (ColumnInfo) and the offset/index This is the order in which columns are to be printed



789
790
791
792
793
794
# File 'lib/rbcurse/experimental/widgets/tablewidget.rb', line 789

def each_column
  @chash.each_with_index { |c, i| 
    next if c.hidden
    yield c,i if block_given?
  }
end

#ensure_visible(row = @current_index) ⇒ Object

Ensure current row is visible, if not make it first row

This overrides textpad due to header_adjustment, otherwise
during next_match, the header overrides the found row.

Parameters:

  • current_index (default if not given)


781
782
783
784
785
# File 'lib/rbcurse/experimental/widgets/tablewidget.rb', line 781

def ensure_visible row = @current_index
  unless is_visible? row
      @prow = @current_index - @_header_adjustment
  end
end

#expand_columnObject



481
482
483
484
485
486
487
# File 'lib/rbcurse/experimental/widgets/tablewidget.rb', line 481

def expand_column
  x = _convert_curpos_to_column
  w = get_column(x).width
  column_width x, w+1 if w
  @coffsets = nil
  fire_dimension_changed
end

#expand_column_to_max_widthObject

find the width of the longest item in the current columns and expand the width to that.



501
502
503
504
505
# File 'lib/rbcurse/experimental/widgets/tablewidget.rb', line 501

def expand_column_to_max_width
  x = _convert_curpos_to_column
  w = calculate_column_width x
  expand_column_to_width w
end

#expand_column_to_width(w = nil) ⇒ Object



488
489
490
491
492
493
494
495
496
497
498
# File 'lib/rbcurse/experimental/widgets/tablewidget.rb', line 488

def expand_column_to_width w=nil
  x = _convert_curpos_to_column
  unless w
    # expand to width of current cell
    s = @content[@current_index][x]
    w = s.to_s.length + 1
  end
  column_width x, w
  @coffsets = nil
  fire_dimension_changed
end

#fire_action_eventObject



698
699
700
701
702
703
704
705
706
707
708
709
710
711
# File 'lib/rbcurse/experimental/widgets/tablewidget.rb', line 698

def fire_action_event
  if header_row?
    if @table_row_sorter
      x = _convert_curpos_to_column
      c = @chash[x]
      # convert to index in data model since sorter only has data_model
      index = c.index
      @table_row_sorter.toggle_sort_order index
      @table_row_sorter.sort
      fire_dimension_changed
    end
  end
  super
end

#get_column(index) ⇒ Object

retrieve the column info structure for the given offset. The offset pertains to the visible offset not actual offset in data model. These two differ when we move a column.

Returns:

  • ColumnInfo object containing width align color bgcolor attrib hidden



394
395
396
397
398
399
400
401
# File 'lib/rbcurse/experimental/widgets/tablewidget.rb', line 394

def get_column index
  return @chash[index] if @chash[index]
  # create a new entry since none present
  c = ColumnInfo.new
  c.index = index
  @chash[index] = c
  return c
end

#header_row?Boolean

Returns:

  • (Boolean)


694
695
696
# File 'lib/rbcurse/experimental/widgets/tablewidget.rb', line 694

def header_row?
  @prow == 0
end

#matching_indicesObject

yields each column to caller method for true returned, collects index of row into array and returns the array Value yielded can be fixnum or date etc



748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
# File 'lib/rbcurse/experimental/widgets/tablewidget.rb', line 748

def matching_indices 
  raise "block required for matching_indices" unless block_given?
  @indices = []
  ## content can be string or Chunkline, so we had to write <tt>index</tt> for this.
  @content.each_with_index do |fields, ix|
    flag = yield ix, fields
    if flag
      @indices << ix 
    end
  end
  $log.debug "XXX:  INDICES found #{@indices}"
  if @indices.count > 0
    fire_dimension_changed
    init_vars
  else
    @indices = nil
  end
  #return @indices
end

#model_row(index) ⇒ Object



563
564
565
566
567
568
569
570
571
572
# File 'lib/rbcurse/experimental/widgets/tablewidget.rb', line 563

def model_row index
  array = @content[index]
  array.each_with_index { |c,i| 
    # if columns added later we could be overwriting the width
    ch = get_column(i)
    ch.width = c.to_s.length + 2
  }
  # maintains index in current pointer and gives next or prev
  @column_pointer = Circular.new array.size()-1
end

#move_column(ix, newix) ⇒ Object

should all this move into table column model or somepn move a column from offset ix to offset newix



643
644
645
646
647
648
649
# File 'lib/rbcurse/experimental/widgets/tablewidget.rb', line 643

def move_column ix, newix
  acol = @chash.delete_at ix 
  @chash.insert newix, acol
  _invalidate_width_cache
  #tmce = TableColumnModelEvent.new(ix, newix, self, :MOVE)
  #fire_handler :TABLE_COLUMN_MODEL_EVENT, tmce
end

#next_columnObject

jump cursor to next column TODO : if cursor goes out of view, then pad should scroll right or left and down



461
462
463
464
465
466
467
468
469
# File 'lib/rbcurse/experimental/widgets/tablewidget.rb', line 461

def next_column
  # TODO take care of multipliers
  _calculate_column_offsets unless @coffsets
  c = @column_pointer.next
  cp = @coffsets[c] 
  #$log.debug " next_column #{c} , #{cp} "
  @curpos = cp if cp
  down() if c < @column_pointer.last_index
end

#next_match(str) ⇒ Object

Find the next row that contains given string Overrides textpad since each line is an array NOTE does not go to next match within row NOTE: FIXME ensure_visible puts prow = current_index so in this case, the header

overwrites the matched row.

Parameters:

  • String

    to find

Returns:

  • row and col offset of match, or nil



720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
# File 'lib/rbcurse/experimental/widgets/tablewidget.rb', line 720

def next_match str
  _calculate_column_offsets unless @coffsets
  first = nil
  ## content can be string or Chunkline, so we had to write <tt>index</tt> for this.
  @content.each_with_index do |fields, ix|
    #col = line.index str
    #fields.each_with_index do |f, jx|
    #@chash.each_with_index do |c, jx|
      #next if c.hidden
    each_column do |c,jx|
      f = fields[c.index]
      # value can be numeric
      col = f.to_s.index str
      if col
        col += @coffsets[jx] 
        first ||= [ ix, col ]
        if ix > @current_index
          return [ix, col]
        end
      end
    end
  end
  return first
end

#padrefreshObject

refresh pad onto window overrides super



675
676
677
678
679
680
681
682
683
684
685
686
687
688
# File 'lib/rbcurse/experimental/widgets/tablewidget.rb', line 675

def padrefresh
  top = @window.top
  left = @window.left
  sr = @startrow + top
  sc = @startcol + left
  # first do header always in first row
  retval = FFI::NCurses.prefresh(@pad,0,@pcol, sr , sc , 2 , @cols+ sc );
  # now print rest of data
  # h is header_adjustment
  h = 1 
  retval = FFI::NCurses.prefresh(@pad,@prow + h,@pcol, sr + h , sc , @rows + sr  , @cols+ sc );
  $log.warn "XXX:  PADREFRESH #{retval}, #{@prow}, #{@pcol}, #{sr}, #{sc}, #{@rows+sr}, #{@cols+sc}." if retval == -1
  # padrefresh can fail if width is greater than NCurses.COLS
end

#prev_columnObject

jump cursor to previous column TODO : if cursor goes out of view, then pad should scroll right or left and down



472
473
474
475
476
477
478
479
480
# File 'lib/rbcurse/experimental/widgets/tablewidget.rb', line 472

def prev_column
  # TODO take care of multipliers
  _calculate_column_offsets unless @coffsets
  c = @column_pointer.previous
  cp = @coffsets[c] 
  #$log.debug " prev #{c} , #{cp} "
  @curpos = cp if cp
  up() if c > @column_pointer.last_index
end

#remove_column(tc) ⇒ Object



654
655
656
657
# File 'lib/rbcurse/experimental/widgets/tablewidget.rb', line 654

def remove_column tc
  raise "to figure out add_column"
  _invalidate_width_cache
end

#render_allObject



795
796
797
798
799
800
801
802
803
804
805
806
# File 'lib/rbcurse/experimental/widgets/tablewidget.rb', line 795

def render_all
  if @indices && @indices.count > 0
    @indices.each_with_index do |ix, jx|
      render @pad, jx, @content[ix]
    end
  else
    @content.each_with_index { |line, ix|
      #FFI::NCurses.mvwaddstr(@pad,ix, 0, @content[ix])
      render @pad, ix, line
    }
  end
end

#renderer(r) ⇒ Object

def method_missing(name, *args) @tp.send(name, *args) end

supply a custom renderer that implements render()

See Also:

  • render


521
522
523
# File 'lib/rbcurse/experimental/widgets/tablewidget.rb', line 521

def renderer r
  @renderer = r
end

#resultset(columns, data) ⇒ Object

set column array and data array in one shot Erases any existing content



587
588
589
590
591
592
593
594
595
# File 'lib/rbcurse/experimental/widgets/tablewidget.rb', line 587

def resultset columns, data
  @content = []
  _init_model columns
  @content << columns
  @_header_adjustment = 1
  
  @content.concat( data)
  fire_dimension_changed
end

#text(lines, fmt = :none) ⇒ Object

insert entire database in one shot WARNING: overwrites columns if put there, should contain columns already as in CSV data

Parameters:

  • lines

    is an array or arrays



578
579
580
581
582
# File 'lib/rbcurse/experimental/widgets/tablewidget.rb', line 578

def text lines, fmt=:none
  _init_model lines[0]
  fire_dimension_changed
  super
end