hackercli 0.0.2 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/bin/hackercli.rb +4 -0
- data/bin/hackman.rb +731 -432
- data/lib/hackercli/version.rb +1 -1
- metadata +2 -2
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA1:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 0c4b0da9c2d3080b4e089e3e6cd067932ec532b2
         | 
| 4 | 
            +
              data.tar.gz: 3eed3f9cb25b8d34123d2f5f3b243ca28b6d0c20
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: ba88a33dbf30d7a59a7b68a4edc88e8497bea305e96dc70d6f0faec143ba3b90407d7c26af92e2d6bce8937da7602343973c9dae963f08e9fa851ec02f466bea
         | 
| 7 | 
            +
              data.tar.gz: 846fef7529f021b205d55165014eb75efb3e36ed50d30f43d90dd9b4d9fce8945ffcc16d24f364ea3c2f2bb5ee8f47ac6c683752588d227a1c23098beb8fd19c
         | 
    
        data/bin/hackercli.rb
    CHANGED
    
    | @@ -42,6 +42,7 @@ class Bigrss | |
| 42 42 |  | 
| 43 43 | 
             
                resp = []
         | 
| 44 44 | 
             
                filename = @options[:url]
         | 
| 45 | 
            +
                ymlpath = @options[:ymlpath]
         | 
| 45 46 | 
             
                page[:page_url] = filename
         | 
| 46 47 | 
             
                now = Time.now
         | 
| 47 48 | 
             
                page[:create_time_seconds] = now.to_i
         | 
