class Canis::App

This is the Application class which does the job of setting up the environment, and closing it at the end.

Attributes

config[R]
form[R]
quit_key[W]
window[R]

Public Class Methods

new(config={}) click to toggle source

TODO: i should be able to pass window coords here in config :title

# File lib/canis/core/util/app.rb, line 85
def initialize config={}, &block
  @config = config


  widget_shortcuts_init
  @variables = {}
  # if we are creating child objects then we will not use outer form. this object will manage
  #@current_object = []  # 2014-08-29 - 17:35 unused
  @_system_commands = %w{ bind_global bind_component field_help_text }

  init_vars
  $log.debug "XXX APP CONFIG: #{@config}  " if $log.debug? 
  run &block
end

Public Instance Methods

bind_global() click to toggle source

bind a key to a method at global (form) level Note that individual component may be overriding this.

# File lib/canis/core/util/app.rb, line 265
def bind_global
  opts = get_all_commands
  cmd = rb_gets("Select a command (<tab> for choices) : ", opts)
  if cmd.nil? || cmd == ""
    rb_puts "Aborted."
    return
  end
  key = []
  str = ""
  # the next is fine but does not allow user to enter a control or alt or function character
  # since it uses Field. It is fine if you want to force alphanum input
  ch = rb_getchar("Enter one or two keys. Finish with <ENTER>. Enter first key:")
  unless ch
    rb_puts "Aborted. <Press a key>"
    return
  end
  key << ch
  str << keycode_tos(ch)
  ch = rb_getchar  "Got #{str}. Enter second key or hit return:"
  unless ch
    rb_puts "Aborted. <Press a key>"
    return
  end
  if ch == KEY_ENTER || ch == 13
  else
    key << ch
    str << keycode_tos(ch)
  end
  if !key.empty?
    rb_puts "Binding #{cmd} to #{str}. "
    key = key[0] if key.size == 1
    #@form.bind_key(key, cmd.to_sym) # not finding it, getting called by that comp
    @form.bind_key(key){ send(cmd.to_sym) }
  end
end
close() click to toggle source
# File lib/canis/core/util/app.rb, line 113
def close
  $log.debug " INSIDE CLOSE, #{@stop_ncurses_on_close} "
  @window.destroy if @window
  $log.debug " INSIDE CLOSE, #{@stop_ncurses_on_close} "
  if @stop_ncurses_on_close
    Canis::stop_ncurses
    $log.debug " CLOSING NCURSES"
  end
  $log.debug " CLOSING APP"
end
field_help_text() click to toggle source

displays help_text associated with field. 2011-10-15

# File lib/canis/core/util/app.rb, line 301
def field_help_text
  f = @form.get_current_field
  if f.respond_to?('help_text')
    h = f.help_text
    h = "No help text defined for this field.\nTry F1, or press '?' for key-bindings." unless h
    textdialog "#{h}", :title => "Widget Help Text"
  else
    alert "Could not get field #{f} or does not respond to helptext. Try F1 or '?'"
  end
end
get_all_commands() click to toggle source
# File lib/canis/core/util/app.rb, line 256
def get_all_commands
  opts = @_system_commands.dup
  if respond_to? :get_commands
    opts.push(*get_commands())
  end
  opts
end
get_binding() click to toggle source

used only by LiveConsole, if enables in an app, usually only during testing.

# File lib/canis/core/util/app.rb, line 219
def get_binding
  return binding()
end
get_command_from_user(choices=["quit","help", "suspend", "shell_output"]) click to toggle source

prompts user for a command. we need to get this back to the calling app or have some block stuff TODO Actually, this is naive, you would want to pass some values in like current data value or lines ?? Also may want command completion, or help so all commands can be displayed NOTE: This is gonna change very soon - 2012-01-8

