Logger.make_buf
 
   bm.draw_screen
-  imode.load_more_threads :num => ibuf.content_height, :when_done => lambda {   reporting_thread { sleep 1; PollManager.poll } }
+  imode.load_threads :num => ibuf.content_height, :when_done => lambda {   reporting_thread { sleep 1; PollManager.poll } }
 
   PollManager.start_thread
 
             log "built query from #{text.inspect}: #{qobj}"
             mode = SearchResultsMode.new qobj
             bm.spawn "search: \"#{short_text}\"", mode
-            mode.load_more_threads :num => mode.buffer.content_height
+            mode.load_threads :num => mode.buffer.content_height
           rescue Ferret::QueryParser::QueryParseException => e
             bm.flash "Couldn't parse query."
           end
             b = BufferManager.spawn_unless_exists(:draft) do
               mode = LabelSearchResultsMode.new [:draft]
             end
-            b.mode.load_more_threads :num => b.content_height
+            b.mode.load_threads :num => b.content_height
           end
         when :nothing
         when :redraw
 
   def multi_search people
     mode = PersonSearchResultsMode.new people
     BufferManager.spawn "personal search results", mode
-    mode.load_more_threads :num => mode.buffer.content_height
+    mode.load_threads :num => mode.buffer.content_height
   end
 
   def search
 
   register_keymap do |k|
     ## overwrite toggle_archived with archive
     k.add :archive, "Archive thread (remove from inbox)", 'a'
-    k.add :load_more_threads, "Load #{LOAD_MORE_THREAD_NUM} more threads", 'M'
-    k.add :reload, "Discard threads and reload", 'D'
   end
 
   def initialize
 
   def is_relevant? m; m.has_label? :inbox; end
 
-  def load_more_threads opts={}
+  def load_threads opts={}
     n = opts[:num] || ThreadIndexMode::LOAD_MORE_THREAD_NUM
     load_n_threads_background n, :label => :inbox,
                                  :load_killed => false,
       BufferManager.flash "Added #{num} threads."
     end)
   end
-
-  def reload
-    drop_all_threads
-    BufferManager.draw_screen
-    load_more_threads :num => buffer.content_height
-  end
 end
 
 end
 
       b = BufferManager.spawn_unless_exists(label) do
         mode = LabelSearchResultsMode.new [label]
       end
-      b.mode.load_more_threads :num => b.content_height
+      b.mode.load_threads :num => b.content_height
     end
   end
 end
 
 module Redwood
 
 class LabelSearchResultsMode < ThreadIndexMode
-  register_keymap do |k|
-    k.add :load_more_threads, "Load #{LOAD_MORE_THREAD_NUM} more threads", 'M'
-  end
-
   def initialize labels
     @labels = labels
     super
 
   def is_relevant? m; @labels.all? { |l| m.has_label? l }; end
 
-  def load_more_threads opts={}
+  def load_threads opts={}
     n = opts[:num] || ThreadIndexMode::LOAD_MORE_THREAD_NUM
     load_n_threads_background n, :labels => @labels,
                                  :load_killed => true,
 
 module Redwood
 
 class PersonSearchResultsMode < ThreadIndexMode
-  register_keymap do |k|
-    k.add :load_more_threads, "Load #{LOAD_MORE_THREAD_NUM} more threads", 'M'
-  end
-
   def initialize people
     @people = people
     super
 
   def is_relevant? m; @people.any? { |p| m.from == p }; end
 
-  def load_more_threads opts={}
+  def load_threads opts={}
     n = opts[:num] || ThreadIndexMode::LOAD_MORE_THREAD_NUM
     load_n_threads_background n, :participants => @people,
                                  :load_killed => true,
 
 module Redwood
 
 class SearchResultsMode < ThreadIndexMode
-  register_keymap do |k|
-    k.add :load_more_threads, "Load #{LOAD_MORE_THREAD_NUM} more threads", 'M'
-  end
-
   def initialize qobj
     @qobj = qobj
     super
   ## TODO: think about this
   def is_relevant? m; super; end
 
-  def load_more_threads opts={}
+  def load_threads opts={}
     n = opts[:num] || ThreadIndexMode::LOAD_MORE_THREAD_NUM
     load_n_threads_background n, :qobj => @qobj,
                                  :load_killed => true,
 
 module Redwood
 
+## subclasses should implement load_threads
+
 class ThreadIndexMode < LineCursorMode
   DATE_WIDTH = Time::TO_NICE_S_MAX_LEN
   FROM_WIDTH = 15
   LOAD_MORE_THREAD_NUM = 20
 
   register_keymap do |k|
+    k.add :load_threads, "Load #{LOAD_MORE_THREAD_NUM} more threads", 'M'
+    k.add :reload, "Discard threads and reload", 'D'
     k.add :toggle_archived, "Toggle archived status", 'a'
     k.add :toggle_starred, "Star or unstar all messages in thread", '*'
     k.add :toggle_new, "Toggle new/read status of all messages in thread", 'N'
   def lines; @text.length; end
   def [] i; @text[i]; end
 
+  def reload
+    drop_all_threads
+    BufferManager.draw_screen
+    load_threads :num => buffer.content_height
+  end
+
   ## open up a thread view window
   def select t=nil
     t ||= @threads[curpos]
 
   def save
     threads = @threads + @hidden_threads.keys
-    mbid = BufferManager.say "Saving threads..."
-    threads.each_with_index do |t, i|
-      if i % 5 == 0
-        BufferManager.say "Saving thread #{i + 1} of #{threads.length}...",
-                          mbid
+
+    BufferManager.say("Saving threads...") do |say_id|
+      threads.each_with_index do |t, i|
+        next unless t.dirty?
+        BufferManager.say "Saving thread #{i +1} of #{threads.length}...", say_id
+        t.save Index
       end
-      t.save Index
     end
-    BufferManager.clear mbid
   end
 
   def cleanup