]> git.cworth.org Git - sup/blobdiff - lib/sup/modes/thread-view-mode.rb
Merge branch 'master' into next
[sup] / lib / sup / modes / thread-view-mode.rb
index a7fa964f3125deb3290fc3cf7c44b05231fc572f..42c62809fa8f80cb10c4730948de7fa4a208eff6 100644 (file)
@@ -30,6 +30,7 @@ EOS
     k.add :activate_chunk, "Expand/collapse or activate item", :enter
     k.add :expand_all_messages, "Expand/collapse all messages", 'E'
     k.add :edit_draft, "Edit draft", 'e'
+    k.add :send_draft, "Send draft", 'y'
     k.add :edit_labels, "Edit or add labels for a thread", 'l'
     k.add :expand_all_quotes, "Expand/collapse all quotes in a message", 'o'
     k.add :jump_to_next_open, "Jump to next open message", 'n'
@@ -49,20 +50,28 @@ EOS
     k.add :unsubscribe_from_list, "Subscribe to/unsubscribe from mailing list", ")"
     k.add :pipe_message, "Pipe message or attachment to a shell command", '|'
 
-    k.add_multi "(A)rchive/(d)elete/mark as (s)pam/mark as u(N)read:", '.' do |kk|
+    k.add_multi "(a)rchive/(d)elete/mark as (s)pam/mark as u(N)read:", '.' do |kk|
       kk.add :archive_and_kill, "Archive this thread and kill buffer", 'a'
       kk.add :delete_and_kill, "Delete this thread and kill buffer", 'd'
       kk.add :spam_and_kill, "Mark this thread as spam and kill buffer", 's'
       kk.add :unread_and_kill, "Mark this thread as unread and kill buffer", 'N'
     end
 
-    k.add_multi "(A)rchive/(d)elete/mark as (s)pam/mark as u(N)read/do (n)othing:", ',' do |kk|
+    k.add_multi "(a)rchive/(d)elete/mark as (s)pam/mark as u(N)read/do (n)othing:", ',' do |kk|
       kk.add :archive_and_next, "Archive this thread, kill buffer, and view next", 'a'
       kk.add :delete_and_next, "Delete this thread, kill buffer, and view next", 'd'
       kk.add :spam_and_next, "Mark this thread as spam, kill buffer, and view next", 's'
       kk.add :unread_and_next, "Mark this thread as unread, kill buffer, and view next", 'N'
       kk.add :do_nothing_and_next, "Kill buffer, and view next", 'n'
     end
+
+    k.add_multi "(a)rchive/(d)elete/mark as (s)pam/mark as u(N)read/do (n)othing:", ']' do |kk|
+      kk.add :archive_and_prev, "Archive this thread, kill buffer, and view previous", 'a'
+      kk.add :delete_and_prev, "Delete this thread, kill buffer, and view previous", 'd'
+      kk.add :spam_and_prev, "Mark this thread as spam, kill buffer, and view previous", 's'
+      kk.add :unread_and_prev, "Mark this thread as unread, kill buffer, and view previous", 'N'
+      kk.add :do_nothing_and_prev, "Kill buffer, and view previous", 'n'
+    end
   end
 
   ## there are a couple important instance variables we hold to format
@@ -140,7 +149,7 @@ EOS
   def subscribe_to_list
     m = @message_lines[curpos] or return
     if m.list_subscribe && m.list_subscribe =~ /<mailto:(.*?)\?(subject=(.*?))>/
-      ComposeMode.spawn_nicely :from => AccountManager.account_for(m.recipient_email), :to => [PersonManager.person_for($1)], :subj => $3
+      ComposeMode.spawn_nicely :from => AccountManager.account_for(m.recipient_email), :to => [Person.from_address($1)], :subj => $3
     else
       BufferManager.flash "Can't find List-Subscribe header for this message."
     end
@@ -149,7 +158,7 @@ EOS
   def unsubscribe_from_list
     m = @message_lines[curpos] or return
     if m.list_unsubscribe && m.list_unsubscribe =~ /<mailto:(.*?)\?(subject=(.*?))>/
-      ComposeMode.spawn_nicely :from => AccountManager.account_for(m.recipient_email), :to => [PersonManager.person_for($1)], :subj => $3
+      ComposeMode.spawn_nicely :from => AccountManager.account_for(m.recipient_email), :to => [Person.from_address($1)], :subj => $3
     else
       BufferManager.flash "Can't find List-Unsubscribe header for this message."
     end
