]> git.cworth.org Git - sup/blobdiff - lib/sup/buffer.rb
maybe fix disappearign status lines when a #say and an #ask are both active
[sup] / lib / sup / buffer.rb
index dc3b44e6e385cfe1bf7709c347542d53a95c402e..b2106718f21a57ec3ad0b27d39c716dedf90e2d7 100644 (file)
@@ -1,6 +1,8 @@
 require 'etc'
 require 'thread'
+require 'ncurses'
 
+if defined? Ncurses
 module Ncurses
   def rows
     lame, lamer = [], []
@@ -14,6 +16,12 @@ module Ncurses
     lamer.first
   end
 
+  def curx
+    lame, lamer = [], []
+    stdscr.getyx lame, lamer
+    lamer.first
+  end
+
   def mutex; @mutex ||= Mutex.new; end
   def sync &b; mutex.synchronize(&b); end
 
@@ -27,12 +35,16 @@ module Ncurses
     end
   end
 
-  module_function :rows, :cols, :nonblocking_getch, :mutex, :sync
+  module_function :rows, :cols, :curx, :nonblocking_getch, :mutex, :sync
+
+  remove_const :KEY_ENTER
+  remove_const :KEY_CANCEL
 
   KEY_ENTER = 10
-  KEY_CANCEL = ?\a # ctrl-g
+  KEY_CANCEL = 7 # ctrl-g
   KEY_TAB = 9
 end
+end
 
 module Redwood
 
@@ -121,6 +133,11 @@ class BufferManager
 
   attr_reader :focus_buf
 
+  ## we have to define the key used to continue in-buffer search here, because
+  ## it has special semantics that BufferManager deals with---current searches
+  ## are canceled by any keypress except this one.
+  CONTINUE_IN_BUFFER_SEARCH_KEY = "n"
+
   def initialize
     @name_map = {}
     @buffers = []
@@ -138,7 +155,8 @@ class BufferManager
   def buffers; @name_map.to_a; end
 
   def focus_on buf
-    raise ArgumentError, "buffer not on stack: #{buf.inspect}" unless @buffers.member? buf
+    return unless @buffers.member? buf
+
     return if buf == @focus_buf 
     @focus_buf.blur if @focus_buf
     @focus_buf = buf
@@ -146,7 +164,7 @@ class BufferManager
   end
 
   def raise_to_front buf
-    raise ArgumentError, "buffer not on stack: #{buf.inspect}" unless @buffers.member? buf
+    return unless @buffers.member? buf
 
     @buffers.delete buf
     if @buffers.length > 0 && @buffers.last.force_to_top?
@@ -178,7 +196,13 @@ class BufferManager
   end
 
   def handle_input c
-    @focus_buf && @focus_buf.mode.handle_input(c)
+    if @focus_buf
+      if @focus_buf.mode.in_search? && c != CONTINUE_IN_BUFFER_SEARCH_KEY[0]
+        @focus_buf.mode.cancel_search!
+        @focus_buf.mark_dirty
+      end
+      @focus_buf.mode.handle_input c
+    end
   end
 
   def exists? n; @name_map.member? n; end
@@ -235,11 +259,12 @@ class BufferManager
   def spawn_unless_exists title, opts={}
     if @name_map.member? title
       raise_to_front @name_map[title] unless opts[:hidden]
+      nil
     else
       mode = yield
       spawn title, mode, opts
+      @name_map[title]
     end
-    @name_map[title]
   end
 
   def spawn title, mode, opts={}
@@ -322,18 +347,46 @@ class BufferManager
       ## TODO: something intelligent here
       ## for now I will simply prohibit killing the inbox buffer.
     else
-      raise_to_front @buffers.last
+      last = @buffers.last
+      @focus_buf ||= last
+      raise_to_front last
     end
   end
 
   def ask_with_completions domain, question, completions, default=nil
     ask domain, question, default do |s|
-      completions.select { |x| x =~ /^#{s}/i }.map { |x| [x.downcase, x] }
+      completions.select { |x| x =~ /^#{s}/i }.map { |x| [x, x] }
+    end
+  end
+
+  def ask_many_with_completions domain, question, completions, default=nil
+    ask domain, question, default do |partial|
+      prefix, target = 
+        case partial
+        when /^\s*$/
+          ["", ""]
+        when /^(.*\s+)?(.*?)$/
+          [$1 || "", $2]
+        else
+          raise "william screwed up completion: #{partial.inspect}"
+        end
+
+      completions.select { |x| x =~ /^#{target}/i }.map { |x| [prefix + x, x] }
+    end
+  end
+
+  def ask_many_emails_with_completions domain, question, completions, default=nil
+    ask domain, question, default do |partial|
+      prefix, target = partial.split_on_commas_with_remainder
+      Redwood::log "before: prefix #{prefix.inspect}, target #{target.inspect}"
+      target ||= prefix.pop || ""
+      prefix = prefix.join(", ") + (prefix.empty? ? "" : ", ")
+      Redwood::log "after: prefix #{prefix.inspect}, target #{target.inspect}"
+      completions.select { |x| x =~ /^#{target}/i }.map { |x| [prefix + x, x] }
     end
   end
 
-  ## returns an ARRAY of filenames!
-  def ask_for_filenames domain, question, default=nil
+  def ask_for_filename domain, question, default=nil
     answer = ask domain, question, default do |s|
       if s =~ /(~([^\s\/]*))/ # twiddle directory expansion
         full = $1
@@ -361,13 +414,51 @@ class BufferManager
         elsif File.directory?(answer)
           spawn_modal "file browser", FileBrowserMode.new(answer)
         else
-          [answer]
+          answer
         end
     end
 
-    answer || []
+    answer
+  end
+
+  ## 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 += " " unless default.empty?
+
+    applyable_labels = (LabelManager.applyable_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.each do |l|
+      if forbidden_labels.include?(l) || LabelManager::RESERVED_LABELS.include?(l)
+        BufferManager.flash "'#{l}' is a reserved label!"
+        return
+      end
+    end
+    user_labels
+  end
+
+  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
+    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) }
+    end
   end
 
+
   def ask domain, question, default=nil, &block
     raise "impossible!" if @asking
     @asking = true
@@ -384,7 +475,8 @@ class BufferManager
     Ncurses.sync do
       tf.activate question, default, &block
       @dirty = true
-      draw_screen :skip_minibuf => true, :sync => false
+      #draw_screen :skip_minibuf => true, :sync => false
+      draw_screen :sync => false
     end
 
     ret = nil
@@ -409,7 +501,6 @@ class BufferManager
         tf.position_cursor
       elsif tf.roll_completions?
         completion_buf.mode.roll
-
         draw_screen :skip_minibuf => true
         tf.position_cursor
       end