end
def mutex; @mutex ||= Mutex.new; end
- def sync &b; mutex.synchronize &b; end
+ def sync &b; mutex.synchronize(&b); end
## aaahhh, user input. who would have though that such a simple
## idea would be SO FUCKING COMPLICATED?! because apparently
class Buffer
attr_reader :mode, :x, :y, :width, :height, :title
bool_reader :dirty
+ bool_accessor :force_to_top
def initialize window, mode, width, height, opts={}
@w = window
@dirty = true
@focus = false
@title = opts[:title] || ""
+ @force_to_top = opts[:force_to_top] || false
@x, @y, @width, @height = 0, 0, width, height
end
@minibuf_mutex = Mutex.new
@textfields = {}
@flash = nil
- @shelled = false
+ @shelled = @asking = false
self.class.i_am_the_instance self
end
def raise_to_front buf
raise ArgumentError, "buffer not on stack: #{buf.inspect}" unless @buffers.member? buf
+
@buffers.delete buf
- @buffers.push buf
- focus_on buf
+ if @buffers.length > 0 && @buffers.last.force_to_top?
+ @buffers.insert(-2, buf)
+ else
+ @buffers.push buf
+ focus_on buf
+ end
@dirty = true
end
+ ## we reset force_to_top when rolling buffers. this is so that the
+ ## human can actually still move buffers around, while still
+ ## programmatically being able to pop stuff up in the middle of
+ ## drawing a window without worrying about covering it up.
+ ##
+ ## if we ever start calling roll_buffers programmatically, we will
+ ## have to change this. but it's not clear that we will ever actually
+ ## do that.
def roll_buffers
+ @buffers.last.force_to_top = false
raise_to_front @buffers.first
end
def roll_buffers_backwards
return unless @buffers.length > 1
+ @buffers.last.force_to_top = false
raise_to_front @buffers[@buffers.length - 2]
end
def [] n; @name_map[n]; end
def []= n, b
raise ArgumentError, "duplicate buffer name" if b && @name_map.member?(n)
+ raise ArgumentError, "title must be a string" unless n.is_a? String
@name_map[n] = b
end
end
def spawn title, mode, opts={}
+ raise ArgumentError, "title must be a string" unless title.is_a? String
realtitle = title
num = 2
while @name_map.member? realtitle
## w = Ncurses::WINDOW.new(height, width, (opts[:top] || 0),
## (opts[:left] || 0))
w = Ncurses.stdscr
- b = Buffer.new w, mode, width, height, :title => realtitle
+ b = Buffer.new w, mode, width, height, :title => realtitle, :force_to_top => (opts[:force_to_top] || false)
mode.buffer = b
@name_map[realtitle] = b
+
+ @buffers.unshift b
if opts[:hidden]
- @buffers.unshift b
focus_on b unless @focus_buf
else
- @buffers.push b
raise_to_front b
end
b
end
+ def kill_all_buffers_safely
+ until @buffers.empty?
+ ## inbox mode always claims it's unkillable. we'll ignore it.
+ return false unless @buffers.first.mode.is_a?(InboxMode) || @buffers.first.mode.killable?
+ kill_buffer @buffers.first
+ end
+ true
+ end
+
+ def kill_buffer_safely buf
+ return false unless buf.mode.killable?
+ kill_buffer buf
+ true
+ end
+
def kill_all_buffers
kill_buffer @buffers.first until @buffers.empty?
end
ret
end
+ ## returns true (y), false (n), or nil (ctrl-g / cancel)
def ask_yes_or_no question
- r = ask_getch(question, "ynYN")
- case r
+ case(r = ask_getch question, "ynYN")
when ?y, ?Y
true
when nil
def say s, id=nil
new_id = nil
+
@minibuf_mutex.synchronize do
new_id = id.nil?
id ||= @minibuf_stack.length