| @@ -55,6 +56,8 @@ class Bigrss | |
| 55 56 | 
             
                content.gsub!(''',"'")
         | 
| 56 57 | 
             
                content.gsub!('4','"')
         | 
| 57 58 | 
             
                content = CGI.unescapeHTML(content)
         | 
| 59 | 
            +
                # next line dirties current dir, does not respect path of yml
         | 
| 60 | 
            +
                outfile = File.join(ymlpath, outfile) if ymlpath
         | 
| 58 61 | 
             
                File.open("#{outfile}.rss","w") {|ff| ff.write(content) }
         | 
| 59 62 | 
             
                items = content.scan(/<item>(.*?)<\/item>/)
         | 
| 60 63 | 
             
                items.each_with_index do |e,i|
         | 
| @@ -183,6 +186,7 @@ Usage: #{$0} [options] | |
| 183 186 | 
             
                  end
         | 
| 184 187 | 
             
                  opts.on("-y yml path", String,"--yml-path", "save as YML file") do |v|
         | 
| 185 188 | 
             
                    ymlfile = v
         | 
| 189 | 
            +
                    options[:ymlpath] = File.dirname(v)
         | 
| 186 190 | 
             
                  end
         | 
| 187 191 | 
             
                  #opts.on("-s SUBREDDIT", String,"--subreddit", "Get articles from subreddit named SUBREDDIT") do |v|
         | 
| 188 192 | 
             
                    #options[:subreddit] = v
         | 
    
        data/bin/hackman.rb
    CHANGED
    
    | @@ -5,7 +5,7 @@ | |
| 5 5 | 
             
            #       Author: j kepler  http://github.com/mare-imbrium/canis/
         | 
| 6 6 | 
             
            #         Date: 2014-08-09 - 10:12
         | 
| 7 7 | 
             
            #      License: MIT
         | 
| 8 | 
            -
            #  Last update: 2014-08 | 
| 8 | 
            +
            #  Last update: 2014-09-08 19:45
         | 
| 9 9 | 
             
            # ----------------------------------------------------------------------------- #
         | 
| 10 10 | 
             
            #  hackman.rb  Copyright (C) 2012-2014 j kepler
         | 
| 11 11 | 
             
            # encoding: utf-8
         | 
| @@ -13,401 +13,606 @@ require 'canis/core/util/app' | |
| 13 13 | 
             
            require 'canis/core/util/rcommandwindow'
         | 
| 14 14 | 
             
            require 'fileutils'
         | 
| 15 15 | 
             
            require 'pathname'
         | 
| 16 | 
            +
            require 'open3'
         | 
| 16 17 | 
             
            require 'canis/core/include/defaultfilerenderer'
         | 
| 17 18 | 
             
            require 'canis/core/include/appmethods'
         | 
| 18 19 |  | 
| 19 20 | 
             
            # TODO : 
         | 
| 20 | 
            -
            #    -  | 
| 21 | 
            +
            #    - Make this a standard for other similar command line ncurses apps, like rigel. we can use
         | 
| 22 | 
            +
            #      this as a template for others.
         | 
| 23 | 
            +
            #    - make the menu a little more like the mc usermenu
         | 
| 24 | 
            +
            #    x change location of data cache, hacker-cli to also use the same for rss file saving
         | 
| 25 | 
            +
            #    x make color schemes into a hash 'name' => scheme, and each scheme should have named colors such as 
         | 
| 26 | 
            +
            #       header-bg, body-bg body-fg, status etc, so it is clear. load from config file so user can change.
         | 
| 27 | 
            +
            #    x maybe instead of diong each binding we should be ablde to pass a hash to form
         | 
| 28 | 
            +
            #       or give it a method which it will invoke to handle keys. Form or window may have a keyhandler ??
         | 
| 29 | 
            +
            #       TODO 
         | 
| 30 | 
            +
            #    x create a class and put stuff in there, these methods are going into global, and can conflict
         | 
| 31 | 
            +
            #    x put one url per lne of detail, put comments with comment url, and time with other.
         | 
| 32 | 
            +
            #    x fix update help_text
         | 
| 33 | 
            +
            #    x need to trap the output of hackercli in case of error.
         | 
| 34 | 
            +
            #    x some case URL not showing in Open menu - maybe too large the window. check max size
         | 
| 35 | 
            +
            #    x add to forum list, remove, save
         | 
| 21 36 | 
             
            #    - specify gui browser and text browser, and on commandline use same keys as corvus
         | 
| 22 | 
            -
            #    - create a class and put stuff in there, these methods are going into global, and can conflict
         | 
| 23 | 
            -
            #    - we should have same mechanism for key bindings as corvus, something that can even be loaded?
         | 
| 24 37 | 
             
            #    
         | 
| 25 | 
            -
            #     | 
| 26 | 
            -
            #     | 
| 38 | 
            +
            #    ? Use ' as bookmark etc just as in cetus and corvus, keep to same keys
         | 
| 39 | 
            +
            #    ? multibuffers so user can backspace and do M-n etc
         | 
| 27 40 | 
             
            #    
         | 
| 28 | 
            -
             | 
| 29 | 
            -
             | 
| 30 | 
            -
               | 
| 31 | 
            -
               | 
| 32 | 
            -
              [ | 
| 33 | 
            -
             | 
| 34 | 
            -
            ]
         | 
| 35 | 
            -
             | 
| 36 | 
            -
             | 
| 37 | 
            -
             | 
| 38 | 
            -
             | 
| 39 | 
            -
            def choose_forum
         | 
| 40 | 
            -
              # scrollable filterable list
         | 
| 41 | 
            -
              str = display_list $forumlist, :title => "Select a forum"
         | 
| 42 | 
            -
              return unless str
         | 
| 43 | 
            -
              return if str == ""
         | 
| 44 | 
            -
              $current_forum = str
         | 
| 45 | 
            -
              forum = str
         | 
| 46 | 
            -
              get_data forum if forum
         | 
| 47 | 
            -
            end
         | 
| 48 | 
            -
            def next_forum
         | 
| 49 | 
            -
              index = $forumlist.index($current_forum)
         | 
| 50 | 
            -
              index = index >= $forumlist.count - 1 ? 0 : index + 1
         | 
| 51 | 
            -
              get_data $forumlist[index]
         | 
| 52 | 
            -
            end
         | 
| 53 | 
            -
            def prev_forum
         | 
| 54 | 
            -
              index = $forumlist.index($current_forum)
         | 
| 55 | 
            -
              index = index == 0? $forumlist.count - 1 : index - 1
         | 
| 56 | 
            -
              get_data $forumlist[index]
         | 
| 57 | 
            -
            end
         | 
| 58 | 
            -
            # if components have some commands, can we find a way of passing the command to them
         | 
| 59 | 
            -
            # method_missing gave a stack overflow.
         | 
| 60 | 
            -
            def execute_this(meth, *args)
         | 
| 61 | 
            -
              alert " #{meth} not found ! "
         | 
| 62 | 
            -
              $log.debug "app email got #{meth}  " if $log.debug? 
         | 
| 63 | 
            -
              cc = @form.get_current_field
         | 
| 64 | 
            -
              [cc].each do |c|  
         | 
| 65 | 
            -
                if c.respond_to?(meth, true)
         | 
| 66 | 
            -
                  c.send(meth, *args)
         | 
| 67 | 
            -
                  return true
         | 
| 68 | 
            -
                end
         | 
| 69 | 
            -
              end
         | 
| 70 | 
            -
              false
         | 
| 71 | 
            -
            end
         | 
| 72 | 
            -
            def open_url url
         | 
| 73 | 
            -
              shell_out "elinks #{url}"
         | 
| 74 | 
            -
              #Window.refresh_all
         | 
| 75 | 
            -
            end
         | 
| 76 | 
            -
             | 
| 77 | 
            -
              ## 
         | 
| 78 | 
            -
              # Menu creator which displays a menu and executes methods based on keys.
         | 
| 79 | 
            -
              # In some cases, we call this and then do a case statement on either key or binding.
         | 
| 80 | 
            -
              # @param String title
         | 
| 81 | 
            -
              # @param hash of keys and methods to call
         | 
| 82 | 
            -
              # @return key pressed, and binding (if found, and responded)
         | 
| 41 | 
            +
            module HackerCli
         | 
| 42 | 
            +
              VERSION="0.0.3"
         | 
| 43 | 
            +
              CONFIG_FILE="~/.hackman.yml"
         | 
| 44 | 
            +
              # in grey version, cannot see the other links.
         | 
| 45 | 
            +
              OLDCOLOR_SCHEMES=[ 
         | 
| 46 | 
            +
                [20,19,17, 18, :white, :green], # 0 band in header, 1 - menu bgcolor.  2 - bgcolor of main screen, 3 - status, 4 fg color body, detail color (url and comment count)
         | 
| 47 | 
            +
                [17,19,18, 20, :white, :green], # 0 band in header, 1 - menu bgcolor.  2 - bgcolor of main screen, 3 - status
         | 
| 48 | 
            +
                [236,236,0, 232,:white, :green], # 0 band in header, 1 - menu bgcolor.  2 - bgcolor of main screen, 3 - status
         | 
| 49 | 
            +
                [236,236,244, 250, :black, :green] # 0 band in header, 1 - menu bgcolor.  2 - bgcolor of main screen, 3 - status
         | 
| 50 | 
            +
              ]
         | 
| 51 | 
            +
              # put all methods and data into this class, so we don't pollute global space. Or get mixed into App's space.
         | 
| 83 52 | 
             
              #
         | 
| 84 | 
            -
               | 
| 85 | 
            -
                 | 
| 86 | 
            -
             | 
| 87 | 
            -
             | 
| 88 | 
            -
             | 
| 89 | 
            -
             | 
| 90 | 
            -
             | 
| 91 | 
            -
             | 
| 92 | 
            -
             | 
| 93 | 
            -
             | 
| 94 | 
            -
             | 
| 95 | 
            -
                   | 
| 96 | 
            -
                   | 
| 97 | 
            -
             | 
| 98 | 
            -
             | 
| 99 | 
            -
             | 
| 100 | 
            -
             | 
| 101 | 
            -
             | 
| 102 | 
            -
                   | 
| 103 | 
            -
             | 
| 53 | 
            +
              class Hackman
         | 
| 54 | 
            +
                def initialize app, options
         | 
| 55 | 
            +
                  @app = app
         | 
| 56 | 
            +
                  @options = options
         | 
| 57 | 
            +
                  @form = app.form
         | 
| 58 | 
            +
                  @hash = nil
         | 
| 59 | 
            +
                  @cache_path = "."
         | 
| 60 | 
            +
                  @toggle_titles_only = false
         | 
| 61 | 
            +
                  @toggle_offline = false
         | 
| 62 | 
            +
                  @logger = @app.logger
         | 
| 63 | 
            +
             | 
| 64 | 
            +
                  @fg = :white
         | 
| 65 | 
            +
                  @_forumlist = %w{ hacker ruby programming scifi science haskell java scala cpp c_programming d_language golang vim emacs unix linux bash zsh commandline vimplugins python ars slashdot }
         | 
| 66 | 
            +
                  @browser_mode = options[:browser_mode] || 'text'
         | 
| 67 | 
            +
                  @browser_text = options[:browser_text] || 'elinks'
         | 
| 68 | 
            +
                  @browser_gui = options[:browser_gui] || 'open'
         | 
| 69 | 
            +
                  @cache_path = options[:cache_path] || "."
         | 
| 70 | 
            +
                  config_file = options[:config_file]
         | 
| 71 | 
            +
                  config_read config_file
         | 
| 72 | 
            +
                  @binding ||= default_bindings
         | 
| 73 | 
            +
                  @color_schemes ||= default_color_schemes
         | 
| 74 | 
            +
                  # we should actually pick the fist, since the name could have changed
         | 
| 75 | 
            +
                  @color_scheme = @color_schemes.values.first
         | 
| 76 | 
            +
                  @forumlist ||= (options[:list] || @_forumlist)
         | 
| 77 | 
            +
                  handle_keys @binding
         | 
| 78 | 
            +
                  #form_bind @binding
         | 
| 79 | 
            +
                  @cache_path = File.expand_path(@cache_path)
         | 
| 80 | 
            +
                end
         | 
| 81 | 
            +
                def config_read config_file=nil
         | 
| 82 | 
            +
                  config_file ||= CONFIG_FILE
         | 
| 83 | 
            +
                  config_file = File.expand_path(config_file)
         | 
| 84 | 
            +
                  if config_file 
         | 
| 85 | 
            +
                    if File.exists? config_file
         | 
| 86 | 
            +
                      #eval(File.open(File.expand_path(config_file)).read)
         | 
| 87 | 
            +
                      obj = YAML::load( File.open( config_file ) )
         | 
| 88 | 
            +
                      #%w{ :binding :forumlist :cache_path}.each do |e|
         | 
| 89 | 
            +
                        #if obj[e]
         | 
| 90 | 
            +
                      obj.keys.each do |e|
         | 
| 91 | 
            +
                          instance_variable_set("@#{e}", obj[e])
         | 
| 92 | 
            +
                      end
         | 
| 93 | 
            +
                    else
         | 
| 94 | 
            +
                      alert "NOT EXISTS config file #{config_file} "
         | 
| 95 | 
            +
                    end
         | 
| 104 96 | 
             
                  end
         | 
| 105 97 | 
             
                end
         | 
| 106 | 
            -
                 | 
| 107 | 
            -
             | 
| 108 | 
            -
             | 
| 109 | 
            -
             | 
| 110 | 
            -
             | 
| 111 | 
            -
             | 
| 112 | 
            -
             | 
| 113 | 
            -
             | 
| 114 | 
            -
             | 
| 115 | 
            -
             | 
| 116 | 
            -
             | 
| 117 | 
            -
             | 
| 118 | 
            -
             | 
| 119 | 
            -
             | 
| 120 | 
            -
             | 
| 121 | 
            -
                 | 
| 122 | 
            -
                 | 
| 123 | 
            -
             | 
| 124 | 
            -
             | 
| 125 | 
            -
             | 
| 126 | 
            -
                   | 
| 127 | 
            -
             | 
| 128 | 
            -
             | 
| 129 | 
            -
             | 
| 130 | 
            -
             | 
| 131 | 
            -
             | 
| 132 | 
            -
             | 
| 133 | 
            -
                 | 
| 134 | 
            -
             | 
| 135 | 
            -
             | 
| 136 | 
            -
                 | 
| 137 | 
            -
                 | 
| 138 | 
            -
                 | 
| 139 | 
            -
             | 
| 140 | 
            -
                 | 
| 141 | 
            -
                 | 
| 142 | 
            -
             | 
| 143 | 
            -
                 | 
| 144 | 
            -
                 | 
| 145 | 
            -
                 | 
| 146 | 
            -
             | 
| 147 | 
            -
             | 
| 148 | 
            -
             | 
| 149 | 
            -
             | 
| 150 | 
            -
             | 
| 151 | 
            -
             | 
| 152 | 
            -
             | 
| 153 | 
            -
             | 
| 154 | 
            -
             | 
| 155 | 
            -
                 | 
| 156 | 
            -
                # trying to pass populists block to listbox
         | 
| 157 | 
            -
                lb = Canis::TextPad.new form, listconfig, &block
         | 
| 158 | 
            -
                if fmt == :none
         | 
| 159 | 
            -
                  lb.text(list)
         | 
| 160 | 
            -
                else
         | 
| 161 | 
            -
                  lb.text(list, fmt)
         | 
| 98 | 
            +
                # save current config to a yml file, so user can modify it
         | 
| 99 | 
            +
                # This is included since its a bit difficult to create this file if you don't remember YML format.
         | 
| 100 | 
            +
                def save_config filename=nil
         | 
| 101 | 
            +
                  unless filename
         | 
| 102 | 
            +
                    filename = get_string "Enter filename to save configuration to:"
         | 
| 103 | 
            +
                    return unless filename
         | 
| 104 | 
            +
                  end
         | 
| 105 | 
            +
                  xx = {}
         | 
| 106 | 
            +
                  [:binding, :forumlist, :browser_gui, :browser_text, :cache_path, :color_schemes, :color_scheme].each do |e|
         | 
| 107 | 
            +
                    xx[e] = instance_variable_get "@#{e}"
         | 
| 108 | 
            +
                  end
         | 
| 109 | 
            +
                  File.open(filename, 'w' ) do |f|
         | 
| 110 | 
            +
                    f << YAML::dump(xx)
         | 
| 111 | 
            +
                  end
         | 
| 112 | 
            +
                  @app.message "Config saved to #{filename} in YML format"
         | 
| 113 | 
            +
                end
         | 
| 114 | 
            +
                def default_color_schemes
         | 
| 115 | 
            +
                  @color_schemes={}
         | 
| 116 | 
            +
                  @color_schemes['deep blue'] = { :header_bg => 20, :menu_bg => 19, :body_bg => 17, :status_bg => 18, :body_fg => :white, 
         | 
| 117 | 
            +
                                                  :body_detail => :green }
         | 
| 118 | 
            +
                  @color_schemes['medium blue'] = { :header_bg => 17, :menu_bg => 19, :body_bg => 18, :status_bg => 20, :body_fg => :white, 
         | 
| 119 | 
            +
                                                    :body_detail => :green }
         | 
| 120 | 
            +
                  @color_schemes['black body'] = { :header_bg => 236, :menu_bg => 236, :body_bg => 0, :status_bg => 232, :body_fg => :white, 
         | 
| 121 | 
            +
                                                   :body_detail => :green }
         | 
| 122 | 
            +
                  @color_schemes['grey body'] = { :header_bg => 236, :menu_bg => 236, :body_bg => 244, :status_bg => 250, :body_fg => :black, 
         | 
| 123 | 
            +
                                                  :body_detail => :green }
         | 
| 124 | 
            +
                  return @color_schemes
         | 
| 125 | 
            +
                end
         | 
| 126 | 
            +
                def articles
         | 
| 127 | 
            +
                  @hash[:articles]
         | 
| 128 | 
            +
                end
         | 
| 129 | 
            +
                # return current color scheme
         | 
| 130 | 
            +
                def color_scheme
         | 
| 131 | 
            +
                  @color_scheme
         | 
| 132 | 
            +
                end
         | 
| 133 | 
            +
                def forumlist
         | 
| 134 | 
            +
                  @forumlist
         | 
| 135 | 
            +
                end
         | 
| 136 | 
            +
                def default_bindings
         | 
| 137 | 
            +
                @binding = {
         | 
| 138 | 
            +
                  "`" => "main_menu",
         | 
| 139 | 
            +
                  "=" => "toggle_menu",
         | 
| 140 | 
            +
                  ">" => "next_forum",
         | 
| 141 | 
            +
                  "<" => "prev_forum",
         | 
| 142 | 
            +
                  "z" => "goto_article",
         | 
| 143 | 
            +
                  "o" => "display_links",
         | 
| 144 | 
            +
                  "<CR>" => "display_links",
         | 
| 145 | 
            +
                  "<C-f>" => "display_links",
         | 
| 146 | 
            +
                  "<F2>" => "choose_forum"
         | 
| 147 | 
            +
                }
         | 
| 162 148 | 
             
                end
         | 
| 149 | 
            +
             | 
| 150 | 
            +
             | 
| 151 | 
            +
                # prompt user to select a forum, and fetch data for it.
         | 
| 152 | 
            +
                def choose_forum
         | 
| 153 | 
            +
                  # scrollable filterable list
         | 
| 154 | 
            +
                  str = display_list @forumlist, :title => "Select a forum"
         | 
| 155 | 
            +
                  return unless str
         | 
| 156 | 
            +
                  return if str == ""
         | 
| 157 | 
            +
                  @current_forum = str
         | 
| 158 | 
            +
                  forum = str
         | 
| 159 | 
            +
                  get_data forum if forum
         | 
| 160 | 
            +
                end
         | 
| 161 | 
            +
                # add a forum at runtime, by default this will be a reddit subforum
         | 
| 162 | 
            +
                def add_forum forum=nil
         | 
| 163 | 
            +
                  unless forum
         | 
| 164 | 
            +
                    forum = get_string "Add a reddit subforum: "
         | 
| 165 | 
            +
                    return if forum.nil? or forum == ""
         | 
| 166 | 
            +
                  end
         | 
| 167 | 
            +
                  @forumlist << forum
         | 
| 168 | 
            +
                  get_data forum
         | 
| 169 | 
            +
                end
         | 
| 170 | 
            +
                def remove_forum forum=nil
         | 
| 171 | 
            +
                  unless forum
         | 
| 172 | 
            +
                    forum = display_list @forumlist, :title => "Select a forum"
         | 
| 173 | 
            +
                    return if forum.nil? or forum == ""
         | 
| 174 | 
            +
                  end
         | 
| 175 | 
            +
                  @forumlist.delete forum
         | 
| 176 | 
            +
                end
         | 
| 177 | 
            +
                def next_forum
         | 
| 178 | 
            +
                  index = @forumlist.index(@current_forum)
         | 
| 179 | 
            +
                  index = index >= @forumlist.count - 1 ? 0 : index + 1
         | 
| 180 | 
            +
                  get_data @forumlist[index]
         | 
| 181 | 
            +
                end
         | 
| 182 | 
            +
                def prev_forum
         | 
| 183 | 
            +
                  index = @forumlist.index(@current_forum)
         | 
| 184 | 
            +
                  index = index == 0? @forumlist.count - 1 : index - 1
         | 
| 185 | 
            +
                  get_data @forumlist[index]
         | 
| 186 | 
            +
                end
         | 
| 187 | 
            +
                # if components have some commands, can we find a way of passing the command to them
         | 
| 188 | 
            +
                # method_missing gave a stack overflow.
         | 
| 189 | 
            +
                def execute_this(meth, *args)
         | 
| 190 | 
            +
                  alert " #{meth} not found ! "
         | 
| 191 | 
            +
                  $log.debug "app email got #{meth}  " if $log.debug? 
         | 
| 192 | 
            +
                  cc = @form.get_current_field
         | 
| 193 | 
            +
                  [cc].each do |c|  
         | 
| 194 | 
            +
                    if c.respond_to?(meth, true)
         | 
| 195 | 
            +
                      c.send(meth, *args)
         | 
| 196 | 
            +
                      return true
         | 
| 197 | 
            +
                    end
         | 
| 198 | 
            +
                  end
         | 
| 199 | 
            +
                  false
         | 
| 200 | 
            +
                end
         | 
| 201 | 
            +
                def open_url url, app
         | 
| 202 | 
            +
                  #shell_out "elinks #{url}"
         | 
| 203 | 
            +
                  shell_out "#{app} #{url}"
         | 
| 204 | 
            +
                  #Window.refresh_all
         | 
| 205 | 
            +
                end
         | 
| 206 | 
            +
             | 
| 207 | 
            +
                ## 
         | 
| 208 | 
            +
                # Menu creator which displays a menu and executes methods based on keys.
         | 
| 209 | 
            +
                # In some cases, we call this and then do a case statement on either key or binding.
         | 
| 210 | 
            +
                # @param String title
         | 
| 211 | 
            +
                # @param hash of keys and methods to call
         | 
| 212 | 
            +
                # @return key pressed, and binding (if found, and responded). Can return NIL nil if esc pressed
         | 
| 163 213 | 
             
                #
         | 
| 164 | 
            -
                 | 
| 165 | 
            -
             | 
| 166 | 
            -
             | 
| 167 | 
            -
             | 
| 168 | 
            -
                   | 
| 169 | 
            -
                   | 
| 170 | 
            -
             | 
| 171 | 
            -
             | 
| 172 | 
            -
             | 
| 173 | 
            -
                   | 
| 174 | 
            -
             | 
| 175 | 
            -
             | 
| 176 | 
            -
             | 
| 177 | 
            -
             | 
| 178 | 
            -
             | 
| 179 | 
            -
             | 
| 180 | 
            -
             | 
| 181 | 
            -
             | 
| 214 | 
            +
                def menu title, hash, config={}, &block
         | 
| 215 | 
            +
                  raise ArgumentError, "Nil hash received by menu" unless hash
         | 
| 216 | 
            +
                  list = []
         | 
| 217 | 
            +
                  list << config[:subtitle] if config[:subtitle]
         | 
| 218 | 
            +
                  config.delete(:subtitle)
         | 
| 219 | 
            +
                  hash.each_pair { |k, v| list << " #[fg=yellow, bold] #{k} #[/end]    #[fg=green] #{v} #[/end]" }
         | 
| 220 | 
            +
                  #  s="#[fg=green]hello there#[fg=yellow, bg=black, dim]"
         | 
| 221 | 
            +
                  config[:title] = title
         | 
| 222 | 
            +
                  config[:width] = hash.values.max_by(&:length).length + 13
         | 
| 223 | 
            +
                  # need to have a proper check, which takes +left+ / column into account
         | 
| 224 | 
            +
                  config[:width] = FFI::NCurses.COLS - 10 if config[:width] > FFI::NCurses.COLS
         | 
| 225 | 
            +
                  ch = padpopup list, config, &block
         | 
| 226 | 
            +
                  return unless ch
         | 
| 227 | 
            +
                  if ch.size > 1
         | 
| 228 | 
            +
                    # could be a string due to pressing enter
         | 
| 229 | 
            +
                    # but what if we format into multiple columns
         | 
| 230 | 
            +
                    ch = ch.strip[0]
         | 
| 231 | 
            +
                  end
         | 
| 232 | 
            +
             | 
| 233 | 
            +
                  binding = hash[ch]
         | 
| 234 | 
            +
                  binding = hash[ch.to_sym] unless binding
         | 
| 235 | 
            +
                  if binding
         | 
| 236 | 
            +
                    if respond_to?(binding, true)
         | 
| 237 | 
            +
                      send(binding)
         | 
| 182 238 | 
             
                    end
         | 
| 239 | 
            +
                  end
         | 
| 240 | 
            +
                  return ch, binding
         | 
| 241 | 
            +
                end
         | 
| 242 | 
            +
                # pops up a list, taking a single key and returning if it is in range of 33 and 126
         | 
| 243 | 
            +
                # Called by menu, print_help, show_marks etc
         | 
| 244 | 
            +
                # You may pass valid chars or ints so it only returns on pressing those.
         | 
| 245 | 
            +
                #
         | 
| 246 | 
            +
                # @param Array of lines to print which may be formatted using :tmux format
         | 
| 247 | 
            +
                # @return character pressed (ch.chr)
         | 
| 248 | 
            +
                # @return nil if escape or C-q pressed
         | 
| 249 | 
            +
                #
         | 
| 250 | 
            +
                def padpopup list, config={}, &block
         | 
| 251 | 
            +
                  max_visible_items = config[:max_visible_items]
         | 
| 252 | 
            +
                  row = config[:row] || 1
         | 
| 253 | 
            +
                  col = config[:col] || 1
         | 
| 254 | 
            +
                  # format options are :ansi :tmux :none
         | 
| 255 | 
            +
                  fmt = config[:format] || :tmux
         | 
| 256 | 
            +
                  config.delete :format
         | 
| 257 | 
            +
                  relative_to = config[:relative_to]
         | 
| 258 | 
            +
                  if relative_to
         | 
| 259 | 
            +
                    layout = relative_to.form.window.layout
         | 
| 260 | 
            +
                    row += layout[:top]
         | 
| 261 | 
            +
                    col += layout[:left]
         | 
| 262 | 
            +
                  end
         | 
| 263 | 
            +
                  config.delete :relative_to
         | 
| 264 | 
            +
                  # still has the formatting in the string so length is wrong.
         | 
| 265 | 
            +
                  #longest = list.max_by(&:length)
         | 
| 266 | 
            +
                  width = config[:width] || 60
         | 
| 267 | 
            +
                  if config[:title]
         | 
| 268 | 
            +
                    width = config[:title].size + 2 if width < config[:title].size
         | 
| 269 | 
            +
                  end
         | 
| 270 | 
            +
                  height = config[:height]
         | 
| 271 | 
            +
                  height ||= [max_visible_items || 25, list.length+2].min 
         | 
| 272 | 
            +
                  #layout(1+height, width+4, row, col) 
         | 
| 273 | 
            +
                  layout = { :height => 0+height, :width => 0+width, :top => row, :left => col } 
         | 
| 274 | 
            +
                  window = Canis::Window.new(layout)
         | 
| 275 | 
            +
                  form = Canis::Form.new window
         | 
| 183 276 |  | 
| 184 | 
            -
             | 
| 185 | 
            -
             | 
| 186 | 
            -
             | 
| 277 | 
            +
                  ## added 2013-03-13 - 18:07 so caller can be more specific on what is to be returned
         | 
| 278 | 
            +
                  valid_keys_int = config.delete :valid_keys_int
         | 
| 279 | 
            +
                  valid_keys_char = config.delete :valid_keys_char
         | 
| 187 280 |  | 
| 188 | 
            -
             | 
| 189 | 
            -
             | 
| 190 | 
            -
             | 
| 191 | 
            -
             | 
| 192 | 
            -
             | 
| 193 | 
            -
             | 
| 194 | 
            -
             | 
| 281 | 
            +
                  listconfig = config[:listconfig] || {}
         | 
| 282 | 
            +
                  #listconfig[:list] = list
         | 
| 283 | 
            +
                  listconfig[:width] = width 
         | 
| 284 | 
            +
                  listconfig[:height] = height 
         | 
| 285 | 
            +
                  # pass this in config so less dependences
         | 
| 286 | 
            +
                  listconfig[:bgcolor] = @color_scheme[:menu_bg]
         | 
| 287 | 
            +
                  #listconfig[:selection_mode] ||= :single
         | 
| 288 | 
            +
                  listconfig.merge!(config)
         | 
| 289 | 
            +
                  listconfig.delete(:row); 
         | 
| 290 | 
            +
                  listconfig.delete(:col); 
         | 
| 291 | 
            +
                  #listconfig[:row] = 1
         | 
| 292 | 
            +
                  #listconfig[:col] = 1
         | 
| 293 | 
            +
                  # trying to pass populists block to listbox
         | 
| 294 | 
            +
                  lb = Canis::TextPad.new form, listconfig, &block
         | 
| 295 | 
            +
                  if fmt == :none
         | 
| 296 | 
            +
                    lb.text(list)
         | 
| 297 | 
            +
                  else
         | 
| 298 | 
            +
                    lb.text(list, fmt)
         | 
| 299 | 
            +
                  end
         | 
| 300 | 
            +
                  #
         | 
| 301 | 
            +
                  #window.bkgd(Ncurses.COLOR_PAIR($reversecolor));
         | 
| 302 | 
            +
                  form.repaint
         | 
| 303 | 
            +
                  Ncurses::Panel.update_panels
         | 
| 304 | 
            +
                  if valid_keys_int.nil? && valid_keys_char.nil?
         | 
| 305 | 
            +
                    # changed 32 to 33 so space can scroll list
         | 
| 306 | 
            +
                    valid_keys_int = (33..126)
         | 
| 307 | 
            +
                  end
         | 
| 308 | 
            +
             | 
| 309 | 
            +
                  begin
         | 
| 310 | 
            +
                    while((ch = window.getchar()) != 999 )
         | 
| 311 | 
            +
             | 
| 312 | 
            +
                      # if a char range or array has been sent, check if the key is in it and send back
         | 
| 313 | 
            +
                      # else just stay here
         | 
| 314 | 
            +
                      if valid_keys_char
         | 
| 315 | 
            +
                        if ch > 32 && ch < 127
         | 
| 316 | 
            +
                          chr = ch.chr
         | 
| 317 | 
            +
                          return chr if valid_keys_char.include? chr
         | 
| 318 | 
            +
                        end
         | 
| 195 319 | 
             
                      end
         | 
| 196 | 
            -
             | 
| 197 | 
            -
                      if  | 
| 198 | 
            -
             | 
| 320 | 
            +
             | 
| 321 | 
            +
                      # if the user specified an array or range of ints check against that
         | 
| 322 | 
            +
                      # therwise use the range of 33 .. 126
         | 
| 323 | 
            +
                      return ch.chr if valid_keys_int.include? ch
         | 
| 324 | 
            +
             | 
| 325 | 
            +
                      case ch
         | 
| 326 | 
            +
                      when ?\C-q.getbyte(0)
         | 
| 327 | 
            +
                        break
         | 
| 328 | 
            +
                      else
         | 
| 329 | 
            +
                        if ch == 13 || ch == 10
         | 
| 330 | 
            +
                          s = lb.current_value.to_s # .strip #if lb.selection_mode != :multiple
         | 
| 331 | 
            +
                          return s
         | 
| 332 | 
            +
                        end
         | 
| 333 | 
            +
                        # close if escape or double escape
         | 
| 334 | 
            +
                        if ch == 27 || ch == 2727
         | 
| 335 | 
            +
                          return nil
         | 
| 336 | 
            +
                        end
         | 
| 337 | 
            +
                        lb.handle_key ch
         | 
| 338 | 
            +
                        form.repaint
         | 
| 199 339 | 
             
                      end
         | 
| 200 | 
            -
                      lb.handle_key ch
         | 
| 201 | 
            -
                      form.repaint
         | 
| 202 340 | 
             
                    end
         | 
| 341 | 
            +
                  ensure
         | 
| 342 | 
            +
                    window.destroy  
         | 
| 203 343 | 
             
                  end
         | 
| 204 | 
            -
             | 
| 205 | 
            -
                  window.destroy  
         | 
| 344 | 
            +
                  return nil
         | 
| 206 345 | 
             
                end
         | 
| 207 | 
            -
                 | 
| 208 | 
            -
             | 
| 209 | 
            -
             | 
| 210 | 
            -
             | 
| 211 | 
            -
            def main_menu
         | 
| 212 | 
            -
             | 
| 213 | 
            -
             | 
| 214 | 
            -
             | 
| 215 | 
            -
             | 
| 216 | 
            -
             | 
| 217 | 
            -
             | 
| 218 | 
            -
             | 
| 219 | 
            -
             | 
| 220 | 
            -
             | 
| 221 | 
            -
             | 
| 222 | 
            -
             | 
| 223 | 
            -
              h = { 
         | 
| 224 | 
            -
                "t" => :toggle_titles_only,
         | 
| 225 | 
            -
                :x => :extras
         | 
| 226 | 
            -
              }
         | 
| 227 | 
            -
              ch, binding = menu "Main Menu", h
         | 
| 228 | 
            -
              #alert "Menu got #{ch}, #{binding}" if ch
         | 
| 229 | 
            -
            end
         | 
| 230 | 
            -
            def color_scheme_select ch=nil
         | 
| 231 | 
            -
              unless ch
         | 
| 232 | 
            -
                h = { 
         | 
| 233 | 
            -
                  "0" => 'dark blue body',
         | 
| 234 | 
            -
                  "1" => 'medium blue body',
         | 
| 235 | 
            -
                  "2" => 'black body',
         | 
| 236 | 
            -
                  "3" => 'grey body',
         | 
| 237 | 
            -
                  "b" => 'change body color',
         | 
| 238 | 
            -
                  "f" => 'change body fg color',
         | 
| 239 | 
            -
                  "c" => 'cycle body color'
         | 
| 240 | 
            -
                }
         | 
| 241 | 
            -
                ch, binding = menu "Color Menu", h
         | 
| 242 | 
            -
              end
         | 
| 243 | 
            -
              case ch
         | 
| 244 | 
            -
              when "1", "2", "0", "3"
         | 
| 245 | 
            -
                $color_scheme = COLOR_SCHEMES[ch.to_i] || COLOR_SCHEMES.first
         | 
| 246 | 
            -
                $fg = $color_scheme[4]
         | 
| 247 | 
            -
              when "b"
         | 
| 248 | 
            -
                n = get_string "Enter a number for background color (0..255): "
         | 
| 249 | 
            -
                n = n.to_i
         | 
| 250 | 
            -
                $color_scheme[2] = n
         | 
| 251 | 
            -
              when "4", "f"
         | 
| 252 | 
            -
                n = get_string "Enter a number for fg color (0..255) : "
         | 
| 253 | 
            -
                $fg = n.to_i
         | 
| 254 | 
            -
              when "c"
         | 
| 255 | 
            -
                # increment bg color
         | 
| 256 | 
            -
                n = $color_scheme[2]
         | 
| 257 | 
            -
                n += 1
         | 
| 258 | 
            -
                n = 0 if n > 255
         | 
| 259 | 
            -
                $color_scheme[2] = n
         | 
| 260 | 
            -
              when "C"
         | 
| 261 | 
            -
                # decrement bg color
         | 
| 262 | 
            -
                n = $color_scheme[2]
         | 
| 263 | 
            -
                n -= 1
         | 
| 264 | 
            -
                n = 255 if n < 0
         | 
| 265 | 
            -
                $color_scheme[2] = n
         | 
| 266 | 
            -
              end
         | 
| 267 | 
            -
             | 
| 268 | 
            -
              h = @form.by_name["header"]
         | 
| 269 | 
            -
              tv = @form.by_name["tv"]
         | 
| 270 | 
            -
              sl = @form.by_name["sl"]
         | 
| 271 | 
            -
              tv.bgcolor = $color_scheme[2]
         | 
| 272 | 
            -
              #tv.color = 255
         | 
| 273 | 
            -
              tv.color = $fg
         | 
| 274 | 
            -
              sl.color = $color_scheme[3]
         | 
| 275 | 
            -
              h.bgcolor = $color_scheme[0]
         | 
| 276 | 
            -
              message "bgcolor is #{$color_scheme[2]}. :: #{$color_scheme.join(",")}, CP:#{tv.color_pair}=#{tv.color} / #{tv.bgcolor} "
         | 
| 277 | 
            -
              refresh
         | 
| 278 | 
            -
            end
         | 
| 279 | 
            -
            def refresh
         | 
| 280 | 
            -
              show $current_file
         | 
| 281 | 
            -
            end
         | 
| 282 | 
            -
             | 
| 283 | 
            -
            def toggle_titles_only
         | 
| 284 | 
            -
              $toggle_titles_only = !$toggle_titles_only
         | 
| 285 | 
            -
              show $current_file
         | 
| 286 | 
            -
            end
         | 
| 287 | 
            -
            App.new do 
         | 
| 288 | 
            -
              @startdir ||= File.expand_path("..")
         | 
| 289 | 
            -
              @hash = nil
         | 
| 290 | 
            -
              def get_item_for_line line
         | 
| 291 | 
            -
                index = (line - @hash[:first]) / @hash[:diff]
         | 
| 292 | 
            -
                @hash[:articles][index]
         | 
| 293 | 
            -
              end
         | 
| 294 | 
            -
              def title_right text
         | 
| 295 | 
            -
                w = @form.by_name["header"]
         | 
| 296 | 
            -
                w.text_right text
         | 
| 297 | 
            -
              end
         | 
| 298 | 
            -
              def title text
         | 
| 299 | 
            -
                w = @form.by_name["header"]
         | 
| 300 | 
            -
                w.text_center text
         | 
| 301 | 
            -
              end
         | 
| 302 | 
            -
              def color_line(fg,bg,attr,text)
         | 
| 303 | 
            -
                a = "#["
         | 
| 304 | 
            -
                a = []
         | 
| 305 | 
            -
                a << "fg=#{fg}" if fg
         | 
| 306 | 
            -
                a << "bg=#{bg}" if bg
         | 
| 307 | 
            -
                a << "#{attr}" if attr
         | 
| 308 | 
            -
                str = "#[" + a.join(",") + "]#{text}#[end]"
         | 
| 309 | 
            -
              end
         | 
| 310 | 
            -
              def goto_article n=$multiplier
         | 
| 311 | 
            -
                i = ((n-1) * @hash[:diff]) +  @hash[:first] 
         | 
| 312 | 
            -
                w = @form.by_name["tv"]
         | 
| 313 | 
            -
                w.goto_line i
         | 
| 314 | 
            -
              end
         | 
| 315 | 
            -
             | 
| 316 | 
            -
              def OLDshow file
         | 
| 317 | 
            -
                w = @form.by_name["tv"]
         | 
| 318 | 
            -
                if File.directory? file
         | 
| 319 | 
            -
                  lines = Dir.entries(file)
         | 
| 320 | 
            -
                  w.text lines
         | 
| 321 | 
            -
                  w.title "[ #{file} ]"
         | 
| 322 | 
            -
                elsif File.exists? file
         | 
| 323 | 
            -
                  lines = File.open(file,'r').readlines 
         | 
| 324 | 
            -
                  w.text lines
         | 
| 325 | 
            -
                  w.title "[ #{file} ]"
         | 
| 346 | 
            +
                # main options, invokable on backtick.
         | 
| 347 | 
            +
                # TODO add selection of browser
         | 
| 348 | 
            +
                # r for reload
         | 
| 349 | 
            +
                # 1,2 a c view article, comments
         | 
| 350 | 
            +
                def main_menu
         | 
| 351 | 
            +
                  h = { 
         | 
| 352 | 
            +
                    :f => :choose_forum,
         | 
| 353 | 
            +
                    :c => :color_scheme_select,
         | 
| 354 | 
            +
                    #:s => :sort_menu, 
         | 
| 355 | 
            +
                    #:F => :filter_menu,
         | 
| 356 | 
            +
                    :a => :add_forum,
         | 
| 357 | 
            +
                    :d => :remove_forum,
         | 
| 358 | 
            +
                    :x => :extras
         | 
| 359 | 
            +
                  }
         | 
| 360 | 
            +
                  ch, binding = menu "Main Menu", h
         | 
| 361 | 
            +
                  #alert "Menu got #{ch}, #{binding}" if ch
         | 
| 326 362 | 
             
                end
         | 
| 327 | 
            -
             | 
| 328 | 
            -
             | 
| 329 | 
            -
             | 
| 330 | 
            -
             | 
| 331 | 
            -
             | 
| 332 | 
            -
             | 
| 333 | 
            -
             | 
| 334 | 
            -
             | 
| 335 | 
            -
             | 
| 336 | 
            -
             | 
| 337 | 
            -
                 | 
| 338 | 
            -
             | 
| 339 | 
            -
             | 
| 340 | 
            -
             | 
| 341 | 
            -
             | 
| 342 | 
            -
             | 
| 343 | 
            -
             | 
| 344 | 
            -
             | 
| 345 | 
            -
                else
         | 
| 346 | 
            -
                  alert "Host not known: #{url} "
         | 
| 347 | 
            -
                end
         | 
| 348 | 
            -
                articles = obj[:articles]
         | 
| 349 | 
            -
                count = articles.count
         | 
| 350 | 
            -
                #lines << color_line(:red,COLOR_SCHEME[1],nil,"#{file}  #{obj[:page_url]}  |  #{count} articles | fetched  #{obj[:create_time]}")
         | 
| 351 | 
            -
                #lines << ("-" * lines.last.size )
         | 
| 352 | 
            -
                @hash = Hash.new
         | 
| 353 | 
            -
                @hash[:first] = lines.size
         | 
| 354 | 
            -
                @hash[:articles] = articles
         | 
| 355 | 
            -
             | 
| 356 | 
            -
                articles.each_with_index do |a, i|
         | 
| 357 | 
            -
                  bg = i
         | 
| 358 | 
            -
                  bg = 0 if i > 255
         | 
| 359 | 
            -
                  line = "%3s  %s  " % [i+1 , a[:title] ]
         | 
| 360 | 
            -
                  #lines << color_line($fg, bg, nil, line)
         | 
| 361 | 
            -
                  lines << line
         | 
| 362 | 
            -
                  if !$toggle_titles_only
         | 
| 363 | 
            -
                    url = a[:article_url] || a[:url]
         | 
| 364 | 
            -
                    l = "        %s | %s" % [url, a[:comments_url] ]
         | 
| 365 | 
            -
                    l = "#[fg=green, underline]" + l + "#[end]"
         | 
| 366 | 
            -
                    lines << l
         | 
| 367 | 
            -
                    detail = []
         | 
| 368 | 
            -
                    if a.key? :comment_count
         | 
| 369 | 
            -
                      detail << a[:comment_count]
         | 
| 363 | 
            +
                # TODO uses text browser t, use gui browser g
         | 
| 364 | 
            +
                # l - long list (what is currently t)
         | 
| 365 | 
            +
                def toggle_menu
         | 
| 366 | 
            +
                  h = { 
         | 
| 367 | 
            +
                    "t" => :toggle_titles_only,
         | 
| 368 | 
            +
                    "O" => :toggle_offline
         | 
| 369 | 
            +
                    #:x => :extras
         | 
| 370 | 
            +
                  }
         | 
| 371 | 
            +
                  ch, binding = menu "Main Menu", h
         | 
| 372 | 
            +
                  #alert "Menu got #{ch}, #{binding}" if ch
         | 
| 373 | 
            +
                end
         | 
| 374 | 
            +
                def color_scheme_select ch=nil
         | 
| 375 | 
            +
                  unless ch
         | 
| 376 | 
            +
                    h = {}
         | 
| 377 | 
            +
                    ctr = 0
         | 
| 378 | 
            +
                    @color_schemes.each_pair do |k,v|
         | 
| 379 | 
            +
                      ctr += 1
         | 
| 380 | 
            +
                      h[ctr.to_s] = k
         | 
| 370 381 | 
             
                    end
         | 
| 371 | 
            -
             | 
| 372 | 
            -
             | 
| 382 | 
            +
             | 
| 383 | 
            +
                    h = h.merge({ 
         | 
| 384 | 
            +
                      #"0" => 'dark blue body',
         | 
| 385 | 
            +
                      #"1" => 'medium blue body',
         | 
| 386 | 
            +
                      #"2" => 'black body',
         | 
| 387 | 
            +
                      #"3" => 'grey body',
         | 
| 388 | 
            +
                      "b" => 'change body color',
         | 
| 389 | 
            +
                      "f" => 'change body fg color',
         | 
| 390 | 
            +
                      "d" => 'change body detail color',
         | 
| 391 | 
            +
                      "c" => 'cycle body color'
         | 
| 392 | 
            +
                    })
         | 
| 393 | 
            +
                    ch, binding = menu "Color Menu", h
         | 
| 394 | 
            +
                  end
         | 
| 395 | 
            +
                  case ch
         | 
| 396 | 
            +
                  when "1", "2", "0", "3","4","5","6"
         | 
| 397 | 
            +
                    @color_scheme = @color_schemes[binding]
         | 
| 398 | 
            +
                    @fg = @color_scheme[:body_fg]
         | 
| 399 | 
            +
                  when "b"
         | 
| 400 | 
            +
                    n = get_string "Enter a number for background color (0..255): "
         | 
| 401 | 
            +
                    unless n =~ /^\d+$/
         | 
| 402 | 
            +
                      n = Canis::ColorMap.colors.index(n.to_sym)
         | 
| 403 | 
            +
                      return unless n
         | 
| 373 404 | 
             
                    end
         | 
| 374 | 
            -
                     | 
| 375 | 
            -
             | 
| 376 | 
            -
             | 
| 405 | 
            +
                    n = n.to_i
         | 
| 406 | 
            +
                    @color_scheme[:body_bg] = n
         | 
| 407 | 
            +
                  when "f"
         | 
| 408 | 
            +
                    n = get_string "Enter a number for fg color (0..255) : "
         | 
| 409 | 
            +
                    unless n =~ /^\d+$/
         | 
| 410 | 
            +
                      n = Canis::ColorMap.colors.index(n.to_sym)
         | 
| 411 | 
            +
                      return unless n
         | 
| 377 412 | 
             
                    end
         | 
| 413 | 
            +
                    @fg = n.to_i
         | 
| 414 | 
            +
                    @color_scheme[:body_fg] = n.to_i
         | 
| 415 | 
            +
                  when "d"
         | 
| 416 | 
            +
                    n = get_string "Enter a number for detail line color (0..255): "
         | 
| 417 | 
            +
                    unless n =~ /^\d+$/
         | 
| 418 | 
            +
                      n = Canis::ColorMap.colors.index(n.to_sym)
         | 
| 419 | 
            +
                      return unless n
         | 
| 420 | 
            +
                    end
         | 
| 421 | 
            +
                    n = n.to_i
         | 
| 422 | 
            +
                    @color_scheme[:body_detail] = n
         | 
| 423 | 
            +
                  when "c"
         | 
| 424 | 
            +
                    # increment bg color
         | 
| 425 | 
            +
                    n = @color_scheme[:body_bg]
         | 
| 426 | 
            +
                    n += 1
         | 
| 427 | 
            +
                    n = 0 if n > 255
         | 
| 428 | 
            +
                    @color_scheme[:body_bg] = n
         | 
| 429 | 
            +
                  when "C"
         | 
| 430 | 
            +
                    # decrement bg color
         | 
| 431 | 
            +
                    n = @color_scheme[:body_bg]
         | 
| 432 | 
            +
                    n -= 1
         | 
| 433 | 
            +
                    n = 255 if n < 0
         | 
| 434 | 
            +
                    @color_scheme[:body_bg] = n
         | 
| 378 435 | 
             
                  end
         | 
| 379 | 
            -
             | 
| 436 | 
            +
             | 
| 437 | 
            +
                  h = @form.by_name["header"]
         | 
| 438 | 
            +
                  tv = @form.by_name["tv"]
         | 
| 439 | 
            +
                  sl = @form.by_name["sl"]
         | 
| 440 | 
            +
                  tv.bgcolor = @color_scheme[:body_bg]
         | 
| 441 | 
            +
                  #tv.color = 255
         | 
| 442 | 
            +
                  tv.color = @fg
         | 
| 443 | 
            +
                  sl.color = @color_scheme[:status_bg]
         | 
| 444 | 
            +
                  h.bgcolor = @color_scheme[:header_bg]
         | 
| 445 | 
            +
                  #@app.message "bgcolor is #{@color_scheme[:body_bg]}. :: #{@color_scheme.join(",")}, CP:#{tv.color_pair}=#{tv.color} / #{tv.bgcolor} "
         | 
| 446 | 
            +
                  refresh
         | 
| 447 | 
            +
                end
         | 
| 448 | 
            +
                def extras
         | 
| 449 | 
            +
                  h = { 
         | 
| 450 | 
            +
                    "s" => :save_config
         | 
| 451 | 
            +
                  }
         | 
| 452 | 
            +
                  ch, binding = menu "Extras ", h
         | 
| 453 | 
            +
                end
         | 
| 454 | 
            +
                def refresh
         | 
| 455 | 
            +
                  display_yml @current_file
         | 
| 380 456 | 
             
                end
         | 
| 381 | 
            -
                w.text(lines, :content_type =>  :tmux)
         | 
| 382 | 
            -
                w.title "[ #{file} ]"
         | 
| 383 457 |  | 
| 384 | 
            -
                 | 
| 385 | 
            -
             | 
| 386 | 
            -
             | 
| 387 | 
            -
                 | 
| 388 | 
            -
                 | 
| 389 | 
            -
             | 
| 390 | 
            -
             | 
| 391 | 
            -
             | 
| 392 | 
            -
             | 
| 393 | 
            -
                 | 
| 394 | 
            -
             | 
| 395 | 
            -
             | 
| 396 | 
            -
             | 
| 397 | 
            -
             | 
| 398 | 
            -
             | 
| 399 | 
            -
             | 
| 458 | 
            +
                def toggle_titles_only
         | 
| 459 | 
            +
                  @toggle_titles_only = !@toggle_titles_only
         | 
| 460 | 
            +
                  show @current_file
         | 
| 461 | 
            +
                end
         | 
| 462 | 
            +
                def toggle_offline
         | 
| 463 | 
            +
                  @toggle_offline = !@toggle_offline
         | 
| 464 | 
            +
                end
         | 
| 465 | 
            +
                # moved from inside App
         | 
| 466 | 
            +
                #
         | 
| 467 | 
            +
                def get_item_for_line line
         | 
| 468 | 
            +
                  index = (line - @hash[:first]) / @hash[:diff]
         | 
| 469 | 
            +
                  @hash[:articles][index]
         | 
| 470 | 
            +
                end
         | 
| 471 | 
            +
                def title_right text
         | 
| 472 | 
            +
                  w = @form.by_name["header"]
         | 
| 473 | 
            +
                  w.text_right text
         | 
| 474 | 
            +
                end
         | 
| 475 | 
            +
                def title text
         | 
| 476 | 
            +
                  w = @form.by_name["header"]
         | 
| 477 | 
            +
                  w.text_center text
         | 
| 478 | 
            +
                end
         | 
| 479 | 
            +
                def color_line(fg,bg,attr,text)
         | 
| 480 | 
            +
                  a = "#["
         | 
| 481 | 
            +
                  a = []
         | 
| 482 | 
            +
                  a << "fg=#{fg}" if fg
         | 
| 483 | 
            +
                  a << "bg=#{bg}" if bg
         | 
| 484 | 
            +
                  a << "#{attr}" if attr
         | 
| 485 | 
            +
                  str = "#[" + a.join(",") + "]#{text}#[end]"
         | 
| 486 | 
            +
                end
         | 
| 487 | 
            +
                def goto_article n=$multiplier
         | 
| 488 | 
            +
                  i = ((n-1) * @hash[:diff]) +  @hash[:first] 
         | 
| 489 | 
            +
                  w = @form.by_name["tv"]
         | 
| 490 | 
            +
                  w.goto_line i
         | 
| 491 | 
            +
                end
         | 
| 492 | 
            +
                def display_links
         | 
| 493 | 
            +
                  # if multiplier is 0, use current line
         | 
| 494 | 
            +
                  art =  self.articles[$multiplier - 1]
         | 
| 495 | 
            +
                  if $multiplier == 0
         | 
| 496 | 
            +
                    tv = @form.by_name["tv"]
         | 
| 497 | 
            +
                    index = tv.current_index
         | 
| 498 | 
            +
                    art = get_item_for_line index
         | 
| 400 499 | 
             
                  end
         | 
| 500 | 
            +
                  show_links art
         | 
| 401 501 | 
             
                end
         | 
| 402 | 
            -
             | 
| 403 | 
            -
             | 
| 404 | 
            -
             | 
| 405 | 
            -
             | 
| 406 | 
            -
             | 
| 407 | 
            -
             | 
| 408 | 
            -
             | 
| 409 | 
            -
             | 
| 410 | 
            -
             | 
| 502 | 
            +
             | 
| 503 | 
            +
                # display the given yml file. 
         | 
| 504 | 
            +
                # Converts the yml object to an array for textpad
         | 
| 505 | 
            +
                def display_yml file
         | 
| 506 | 
            +
                  w = @form.by_name["tv"]
         | 
| 507 | 
            +
             | 
| 508 | 
            +
                  obj = YAML::load( File.open( file ) )
         | 
| 509 | 
            +
                  lines = Array.new
         | 
| 510 | 
            +
                  url = obj[:page_url]
         | 
| 511 | 
            +
                  host = nil
         | 
| 512 | 
            +
                  if url.index("reddit")
         | 
| 513 | 
            +
                    host = "reddit"
         | 
| 514 | 
            +
                  elsif url.index("ycombinator")
         | 
| 515 | 
            +
                    host = "hacker"
         | 
| 516 | 
            +
                  elsif url.index("ars")
         | 
| 517 | 
            +
                    host = "ars"
         | 
| 518 | 
            +
                  elsif url.index("slashdot")
         | 
| 519 | 
            +
                    host = "slashdot"
         | 
| 520 | 
            +
                  else
         | 
| 521 | 
            +
                    alert "Host not known: #{url} "
         | 
| 522 | 
            +
                  end
         | 
| 523 | 
            +
                  articles = obj[:articles]
         | 
| 524 | 
            +
                  count = articles.count
         | 
| 525 | 
            +
                  #lines << color_line(:red,COLOR_SCHEME[1],nil,"#{file}  #{obj[:page_url]}  |  #{count} articles | fetched  #{obj[:create_time]}")
         | 
| 526 | 
            +
                  #lines << ("-" * lines.last.size )
         | 
| 527 | 
            +
                  @hash = Hash.new
         | 
| 528 | 
            +
                  @hash[:first] = lines.size
         | 
| 529 | 
            +
                  @hash[:articles] = articles
         | 
| 530 | 
            +
                  dc = @color_scheme[:body_detail]
         | 
| 531 | 
            +
             | 
| 532 | 
            +
                  articles.each_with_index do |a, i|
         | 
| 533 | 
            +
                    bg = i
         | 
| 534 | 
            +
                    bg = 0 if i > 255
         | 
| 535 | 
            +
                    line = "%3s  %s  " % [i+1 , a[:title] ]
         | 
| 536 | 
            +
                    #lines << color_line(@fg, bg, nil, line)
         | 
| 537 | 
            +
                    lines << line
         | 
| 538 | 
            +
                    if !@toggle_titles_only
         | 
| 539 | 
            +
                      line1 = []
         | 
| 540 | 
            +
                      line2 = []
         | 
| 541 | 
            +
                      url = a[:article_url] || a[:url]
         | 
| 542 | 
            +
                      line1 << url
         | 
| 543 | 
            +
                      #l = "        %s | %s" % [url, a[:comments_url] ]
         | 
| 544 | 
            +
                      line2 << a[:comments_url] if a[:comments_url]
         | 
| 545 | 
            +
                      #l = "#[fg=#{dc}, underline]" + l + "#[end]"
         | 
| 546 | 
            +
                      #lines << l
         | 
| 547 | 
            +
                      #detail = []
         | 
| 548 | 
            +
                      if a.key? :comment_count
         | 
| 549 | 
            +
                        #detail << a[:comment_count]
         | 
| 550 | 
            +
                        line1 << a[:comment_count]
         | 
| 551 | 
            +
                      end
         | 
| 552 | 
            +
                      if a.key? :pubdate
         | 
| 553 | 
            +
                        #detail << a[:pubdate]
         | 
| 554 | 
            +
                        line2 << a[:pubdate]
         | 
| 555 | 
            +
                      end
         | 
| 556 | 
            +
                      #unless detail.empty?
         | 
| 557 | 
            +
                        l =  "#[fg=#{dc}]" + "         " + line1.join(" | ") + "#[end]"
         | 
| 558 | 
            +
                        lines << l
         | 
| 559 | 
            +
                        l =  "#[fg=#{dc}]" + "         " + line2.join(" | ") + "#[end]"
         | 
| 560 | 
            +
                        lines << l
         | 
| 561 | 
            +
                      #end
         | 
| 562 | 
            +
                    end
         | 
| 563 | 
            +
                    @hash[:diff] ||= lines.size - @hash[:first]
         | 
| 564 | 
            +
                  end
         | 
| 565 | 
            +
                  w.text(lines, :content_type =>  :tmux)
         | 
| 566 | 
            +
                  w.title "[ #{file} ]"
         | 
| 567 | 
            +
             | 
| 568 | 
            +
                  i = @hash[:first] || 1
         | 
| 569 | 
            +
                  w.goto_line i
         | 
| 570 | 
            +
                  @current_file = file
         | 
| 571 | 
            +
                  #@current_forum = file_to_forum file
         | 
| 572 | 
            +
                  title "#{@current_forum} (#{count} articles) "
         | 
| 573 | 
            +
                  title_right obj[:create_time]
         | 
| 574 | 
            +
                end
         | 
| 575 | 
            +
                def file_to_forum filename
         | 
| 576 | 
            +
                  forum = File.basename(filename).sub(File.extname(filename),"").sub("__","/")
         | 
| 577 | 
            +
                end
         | 
| 578 | 
            +
                def forum_to_file forum
         | 
| 579 | 
            +
                  file = "#{forum}.yml".sub("/","__")
         | 
| 580 | 
            +
                  file = "#{@cache_path}/#{file}"
         | 
| 581 | 
            +
                end
         | 
| 582 | 
            +
                alias :show :display_yml
         | 
| 583 | 
            +
                def get_data forum
         | 
| 584 | 
            +
                  #file = forum + ".yml"
         | 
| 585 | 
            +
                  file = forum_to_file forum
         | 
| 586 | 
            +
                  if File.exists? file and fresh?(file)
         | 
| 587 | 
            +
                  else
         | 
| 588 | 
            +
                    progress_dialog :color_pair => $reversecolor do |sw|
         | 
| 589 | 
            +
                      sw.print "Fetching #{forum} ..."
         | 
| 590 | 
            +
                      #system("hackercli.rb -y #{file} #{forum}")
         | 
| 591 | 
            +
                      o,e,s = Open3.capture3("hackercli.rb -y #{file} #{forum}")
         | 
| 592 | 
            +
                      unless s.success?
         | 
| 593 | 
            +
                        $log.debug "  error from capture3 #{e}"
         | 
| 594 | 
            +
                        alert e
         | 
| 595 | 
            +
                        return
         | 
| 596 | 
            +
                      end
         | 
| 597 | 
            +
                    end
         | 
| 598 | 
            +
                  end
         | 
| 599 | 
            +
                  if File.exists? file
         | 
| 600 | 
            +
                    @current_forum = forum
         | 
| 601 | 
            +
                    display_yml file
         | 
| 602 | 
            +
                  else
         | 
| 603 | 
            +
                    alert "#{file} not created. Check externally. run hackercli.rb -y #{file} #{forum} externally"
         | 
| 604 | 
            +
                  end
         | 
| 605 | 
            +
                end
         | 
| 606 | 
            +
                # return true if younger than one hour
         | 
| 607 | 
            +
                def fresh? file
         | 
| 608 | 
            +
                  return true if @toggle_offline
         | 
| 609 | 
            +
             | 
| 610 | 
            +
                  f = File.stat(file)
         | 
| 611 | 
            +
                  now = Time.now
         | 
| 612 | 
            +
                  return (( now - f.mtime) < 7200)
         | 
| 613 | 
            +
                end
         | 
| 614 | 
            +
                def show_links art
         | 
| 615 | 
            +
                  return unless art
         | 
| 411 616 | 
             
                  links = {}
         | 
| 412 617 | 
             
                  keys = %w{a b c d e f}
         | 
| 413 618 | 
             
                  i = 0
         | 
| @@ -417,61 +622,154 @@ App.new do | |
| 417 622 | 
             
                      i += 1
         | 
| 418 623 | 
             
                    end
         | 
| 419 624 | 
             
                  end
         | 
| 420 | 
            -
                  ch, binding = menu " | 
| 625 | 
            +
                  ch, binding = menu "Select a link", links, :subtitle => " Enter Upper case letter to open in gui"
         | 
| 421 626 | 
             
                  #alert "is #{index}: #{art[:title]} #{ch}:#{binding} "
         | 
| 627 | 
            +
                  app = @browser_text || "elinks"
         | 
| 628 | 
            +
                  unless binding
         | 
| 629 | 
            +
                    return unless ch
         | 
| 630 | 
            +
                    # it must be an upper case for GUI
         | 
| 631 | 
            +
                    return unless ch == ch.upcase
         | 
| 632 | 
            +
                    ch = ch.downcase
         | 
| 633 | 
            +
                    return unless keys.include? ch
         | 
| 634 | 
            +
                    binding = links[ch]
         | 
| 635 | 
            +
                    app = @browser_gui || "open"
         | 
| 636 | 
            +
                  end
         | 
| 422 637 | 
             
                  if binding
         | 
| 423 | 
            -
                    open_url binding
         | 
| 638 | 
            +
                    open_url binding, app
         | 
| 424 639 | 
             
                  end
         | 
| 640 | 
            +
                end
         | 
| 641 | 
            +
                # since this does not happen inside form's loop, therefore form is unable to repaint, repaint
         | 
| 642 | 
            +
                # happens only after a keystroke
         | 
| 643 | 
            +
                # This allows us to pass in a hash with string names for methods. This hash can be easily updated,
         | 
| 644 | 
            +
                # or even read in from a config file/yml file. It is assumed here that all the string names
         | 
| 645 | 
            +
                # correspond to names of methods withing this class, so no class references are required.
         | 
| 646 | 
            +
                # TODO split the command if there are spaces.
         | 
| 647 | 
            +
                def handle_keys hash
         | 
| 648 | 
            +
                  @app.keypress do |str|
         | 
| 649 | 
            +
                    binding = hash[str]
         | 
| 650 | 
            +
                    if binding
         | 
| 651 | 
            +
                      binding = binding.to_sym
         | 
| 652 | 
            +
                      if respond_to?(binding, true)
         | 
| 653 | 
            +
                        send(binding)
         | 
| 654 | 
            +
                      else
         | 
| 655 | 
            +
                        #alert "unresponded to #{str}"
         | 
| 656 | 
            +
                      end
         | 
| 657 | 
            +
                    end
         | 
| 658 | 
            +
                  end
         | 
| 659 | 
            +
                end
         | 
| 660 | 
            +
             | 
| 661 | 
            +
                # Should work on this as a means of binding each element of a hash into forms keymap.
         | 
| 662 | 
            +
                # FIXME works except that multiplier not working ??
         | 
| 663 | 
            +
                def form_bind hash
         | 
| 664 | 
            +
                  hash.each_pair do |k, v|
         | 
| 665 | 
            +
                    nk = key_to_i(k)
         | 
| 666 | 
            +
                    desc = "??"
         | 
| 667 | 
            +
                    desc = v if v.is_a? String or v.is_a? Symbol
         | 
| 668 | 
            +
                    @form.bind_key(nk, desc) { self.send(v) }
         | 
| 669 | 
            +
                  end
         | 
| 670 | 
            +
                end
         | 
| 671 | 
            +
                # convert a key in the format to an int so it can be mapped using bind_key
         | 
| 672 | 
            +
                # "[a-zA-Z"] etc a single cahr
         | 
| 673 | 
            +
                # C-a to C-z
         | 
| 674 | 
            +
                # M-a to M-z
         | 
| 675 | 
            +
                # F1 .. F10
         | 
| 676 | 
            +
                # This does not take complex cases yet. It is a simplistic conversion.
         | 
| 677 | 
            +
                def key_to_i k
         | 
| 678 | 
            +
                  if k.size == 1
         | 
| 679 | 
            +
                    return k.getbyte(0)
         | 
| 680 | 
            +
                  end
         | 
| 681 | 
            +
                  if k =~ /^<M-/
         | 
| 682 | 
            +
                    ch = k[3]
         | 
| 683 | 
            +
                    return 128 + ch.ord
         | 
| 684 | 
            +
                  elsif k == "<CR>"
         | 
| 685 | 
            +
                    return 13
         | 
| 686 | 
            +
                  elsif k =~ /^<[Cc]/
         | 
| 687 | 
            +
                    ch = k[3]
         | 
| 688 | 
            +
                    x = ch.ord - "a".ord + 1
         | 
| 689 | 
            +
                  elsif k[0,2] == "<F"
         | 
| 690 | 
            +
                    ch = k[2..-2]
         | 
| 691 | 
            +
                    return 264 + ch.to_i
         | 
| 692 | 
            +
                  else
         | 
| 693 | 
            +
                    alert "not able to bind #{k}"
         | 
| 694 | 
            +
                  end
         | 
| 695 | 
            +
             | 
| 696 | 
            +
                end
         | 
| 697 | 
            +
              end # class
         | 
| 698 | 
            +
            end # module HackerCli
         | 
| 699 | 
            +
            include HackerCli
         | 
| 700 | 
            +
             | 
| 701 | 
            +
            # http://www.ruby-doc.org/stdlib/libdoc/optparse/rdoc/classes/OptionParser.html
         | 
| 702 | 
            +
            require 'optparse'
         | 
| 703 | 
            +
            options = {}
         | 
| 704 | 
            +
            app = File.basename $0
         | 
| 705 | 
            +
            OptionParser.new do |opts|
         | 
| 706 | 
            +
              opts.banner = %Q{
         | 
| 707 | 
            +
            #{app} version #{VERSION} (YML version)
         | 
| 708 | 
            +
            Usage: #{app} [options]
         | 
| 709 | 
            +
            }
         | 
| 710 | 
            +
             | 
| 711 | 
            +
              #opts.on("-m MODE", String,"--mode", "Use 'text' or 'gui' browser") do |v|
         | 
| 712 | 
            +
                #options[:browser_mode] = v
         | 
| 713 | 
            +
              #end
         | 
| 714 | 
            +
              opts.on("-t browser", String,"--text", "browser for text mode, default elinks") do |v|
         | 
| 715 | 
            +
                options[:browser_text] = v
         | 
| 716 | 
            +
              end
         | 
| 717 | 
            +
              opts.on("-g browser", String,"--gui", "browser for gui mode, default open") do |v|
         | 
| 718 | 
            +
                options[:browser_gui] = v
         | 
| 425 719 | 
             
              end
         | 
| 426 | 
            -
               | 
| 427 | 
            -
             | 
| 720 | 
            +
              opts.on("-c cache dir", String,"--cache-dir", "location to store yml files, default .") do |v|
         | 
| 721 | 
            +
                options[:cache_path] = File.expand_path(v)
         | 
| 722 | 
            +
              end
         | 
| 723 | 
            +
              opts.on("-u config_file", String,"--config-file", "path to load config info from") do |v|
         | 
| 724 | 
            +
                options[:config_file] = v
         | 
| 725 | 
            +
              end
         | 
| 726 | 
            +
              opts.on("--list x,y,z", Array, "Example 'list' of forums: hacker,ruby,programming...") do |list|
         | 
| 727 | 
            +
                options[:list] = list
         | 
| 728 | 
            +
              end
         | 
| 729 | 
            +
              # file age in hours
         | 
| 730 | 
            +
              # offline mode
         | 
| 731 | 
            +
              # config file path
         | 
| 732 | 
            +
            end.parse!
         | 
| 733 | 
            +
            App.new do 
         | 
| 734 | 
            +
              def logger; return @log; end
         | 
| 735 | 
            +
              @log = create_logger "hacker.log"
         | 
| 736 | 
            +
              @h = Hackman.new self, options
         | 
| 737 | 
            +
              @color_scheme = @h.color_scheme
         | 
| 428 738 | 
             
              @header = app_header "hackman #{VERSION}", :text_center => "RSS Reader", :name => "header",
         | 
| 429 | 
            -
                :text_right =>" | 
| 739 | 
            +
                :text_right =>"Menu `", :color => :white, :bgcolor => @color_scheme[:header_bg]
         | 
| 430 740 | 
             
              message "Press F10 (or qq) to exit, F1 Help, ` for Menu  "
         | 
| 431 741 |  | 
| 432 742 |  | 
| 743 | 
            +
             | 
| 433 744 |  | 
| 434 745 | 
             
                # commands that can be mapped to or executed using M-x
         | 
| 435 746 | 
             
                # however, commands of components aren't yet accessible.
         | 
| 436 747 | 
             
                def get_commands
         | 
| 437 748 | 
             
                  %w{ choose_forum next_forum prev_forum }
         | 
| 438 749 | 
             
                end
         | 
| 750 | 
            +
                # help text for F1, but this needs to be kept consistent with @bindings,
         | 
| 751 | 
            +
                # if that is changed, then how does this show the change, considering that
         | 
| 752 | 
            +
                # the config file will be read in Hackman, not here.
         | 
| 439 753 | 
             
                def help_text
         | 
| 440 754 | 
             
                  <<-eos
         | 
| 441 | 
            -
                            | 
| 442 | 
            -
             | 
| 443 | 
            -
                  These are some features for either getting filenames from user
         | 
| 444 | 
            -
                  at the bottom of the window like vim and others do, or filtering
         | 
| 445 | 
            -
                  from a list (like ControlP plugin). Or seeing a file at bottom
         | 
| 446 | 
            -
                  of screen for a quick preview.
         | 
| 755 | 
            +
                           Hackman Help
         | 
| 447 756 |  | 
| 448 | 
            -
                   | 
| 757 | 
            +
                  F2       -   forum selection (interface like Ctrl-P, very minimal)
         | 
| 449 758 | 
             
                  F1       -   Help
         | 
| 450 759 | 
             
                  F10      -   Quit application
         | 
| 451 760 | 
             
                  qq       -   Quit application
         | 
| 452 | 
            -
             | 
| 453 | 
            -
             | 
| 454 | 
            -
                   | 
| 455 | 
            -
             | 
| 456 | 
            -
             | 
| 457 | 
            -
                   | 
| 458 | 
            -
             | 
| 459 | 
            -
             | 
| 460 | 
            -
             | 
| 461 | 
            -
                   | 
| 462 | 
            -
             | 
| 463 | 
            -
                   | 
| 464 | 
            -
                                     Press <ENTER> to select, arrow keys to traverse, 
         | 
| 465 | 
            -
                                     and characters to filter list.
         | 
| 466 | 
            -
                  testdisplaytext  - display text at bottom (current file contents)
         | 
| 467 | 
            -
                                     Press <ENTER> when done.
         | 
| 468 | 
            -
             | 
| 469 | 
            -
                  The file/dir selection options are very minimally functional. Improvements
         | 
| 470 | 
            -
                  and thorough testing are required. I've only tested them out gingerly.
         | 
| 471 | 
            -
             | 
| 472 | 
            -
                  testchoosedir and file were earlier like Emacs/memacs with TAB completion
         | 
| 473 | 
            -
                  but have now moved to the much faster and friendlier ControlP plugin like
         | 
| 474 | 
            -
                  'filter as you type' format.
         | 
| 761 | 
            +
             | 
| 762 | 
            +
                  ` (backtick) - Main Menu (add, remove, change forum)
         | 
| 763 | 
            +
                  = (Equal)    - Toggle Menu (titles only)
         | 
| 764 | 
            +
             | 
| 765 | 
            +
                  o        - open url menu for current article (under cursor)
         | 
| 766 | 
            +
                  <n>o     - open url menu for <n>th article
         | 
| 767 | 
            +
                  <n>z     - goto <n>th article
         | 
| 768 | 
            +
             | 
| 769 | 
            +
                  "<"      - previous forum in list
         | 
| 770 | 
            +
                  ">"      - next forum in list
         | 
| 771 | 
            +
             | 
| 772 | 
            +
                  "/"      - search within the page (case-sensitive). Append "/i" to ignore case.
         | 
| 475 773 |  | 
| 476 774 | 
             
                  -----------------------------------------------------------------------
         | 
| 477 775 | 
             
                  :n or Alt-n for general help.
         | 
| @@ -481,51 +779,52 @@ App.new do | |
| 481 779 | 
             
                #install_help_text help_text
         | 
| 482 780 |  | 
| 483 781 | 
             
                def app_menu
         | 
| 484 | 
            -
                   | 
| 485 | 
            -
                  Dir.chdir(@curdir) if Dir.pwd != @curdir
         | 
| 782 | 
            +
                  # TODO update and fix this
         | 
| 486 783 | 
             
                  require 'canis/core/util/promptmenu'
         | 
| 487 784 | 
             
                  menu = PromptMenu.new self do
         | 
| 488 785 | 
             
                    item :f, :choose_forum
         | 
| 489 | 
            -
                    item : | 
| 786 | 
            +
                    item :n, :next_forum
         | 
| 787 | 
            +
                    item :p, :prev_forum
         | 
| 788 | 
            +
                    item :a, :add_forum
         | 
| 789 | 
            +
                    item :d, :remove_forum
         | 
| 490 790 | 
             
                  end
         | 
| 491 791 | 
             
                  menu.display_new :title => "Menu"
         | 
| 492 792 | 
             
                end
         | 
| 493 793 | 
             
                # BINDING SECTION
         | 
| 494 | 
            -
             | 
| 495 | 
            -
               | 
| 496 | 
            -
              @form.bind_key( | 
| 497 | 
            -
              @form.bind_key(FFI::NCurses:: | 
| 498 | 
            -
              @form.bind_key(FFI::NCurses:: | 
| 499 | 
            -
              @form.bind_key( | 
| 794 | 
            +
                if false
         | 
| 795 | 
            +
              #@form.bind_key(?:, "App Menu") { app_menu; }
         | 
| 796 | 
            +
              @form.bind_key(?`, "Main Menu") { @h.main_menu; }
         | 
| 797 | 
            +
              @form.bind_key(FFI::NCurses::KEY_F2, "Main Menu") { @h.choose_forum; }
         | 
| 798 | 
            +
              @form.bind_key(FFI::NCurses::KEY_F3, "Cycle bgcolor") { @h.color_scheme_select "c"; }
         | 
| 799 | 
            +
              @form.bind_key(FFI::NCurses::KEY_F4, "Cycle bgcolor") { @h.color_scheme_select "C"; }
         | 
| 800 | 
            +
              @form.bind_key($kh_int["S-F3"], "Cycle bgcolor") { @h.color_scheme_select "C"; }
         | 
| 500 801 | 
             
              @form.bind_key(?=, "Toggle Menu") { 
         | 
| 501 | 
            -
                toggle_menu; 
         | 
| 802 | 
            +
                @h.toggle_menu; 
         | 
| 502 803 | 
             
              }
         | 
| 503 | 
            -
              @form.bind_key(?<, "Previous Forum") { prev_forum; }
         | 
| 504 | 
            -
              @form.bind_key(?>, "Next Forum") { next_forum; }
         | 
| 804 | 
            +
              @form.bind_key(?<, "Previous Forum") { @h.prev_forum; }
         | 
| 805 | 
            +
              @form.bind_key(?>, "Next Forum") { @h.next_forum; }
         | 
| 806 | 
            +
                end
         | 
| 505 807 |  | 
| 808 | 
            +
                @form.help_manager.help_text = help_text
         | 
| 809 | 
            +
             | 
| 810 | 
            +
              begin
         | 
| 506 811 | 
             
              stack :margin_top => 1, :margin_left => 0, :width => :expand , :height => FFI::NCurses.LINES-2 do
         | 
| 507 812 | 
             
                tv = textpad :height_pc => 100, :width_pc => 100, :name => "tv", :suppress_borders => true,
         | 
| 508 | 
            -
                  :bgcolor =>  | 
| 813 | 
            +
                  :bgcolor => @color_scheme[:body_bg], :color => 255, :attr => NORMAL
         | 
| 509 814 | 
             
                #tv.renderer ruby_renderer
         | 
| 510 | 
            -
                tv.bind(:PRESS) {|ev|
         | 
| 511 | 
            -
                  index = ev.current_index
         | 
| 512 | 
            -
                  art = get_item_for_line index
         | 
| 513 | 
            -
                  show_links art
         | 
| 514 | 
            -
                }
         | 
| 515 | 
            -
                tv.bind_key(?z) { goto_article }
         | 
| 516 | 
            -
                tv.bind_key(?o) { 
         | 
| 517 | 
            -
                  # if multiplier is 0, use current line
         | 
| 518 | 
            -
                  art =  @hash[:articles][$multiplier - 1]
         | 
| 519 | 
            -
                  if $multiplier == 0
         | 
| 520 | 
            -
                    index = tv.current_index
         | 
| 521 | 
            -
                    art = get_item_for_line index
         | 
| 522 | 
            -
                  end
         | 
| 523 | 
            -
                  show_links art
         | 
| 524 | 
            -
                  }
         | 
| 815 | 
            +
                #tv.bind(:PRESS) {|ev| display_links }
         | 
| 525 816 | 
             
                tv.text_patterns[:articles] = Regexp.new(/^ *\d+ /)
         | 
| 526 817 | 
             
                tv.bind_key(KEY_TAB, "goto article") { tv.next_regex(:articles) }
         | 
| 527 818 | 
             
              end # stack
         | 
| 528 819 |  | 
| 529 | 
            -
              sl = status_line :row => Ncurses.LINES-1, :bgcolor => :yellow, :color =>  | 
| 530 | 
            -
              choose_forum 
         | 
| 820 | 
            +
              sl = status_line :row => Ncurses.LINES-1, :bgcolor => :yellow, :color => @color_scheme[:status_bg]
         | 
| 821 | 
            +
              @h.choose_forum 
         | 
| 822 | 
            +
              rescue => ex
         | 
| 823 | 
            +
                textdialog ["Error in hackman: #{ex} ", *ex.backtrace], :title => "Exception"
         | 
| 824 | 
            +
                $log.debug( ex) if ex
         | 
| 825 | 
            +
                $log.debug(ex.backtrace.join("\n")) if ex
         | 
| 826 | 
            +
              ensure
         | 
| 827 | 
            +
                p ex if ex
         | 
| 828 | 
            +
                p(ex.backtrace.join("\n")) if ex
         | 
| 829 | 
            +
              end
         | 
| 531 830 | 
             
            end # app
         | 
    
        data/lib/hackercli/version.rb
    CHANGED
    
    
    
        metadata
    CHANGED
    
    | @@ -1,14 +1,14 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: hackercli
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 0.0. | 
| 4 | 
            +
              version: 0.0.3
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - kepler
         | 
| 8 8 | 
             
            autorequire: 
         | 
| 9 9 | 
             
            bindir: bin
         | 
| 10 10 | 
             
            cert_chain: []
         | 
| 11 | 
            -
            date: 2014-08 | 
| 11 | 
            +
            date: 2014-09-08 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies:
         | 
| 13 13 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 14 14 | 
             
              name: bundler
         |