class Canis::Field

Text edit field NOTE: width is the length of the display whereas maxlen is the maximum size that the value can take. Thus, maxlen can exceed width. Currently, maxlen defaults to width which defaults to 20. NOTE: Use +text(val)+ to set value, and +text()+ to retrieve value

Example

f = Field.new @form, text: "Some value", row: 10, col: 2

Field introduces an event :CHANGE which is fired for each character deleted or inserted TODO: some methods should return self, so chaining can be done. Not sure if the return value of the

fire_handler is being checked.
NOTE: i have just added repain_required check in Field before repaint
this may mean in some places field does not paint. repaint_require will have to be set
to true in those cases. this was since field was overriding a popup window that was not modal.

Attributes

above[RW]

for numeric fields, specify lower or upper limit of entered value

below[RW]

for numeric fields, specify lower or upper limit of entered value

buffer[R]
datatype[RW]
this accesses the field created or passed with set_label

attr_reader :label

this is the class of the field set in +text()+, so value is returned in same class
@example : Integer, Integer, Float
field_col[R]

column on which field printed, usually the same as col unless label used. Required by History to popup field history.

original_value[R]
overwrite_mode[RW]

Public Class Methods

new(form=nil, config={}) click to toggle source

required due to labels. Is updated after printing

# so can be nil if accessed early 2011-12-8
Calls superclass method Canis::Widget::new
# File lib/canis/core/widgets/rwidget.rb, line 2307
def initialize form=nil, config={}, &block
  @form = form
  @buffer = String.new
  #@type=config.fetch("type", :varchar)
  @row = 0
  @col = 0
  @editable = true
  @focusable = true
  #@event_args = {}             # arguments passed at time of binding, to use when firing event
  map_keys 
  init_vars
  register_events(:CHANGE)
  super
  @width ||= 20
  @maxlen ||= @width
end

Public Instance Methods

addcol(num) click to toggle source

add a column to cursor position. Field

# File lib/canis/core/widgets/rwidget.rb, line 2704
def addcol num
  if num < 0
    if @form.col <= @col + @col_offset
     # $log.debug " error trying to cursor back #{@form.col}"
      return -1
    end
  elsif num > 0
    if @form.col >= @col + @col_offset + @width
  #    $log.debug " error trying to cursor forward #{@form.col}"
      return -1
    end
  end
  @form.addcol num
end
chars_allowed(*val)
Alias for: type
cursor_backward() click to toggle source
# File lib/canis/core/widgets/rwidget.rb, line 2664
  def cursor_backward
    if @curpos > 0
      @curpos -= 1
      if @pcol > 0 and @form.col == @col + @col_offset
        @pcol -= 1
      end
      addcol -1
    elsif @pcol > 0 #  added 2008-11-26 23:05
      @pcol -= 1   
    end
 #   $log.debug " crusor back cp:#{@curpos} pcol:#{@pcol} b.l:#{@buffer.length} d_l:#{@display_length} fc:#{@form.col}"
=begin
# this is perfect if not scrolling, but now needs changes
    if @curpos > 0
      @curpos -= 1
      addcol -1
    end
=end
  end
cursor_end() click to toggle source

goto end of field, “end” is a keyword so could not use it.

# File lib/canis/core/widgets/rwidget.rb, line 2618
def cursor_end
  blen = @buffer.rstrip.length
  if blen < @width
    set_form_col blen
  else
    # there is a problem here FIXME.
    @pcol = blen-@width
    #set_form_col @width-1
    set_form_col blen
  end
  @curpos = blen # this is position in array where editing or motion is to happen regardless of what you see
                 # regardless of pcol (panning)
  #  $log.debug " crusor END cp:#{@curpos} pcol:#{@pcol} b.l:#{@buffer.length} d_l:#{@width} fc:#{@form.col}"
  #set_form_col @buffer.length
end
cursor_forward() click to toggle source
# File lib/canis/core/widgets/rwidget.rb, line 2655
def cursor_forward
  if @curpos < @buffer.length 
    if addcol(1)==-1  # go forward if you can, else scroll
      @pcol += 1 if @pcol < @width 
    end
    @curpos += 1
  end
 # $log.debug " crusor FORWARD cp:#{@curpos} pcol:#{@pcol} b.l:#{@buffer.length} d_l:#{@display_length} fc:#{@form.col}"
