_ split out threading & message chunk parsing to a separate library
 _ decode RFC 2047 ("encoded word") headers
   - see: http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/101949, http://dev.rubyonrails.org/ticket/6807
+_ refactor all the *-search-results-mode classes into one.
+x autoload more threads when you go down
 x add a sync-back tool that at least works for mboxes
 x thread by subject configurable in config.yaml
 x view as text command if the mime view command fails for an attachment
 
 module Redwood
 
+## extends ScrollMode to have a line-based cursor.
 class LineCursorMode < ScrollMode
   register_keymap do |k|
     ## overwrite scrollmode binding on arrow keys for cursor movement
   def initialize cursor_top=0, opts={}
     @cursor_top = cursor_top
     @curpos = cursor_top
+    @load_more_callbacks = []
+    @load_more_callbacks_m = Mutex.new
+    @load_more_callbacks_active = false
     super opts
   end
 
 
 protected
 
+  ## callbacks when the cursor is asked to go beyond the bottom
+  def to_load_more &b
+    @load_more_callbacks << b
+  end
+
   def draw_line ln, opts={}
     if ln == @curpos
       super ln, :highlight => true, :debug => opts[:debug]
 
   def line_down # overwrite scrollmode
     super
+    call_load_more_callbacks([topline + buffer.content_height - lines, 10].max) if topline + buffer.content_height > lines
     set_cursor_pos topline if @curpos < topline
   end
 
   end
 
   def cursor_down
+    call_load_more_callbacks buffer.content_height if @curpos == lines - 1
     return false unless @curpos < lines - 1
+
     if @curpos >= botline - 1
       page_down
       set_cursor_pos topline
     end
   end
 
+  ## more complicated than one might think. three behaviors.
   def page_down
-    if topline >= lines - buffer.content_height
+    ## if we're on the last page, and it's not a full page, just move
+    ## the cursor down to the bottom and assume we can't load anything
+    ## else via the callbacks.
+    if topline > lines - buffer.content_height
       set_cursor_pos(lines - 1)
+
+    ## if we're on the last page, and it's a full page, try and load
+    ## more lines via the callbacks and then shift the page down
+    elsif topline == lines - buffer.content_height
+      call_load_more_callbacks buffer.content_height
+      super
+
+    ## otherwise, just move down
     else
       relpos = @curpos - topline
       super
     @status = l > 0 ? "line #{@curpos + 1} of #{l}" : ""
   end
 
+  def call_load_more_callbacks size
+    go = 
+      @load_more_callbacks_m.synchronize do
+        if @load_more_callbacks_active
+          false
+        else
+          @load_more_callbacks_active = true
+        end
+    end
+
+    return unless go
+
+    @load_more_callbacks.each { |c| c.call size }
+    @load_more_callbacks_active = false
+  end    
+
 end
 
 end
 
 module Redwood
 
 ## subclasses should implement load_threads
-
 class ThreadIndexMode < LineCursorMode
   DATE_WIDTH = Time::TO_NICE_S_MAX_LEN
   FROM_WIDTH = 15
     @date_width = DATE_WIDTH
     @from_width = FROM_WIDTH
     @size_width = nil
-
+    @last_load_more_size = nil
+    
     @tags = Tagger.new self
     
     initialize_threads
     update
 
     UpdateManager.register self
+
+    to_load_more do |size|
+      next if @last_load_more_size == 0
+      load_threads :num => size,
+                   :when_done => lambda { |num| @last_load_more_size = num }
+      sleep 1.0 # give 'em a chance to load
+    end
   end
 
   def lines; @text.length; end
 
   def status
     if (l = lines) == 0
-      ""
+      "line 0 of 0"
     else
       "line #{curpos + 1} of #{l} #{dirty? ? '*modified*' : ''}"
     end