def handle_key(ch)
    # 2014-08-19 - 21:10 moving to init, so that user may override or remove
    #map_keys unless @keys_mapped
    handled = :UNHANDLED # 2011-10-4 
        if ch ==  ?\C-u.getbyte(0)
          ret = universal_argument
          $log.debug "C-u FORM set MULT to #{$multiplier}, ret = #{ret}  "
          return 0 if ret == 0
          ch = ret # unhandled char
        elsif ch >= ?\M-1.getbyte(0) && ch <= ?\M-9.getbyte(0)
          if $catch_alt_digits # emacs EMACS
            ret = digit_argument ch
            $log.debug " FORM set MULT DA to #{$multiplier}, ret = #{ret}  "
            return 0 if ret == 0 # don't see this happening
            ch = ret # unhandled char
          end
        end

        $current_key = ch
        case ch
        when -1
          return
        when 1000, 12
          # NOTE this works if widgets cover entire screen like text areas and lists but not in 
          #  dialogs where there is blank space. only widgets are painted.
          # testing out 12 is C-l
          $log.debug " form REFRESH_ALL repaint_all HK #{ch} #{self}, #{@name} "
          repaint_all_widgets
          return
        when FFI::NCurses::KEY_RESIZE  # SIGWINCH 
          # note that in windows that have dialogs or text painted on window such as title or 
          #  box, the clear call will clear it out. these are not redrawn.
          lines = Ncurses.LINES
          cols = Ncurses.COLS
          x = Ncurses.stdscr.getmaxy
          y = Ncurses.stdscr.getmaxx
          $log.debug " form RESIZE HK #{ch} #{self}, #{@name}, #{ch}, x #{x} y #{y}  lines #{lines} , cols: #{cols} "
          #alert "SIGWINCH WE NEED TO RECALC AND REPAINT resize #{lines}, #{cols}: #{x}, #{y} "

          # next line may be causing flicker, can we do without.
          Ncurses.endwin
          @window.wrefresh
          @window.wclear
          if @layout_manager
            @layout_manager.do_layout
            # we need to redo statusline and others that layout ignores
          else
            @widgets.each { |e| e.repaint_all(true) } # trying out
          end
          ## added RESIZE on 2012-01-5 
          ## stuff that relies on last line such as statusline dock etc will need to be redrawn.
          fire_handler :RESIZE, self 
        else
          field =  get_current_field
          if $log.debug?
            keycode = keycode_tos(ch)
            $log.debug " form HK #{ch} #{self}, #{@name}, #{keycode}, field: giving to: #{field}, #{field.name}  " if field
          end
          handled = :UNHANDLED 
          handled = field.handle_key ch unless field.nil? # no field focussable
          $log.debug "handled inside Form #{ch} from #{field} got #{handled}  "
          # some widgets like textarea and list handle up and down
          if handled == :UNHANDLED or handled == -1 or field.nil?
            case ch
            when KEY_TAB, ?\M-\C-i.getbyte(0)  # tab and M-tab in case widget eats tab (such as Table)
              ret = select_next_field
              return ret if ret == :NO_NEXT_FIELD
              # alt-shift-tab  or backtab (in case Table eats backtab)
            when FFI::NCurses::KEY_BTAB, 481 ## backtab added 2008-12-14 18:41 
              ret = select_prev_field
              return ret if ret == :NO_PREV_FIELD
            when FFI::NCurses::KEY_UP
              ret = select_prev_field
              return ret if ret == :NO_PREV_FIELD
            when FFI::NCurses::KEY_DOWN
              ret = select_next_field
              return ret if ret == :NO_NEXT_FIELD
            else
              #$log.debug " before calling process_key in form #{ch}  " if $log.debug? 
              ret = process_key ch, self
              # seems we need to flushinp in case composite has pushed key
              $log.debug "FORM process_key #{ch} got ret #{ret} in #{self}, flushing input "
              # 2014-06-01 - 17:01 added flush, maybe at some point we could do it only if unhandled
              #   in case some method wishes to actually push some keys
              Ncurses.flushinp
              return :UNHANDLED if ret == :UNHANDLED
            end
          elsif handled == :NO_NEXT_FIELD || handled == :NO_PREV_FIELD # 2011-10-4 
            return handled
          end
        end
       $log.debug " form before repaint #{self} , #{@name}, ret #{ret}"
       repaint
       $last_key = ch
       ret || 0  # 2011-10-17 
  end