end
cursor_home() click to toggle source

position cursor at start of field

# File lib/canis/core/widgets/rwidget.rb, line 2611
def cursor_home
  @curpos = 0
  @pcol = 0
  set_form_col 0
end
default(*val)
Alias for: text
delete_at(index=@curpos) click to toggle source
# File lib/canis/core/widgets/rwidget.rb, line 2417
def delete_at index=@curpos
  return -1 if !@editable 
  char = @buffer.slice!(index,1)
  #$log.debug " delete at #{index}: #{@buffer.length}: #{@buffer}"
  @modified = true
  #fire_handler :CHANGE, self    # 2008-12-09 14:51
  fire_handler :CHANGE, InputDataEvent.new(@curpos,@curpos, self, :DELETE, 0, char)     # 2010-09-11 13:01
end
delete_curr_char() click to toggle source
# File lib/canis/core/widgets/rwidget.rb, line 2683
def delete_curr_char
  return -1 unless @editable
  delete_at
  set_modified 
end
delete_eol() click to toggle source
# File lib/canis/core/widgets/rwidget.rb, line 2646
def delete_eol
  return -1 unless @editable
  pos = @curpos-1
  @delete_buffer = @buffer[@curpos..-1]
  # if pos is 0, pos-1 becomes -1, end of line!
  @buffer = pos == -1 ? "" : @buffer[0..pos]
  fire_handler :CHANGE, InputDataEvent.new(@curpos,@curpos+@delete_buffer.length, self, :DELETE, 0, @delete_buffer)
  return @delete_buffer
end
delete_prev_char() click to toggle source
# File lib/canis/core/widgets/rwidget.rb, line 2688
def delete_prev_char
  return -1 if !@editable 
  return if @curpos <= 0
  # if we've panned, then unpan, and don't move cursor back
  # Otherwise, adjust cursor (move cursor back as we delete)
  adjust = true
  if @pcol > 0
    @pcol -= 1
    adjust = false
  end
  @curpos -= 1 if @curpos > 0
  delete_at
  addcol -1 if adjust # move visual cursor back
  set_modified 
end
getvalue() click to toggle source

converts back into original type

changed to convert on 2009-01-06 23:39
# File lib/canis/core/widgets/rwidget.rb, line 2456
def getvalue
  dt = @datatype || String
  case dt.to_s
  when "String"
    return @buffer
  when "Integer"
    return @buffer.to_i
  when "Float"
    return @buffer.to_f
  else
    return @buffer.to_s
  end
end
handle_key(ch) click to toggle source

field

Calls superclass method Canis::Widget#handle_key
# File lib/canis/core/widgets/rwidget.rb, line 2586
def handle_key ch
  @repaint_required = true 
  case ch
  when 32..126
    #$log.debug("FIELD: ch #{ch} ,at #{@curpos}, buffer:[#{@buffer}] bl: #{@buffer.to_s.length}")
    putc ch
  when 27 # cannot bind it
    #text @original_value
    # commented above and changed 2014-05-12 - 20:05 I think above creates positioning issues. TEST XXX
    restore_original_value
  else
    ret = super
    return ret
  end
  0 # 2008-12-16 23:05 without this -1 was going back so no repaint
end
in_range?( val ) click to toggle source

checks field against valid_range, above and below , returning true if it passes set attributes, false if it fails any one.

# File lib/canis/core/widgets/rwidget.rb, line 2752
def in_range?( val )
  val = val.to_i
  (@above.nil? or val > @above) and
    (@below.nil? or val < @below) and
    (@valid_range.nil? or @valid_range.include?(val))
end
init_vars() click to toggle source
# File lib/canis/core/widgets/rwidget.rb, line 2323
def init_vars
  @pcol = 0                    # needed for horiz scrolling
  @curpos = 0                  # current cursor position in buffer
                               # this is the index where characters are put or deleted
  #                            # when user edits
  @modified = false
  @repaint_required = true
end
label(*val) click to toggle source
# File lib/canis/core/widgets/rwidget.rb, line 2500
def label *val
  return @label if val.empty?
  raise "Field does not allow setting of label. Please use LabeledField instead with lcol for label column"