# File lib/canis/core/util/app.rb, line 317
def get_command_from_user choices=["quit","help", "suspend", "shell_output"]
  @_command_history ||= Array.new
  str = rb_gets("Cmd: ", choices) { |q| q.default = @_previous_command; q.history = @_command_history }
          @_command_history << str unless @_command_history.include? str
  # shell the command
  if str =~ /^!/
    str = str[1..-1]
    suspend(false) { 
      #system(str);
      $log.debug "XXX STR #{str}  " if $log.debug? 

      output=`#{str}`
      system("echo ' ' ");
      $log.debug "XXX output #{output} " if $log.debug? 
      system("echo '#{output}' ");
      system("echo Press Enter to continue.");
      system("read"); 
    }
    return nil # i think
  else
    # TODO
    # here's where we can take internal commands
    #alert "[#{str}] string did not match :!"
    str = str.to_s #= str[1..-1]
    cmdline = str.split
    cmd = cmdline.shift #.to_sym
    return unless cmd # added 2011-09-11 FFI
    f = @form.get_current_field
    if respond_to?(cmd, true)
      if cmd == "close"
        throw :close # other seg faults in del_panel window.destroy executes 2x
      else
        res = send cmd, *cmdline
      end
    elsif f.respond_to?(cmd, true)
      res = f.send(cmd, *cmdline)
    else
      alert "App: #{self.class} does not respond to #{cmd} "
      ret = false
      # what is this execute_this: some kind of general routine for all apps ?
      ret = execute_this(cmd, *cmdline) if respond_to?(:execute_this, true)
      rb_puts("#{self.class} does not respond to #{cmd} ", :color_pair => $promptcolor) unless ret
      # should be able to say in red as error
    end
  end
end
hline(config={}) click to toggle source

displays a horizontal line takes col (column to start from) from current stack take row from app_row

requires width to be passed in config, else defaults to 20 @example

hline :width => 55
# File lib/canis/core/util/app.rb, line 384
def hline config={}
  raise "I think this is unused. removed if this does not raise 2014-08-29 - 12:43 "
  row = config[:row] || @app_row
  width = config[:width] || 20
  _position config
  col = config[:col] || 1
  @color_pair = config[:color_pair] || $datacolor
  @attrib = config[:attrib] || Ncurses::A_NORMAL
  @window.attron(Ncurses.COLOR_PAIR(@color_pair) | @attrib)
  @window.mvwhline( row, col, FFI::NCurses::ACS_HLINE, width)
  @window.attron(Ncurses.COLOR_PAIR(@color_pair) | @attrib)
  @app_row += 1
end
init_vars() click to toggle source
# File lib/canis/core/util/app.rb, line 99
def init_vars
  @quit_key ||= FFI::NCurses::KEY_F10
  # actually this should be maintained inside ncurses pack, so not loaded 2 times.
  # this way if we call an app from existing program, App won't start ncurses.
  unless $ncurses_started
    init_ncurses
  end
  $lastline = Ncurses.LINES - 1
  unless $log
    logpath=ENV["CANIS_LOG_PATH"]
    $log = create_logger(logpath || "/dev/null")
  end
end
keypress(&block) click to toggle source

returns a symbol of the key pressed e.g. :C_c for Ctrl-C :Space, :bs, :M_d etc NOTE 2014-08-15 Trying with just string returned by keycode_tos This should allow for simpler apps that want to handle keys perhaps taken from a config file

rather than bind each key manually.
# File lib/canis/core/util/app.rb, line 209
def keypress &block
 @keyblock = block
end
logger() click to toggle source
# File lib/canis/core/util/app.rb, line 112
def logger; return $log; end
loop(&block) click to toggle source

This method is called by run, and thus in most cases this is what is used. run calls this without a block not sure, but user shuld be able to trap keystrokes if he wants but do i still call handle_key if he does, or give him total control. But loop is already called by framework

