Class: Umbra::Field

Inherits:
Widget show all
Defined in:
lib/umbra/field.rb

Direct Known Subclasses

LabeledField

Instance Attribute Summary collapse

Attributes inherited from Widget

#col, #col_offset, #curpos, #focusable, #graphic, #handler, #key_label, #modified, #name, #repaint_required, #row, #row_offset, #state

Instance Method Summary collapse

Methods inherited from Widget

#_form=, #color_pair, #command, #getvalue_for_paint, #height, #highlight_attr, #repaint_all, #rowcol, #set_form_col, #set_form_row, #touch, #variable_set, #visible, #width

Methods included from KeyMappingHandler

#_process_key, #bind_key, #bind_keys, #process_key, #unbind_key

Methods included from EventHandler

#bind_event, #event?, #fire_handler, #fire_property_change, #register_events

Constructor Details

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

# so can be nil if accessed early



96
97
98
99
100
101
102
103
104
105
106
107
108
109
# File 'lib/umbra/field.rb', line 96

def initialize config={}, &block
  @buffer = String.new
  @row = 0
  @col = 0
  @editable = true
  @focusable = true

  map_keys 
  init_vars
  register_events(:CHANGE)
  super
  @width ||= 20
  @maxlen ||= @width
end

Instance Attribute Details

#aboveObject

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



66
67
68
# File 'lib/umbra/field.rb', line 66

def above
  @above
end

#belowObject

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



66
67
68
# File 'lib/umbra/field.rb', line 66

def below
  @below
end

#bufferObject (readonly)

actual buffer being used for storage



59
60
61
# File 'lib/umbra/field.rb', line 59

def buffer
  @buffer
end

#datatypeObject

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

Examples:

: Integer, Integer, Float



86
87
88
# File 'lib/umbra/field.rb', line 86

def datatype
  @datatype
end

#editableObject

any new widget that has editable should have modified also



75
76
77
# File 'lib/umbra/field.rb', line 75

def editable
  @editable
end

#field_colObject (readonly)

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



92
93
94
# File 'lib/umbra/field.rb', line 92

def field_col
  @field_col
end

#maskObject

aliased to type attr_accessor :chars_allowed # regex, what characters to allow entry, will ignore all else character to show, earlier called show which clashed with Widget method show



71
72
73
# File 'lib/umbra/field.rb', line 71

def mask
  @mask
end

#maxlenObject

maximum length allowed into field



58
59
60
# File 'lib/umbra/field.rb', line 58

def maxlen
  @maxlen
end

#null_allowedObject

allow nulls, don’t validate if null # added , boolean



72
73
74
# File 'lib/umbra/field.rb', line 72

def null_allowed
  @null_allowed
end

#original_valueObject (readonly)

value on entering field



87
88
89
# File 'lib/umbra/field.rb', line 87

def original_value
  @original_value
end

#overwrite_modeObject

true or false INSERT OVERWRITE MODE



88
89
90
# File 'lib/umbra/field.rb', line 88

def overwrite_mode
  @overwrite_mode
end

#valid_rangeObject

validate against numeric range, should respond to include?



64
65
66
# File 'lib/umbra/field.rb', line 64

def valid_range
  @valid_range
end

#valid_regexObject

validate against regular expression (+match()+)



63
64
65
# File 'lib/umbra/field.rb', line 63

def valid_regex
  @valid_regex
end

#valuesObject

validate against provided list, (include?)



62
63
64
# File 'lib/umbra/field.rb', line 62

def values
  @values
end

Instance Method Details

#cursor_backwardObject

move cursor back one character, called with KEY_LEFT action.



401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
# File 'lib/umbra/field.rb', line 401

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 
    # pan left only when cursor pos is 0
    @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_endObject

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



348
349
350
351
352
353
354
355
356
357
358
359
# File 'lib/umbra/field.rb', line 348

def cursor_end
  blen = @buffer.rstrip.length
  if blen < @width
    set_col_offset blen
  else
    @pcol = blen-@width
    #set_form_col @width-1
    set_col_offset 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)
end

#cursor_forwardObject

move cursor forward one character, called with KEY_RIGHT action.



391
392
393
394
395
396
397
398
399
# File 'lib/umbra/field.rb', line 391

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_homeObject

position cursor at start of field



341
342
343
344
345
# File 'lib/umbra/field.rb', line 341

def cursor_home
  @curpos = 0
  @pcol = 0
  set_col_offset 0
end

#delete_at(index = @curpos) ⇒ Object

delete a character from the buffer at given position Called by delete_curr_char and delete_prev_char.



204
205
206
207
208
209
210
# File 'lib/umbra/field.rb', line 204

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, InputDataEvent.new(@curpos,@curpos, self, :DELETE, 0, char)     # 2010-09-11 13:01 
end

#delete_curr_charObject

# this is perfect if not scrolling, but now needs changes

if @curpos > 0
  @curpos -= 1
  addcol -1
end


421
422
423
424
425
# File 'lib/umbra/field.rb', line 421

def delete_curr_char
  return -1 unless @editable
  delete_at
  @modified = true
end

#delete_eolObject

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  # this does not exist since it called form