end
map_keys() click to toggle source
# File lib/canis/core/widgets/rwidget.rb, line 2568
def map_keys
  return if @keys_mapped
  bind_key(FFI::NCurses::KEY_LEFT, :cursor_backward )
  bind_key(FFI::NCurses::KEY_RIGHT, :cursor_forward )
  bind_key(FFI::NCurses::KEY_BACKSPACE, :delete_prev_char )
  bind_key(127, :delete_prev_char )
  bind_key(330, :delete_curr_char )
  bind_key(?\C-a, :cursor_home )
  bind_key(?\C-e, :cursor_end )
  bind_key(?\C-k, :delete_eol )
  bind_key(?\C-_, :undo_delete_eol )
  #bind_key(27){ text @original_value }
  bind_key(?\C-g, 'revert'){ text @original_value } # 2011-09-29 V1.3.1 ESC did not work
  @keys_mapped = true
end
modified?() click to toggle source

overriding widget, check for value change

2009-01-18 12:25
# File lib/canis/core/widgets/rwidget.rb, line 2770
def modified?
  getvalue() != @original_value
end
on_enter() click to toggle source

save original value on enter, so we can check for modified. 2009-01-18 12:25

2011-10-9 I have changed to take @buffer since getvalue returns a datatype
and this causes a crash in set_original on cursor forward.
Calls superclass method Canis::Widget#on_enter
# File lib/canis/core/widgets/rwidget.rb, line 2762
def on_enter
  #@original_value = getvalue.dup rescue getvalue
  @original_value = @buffer.dup # getvalue.dup rescue getvalue
  super
end
on_leave() click to toggle source

upon leaving a field returns false if value not valid as per values or valid_regex 2008-12-22 12:40 if null_allowed, don't validate, but do fire_handlers

Calls superclass method Canis::Widget#on_leave
# File lib/canis/core/widgets/rwidget.rb, line 2721
def on_leave
  val = getvalue
  #$log.debug " FIELD ON LEAVE:#{val}. #{@values.inspect}"
  valid = true
  if val.to_s.empty? && @null_allowed
    #$log.debug " empty and null allowed"
  else
    if !@values.nil?
      valid = @values.include? val
      raise FieldValidationException, "Field value (#{val}) not in values: #{@values.join(',')}" unless valid
    end
    if !@valid_regex.nil?
      valid = @valid_regex.match(val.to_s)
      raise FieldValidationException, "Field not matching regex #{@valid_regex}" unless valid
    end
    # added valid_range for numerics 2011-09-29
    if !in_range?(val)
      raise FieldValidationException, "Field not matching range #{@valid_range}, above #{@above} or below #{@below}  "
    end
  end
  # here is where we should set the forms modified to true - 2009-01
  if modified?
    set_modified true
  end
  # if super fails we would have still set modified to true
  super
  #return valid
end
position_label() click to toggle source

FIXME this may not work since i have disabled -1, now i do not set row and col

# File lib/canis/core/widgets/rwidget.rb, line 2505
def position_label
  $log.debug "XXX: LABEL row #{@label.row}, #{@label.col} "
  @label.row  @row unless @label.row #if @label.row == -1
  @label.col  @col-(@label.name.length+1) unless @label.col #if @label.col == -1
  @label.label_for(self) # this line got deleted when we redid stuff !
  $log.debug "   XXX: LABEL row #{@label.row}, #{@label.col} "
end
putc(c) click to toggle source

TODO : sending c>=0 allows control chars to go. Should be >= ?A i think.

# File lib/canis/core/widgets/rwidget.rb, line 2401
def putc c
  if c >= 0 and c <= 127
    ret = putch c.chr
    if ret == 0
      if addcol(1) == -1  # if can't go forward, try scrolling
        # scroll if exceeding display len but less than max len
        if @curpos > @width && @curpos <= @maxlen
          @pcol += 1 if @pcol < @width 
        end
      end
      set_modified 
      return 0 # 2010-09-11 12:59 else would always return -1
    end
  end
  return -1
end
putch(char) click to toggle source

add a char to field, and validate NOTE: this should return self for chaining operations and throw an exception if disabled or exceeding size @param [char] a character to add @return [Integer] 0 if okay, -1 if not editable or exceeding length

