]> git.cworth.org Git - sup/blobdiff - lib/sup/modes/thread-index-mode.rb
'A' archives and kills buffer in thread-view-mode (required significant updatedes...
[sup] / lib / sup / modes / thread-index-mode.rb
index 17d9992d818506a181a4b99a9ca24d332c725d9a..de799000b9d1975d500166e3e8e799cb6db49534 100644 (file)
@@ -1,18 +1,24 @@
+require 'thread'
 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'
     k.add :edit_labels, "Edit or add labels for a thread", 'l'
     k.add :edit_message, "Edit message (drafts only)", 'e'
     k.add :mark_as_spam, "Mark thread as spam", 'S'
-    k.add :kill, "Kill thread (never to be seen in inbox again)", 'K'
+    k.add :delete, "Mark thread for deletion", 'd'
+    k.add :kill, "Kill thread (never to be seen in inbox again)", '&'
     k.add :save, "Save changes now", '$'
     k.add :jump_to_next_new, "Jump to next new thread", :tab
     k.add :reply, "Reply to a thread", 'r'
@@ -40,59 +46,83 @@ class ThreadIndexMode < LineCursorMode
 
   def lines; @text.length; end
   def [] i; @text[i]; end
+  def contains_thread? t; !@lines[t].nil?; end
+
+  def reload
+    drop_all_threads
+    BufferManager.draw_screen
+    load_threads :num => buffer.content_height
+  end
 
   ## open up a thread view window
-  def select
-    this_curpos = curpos
-    t = @threads[this_curpos]
+  def select t=nil
+    t ||= @threads[curpos]
+
+    ## this isn't working entirely. TODO:figure out why
+    # t = t.clone # required so that messages added later on don't completely
+                # screw everything up
 
     ## TODO: don't regen text completely
     Redwood::reporting_thread do
+      BufferManager.say("Loading message bodies...") do |sid|
+        t.each { |m, *o| m.load_from_source! if m }
+      end
       mode = ThreadViewMode.new t, @hidden_labels
       BufferManager.spawn t.subj, mode
       BufferManager.draw_screen
+      mode.jump_to_first_open
+      BufferManager.draw_screen # lame TODO: make this unnecessary
+      ## the first draw_screen is needed before topline and botline
+      ## are set, and the second to show the cursor having moved
     end
   end
+
+  def multi_select threads
+    threads.each { |t| select t }
+  end
   
-  def handle_starred_update m
+  def handle_starred_update sender, m
     return unless(t = @ts.thread_for m)
-    @starred_cache[t] = t.has_label? :starred
     update_text_for_line @lines[t]
+    BufferManager.draw_screen
   end
 
-  def handle_read_update m
-    return unless(t = @ts.thread_for m)
-    @new_cache[t] = false
+  def handle_read_update sender, t
+    return unless @lines[t]
     update_text_for_line @lines[t]
+    BufferManager.draw_screen
   end
 
+  def handle_archived_update *a; handle_read_update *a; end
+
   ## overwrite me!
   def is_relevant? m; false; end
 
-  def handle_add_update m
+  def handle_add_update sender, m
     if is_relevant?(m) || @ts.is_relevant?(m)
       @ts.load_thread_for_message m
-      @new_cache.delete @ts.thread_for(m) # force recalculation of newness
       update
+      BufferManager.draw_screen
     end
   end
 
-  def handle_delete_update mid
+  def handle_delete_update sender, mid
     if @ts.contains_id? mid
       @ts.remove mid
       update
+      BufferManager.draw_screen
     end
   end
 
   def update
     ## let's see you do THIS in python
-    @threads = @ts.threads.select { |t| !@hidden_threads[t] }.sort_by { |t| t.date }.reverse
+    @threads = @ts.threads.select { |t| !@hidden_threads[t] && !t.has_label?(:killed) }.sort_by { |t| t.date }.reverse
     @size_width = (@threads.map { |t| t.size }.max || 0).num_digits
     regen_text
   end
 
   def edit_message
-    t = @threads[curpos] or return
+    return unless(t = @threads[curpos])
     message, *crap = t.find { |m, *o| m.has_label? :draft }
     if message
       mode = ResumeMode.new message
@@ -102,39 +132,56 @@ class ThreadIndexMode < LineCursorMode
     end
   end
 
-  def toggle_starred
+  def actually_toggle_starred t
+    if t.has_label? :starred # if ANY message has a star
+      t.remove_label :starred # remove from all
+    else
+      t.first.add_label :starred # add only to first
+    end
+  end  
+
+  def toggle_starred 
     t = @threads[curpos] or return
-    @starred_cache[t] = t.toggle_label :starred
+    actually_toggle_starred t
     update_text_for_line curpos
     cursor_down
   end
 
   def multi_toggle_starred threads
-    threads.each { |t| @starred_cache[t] = t.toggle_label :starred }
+    threads.each { |t| actually_toggle_starred t }
     regen_text
   end
 
-  def toggle_archived
-    return unless(t = @threads[curpos])
-    t.toggle_label :inbox
+  def actually_toggle_archived t
+    if t.has_label? :inbox
+      t.remove_label :inbox
+      UpdateManager.relay self, :unarchived, t
+    else
+      t.add_label :inbox
+      UpdateManager.relay self, :archived, t
+    end
+  end
+
+  def toggle_archived 
+    t = @threads[curpos] or return
+    actually_toggle_archived t
     update_text_for_line curpos
-    cursor_down
   end
 
   def multi_toggle_archived threads
-    threads.each { |t| t.toggle_label :inbox }
+    threads.each { |t| actually_toggle_archived t }
     regen_text
   end
 
   def toggle_new
     t = @threads[curpos] or return
-    @new_cache[t] = t.toggle_label :unread
+    t.toggle_label :unread
     update_text_for_line curpos
     cursor_down
   end
 
   def multi_toggle_new threads
-    threads.each { |t| @new_cache[t] = t.toggle_label :unread }
+    threads.each { |t| t.toggle_label :unread }
     regen_text
   end
 
@@ -144,9 +191,8 @@ class ThreadIndexMode < LineCursorMode
   end
 
   def jump_to_next_new
-    t = @threads[curpos] or return
-    n = ((curpos + 1) .. lines).find { |i| @new_cache[@threads[i]] }
-    n = (0 ... curpos).find { |i| @new_cache[@threads[i]] } unless n
+    n = ((curpos + 1) ... lines).find { |i| @threads[i].has_label? :unread }
+    n = (0 ... curpos).find { |i| @threads[i].has_label? :unread } unless n
     if n
       set_cursor_pos n
     else
@@ -161,7 +207,20 @@ class ThreadIndexMode < LineCursorMode
 
   def multi_mark_as_spam threads
     threads.each do |t|
-      t.apply_label :spam
+      t.toggle_label :spam
+      hide_thread t
+    end
+    regen_text
+  end
+
+  def delete
+    t = @threads[curpos] or return
+    multi_delete [t]
+  end
+
+  def multi_delete threads
+    threads.each do |t|
+      t.toggle_label :deleted
       hide_thread t
     end
     regen_text
@@ -182,15 +241,14 @@ class ThreadIndexMode < LineCursorMode
 
   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
@@ -220,6 +278,7 @@ class ThreadIndexMode < LineCursorMode
     speciall = (@hidden_labels + LabelManager::RESERVED_LABELS).uniq
     keepl, modifyl = thread.labels.partition { |t| speciall.member? t }
     label_string = modifyl.join(" ")
+    label_string += " " unless label_string.empty?
 
     answer = BufferManager.ask :edit_labels, "edit labels: ", label_string
     return unless answer
@@ -254,6 +313,7 @@ class ThreadIndexMode < LineCursorMode
     t = @threads[curpos] or return
     m = t.latest_message
     return if m.nil? # probably won't happen
+    m.load_from_source!
     mode = ReplyMode.new m
     BufferManager.spawn "Reply to #{m.subj}", mode
   end
@@ -262,6 +322,7 @@ class ThreadIndexMode < LineCursorMode
     t = @threads[curpos] or return
     m = t.latest_message
     return if m.nil? # probably won't happen
+    m.load_from_source!
     mode = ForwardMode.new m
     BufferManager.spawn "Forward of #{m.subj}", mode
     mode.edit
@@ -279,11 +340,13 @@ class ThreadIndexMode < LineCursorMode
   def load_n_threads n=LOAD_MORE_THREAD_NUM, opts={}
     @mbid = BufferManager.say "Searching for threads..."
     orig_size = @ts.size
+    last_update = Time.now - 9999 # oh yeah
     @ts.load_n_threads(@ts.size + n, opts) do |i|
       BufferManager.say "Loaded #{i} threads...", @mbid
-      if i % 5 == 0
+      if (Time.now - last_update) >= 0.25
         update
         BufferManager.draw_screen
+        last_update = Time.now
       end
     end
     @ts.threads.each { |th| th.labels.each { |l| LabelManager << l } }
@@ -296,7 +359,11 @@ class ThreadIndexMode < LineCursorMode
   end
 
   def status
-    "line #{curpos + 1} of #{lines} #{dirty? ? '*modified*' : ''}"
+    if (l = lines) == 0
+      ""
+    else
+      "line #{curpos + 1} of #{l} #{dirty? ? '*modified*' : ''}"
+    end
   end
 
 protected
@@ -309,11 +376,6 @@ protected
     update
   end
 
-  def remove_label_and_hide_thread t, label
-    t.remove_label label
-    hide_thread t
-  end
-
   def hide_thread t
     raise "already hidden" if @hidden_threads[t]
     @hidden_threads[t] = true
@@ -342,18 +404,18 @@ protected
   end
 
   def text_for_thread t
-    date = (@date_cache[t] ||= t.date.to_nice_s(Time.now)) 
-    from = (@who_cache[t] ||= author_text_for_thread(t))
+    date = t.date.to_nice_s(Time.now)
+    from = author_text_for_thread(t)
     if from.length > @from_width
       from = from[0 ... (@from_width - 1)]
       from += "." unless from[-1] == ?\s
     end
 
-    new = @new_cache.member?(t) ? @new_cache[t] : @new_cache[t] = t.has_label?(:unread)
-    starred = @starred_cache.member?(t) ? @starred_cache[t] : @starred_cache[t] = t.has_label?(:starred)
+    new = t.has_label?(:unread)
+    starred = t.has_label?(:starred)
 
-    dp = (@dp_cache[t] ||= t.direct_participants.any? { |p| AccountManager.is_account? p })
-    p = (@p_cache[t] ||= (dp || t.participants.any? { |p| AccountManager.is_account? p }))
+    dp = t.direct_participants.any? { |p| AccountManager.is_account? p }
+    p = dp || t.participants.any? { |p| AccountManager.is_account? p }
 
     base_color = (new ? :index_new_color : :index_old_color)
     [ 
@@ -376,12 +438,7 @@ private
 
   def initialize_threads
     @ts = ThreadSet.new Index.instance
-    @date_cache = {}
-    @who_cache = {}
-    @dp_cache = {}
-    @p_cache = {}
-    @new_cache = {}
-    @starred_cache = {}
+    @ts_mutex = Mutex.new
     @hidden_threads = {}
   end
 end