381
382
383
384
385
386
387
388
389
# File 'lib/umbra/field.rb', line 381

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_charObject

called when user presses backspace. Delete character on left of cursor



428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
# File 'lib/umbra/field.rb', line 428

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
  @modified = true
end

#getvalueObject

converts back into original type

changed to convert on 2009-01-06 23:39 
2018-04-13 - Changed from private to public since I got an error on on_leave
  when this was called from LabeledField.


255
256
257
258
259
260
261
262
263
264
265
266
267
# File 'lib/umbra/field.rb', line 255

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) ⇒ Object

field - handle a key sent for form



316
317
318
319
320
321
322
323
324
325
326
327
328
329
# File 'lib/umbra/field.rb', line 316

def handle_key ch
  $log.debug "inside handle key of field with #{ch}"
  @repaint_required = true 
  case ch
  when 32..126
    putc ch
  when 27 # cannot bind it, so hardcoding it here
    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) ⇒ Boolean

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

Returns:

  • (Boolean)


482
483
484
485
486
487
# File 'lib/umbra/field.rb', line 482

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_varsObject



110
111
112
113
114
115
116
# File 'lib/umbra/field.rb', line 110

def init_vars
  @pcol = 0                    # needed for horiz scrolling
  @curpos = 0                  # current cursor position in buffer (NOT screen/window/field)
                               # this is the index where characters are put or deleted
  #                            # when user edits
  @modified = false
end

#map_keysObject



298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
# File 'lib/umbra/field.rb', line 298

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'){ restore_original_value } 
  @keys_mapped = true
end

#modified?Boolean

overriding widget, check for value change 2018-05-26 - WHAT IS THE POINT of setting @modified if we have a different logic here

Returns:

  • (Boolean)


502
503
504
# File 'lib/umbra/field.rb', line 502

def modified?
  getvalue() != @original_value
end

#on_enterObject

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.


492
493
494
495
496
497
498
# File 'lib/umbra/field.rb', line 492

def on_enter
  #@original_value = getvalue.dup rescue getvalue
  @original_value = @buffer.dup # getvalue.dup rescue getvalue
  $log.debug "  FIELD ORIGINAL VALUE is null in on_enter" unless @original_value
  $log.debug "  on_enter: setting original_value to #{@original_value}"
  super
end

#on_leaveObject

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



446
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
# File 'lib/umbra/field.rb', line 446

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
    
    ## 2018-05-24 - seems we were not calling the :CHANGED listeners at all.
    fire_handler :CHANGED, self

  end
  # here is where we should set the forms modified to true - 2009-01
  ## 2018-05-26 - what is the point of this ? 
  if modified?
    @modified = true
  end
  # if super fails we would have still set modified to true
  super
  #return valid
end

#putc(c) ⇒ Object

add character to field, adjust scrolling. NOTE: handlekey only calls this if between 32 and 126.



185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
# File 'lib/umbra/field.rb', line 185

def putc c
  if c >= 0 and c <= 127
    ret = putch c.chr
    if ret == 0
      # character is valid
      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
      @modified = true
      return 0 
    end
  end
  return -1
end

#putch(char) ⇒ Integer

add a char to field, and validate if disabled or exceeding size

Parameters:

  • a (char)

    character to add

Returns:

  • (Integer)

    0 if okay, -1 if not editable or exceeding length



155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
# File 'lib/umbra/field.rb', line 155

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}]"
  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

#repaintObject



272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
# File 'lib/umbra/field.rb', line 272

def repaint
  return unless @repaint_required
  $log.debug("repaint FIELD: #{name}, r:#{row} c:#{col},wid:#{width},maxlen: #{@maxlen},pcol:#{@pcol},  #{focusable} st: #{@state} ")
  @width = 1 if width == 0
  printval = getvalue_for_paint().to_s
  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 || CP_WHITE
  if @state == :HIGHLIGHTED
    acolor = @highlight_color_pair || CP_RED
  end
  #$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_valueObject

silently restores earlier value without firing handlers, use if exception and you want old value Called when pressing <ESC> or <c-g>.



214
215
216
217
218
219
220
221
222
223
224
# File 'lib/umbra/field.rb', line 214

def restore_original_value
  $log.debug "  FIELD: buffer:#{@buffer}. orig:#{@original_value}: "
  $log.debug "  original value is null " unless @original_value
  $log.debug "  buffer value is null " unless @buffer
  @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

#textObject

2018-03-22 - replaced the old style text(val) with attr style



506
507
508
# File 'lib/umbra/field.rb', line 506

def text
  getvalue
end

#text=(val) ⇒ Object Also known as: default=

set default value of field



511
512
513
514
515
516
# File 'lib/umbra/field.rb', line 511

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) ⇒ Object Also known as: chars_allowed=

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.

Parameters:

  • :integer, (symbol)

    :float, :alpha, :alnum, Float, Integer, Numeric, Regexp



124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
# File 'lib/umbra/field.rb', line 124

def type=(val)

  dtype = val
  #return self if @chars_allowed # disallow changing
  # send in a regexp, we just save it.
  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

#undo_delete_eolObject

does an undo on delete_eol, not a real undo



331
332
333
334
335
336
# File 'lib/umbra/field.rb', line 331

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)     
end