# File lib/canis/core/util/app.rb, line 129
def loop &block
  @form.repaint
  @window.wrefresh
  Ncurses::Panel.update_panels
  @break_key = ?\C-q.getbyte(0)
  # added this extra loop since from some places we exit using throw :close
  # amd that was in a much higher place, and was getting us right out, with
  # no chance of user canceling quit. This extra loop allows us to remain
  # added on 2011-11-24
  while true
    catch :close do
      while((ch = @window.getchar()) != 999 )
        if ch == @break_key || ch == @quit_key
          break
        end


        # 2014-08-19 - 22:51 commented next line, too much choice. keep it simple. delete in a month FIXME
        #yield ch if block # <<<----

        # this is what the user should have control ove. earlier we would put this in
        # a try catch block so user could do what he wanted with the error. Now we
        # need to get it to him somehow, perhaps through a block or on_error event
        begin
          # execute a code block so caller program can handle keys from a hash or whatever.
          # NOTE: these keys will not appear in help
          # FIXME : ideally if its just a hash, we should allow user to give it to form
          #  or widget which it will use, or merge, and be able to print help from
          if @keyblock
            str = keycode_tos ch
            # why did we ever want to convert to a symbol. why not just pass it as is.
            #@keyblock.call(str.gsub(/-/, "_").to_sym) # not used ever
            ret = @keyblock.call(str) 
            if ret
              @form.repaint 
              next
            end
          end
          @form.handle_key ch
        rescue => err
          $log.debug( "app.rb handle_key rescue reached ")
          $log.debug( err.to_s) 
          $log.debug(err.backtrace.join("\n")) 
          textdialog [err.to_s, *err.backtrace], :title => "Exception"
        end
        @window.wrefresh
      end
    end # catch
    stopping = @window.fire_close_handler
    @window.wrefresh
    break if stopping.nil? || stopping
  end # while
end
message(text) click to toggle source

updates a global var with text. Calling app has to set up a Variable with that name and attach to a label so it can be printed.

# File lib/canis/core/util/app.rb, line 214
def message text
  $status_message.value = text # trying out 2011-10-9
end
safe_loop(&block) click to toggle source

if calling loop separately better to call this, since it will shut off ncurses and print error on screen. UNUSED

# File lib/canis/core/util/app.rb, line 185
def safe_loop &block
  begin
    loop &block
  rescue => ex
    $log.debug( "APP.rb rescue reached ")
    $log.debug( ex) if ex
    $log.debug(ex.backtrace.join("\n")) if ex
  ensure
    close
    # putting it here allows it to be printed on screen, otherwise it was not showing at all.
    if ex
      puts "========== EXCEPTION =========="
      p ex 
      puts "==============================="
      puts(ex.backtrace.join("\n")) 
    end
  end
end
suspend(clear=true) { || ... } click to toggle source

suspends curses so you can play around on the shell or in cooked mode like Vim does. Expects a block to be passed. Purpose: you can print some stuff without creating a window, or just run shell commands without coming out. NOTE: if you pass clear as true, then the screen will be cleared and you can use puts or print to print. You may have to flush. However, with clear as false, the screen will not be cleared. You will have to print using printw, and if you expect user input you must do a “system /bin/stty sane” If you print stuff, you will have to put a getch() or system(“read”) to pause the screen.

# File lib/canis/core/util/app.rb, line 234
def suspend clear=true
  return unless block_given?
  Ncurses.def_prog_mode
  if clear
    Ncurses.endwin 
    # NOTE: avoid false since screen remains half off
    # too many issues
  else
    system "/bin/stty sane"
  end
  yield if block_given?
  Ncurses.reset_prog_mode
  if !clear
    # Hope we don't screw your terminal up with this constantly.
    Canis::stop_ncurses
    Canis::start_ncurses  
    #@form.reset_all # not required
  end
  @form.repaint
  @window.wrefresh
  Ncurses::Panel.update_panels
end

Private Instance Methods

_resolve_command(opts, cmd) click to toggle source

if partial command entered then returns matches

# File lib/canis/core/util/app.rb, line 440
def _resolve_command opts, cmd
  return cmd if opts.include? cmd
  matches = opts.grep Regexp.new("^#{cmd}")
end
app_bindings() click to toggle source

Some bindings that were earlier part of App (in the run block), but now optional for an

app. I would like to move it out, so they can be mapped separately to whatever a user wants
and be improved. these are very experimental and bare as yet.

They should also be usable outside of App.