# File lib/canis/core/widgets/rwidget.rb, line 2370
def putch char
  return -1 if !@editable 
  return -1 if !@overwrite_mode && (@buffer.length >= @maxlen)
  blen = @buffer.length
  if @chars_allowed != nil
    return if char.match(@chars_allowed).nil?
  end
  # added insert or overwrite mode 2010-03-17 20:11
  oldchar = nil
  if @overwrite_mode
    oldchar = @buffer[@curpos] 
    @buffer[@curpos] = char
  else
    @buffer.insert(@curpos, char)
  end
  oldcurpos = @curpos
  #$log.warn "XXX:  FIELD CURPOS #{@curpos} blen #{@buffer.length} " #if @curpos > blen
  @curpos += 1 if @curpos < @maxlen
  @modified = true
  #$log.debug " FIELD FIRING CHANGE: #{char} at new #{@curpos}: bl:#{@buffer.length} buff:[#{@buffer}]"
  # i have no way of knowing what change happened and what char was added deleted or changed
  #fire_handler :CHANGE, self    # 2008-12-09 14:51
  if @overwrite_mode
    fire_handler :CHANGE, InputDataEvent.new(oldcurpos,@curpos, self, :DELETE, 0, oldchar) # 2010-09-11 12:43
  end
  fire_handler :CHANGE, InputDataEvent.new(oldcurpos,@curpos, self, :INSERT, 0, char) # 2010-09-11 12:43
  0
end
repaint() click to toggle source
Note that some older widgets like Field repaint every time the form.repaint

+ is called, whether updated or not. I can't remember why this is, but + currently I've not implemented events with these widgets. 2010-01-03 15:00

# File lib/canis/core/widgets/rwidget.rb, line 2517
def repaint
  return unless @repaint_required  # 2010-11-20 13:13 its writing over a window i think TESTING
  if @label_unattached
    alert "came here unattachd"
    @label.set_form(@form)
    @label_unattached = nil
  end
  if @label_unplaced
    alert "came here unplaced"
    position_label
    @label_unplaced = nil
  end
  #@bgcolor ||= $def_bg_color
  #@color   ||= $def_fg_color
  _color = color()
  _bgcolor = bgcolor()
  $log.debug("repaint FIELD: #{id}, #{name}, #{row} #{col},pcol:#{@pcol},  #{focusable} st: #{@state} ")
  @width = 1 if width == 0
  printval = getvalue_for_paint().to_s # added 2009-01-06 23:27
  printval = mask()*printval.length unless @mask.nil?
  if !printval.nil? 
    if printval.length > width # only show maxlen
      printval = printval[@pcol..@pcol+width-1] 
    else
      printval = printval[@pcol..-1]
    end
  end

  acolor = @color_pair || get_color($datacolor, _color, _bgcolor)
  if @state == :HIGHLIGHTED
    _bgcolor = @highlight_bgcolor || _bgcolor
    _color = @highlight_color || _color
    acolor = get_color(acolor, _color, _bgcolor)
  end
  @graphic = @form.window if @graphic.nil? ## cell editor listbox hack
  #$log.debug " Field g:#{@graphic}. r,c,displen:#{@row}, #{@col}, #{@width} c:#{@color} bg:#{@bgcolor} a:#{@attr} :#{@name} "
  r = row
  c = col
  @graphic.printstring r, c, sprintf("%-*s", width, printval), acolor, attr()
  @field_col = c
  @repaint_required = false
end
restore_original_value() click to toggle source

silently restores value without firing handlers, use if exception and you want old value @since 1.4.0 2011-10-2

# File lib/canis/core/widgets/rwidget.rb, line 2428
def restore_original_value
  @buffer = @original_value.dup
  # earlier commented but trying again, since i am getting IndexError in insert 2188
  # Added next 3 lines to fix issue, now it comes back to beginning.
  cursor_home

  @repaint_required = true
end
set_focusable(tf) click to toggle source

deprecated set or unset focusable

# File lib/canis/core/widgets/rwidget.rb, line 2562
def set_focusable(tf)
  $log.warn "pls don't use, deprecated. use focusable(boolean)"
  focusable tf
end
set_form_col(col1=@curpos) click to toggle source

sets the visual cursor on the window at correct place added here since we need to account for pcol. 2011-12-7 NOTE be careful of curpos - pcol being less than 0

