X-Git-Url: https://git.notmuchmail.org/git?a=blobdiff_plain;f=lib%2Fsup%2Fbuffer.rb;h=80eb18e3a22191256c5531f58cbe90cd6cce6abb;hb=d0f57caaf652ffbe9ede9128c5933c65a736ecd2;hp=2b94d79813b299f02eb0cdbcb7621ceff2d246ba;hpb=10efc3ca507585ea25ff6c1d1ceacb80067a3624;p=sup diff --git a/lib/sup/buffer.rb b/lib/sup/buffer.rb index 2b94d79..80eb18e 100644 --- a/lib/sup/buffer.rb +++ b/lib/sup/buffer.rb @@ -14,7 +14,7 @@ module Ncurses 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 @@ -50,6 +50,7 @@ module Redwood 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 @@ -57,6 +58,7 @@ class Buffer @dirty = true @focus = false @title = opts[:title] || "" + @force_to_top = opts[:force_to_top] || false @x, @y, @width, @height = 0, 0, width, height end @@ -139,7 +141,7 @@ class BufferManager @minibuf_mutex = Mutex.new @textfields = {} @flash = nil - @shelled = false + @shelled = @asking = false self.class.i_am_the_instance self end @@ -156,18 +158,33 @@ class BufferManager 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 @@ -179,6 +196,7 @@ class BufferManager 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 @@ -241,6 +259,7 @@ class BufferManager 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 @@ -258,19 +277,34 @@ class BufferManager ## 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 @@ -359,9 +393,9 @@ class BufferManager 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 @@ -399,6 +433,7 @@ class BufferManager def say s, id=nil new_id = nil + @minibuf_mutex.synchronize do new_id = id.nil? id ||= @minibuf_stack.length