# File lib/canis/core/util/app.rb, line 493
def app_bindings
  @form.bind_key([?\C-x, ?c], 'suspend') { suspend(false) do
    system("tput cup 26 0")
    system("tput ed")
    system("echo Enter C-d to return to application")
    system (ENV['PS1']='\s-\v\$ ')
    system(ENV['SHELL']);
  end
  }
  # this is a very rudimentary default command executer, it does not
  # allow tab completion. App should use M-x with names of commands
  # as in appgmail
  # NOTE: This is gonna change very soon - 2012-01-8
  @form.bind_key(?:, 'prompt') { 
    str = get_command_from_user
  }

  # this M-x stuff has to be moved out so it can be used by all. One should be able
  # to add_commands properly to this, and to C-x. I am thinking how to go about this,
  # and what function M-x actually serves.

  @form.bind_key(?\M-x, 'M-x commands'){
    # TODO previous command to be default
    opts = get_all_commands()
    @_command_history ||= Array.new
    # previous command should be in opts, otherwise it is not in this context
    cmd = rb_gets("Command: ", opts){ |q| q.default = @_previous_command; q.history = @_command_history }
    if cmd.nil? || cmd == ""
    else
      @_command_history << cmd unless @_command_history.include? cmd
      cmdline = cmd.split
      cmd = cmdline.shift
      # check if command is a substring of a larger command
      if !opts.include?(cmd)
        rcmd = _resolve_command(opts, cmd) if !opts.include?(cmd)
        if rcmd.size == 1
          cmd = rcmd.first
        elsif !rcmd.empty?
          rb_puts "Cannot resolve #{cmd}. Matches are: #{rcmd} "
        end
      end
      if respond_to?(cmd, true)
        @_previous_command = cmd
        begin
          send cmd, *cmdline
        rescue => exc
          $log.error "ERR EXC: send throwing an exception now. Duh. IMAP keeps crashing haha !! #{exc}  " if $log.debug? 
          if exc
            $log.debug( exc) 
            $log.debug(exc.backtrace.join("\n")) 
            rb_puts exc.to_s
          end
        end
      else
        rb_puts("Command [#{cmd}] not supported by #{self.class} ", :color_pair => $promptcolor)
      end
    end
  }
end
help() click to toggle source

def help; display_app_help; end

i am removing above from rdialog. 2014-05-21 - 12:42 hope next
line works UNTESTED
# File lib/canis/core/util/app.rb, line 413
def help; @form.help_manager.display_help; end
init_ncurses() click to toggle source

Initialize curses

# File lib/canis/core/util/app.rb, line 416
def init_ncurses
  Canis::start_ncurses  # this is initializing colors via ColorMap.setup
  #$ncurses_started = true
  @stop_ncurses_on_close = true
end
quit() click to toggle source

@group positioning of components

# File lib/canis/core/util/app.rb, line 407
def quit
  throw(:close)
end
run(&block) click to toggle source

This is the method that is called by all apps usually that define an App block

# File lib/canis/core/util/app.rb, line 446
def run &block
  begin

    # check if user has passed window coord in config, else root window
    @window = Canis::Window.root_window
    awin = @window
    catch(:close) do
      @form = Form.new @window
      #@form.bind_key(KEY_F1, 'help'){ display_app_help } # NOT REQUIRED NOW 2012-01-7 since form does it
      @form.bind_key([?q,?q], 'quit' ){ throw :close } if $log.debug?

      #@message = Variable.new
      #@message.value = ""
      $status_message ||= Variable.new # remember there are multiple levels of apps
      $status_message.value = ""
      #$error_message.update_command { @message.set_value($error_message.value) }
      if block
        begin
          yield_or_eval &block if block_given? 
          # how the hell does a user trap exception if the loop is hidden from him ? FIXME
          loop
        rescue => ex
          $log.debug( "APP.rb rescue reached ")
          $log.debug( ex) if ex
          $log.debug(ex.backtrace.join("\n")) if ex
        ensure
          close
          # putting it here allows it to be printed on screen, otherwise it was not showing at all.
          if ex
            puts "========== EXCEPTION =========="
            p ex 
            puts "==============================="
            puts(ex.backtrace.join("\n")) 
          end
        end
        nil
      else
        #@close_on_terminate = true
        self
      end #if block
    end # :close
  end
end