def handle_key(ch)
    # 2014-08-19 - 21:10 moving to init, so that user may override or remove
    #map_keys unless @keys_mapped
    handled = :UNHANDLED # 2011-10-4 
        if ch ==  ?\C-u.getbyte(0)
          ret = universal_argument
          $log.debug "C-u FORM set MULT to #{$multiplier}, ret = #{ret}  "
          return 0 if ret == 0
          ch = ret # unhandled char
        elsif ch >= ?\M-1.getbyte(0) && ch <= ?\M-9.getbyte(0)
          if $catch_alt_digits # emacs EMACS
            ret = digit_argument ch
            $log.debug " FORM set MULT DA to #{$multiplier}, ret = #{ret}  "
            return 0 if ret == 0 # don't see this happening
            ch = ret # unhandled char
          end
        end

        $current_key = ch
        case ch
        when -1
          return
        when 1000, 12
          # NOTE this works if widgets cover entire screen like text areas and lists but not in 
          #  dialogs where there is blank space. only widgets are painted.
          # testing out 12 is C-l
          $log.debug " form REFRESH_ALL repaint_all HK #{ch} #{self}, #{@name} "
          repaint_all_widgets
          return
        when FFI::NCurses::KEY_RESIZE  # SIGWINCH 
          # note that in windows that have dialogs or text painted on window such as title or 
          #  box, the clear call will clear it out. these are not redrawn.
          lines = Ncurses.LINES
          cols = Ncurses.COLS
          x = Ncurses.stdscr.getmaxy
          y = Ncurses.stdscr.getmaxx
          $log.debug " form RESIZE HK #{ch} #{self}, #{@name}, #{ch}, x #{x} y #{y}  lines #{lines} , cols: #{cols} "
          #alert "SIGWINCH WE NEED TO RECALC AND REPAINT resize #{lines}, #{cols}: #{x}, #{y} "

          # next line may be causing flicker, can we do without.
          Ncurses.endwin
          @window.wrefresh
          @window.wclear
          if @layout_manager
            @layout_manager.do_layout
            # we need to redo statusline and others that layout ignores
          else
            @widgets.each { |e| e.repaint_all(true) } # trying out
          end
          ## added RESIZE on 2012-01-5 
          ## stuff that relies on last line such as statusline dock etc will need to be redrawn.
          fire_handler :RESIZE, self 
        else
          field =  get_current_field
          if $log.debug?
            keycode = keycode_tos(ch)
            $log.debug " form HK #{ch} #{self}, #{@name}, #{keycode}, field: giving to: #{field}, #{field.name}  " if field
          end
          handled = :UNHANDLED 
          handled = field.handle_key ch unless field.nil? # no field focussable
          $log.debug "handled inside Form #{ch} from #{field} got #{handled}  "
          # some widgets like textarea and list handle up and down
          if handled == :UNHANDLED or handled == -1 or field.nil?
            case ch
            when KEY_TAB, ?\M-\C-i.getbyte(0)  # tab and M-tab in case widget eats tab (such as Table)
              ret = select_next_field
              return ret if ret == :NO_NEXT_FIELD
              # alt-shift-tab  or backtab (in case Table eats backtab)
            when FFI::NCurses::KEY_BTAB, 481 ## backtab added 2008-12-14 18:41 
              ret = select_prev_field
              return ret if ret == :NO_PREV_FIELD
            when FFI::NCurses::KEY_UP
              ret = select_prev_field
              return ret if ret == :NO_PREV_FIELD
            when FFI::NCurses::KEY_DOWN
              ret = select_next_field
              return ret if ret == :NO_NEXT_FIELD
            else
              #$log.debug " before calling process_key in form #{ch}  " if $log.debug? 
              ret = process_key ch, self
              # seems we need to flushinp in case composite has pushed key
              $log.debug "FORM process_key #{ch} got ret #{ret} in #{self}, flushing input "
              # 2014-06-01 - 17:01 added flush, maybe at some point we could do it only if unhandled
              #   in case some method wishes to actually push some keys
              Ncurses.flushinp
              return :UNHANDLED if ret == :UNHANDLED
            end
          elsif handled == :NO_NEXT_FIELD || handled == :NO_PREV_FIELD # 2011-10-4 
            return handled
          end
        end
       $log.debug " form before repaint #{self} , #{@name}, ret #{ret}"
       repaint
       $last_key = ch
       ret || 0  # 2011-10-17 
  end
forms handle keys
 mainly traps tab and backtab to navigate between widgets.
 I know some widgets will want to use tab, e.g edit boxes for entering a tab
  or for completion.
 @throws FieldValidationException
 NOTE : please rescue exceptions when you use this in your main loop and alert() user