]> git.cworth.org Git - sup/blobdiff - lib/sup/buffer.rb
convert a couple of arrays to sets for labels
[sup] / lib / sup / buffer.rb
index f724f88e778fbf1a3d697d780ba1fac805f8460a..2f2dbdaf2f5f3894a4b60728486574c4ce325769 100644 (file)
@@ -51,8 +51,8 @@ module Redwood
 class InputSequenceAborted < StandardError; end
 
 class Buffer
-  attr_reader :mode, :x, :y, :width, :height, :title
-  bool_reader :dirty
+  attr_reader :mode, :x, :y, :width, :height, :title, :atime
+  bool_reader :dirty, :system
   bool_accessor :force_to_top
 
   def initialize window, mode, width, height, opts={}
@@ -63,7 +63,8 @@ class Buffer
     @title = opts[:title] || ""
     @force_to_top = opts[:force_to_top] || false
     @x, @y, @width, @height = 0, 0, width, height
-    @in_x = ENV["TERM"] =~ /(xterm|rxvt|screen)/
+    @atime = Time.at 0
+    @system = opts[:system] || false
   end
 
   def content_height; @height - 1; end
@@ -98,6 +99,7 @@ class Buffer
     @mode.draw
     draw_status status
     commit
+    @atime = Time.now
   end
 
   ## s nil means a blank line!
@@ -106,10 +108,16 @@ class Buffer
 
     @w.attrset Colormap.color_for(opts[:color] || :none, opts[:highlight])
     s ||= ""
-    maxl = @width - x
-    @w.mvaddstr y, x, s[0 ... maxl]
-    unless s.length >= maxl || opts[:no_fill]
-      @w.mvaddstr(y, x + s.length, " " * (maxl - s.length))
+    maxl = @width - x # maximum display width width
+    stringl = maxl    # string "length"
+    ## the next horribleness is thanks to ruby's lack of widechar support
+    stringl += 1 while stringl < s.length && s[0 ... stringl].display_length < maxl
+    @w.mvaddstr y, x, s[0 ... stringl]
+    unless opts[:no_fill]
+      l = s.display_length
+      unless l >= maxl
+        @w.mvaddstr(y, x + l, " " * (maxl - l))
+      end
     end
   end
 
@@ -166,6 +174,15 @@ called at least once per keystroke, so excessive computation is discouraged.
 
 Variables: the same as status-bar-text hook.
 Return value: a string to be used as the terminal title.
+EOS
+
+  HookManager.register "extra-contact-addresses", <<EOS
+A list of extra addresses to propose for tab completion, etc. when the
+user is entering an email address. Can be plain email addresses or can
+be full "User Name <email@domain.tld>" entries.
+
+Variables: none
+Return value: an array of email address strings.
 EOS
 
   def initialize
@@ -178,6 +195,7 @@ EOS
     @textfields = {}
     @flash = nil
     @shelled = @asking = false
+    @in_x = ENV["TERM"] =~ /(xterm|rxvt|screen)/
 
     self.class.i_am_the_instance self
   end
@@ -263,7 +281,8 @@ EOS
         get_status_and_title @focus_buf # must be called outside of the ncurses lock
       end
 
-    print "\033]2;#{title}\07" if title && @in_x
+    ## http://rtfm.etla.org/xterm/ctlseq.html (see Operating System Controls)
+    print "\033]0;#{title}\07" if title && @in_x
 
     Ncurses.mutex.lock unless opts[:sync] == false
 
@@ -328,7 +347,7 @@ EOS
     ## w = Ncurses::WINDOW.new(height, width, (opts[:top] || 0),
     ## (opts[:left] || 0))
     w = Ncurses.stdscr
-    b = Buffer.new w, mode, width, height, :title => realtitle, :force_to_top => (opts[:force_to_top] || false)
+    b = Buffer.new w, mode, width, height, :title => realtitle, :force_to_top => opts[:force_to_top], :system => opts[:system]
     mode.buffer = b
     @name_map[realtitle] = b
 
@@ -398,7 +417,7 @@ EOS
 
   def ask_with_completions domain, question, completions, default=nil
     ask domain, question, default do |s|