@@ -180,7 +189,7 @@ EOS
   def compose
     p = @person_lines[curpos]
     if p
-      ComposeMode.spawn_nicely :to => [p]
+      ComposeMode.spawn_nicely :to_default => p
     else
       ComposeMode.spawn_nicely
     end
@@ -242,7 +251,7 @@ EOS
 
   def edit_as_new
     m = @message_lines[curpos] or return
-    mode = ComposeMode.new(:body => m.quotable_body_lines, :to => m.to, :cc => m.cc, :subj => m.subj, :bcc => m.bcc)
+    mode = ComposeMode.new(:body => m.quotable_body_lines, :to => m.to, :cc => m.cc, :subj => m.subj, :bcc => m.bcc, :refs => m.refs, :replytos => m.replytos)
     BufferManager.spawn "edit as new", mode
     mode.edit_message
   end
@@ -251,7 +260,8 @@ EOS
     chunk = @chunk_lines[curpos] or return
     case chunk
     when Chunk::Attachment
-      fn = BufferManager.ask_for_filename :filename, "Save attachment to file: ", chunk.filename
+      default_dir = File.join(($config[:default_attachment_save_dir] || "."), chunk.filename)
+      fn = BufferManager.ask_for_filename :filename, "Save attachment to file: ", default_dir
       save_to_file(fn) { |f| f.print chunk.raw_content } if fn
     else
       m = @message_lines[curpos]
@@ -275,6 +285,18 @@ EOS
     end
   end
 
+  def send_draft
+    m = @message_lines[curpos] or return
+    if m.is_draft?
+      mode = ResumeMode.new m
+      BufferManager.spawn "Send message", mode
+      BufferManager.kill_buffer self.buffer
+      mode.send_message
+    else
+      BufferManager.flash "Not a draft message!"
+    end
+  end
+
   def jump_to_first_open loose_alignment=false
     m = @message_lines[0] or return
     if @layout[m].state != :closed
@@ -318,6 +340,8 @@ EOS
     end
   end
 
+  IDEAL_TOP_CONTEXT = 3 # try and give 3 rows of top context
+  IDEAL_LEFT_CONTEXT = 4 # try and give 4 columns of left context
   def jump_to_message m, loose_alignment=false
     l = @layout[m]
     left = l.depth * INDENT_SPACES
@@ -325,19 +349,20 @@ EOS
 
     ## jump to the top line
     if loose_alignment
-      jump_to_line [l.top - 3, 0].max # give 3 lines of top context
+      jump_to_line [l.top - IDEAL_TOP_CONTEXT, 0].max # give 3 lines of top context
     else
       jump_to_line l.top
     end
 
     ## jump to the left column
-    if loose_alignment
-      ## try and give 4 columns of left context, but not if it means that
-      ## the right of the message is truncated.
-      jump_to_col [[left - 4, rightcol - l.width - 1].min, 0].max
-    else
-      jump_to_col left
-    end
+    ideal_left = left +
+      if loose_alignment
+        -IDEAL_LEFT_CONTEXT + (l.width - buffer.content_width + IDEAL_LEFT_CONTEXT + 1).clamp(0, IDEAL_LEFT_CONTEXT)
+      else
+        0
+      end
+
+    jump_to_col [ideal_left, 0].max
 
     ## either way, move the cursor to the first line
     set_cursor_pos l.top
@@ -380,6 +405,12 @@ EOS
   def unread_and_next; unread_and_then :next end
   def do_nothing_and_next; do_nothing_and_then :next end
 
+  def archive_and_prev; archive_and_then :prev end
+  def spam_and_prev; spam_and_then :prev end
+  def delete_and_prev; delete_and_then :prev end
+  def unread_and_prev; unread_and_then :prev end
+  def do_nothing_and_prev; do_nothing_and_then :prev end
+
   def archive_and_then op
     dispatch op do
       @thread.remove_label :inbox
@@ -416,15 +447,18 @@ EOS
     return if @dying
     @dying = true
 
+    l = lambda do
+      yield if block_given?
+      BufferManager.kill_buffer_safely buffer
+    end
+
     case op
     when :next
