lib/sup/buffer.rb in sup-0.1 vs lib/sup/buffer.rb in sup-0.2
- old
+ new
@@ -1,8 +1,10 @@
require 'etc'
require 'thread'
+require 'ncurses'
+if defined? Ncurses
module Ncurses
def rows
lame, lamer = [], []
stdscr.getmaxyx lame, lamer
lame.first
@@ -12,10 +14,16 @@
lame, lamer = [], []
stdscr.getmaxyx lame, lamer
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
## magically, this stuff seems to work now. i could swear it didn't
## before. hm.
@@ -25,16 +33,20 @@
else
nil
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
class Buffer
attr_reader :mode, :x, :y, :width, :height, :title
@@ -326,16 +338,33 @@
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
- ## returns an ARRAY of filenames!
- def ask_for_filenames domain, question, default=nil
+ def ask_many_with_completions domain, question, completions, default=nil, sep=" "
+ ask domain, question, default do |partial|
+ prefix, target =
+ case partial#.gsub(/#{sep}+/, sep)
+ when /^\s*$/
+ ["", ""]
+ when /^(.+#{sep})$/
+ [$1, ""]
+ when /^(.*#{sep})?(.+?)$/
+ [$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_for_filename domain, question, default=nil
answer = ask domain, question, default do |s|
if s =~ /(~([^\s\/]*))/ # twiddle directory expansion
full = $1
name = $2.empty? ? Etc.getlogin : $2
dir = Etc.getpwnam(name).dir rescue nil
@@ -359,17 +388,57 @@
if answer.empty?
spawn_modal "file browser", FileBrowserMode.new
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.longname, c.email] }
+ contacts = ContactManager.contacts.map { |c| [ContactManager.alias_for(c), c.longname, c.email] }
+
+ Redwood::log "recent: #{recent.inspect}"
+
+ completions = (recent + contacts).flatten.uniq.sort
+ answer = BufferManager.ask_many_with_completions domain, question, completions, default, /\s*,\s*/
+
+ 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
@textfields[domain] ||= TextField.new Ncurses.stdscr, Ncurses.rows - 1, 0, Ncurses.cols
@@ -391,30 +460,25 @@
tf.position_cursor
Ncurses.sync { Ncurses.refresh }
while true
c = Ncurses.nonblocking_getch
- next unless c # getch timeout
+ next unless c # getch timeout
break unless tf.handle_input c # process keystroke
if tf.new_completions?
kill_buffer completion_buf if completion_buf
- prefix_len =
- if tf.value =~ /\/$/
- 0
- else
- File.basename(tf.value).length
- end
+ shorts = tf.completions.map { |full, short| short }
+ prefix_len = shorts.shared_prefix.length
- mode = CompletionMode.new tf.completions.map { |full, short| short }, :header => "Possible completions for \"#{tf.value}\": ", :prefix_len => prefix_len
+ mode = CompletionMode.new shorts, :header => "Possible completions for \"#{tf.value}\": ", :prefix_len => prefix_len
completion_buf = spawn "<completions>", mode, :height => 10
draw_screen :skip_minibuf => true
tf.position_cursor
elsif tf.roll_completions?
completion_buf.mode.roll
-
draw_screen :skip_minibuf => true
tf.position_cursor
end
Ncurses.sync { Ncurses.refresh }