-      completions.select { |x| x =~ /^#{s}/i }.map { |x| [x, x] }
+      completions.select { |x| x =~ /^#{Regexp::escape s}/i }.map { |x| [x, x] }
     end
   end
 
@@ -414,7 +433,7 @@ EOS
           raise "william screwed up completion: #{partial.inspect}"
         end
 
-      completions.select { |x| x =~ /^#{target}/i }.map { |x| [prefix + x, x] }
+      completions.select { |x| x =~ /^#{Regexp::escape target}/i }.map { |x| [prefix + x, x] }
     end
   end
 
@@ -423,7 +442,7 @@ EOS
       prefix, target = partial.split_on_commas_with_remainder
       target ||= prefix.pop || ""
       prefix = prefix.join(", ") + (prefix.empty? ? "" : ", ")
-      completions.select { |x| x =~ /^#{target}/i }.map { |x| [prefix + x, x] }
+      completions.select { |x| x =~ /^#{Regexp::escape target}/i }.sort_by { |c| [ContactManager.contact_for(c) ? 0 : 1, c] }.map { |x| [prefix + x, x] }
     end
   end
 
@@ -436,7 +455,7 @@ EOS
         if dir
           [[s.sub(full, dir), "~#{name}"]]
         else
-          users.select { |u| u =~ /^#{name}/ }.map do |u|
+          users.select { |u| u =~ /^#{Regexp::escape name}/ }.map do |u|
             [s.sub("~#{name}", "~#{u}"), "~#{u}"]
           end
         end
@@ -449,13 +468,13 @@ EOS
     end
 
     if answer
-      answer = 
+      answer =
         if answer.empty?
           spawn_modal "file browser", FileBrowserMode.new
         elsif File.directory?(answer)
           spawn_modal "file browser", FileBrowserMode.new(answer)
         else
-          answer
+          File.expand_path answer
         end
     end
 
@@ -465,16 +484,18 @@ EOS
   ## returns an array of labels
   def ask_for_labels domain, question, default_labels, forbidden_labels=[]
     default_labels = default_labels - forbidden_labels - LabelManager::RESERVED_LABELS
-    default = default_labels.join(" ")
+    default = default_labels.to_a.join(" ")
     default += " " unless default.empty?
 
-    applyable_labels = (LabelManager.applyable_labels - forbidden_labels).map { |l| LabelManager.string_for l }.sort_by { |s| s.downcase }
+    # here I would prefer to give more control and allow all_labels instead of
+    # user_defined_labels only
+    applyable_labels = (LabelManager.user_defined_labels - forbidden_labels).map { |l| LabelManager.string_for l }.sort_by { |s| s.downcase }
 
     answer = ask_many_with_completions domain, question, applyable_labels, default
 
     return unless answer
 
-    user_labels = answer.split(/\s+/).map { |l| l.intern }
+    user_labels = answer.to_set_of_symbols
     user_labels.each do |l|
       if forbidden_labels.include?(l) || LabelManager::RESERVED_LABELS.include?(l)
         BufferManager.flash "'#{l}' is a reserved label!"
@@ -487,15 +508,16 @@ EOS
   def ask_for_contacts domain, question, default_contacts=[]
     default = default_contacts.map { |s| s.to_s }.join(" ")
     default += " " unless default.empty?
-    
+
     recent = Index.load_contacts(AccountManager.user_emails, :num => 10).map { |c| [c.full_address, c.email] }
     contacts = ContactManager.contacts.map { |c| [ContactManager.alias_for(c), c.full_address, c.email] }
 
-    completions = (recent + contacts).flatten.uniq.sort
+    completions = (recent + contacts).flatten.uniq
+    completions += HookManager.run("extra-contact-addresses") || []
     answer = BufferManager.ask_many_emails_with_completions domain, question, completions, default
 
     if answer
-      answer.split_on_commas.map { |x| ContactManager.contact_for(x.downcase) || PersonManager.person_for(x) }
+      answer.split_on_commas.map { |x| ContactManager.contact_for(x) || Person.from_address(x) }
     end
   end
 
@@ -557,7 +579,6 @@ EOS
 
   def ask_getch question, accept=nil
     raise "impossible!" if @asking
-    @asking = true
 
     accept = accept.split(//).map { |x| x[0] } if accept
 
@@ -570,6 +591,7 @@ EOS
       Ncurses.refresh
     end
 
+    @asking = true
     ret = nil
     done = false
     until done
@@ -701,6 +723,7 @@ EOS
     Ncurses.sync do
       Ncurses.endwin
       system command
+      Ncurses.stdscr.keypad 1
       Ncurses.refresh
       Ncurses.curs_set 0
     end