-      @index_mode.launch_next_thread_after(@thread) do
-        @thread.save Index if block_given? && yield
-        BufferManager.kill_buffer_safely buffer
-      end
+      @index_mode.launch_next_thread_after @thread, &l
+    when :prev
+      @index_mode.launch_prev_thread_before @thread, &l
     when :kill
-      @thread.save Index if yield
-      BufferManager.kill_buffer_safely buffer
+      l.call
     else
       raise ArgumentError, "unknown thread dispatch operation #{op.inspect}"
     end
@@ -506,7 +540,7 @@ private
       (0 ... text.length).each do |i|
         @chunk_lines[@text.length + i] = m
         @message_lines[@text.length + i] = m
-        lw = text[i].flatten.select { |x| x.is_a? String }.map { |x| x.length }.sum
+        lw = text[i].flatten.select { |x| x.is_a? String }.map { |x| x.display_length }.sum
       end
 
       @text += text
@@ -527,7 +561,7 @@ private
           (0 ... text.length).each do |i|
             @chunk_lines[@text.length + i] = c
             @message_lines[@text.length + i] = m
-            lw = text[i].flatten.select { |x| x.is_a? String }.map { |x| x.length }.sum - (depth * INDENT_SPACES)
+            lw = text[i].flatten.select { |x| x.is_a? String }.map { |x| x.display_length }.sum - (depth * INDENT_SPACES)
             l.width = lw if lw > l.width
           end
           @text += text
@@ -542,29 +576,29 @@ private
 
     open_widget = [color, (state == :closed ? "+ " : "- ")]
     new_widget = [color, (m.has_label?(:unread) ? "N" : " ")]
-    starred_widget = 
-      if m.has_label?(:starred)
-        [star_color, "* "]
+    starred_widget = if m.has_label?(:starred)
+        [star_color, "*"]
       else
-        [color, "  "]
+        [color, " "]
       end
+    attach_widget = [color, (m.has_label?(:attachment) ? "@" : " ")]
 
     case state
     when :open
       @person_lines[start] = m.from
-      [[prefix_widget, open_widget, new_widget, starred_widget,
+      [[prefix_widget, open_widget, new_widget, attach_widget, starred_widget,
         [color, 
             "#{m.from ? m.from.mediumname : '?'} to #{m.recipients.map { |l| l.shortname }.join(', ')} #{m.date.to_nice_s} (#{m.date.to_nice_distance_s})"]]]
 
     when :closed
       @person_lines[start] = m.from
-      [[prefix_widget, open_widget, new_widget, starred_widget,
+      [[prefix_widget, open_widget, new_widget, attach_widget, starred_widget,
         [color, 
         "#{m.from ? m.from.mediumname : '?'}, #{m.date.to_nice_s} (#{m.date.to_nice_distance_s})  #{m.snippet}"]]]
 
     when :detailed
       @person_lines[start] = m.from
-      from_line = [[prefix_widget, open_widget, new_widget, starred_widget,
+      from_line = [[prefix_widget, open_widget, new_widget, attach_widget, starred_widget,
           [color, "From: #{m.from ? format_person(m.from) : '?'}"]]]
 
       addressee_lines = []
@@ -601,7 +635,7 @@ private
 
   def format_person_list prefix, people
     ptext = people.map { |p| format_person p }
-    pad = " " * prefix.length
+    pad = " " * prefix.display_length
     [prefix + ptext.first + (ptext.length > 1 ? "," : "")] + 
       ptext[1 .. -1].map_with_index do |e, i|
         pad + e + (i == ptext.length - 1 ? "" : ",")
@@ -622,7 +656,7 @@ private
       [[[:missing_message_color, "#{prefix}<an unreceived message>"]]]
     when Message
       message_patina_lines(chunk, state, start, parent, prefix, color, star_color) +
-        (chunk.is_draft? ? [[[:draft_notification_color, prefix + " >>> This message is a draft. To edit, hit 'e'. <<<"]]] : [])
+        (chunk.is_draft? ? [[[:draft_notification_color, prefix + " >>> This message is a draft. Hit 'e' to edit, 'y' to send. <<<"]]] : [])
 
     else
       raise "Bad chunk: #{chunk.inspect}" unless chunk.respond_to?(:inlineable?) ## debugging