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
@textfields = {}
@flash = nil
@shelled = @asking = false
+ @in_x = ENV["TERM"] =~ /(xterm|rxvt|screen)/
self.class.i_am_the_instance self
end
if opts.member? :status
[opts[:status], opts[:title]]
else
- get_status_and_title(@focus_buf) # must be called outside of the ncurses lock
+ raise "status must be supplied if draw_screen is called within a sync" if opts[:sync] == false
+ get_status_and_title @focus_buf # must be called outside of the ncurses lock
end
- print "\033]2;#{title}\07" if title
+ ## 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
end
def kill_buffer buf
- raise ArgumentError, "buffer not on stack: #{buf.inspect}" unless @buffers.member? buf
+ raise ArgumentError, "buffer not on stack: #{buf}: #{buf.title.inspect}" unless @buffers.member? buf
buf.mode.cleanup
@buffers.delete buf
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
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
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] }
+ completions.select { |x| x =~ /^#{Regexp::escape target}/i }.map { |x| [prefix + x, x] }
end
end
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
elsif File.directory?(answer)
spawn_modal "file browser", FileBrowserMode.new(answer)
else
- answer
+ File.expand_path answer
end
end
contacts = ContactManager.contacts.map { |c| [ContactManager.alias_for(c), c.full_address, c.email] }
completions = (recent + contacts).flatten.uniq.sort
+ completions += HookManager.run("extra-contact-addresses") || []
answer = BufferManager.ask_many_emails_with_completions domain, question, completions, default
if answer
end
end
+ ## for simplicitly, we always place the question at the very bottom of the
+ ## screen
def ask domain, question, default=nil, &block
raise "impossible!" if @asking
@asking = true
tf = @textfields[domain]
completion_buf = nil
- ## this goddamn ncurses form shit is a fucking 1970's nightmare.
- ## jesus christ. the exact sequence of ncurses events that needs
- ## to happen in order to display a form and have the entire screen
- ## not disappear and have the cursor in the right place can only
- ## be determined by hours of trial and error and is TOO FUCKING
- ## COMPLICATED.
+ status, title = get_status_and_title @focus_buf
+
Ncurses.sync do
tf.activate Ncurses.stdscr, Ncurses.rows - 1, 0, Ncurses.cols, question, default, &block
- @dirty = true
- draw_screen :skip_minibuf => true, :sync => false
+ @dirty = true # for some reason that blanks the whole fucking screen
+ draw_screen :sync => false, :status => status, :title => title
tf.position_cursor
Ncurses.refresh
end
Ncurses.sync { Ncurses.refresh }
end
- Ncurses.sync { tf.deactivate }
kill_buffer completion_buf if completion_buf
+
@dirty = true
@asking = false
- draw_screen
+ Ncurses.sync do
+ tf.deactivate
+ draw_screen :sync => false, :status => status, :title => title
+ end
tf.value
end
- ## some pretty lame code in here!
def ask_getch question, accept=nil
+ raise "impossible!" if @asking
+
accept = accept.split(//).map { |x| x[0] } if accept
- flash question
+ status, title = get_status_and_title @focus_buf
Ncurses.sync do
- Ncurses.curs_set 1
+ draw_screen :sync => false, :status => status, :title => title
+ Ncurses.mvaddstr Ncurses.rows - 1, 0, question
Ncurses.move Ncurses.rows - 1, question.length + 1
+ Ncurses.curs_set 1
Ncurses.refresh
end
+ @asking = true
ret = nil
done = false
- @shelled = true
until done
key = Ncurses.nonblocking_getch or next
if key == Ncurses::KEY_CANCEL
done = true
- elsif (accept && accept.member?(key)) || !accept
+ elsif accept.nil? || accept.empty? || accept.member?(key)
ret = key
done = true
end
end
- @shelled = false
-
+ @asking = false
Ncurses.sync do
Ncurses.curs_set 0
- erase_flash
- draw_screen :sync => false
- Ncurses.curs_set 0
+ draw_screen :sync => false, :status => status, :title => title
end
ret
@minibuf_mutex.synchronize do
m = @minibuf_stack.compact
m << @flash if @flash
- m << "" if m.empty?
+ m << "" if m.empty? unless @asking # to clear it
end
Ncurses.mutex.lock unless opts[:sync] == false