# File lib/canis/core/widgets/rwidget.rb, line 2636
def set_form_col col1=@curpos
  @curpos = col1 || 0 # NOTE we set the index of cursor here
  c = @col + @col_offset + @curpos - @pcol
  min = @col + @col_offset
  max = min + @width
  c = min if c < min
  c = max if c > max
  #$log.debug " #{@name} FIELD set_form_col #{c}, curpos #{@curpos}  , #{@col} + #{@col_offset} pcol:#{@pcol} "
  setrowcol nil, c
end
set_label(label) click to toggle source

create a label linked to this field Typically one passes a Label, but now we can pass just a String, a label is created. This differs from label in positioning. The Field is printed on row and col and the label before it. Thus, all fields are aligned on column, however you must leave adequate columns for the label to be printed to the left of the field.

NOTE: 2011-10-20 when field attached to some container, label won't be attached In such cases, use just +label()+ not +set_label()+. @param [Label, String] label object to be associated with this field @return label created which can be further customized. FIXME this may not work since i have disabled -1, now i do not set row and col 2011-11-5

# File lib/canis/core/widgets/rwidget.rb, line 2481
def set_label label
  # added case for user just using a string
  case label
  when String
    # what if no form at this point
    @label_unattached = true unless @form
    label = Label.new @form, {:text => label}
  end
  @label = label
  # in the case of app it won't be set yet FIXME
  # So app sets label to 0 and t his won't trigger
  # can this be delayed to when paint happens XXX
  if @row
    position_label
  else
    @label_unplaced = true
  end
  label
end
text(*val) click to toggle source

Set the value in the field. @param if none given, returns value existing @param value (can be int, float, String)

@return self

# File lib/canis/core/widgets/rwidget.rb, line 2779
def text(*val)
  if val.empty?
    return getvalue()
  else
    return unless val # added 2010-11-17 20:11, dup will fail on nil
    return unless val[0]
    # 2013-04-20 - 19:02 dup failing on fixnum, set_buffer does a dup
    # so maybe i can do without it here
    #s = val[0].dup
    s = val[0]
    _set_buffer(s)
  end
end
Also aliased as: default
text=(val) click to toggle source
# File lib/canis/core/widgets/rwidget.rb, line 2793
def text=(val)
  return unless val # added 2010-11-17 20:11, dup will fail on nil
  # will bomb on integer or float etc !!
  #_set_buffer(val.dup)
  _set_buffer(val)
end
type(*val) click to toggle source

NOTE: earlier there was some confusion over type, chars_allowed and datatype Now type and chars_allowed are merged into one. If you pass a symbol such as :integer, :float or Float Integer then some

standard chars_allowed will be used. Otherwise you may pass a regexp.

@param symbol :integer, :float, :alpha, :alnum, Float, Integer, Numeric, Regexp

# File lib/canis/core/widgets/rwidget.rb, line 2338
def type *val
  return @chars_allowed if val.empty?

  dtype = val[0]
  #return self if @chars_allowed # disallow changing
  if dtype.is_a? Regexp 
    @chars_allowed = dtype
    return self
  end
  dtype = dtype.to_s.downcase.to_sym if dtype.is_a? String
  case dtype # missing to_sym would have always failed due to to_s 2011-09-30 1.3.1
  when :integer, Integer
    @chars_allowed = /\d/
  when :numeric, :float, Numeric, Float
    @chars_allowed = /[\d\.]/ 
  when :alpha
    @chars_allowed = /[a-zA-Z]/ 
  when :alnum
    @chars_allowed = /[a-zA-Z0-9]/ 
  else
    raise ArgumentError, "Field type: invalid datatype specified. Use :integer, :numeric, :float, :alpha, :alnum "
  end
  self
end
Also aliased as: chars_allowed
undo_delete_eol() click to toggle source

does an undo on delete_eol, not a real undo

# File lib/canis/core/widgets/rwidget.rb, line 2603
def undo_delete_eol
  return if @delete_buffer.nil?
  #oldvalue = @buffer
  @buffer.insert @curpos, @delete_buffer 
  fire_handler :CHANGE, InputDataEvent.new(@curpos,@curpos+@delete_buffer.length, self, :INSERT, 0, @delete_buffer)     # 2010-09-11 13:01
end