]> git.notmuchmail.org Git - sup/commitdiff
alias contacts in thread-view-mode
authorwmorgan <wmorgan@5c8cc53c-5e98-4d25-b20a-d8db53a31250>
Wed, 17 Jan 2007 21:34:27 +0000 (21:34 +0000)
committerwmorgan <wmorgan@5c8cc53c-5e98-4d25-b20a-d8db53a31250>
Wed, 17 Jan 2007 21:34:27 +0000 (21:34 +0000)
git-svn-id: svn://rubyforge.org/var/svn/sup/trunk@257 5c8cc53c-5e98-4d25-b20a-d8db53a31250

doc/TODO
lib/sup/contact.rb
lib/sup/modes/contact-list-mode.rb
lib/sup/modes/edit-message-mode.rb
lib/sup/modes/thread-view-mode.rb

index 47b20df6c245299fd1b2bf8efc8b5958f73f8b66..c9e2ae4a862a42c44413b86ce7875f03189496ed 100644 (file)
--- a/doc/TODO
+++ b/doc/TODO
@@ -1,4 +1,5 @@
-alias authors in thread-view-mode
+bugfix: first time vieweing a message only gets the first to:;
+   subsequent views get them all (wtf)
 add a flag to sup-import to force the creation of a new source (see http://rubyforge.org/forum/forum.php?thread_id=10973&forum_id=10340)
 fix bug: when returning from a shelling out, ncurses is crazy 
 batch deletion
@@ -23,6 +24,7 @@ pop
 move sup-import argument handling to getopt
 be able to mark individual messages as spam in thread-view-mode
 
+x alias authors in thread-view-mode
 x fix up contact list mode: should display while loading, and when you add an alias, should move everything else to the right
 x fix bug: envelope-to thing still not working
 x fix snippet repetitions with small snippets
index cf307610893f0ec97c08ad079a139b93a223be1f..badbc8426e447b02f2c18671c2c44ae86c8ac0a5 100644 (file)
@@ -5,33 +5,43 @@ class ContactManager
 
   def initialize fn
     @fn = fn
-    @people = {}
+    @p2a = {} # person to alias map
+    @a2p = {} # alias to person map
 
     if File.exists? fn
       IO.foreach(fn) do |l|
         l =~ /^(\S+): (.*)$/ or raise "can't parse #{fn} line #{l.inspect}"
         aalias, addr = $1, $2
-        @people[aalias] = Person.for addr
+        p = Person.for addr
+        @p2a[p] = aalias
+        @a2p[aalias] = p
       end
     end
 
     self.class.i_am_the_instance self
   end
 
-  def contacts; @people; end
+  def contacts; @p2a.keys; end
   def set_contact person, aalias
-    oldentry = @people.find { |a, p| p == person }
-    @people.delete oldentry.first if oldentry
-    @people[aalias] = person
+    if(pold = @a2p[aalias]) && (pold != person)
+      drop_contact pold
+    end
+    @p2a[person] = aalias
+    @a2p[aalias] = person
   end
-  def drop_contact person; @people.find { |a, p| @people.delete a if p == person }; end
-  def delete t; @people.delete t; end
-  def resolve aalias; @people[aalias]; end
-
+  def drop_contact person
+    if(aalias = @p2a[person])
+      @p2a.delete person
+      @a2p.delete aalias
+    end
+  end    
+  def person_with aalias; @a2p[aalias]; end
+  def alias_for person; @p2a[person]; end
+  def is_contact? person; @p2a.member? person; end
   def save
     File.open(@fn, "w") do |f|
-      @people.keys.sort.each do |aalias|
-        f.puts "#{aalias}: #{@people[aalias].full_address}"
+      @p2a.each do |p, a|
+        f.puts "#{a}: #{p.full_address}"
       end
     end
   end
index 174a48b2bf2c5dac66e2cc239d58216423d7ff3b..dc222073a0efb2c5c3de4ce850e41ca5040bdeab 100644 (file)
@@ -1,12 +1,23 @@
 module Redwood
 
+module CanAliasContacts
+  def alias_contact p
+    a = BufferManager.ask(:alias, "Nickname for #{p.longname}: ", ContactManager.alias_for(p)) or return
+    if a.empty?
+      ContactManager.drop_contact p
+    else
+      ContactManager.set_contact p, a
+    end
+  end
+end
+
 class ContactListMode < LineCursorMode
   LOAD_MORE_CONTACTS_NUM = 10
 
   register_keymap do |k|
     k.add :load_more, "Load #{LOAD_MORE_CONTACTS_NUM} more contacts", 'M'
     k.add :reload, "Drop contact list and reload", 'D'
-    k.add :alias, "Edit alias for contact", 'a'
+    k.add :alias, "Edit nickname/alias for contact", 'a'
     k.add :toggle_tagged, "Tag/untag current line", 't'
     k.add :apply_to_tagged, "Apply next command to all tagged items", ';'
     k.add :search, "Search for messages from particular people", 'S'
@@ -20,6 +31,13 @@ class ContactListMode < LineCursorMode
     super()
   end
 
+  include CanAliasContacts
+  def alias
+    p = @contacts[curpos] or return
+    alias_contact p
+    regen_text
+  end
+
   def lines; @text.length; end
   def [] i; @text[i]; end
 
@@ -75,19 +93,6 @@ class ContactListMode < LineCursorMode
     load
   end
 
-  def alias
-    p = @contacts[curpos] or return
-    a = BufferManager.ask(:alias, "alias for #{p.longname}: ", @user_contacts[p]) or return
-    if a.empty?
-      ContactManager.drop_contact p
-      @user_contacts.delete p
-    else
-      ContactManager.set_contact p, a
-      @user_contacts[p] = a
-    end
-    regen_text # in case columns need to be shifted
-  end
-
   def load_in_background
     Redwood::reporting_thread do
       load
@@ -98,11 +103,11 @@ class ContactListMode < LineCursorMode
 
   def load
     @num ||= buffer.content_height
-    @user_contacts = ContactManager.contacts.invert
+    @user_contacts = ContactManager.contacts
     num = [@num - @user_contacts.length, 0].max
     BufferManager.say("Loading #{num} contacts from index...") do
       recentc = Index.load_contacts AccountManager.user_emails, :num => num
-      @contacts = (@user_contacts.keys + recentc).sort_by { |p| p.sort_by_me }.uniq
+      @contacts = (@user_contacts + recentc).sort_by { |p| p.sort_by_me }.uniq
     end
   end
   
@@ -114,7 +119,7 @@ protected
   end
 
   def text_for_contact p
-    aalias = @user_contacts[p] || ""
+    aalias = ContactManager.alias_for(p) || ""
     [[:tagged_color, @tags.tagged?(p) ? ">" : " "],
      [:none, sprintf("%-#{@awidth}s %-#{@nwidth}s %s", aalias, p.name, p.email)]]
   end
@@ -122,7 +127,7 @@ protected
   def regen_text
     @awidth, @nwidth = 0, 0
     @contacts.each do |p|
-      aalias = @user_contacts[p]
+      aalias = ContactManager.alias_for_person(p)
       @awidth = aalias.length if aalias && aalias.length > @awidth
       @nwidth = p.name.length if p.name && p.name.length > @nwidth
     end
index 0eace095260690384c051b6797c6f16212893310..000c32c2c66892ba5709f4fbc1e6e6690b1267d6 100644 (file)
@@ -61,7 +61,7 @@ protected
       header.each do |k, v|
         next unless MULTI_HEADERS.include?(k) && !v.empty?
         header[k] = v.split_on_commas.map do |name|
-          (p = ContactManager.resolve(name)) && p.full_address || name
+          (p = ContactManager.person_with(name)) && p.full_address || name
         end
       end
 
index 988d8211dd49b33a55ef276e37a23ce6f870eee0..eb0d444c5bd7818d07f60ac5504483ea73ec2666 100644 (file)
@@ -22,14 +22,17 @@ class ThreadViewMode < LineCursorMode
     k.add :collapse_non_new_messages, "Collapse all but new messages", 'N'
     k.add :reply, "Reply to a message", 'r'
     k.add :forward, "Forward a message", 'f'
+    k.add :alias, "Edit alias/nickname for a person", 'a'
     k.add :save_to_disk, "Save message/attachment to disk", 's'
   end
 
-  ## there are three important instance variables we hold to lay out
-  ## the thread. @layout is a map from Message and Chunk objects to
-  ## Layout objects. (for chunks, we only use the state field right
-  ## now.) @message_lines is a map from row #s to Message objects.
-  ## @chunk_lines is a map from row #s to Chunk objects.
+  ## there are a couple important instance variables we hold to lay
+  ## out the thread and to provide line-based functionality. @layout
+  ## is a map from Message and Chunk objects to Layout objects. (for
+  ## chunks, we only use the state field right now.) @message_lines is
+  ## a map from row #s to Message objects. @chunk_lines is a map from
+  ## row #s to Chunk objects. @person_lines is a map from row #s to
+  ## Person objects.
 
   def initialize thread, hidden_labels=[]
     super()
@@ -92,6 +95,13 @@ class ThreadViewMode < LineCursorMode
     mode.edit
   end
 
+  include CanAliasContacts
+  def alias
+    p = @person_lines[curpos] or return
+    alias_contact p
+    regen_text
+  end
+
   def toggle_starred
     return unless(m = @message_lines[curpos])
     if m.has_label? :starred
@@ -240,6 +250,7 @@ private
     @text = []
     @chunk_lines = []
     @message_lines = []
+    @person_lines = []
 
     prevm = nil
     @thread.each do |m, depth, parent|
@@ -300,7 +311,7 @@ private
     end
   end
 
-  def message_patina_lines m, state, parent, prefix
+  def message_patina_lines m, state, start, parent, prefix
     prefix_widget = [:message_patina_color, prefix]
     widget = 
       case state
@@ -318,37 +329,58 @@ private
 
     case state
     when :open
+      @person_lines[start] = m.from
       [[prefix_widget, widget, imp_widget,
         [:message_patina_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, widget, imp_widget,
         [:message_patina_color, 
         "#{m.from ? m.from.mediumname : '?'}, #{m.date.to_nice_s} (#{m.date.to_nice_distance_s})  #{m.snippet}"]]]
+
     when :detailed
-      labels = m.labels# - @hidden_labels
-      x = [[prefix_widget, widget, imp_widget, [:message_patina_color, "From: #{m.from ? m.from.longname : '?'}"]]] +
-        ((m.to.empty? ? [] : break_into_lines("  To: ", m.to.map { |x| x.longname })) +
-           (m.cc.empty? ? [] : break_into_lines("  Cc: ", m.cc.map { |x| x.longname })) +
-           (m.bcc.empty? ? [] : break_into_lines("  Bcc: ", m.bcc.map { |x| x.longname })) +
-           ["  Date: #{m.date.strftime DATE_FORMAT} (#{m.date.to_nice_distance_s})"] +
-           ["  Subject: #{m.subj}"] +
-           [(parent ? "  In reply to: #{parent.from.mediumname}'s message of #{parent.date.strftime DATE_FORMAT}" : nil)] +
-           [labels.empty? ? nil : "  Labels: #{labels.join(', ')}"]
-        ).flatten.compact.map { |l| [[:message_patina_color, prefix + "  " + l]] }
-      #raise x.inspect
-      x
+      @person_lines[start] = m.from
+      from = [[prefix_widget, widget, imp_widget, [:message_patina_color, "From: #{m.from ? format_person(m.from) : '?'}"]]]
+
+      rest = []
+      unless m.to.empty?
+        m.to.each_with_index { |p, i| @person_lines[start + rest.length + from.length + i] = p }
+        rest += format_person_list "  To: ", m.to
+      end
+      unless m.cc.empty?
+        m.cc.each_with_index { |p, i| @person_lines[start + rest.length + from.length + i] = p }
+        rest += format_person_list "  Cc: ", m.cc
+      end
+      unless m.bcc.empty?
+        m.bcc.each_with_index { |p, i| @person_lines[start + rest.length + from.length + i] = p }
+        rest += format_person_list "  Bcc: ", m.bcc
+      end
+
+      rest += [
+        "  Date: #{m.date.strftime DATE_FORMAT} (#{m.date.to_nice_distance_s})",
+        "  Subject: #{m.subj}",
+        (parent ? "  In reply to: #{parent.from.mediumname}'s message of #{parent.date.strftime DATE_FORMAT}" : nil),
+        m.labels.empty? ? nil : "  Labels: #{m.labels.join(', ')}",
+      ].compact
+      
+      from + rest.map { |l| [[:message_patina_color, prefix + "  " + l]] }
     end
   end
 
-  def break_into_lines prefix, list
+  def format_person_list prefix, people
+    ptext = people.map { |p| format_person p }
     pad = " " * prefix.length
-    [prefix + list.first + (list.length > 1 ? "," : "")] + 
-      list[1 .. -1].map_with_index do |e, i|
-        pad + e + (i == list.length - 1 ? "" : ",")
+    [prefix + ptext.first + (ptext.length > 1 ? "," : "")] + 
+      ptext[1 .. -1].map_with_index do |e, i|
+        pad + e + (i == ptext.length - 1 ? "" : ",")
       end
   end
 
+  def format_person p
+    p.longname + (ContactManager.is_contact?(p) ? " (#{ContactManager.alias_for p})" : "")
+  end
 
   def chunk_to_lines chunk, state, start, depth, parent=nil
     prefix = " " * INDENT_SPACES * depth
@@ -358,7 +390,7 @@ private
     when nil
       [[[:message_patina_color, "#{prefix}<an unreceived message>"]]]
     when Message
-      message_patina_lines(chunk, state, parent, prefix) +
+      message_patina_lines(chunk, state, start, parent, prefix) +
         (chunk.is_draft? ? [[[:draft_notification_color, prefix + " >>> This message is a draft. To edit, hit 'e'. <<<"]]] : [])
     when Message::Attachment
       [[[:mime_color, "#{prefix}+ MIME attachment #{chunk.content_type}#{chunk.desc ? ' (' + chunk.desc + ')': ''}"]]]