From b7e2a834ea1110aafb8d5e706754607a352a0159 Mon Sep 17 00:00:00 2001 From: wmorgan Date: Wed, 17 Jan 2007 21:34:27 +0000 Subject: [PATCH] alias contacts in thread-view-mode git-svn-id: svn://rubyforge.org/var/svn/sup/trunk@257 5c8cc53c-5e98-4d25-b20a-d8db53a31250 --- doc/TODO | 4 +- lib/sup/contact.rb | 34 ++++++++----- lib/sup/modes/contact-list-mode.rb | 41 +++++++++------- lib/sup/modes/edit-message-mode.rb | 2 +- lib/sup/modes/thread-view-mode.rb | 78 +++++++++++++++++++++--------- 5 files changed, 104 insertions(+), 55 deletions(-) diff --git a/doc/TODO b/doc/TODO index 47b20df..c9e2ae4 100644 --- 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 diff --git a/lib/sup/contact.rb b/lib/sup/contact.rb index cf30761..badbc84 100644 --- a/lib/sup/contact.rb +++ b/lib/sup/contact.rb @@ -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 diff --git a/lib/sup/modes/contact-list-mode.rb b/lib/sup/modes/contact-list-mode.rb index 174a48b..dc22207 100644 --- a/lib/sup/modes/contact-list-mode.rb +++ b/lib/sup/modes/contact-list-mode.rb @@ -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 diff --git a/lib/sup/modes/edit-message-mode.rb b/lib/sup/modes/edit-message-mode.rb index 0eace09..000c32c 100644 --- a/lib/sup/modes/edit-message-mode.rb +++ b/lib/sup/modes/edit-message-mode.rb @@ -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 diff --git a/lib/sup/modes/thread-view-mode.rb b/lib/sup/modes/thread-view-mode.rb index 988d821..eb0d444 100644 --- a/lib/sup/modes/thread-view-mode.rb +++ b/lib/sup/modes/thread-view-mode.rb @@ -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}"]]] 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 + ')': ''}"]]] -- 2.45.2