lib/sup/modes/edit-message-mode.rb in sup-0.12.1 vs lib/sup/modes/edit-message-mode.rb in sup-0.13.0
- old
+ new
@@ -78,23 +78,23 @@
k.add :edit_message_or_field, "Edit selected field", 'e'
k.add :edit_to, "Edit To:", 't'
k.add :edit_cc, "Edit Cc:", 'c'
k.add :edit_subject, "Edit Subject", 's'
k.add :edit_message, "Edit message", :enter
+ k.add :edit_message_async, "Edit message asynchronously", 'E'
k.add :save_as_draft, "Save as draft", 'P'
k.add :attach_file, "Attach a file", 'a'
k.add :delete_attachment, "Delete an attachment", 'd'
k.add :move_cursor_right, "Move selector to the right", :right, 'l'
k.add :move_cursor_left, "Move selector to the left", :left, 'h'
end
def initialize opts={}
- @header = opts.delete(:header) || {}
+ @header = opts.delete(:header) || {}
@header_lines = []
@body = opts.delete(:body) || []
- @body += sig_lines if $config[:edit_signature] && !opts.delete(:have_signature)
if opts[:attachments]
@attachments = opts[:attachments].values
@attachment_names = opts[:attachments].keys
else
@@ -109,30 +109,56 @@
end
hostname = Socket.gethostname if hostname.nil? or hostname.empty?
@message_id = "<#{Time.now.to_i}-sup-#{rand 10000}@#{hostname}>"
@edited = false
+ @sig_edited = false
@selectors = []
@selector_label_width = 0
+ @async_mode = nil
+ HookManager.run "before-edit", :header => @header, :body => @body
+
+ @account_selector = nil
+ # only show account selector if there is more than one email address
+ if $config[:account_selector] && AccountManager.user_emails.length > 1
+ ## Duplicate e-mail strings to prevent a "can't modify frozen
+ ## object" crash triggered by the String::display_length()
+ ## method in util.rb
+ user_emails_copy = []
+ AccountManager.user_emails.each { |e| user_emails_copy.push e.dup }
+
+ @account_selector =
+ HorizontalSelector.new "Account:", AccountManager.user_emails + [nil], user_emails_copy + ["Customized"]
+
+ if @header["From"] =~ /<?(\S+@(\S+?))>?$/
+ @account_selector.set_to $1
+ @account_user = ""
+ else
+ @account_selector.set_to nil
+ @account_user = @header["From"]
+ end
+
+ add_selector @account_selector
+ end
+
@crypto_selector =
if CryptoManager.have_crypto?
HorizontalSelector.new "Crypto:", [:none] + CryptoManager::OUTGOING_MESSAGE_OPERATIONS.keys, ["None"] + CryptoManager::OUTGOING_MESSAGE_OPERATIONS.values
end
add_selector @crypto_selector if @crypto_selector
-
- HookManager.run "before-edit", :header => @header, :body => @body
+
if @crypto_selector
HookManager.run "crypto-mode", :header => @header, :body => @body, :crypto_selector => @crypto_selector
end
super opts
regen_text
end
def lines; @text.length + (@selectors.empty? ? 0 : (@selectors.length + DECORATION_LINES)) end
-
+
def [] i
if @selectors.empty?
@text[i]
elsif i < @selectors.length
@selectors[i].line @selector_label_width
@@ -159,34 +185,117 @@
def edit_to; edit_field "To" end
def edit_cc; edit_field "Cc" end
def edit_subject; edit_field "Subject" end
- def edit_message
- @file = Tempfile.new "sup.#{self.class.name.gsub(/.*::/, '').camel_to_hyphy}"
+ def save_message_to_file
+ sig = sig_lines.join("\n")
+ @file = Tempfile.new ["sup.#{self.class.name.gsub(/.*::/, '').camel_to_hyphy}", ".eml"]
@file.puts format_headers(@header - NON_EDITABLE_HEADERS).first
@file.puts
@file.puts @body.join("\n")
+ @file.puts sig if ($config[:edit_signature] and !@sig_edited)
@file.close
+ end
+ def set_sig_edit_flag
+ sig = sig_lines.join("\n")
+ if $config[:edit_signature]
+ pbody = @body.join("\n")
+ blen = pbody.length
+ slen = sig.length
+
+ if blen > slen and pbody[blen-slen..blen] == sig
+ @sig_edited = false
+ @body = pbody[0..blen-slen].split("\n")
+ else
+ @sig_edited = true
+ end
+ end
+ end
+
+ def edit_message
+ old_from = @header["From"] if @account_selector
+
+ begin
+ save_message_to_file
+ rescue SystemCallError => e
+ BufferManager.flash "Can't save message to file: #{e.message}"
+ return
+ end
+
editor = $config[:editor] || ENV['EDITOR'] || "/usr/bin/vi"
mtime = File.mtime @file.path
BufferManager.shell_out "#{editor} #{@file.path}"
@edited = true if File.mtime(@file.path) > mtime
return @edited unless @edited
header, @body = parse_file @file.path
@header = header - NON_EDITABLE_HEADERS
+ set_sig_edit_flag
+
+ if @account_selector and @header["From"] != old_from
+ @account_user = @header["From"]
+ @account_selector.set_to nil
+ end
+
handle_new_text @header, @body
+ rerun_crypto_selector_hook
update
@edited
end
+ def edit_message_async
+ begin
+ save_message_to_file
+ rescue SystemCallError => e
+ BufferManager.flash "Can't save message to file: #{e.message}"
+ return
+ end
+
+ @mtime = File.mtime @file.path
+
+ # put up buffer saying you can now edit the message in another
+ # terminal or app, and continue to use sup in the meantime.
+ subject = @header["Subject"] || ""
+ @async_mode = EditMessageAsyncMode.new self, @file.path, subject
+ BufferManager.spawn "Waiting for message \"#{subject}\" to be finished", @async_mode
+
+ # hide ourselves, and wait for signal to resume from async mode ...
+ buffer.hidden = true
+ end
+
+ def edit_message_async_resume being_killed=false
+ buffer.hidden = false
+ @async_mode = nil
+ BufferManager.raise_to_front buffer if !being_killed
+
+ @edited = true if File.mtime(@file.path) > @mtime
+
+ header, @body = parse_file @file.path
+ @header = header - NON_EDITABLE_HEADERS
+ set_sig_edit_flag
+ handle_new_text @header, @body
+ update
+
+ true
+ end
+
def killable?
+ if !@async_mode.nil?
+ return false if !@async_mode.killable?
+ if File.mtime(@file.path) > @mtime
+ @edited = true
+ header, @body = parse_file @file.path
+ @header = header - NON_EDITABLE_HEADERS
+ handle_new_text @header, @body
+ update
+ end
+ end
!edited? || BufferManager.ask_yes_or_no("Discard message?")
end
def unsaved?; edited? end
@@ -203,20 +312,26 @@
BufferManager.flash "Can't read #{fn}: #{e.message}"
end
end
def delete_attachment
- i = curpos - @attachment_lines_offset - DECORATION_LINES - 1
+ i = curpos - @attachment_lines_offset - DECORATION_LINES - 2
if i >= 0 && i < @attachments.size && BufferManager.ask_yes_or_no("Delete attachment #{@attachment_names[i]}?")
@attachments.delete_at i
@attachment_names.delete_at i
update
end
end
protected
+ def rerun_crypto_selector_hook
+ if @crypto_selector && !@crypto_selector.changed_by_user
+ HookManager.run "crypto-mode", :header => @header, :body => @body, :crypto_selector => @crypto_selector
+ end
+ end
+
def mime_encode string
string = [string].pack('M') # basic quoted-printable
string.gsub!(/=\n/,'') # .. remove trailing newline
string.gsub!(/_/,'=5F') # .. encode underscores
string.gsub!(/\?/,'=3F') # .. encode question marks
@@ -240,19 +355,21 @@
def move_cursor_left
if curpos < @selectors.length
@selectors[curpos].roll_left
buffer.mark_dirty
+ update if @account_selector
else
col_left
end
end
def move_cursor_right
if curpos < @selectors.length
@selectors[curpos].roll_right
buffer.mark_dirty
+ update if @account_selector
else
col_right
end
end
@@ -260,19 +377,27 @@
@selectors << s
@selector_label_width = [@selector_label_width, s.label.length].max
end
def update
+ if @account_selector
+ if @account_selector.val.nil?
+ @header["From"] = @account_user
+ else
+ @header["From"] = AccountManager.full_address_for @account_selector.val
+ end
+ end
+
regen_text
buffer.mark_dirty if buffer
end
def regen_text
header, @header_lines = format_headers(@header - NON_EDITABLE_HEADERS) + [""]
@text = header + [""] + @body
- @text += sig_lines unless $config[:edit_signature]
-
+ @text += sig_lines unless @sig_edited
+
@attachment_lines_offset = 0
unless @attachments.empty?
@text += [""]
@attachment_lines_offset = @text.length
@@ -337,11 +462,11 @@
def send_message
return false if !edited? && !BufferManager.ask_yes_or_no("Message unedited. Really send?")
return false if $config[:confirm_no_attachments] && mentions_attachments? && @attachments.size == 0 && !BufferManager.ask_yes_or_no("You haven't added any attachments. Really send?")#" stupid ruby-mode
return false if $config[:confirm_top_posting] && top_posting? && !BufferManager.ask_yes_or_no("You're top-posting. That makes you a bad person. Really send?") #" stupid ruby-mode
- from_email =
+ from_email =
if @header["From"] =~ /<?(\S+@(\S+?))>?$/
$1
else
AccountManager.default_account.email
end
@@ -382,20 +507,20 @@
def build_message date
m = RMail::Message.new
m.header["Content-Type"] = "text/plain; charset=#{$encoding}"
m.body = @body.join("\n")
- m.body += sig_lines.join("\n") unless $config[:edit_signature]
+ m.body += "\n" + sig_lines.join("\n") unless @sig_edited
## body must end in a newline or GPG signatures will be WRONG!
m.body += "\n" unless m.body =~ /\n\Z/
## there are attachments, so wrap body in an attachment of its own
unless @attachments.empty?
body_m = m
body_m.header["Content-Disposition"] = "inline"
m = RMail::Message.new
-
+
m.add_part body_m
@attachments.each { |a| m.add_part a }
end
## do whatever crypto transformation is necessary
@@ -412,11 +537,11 @@
end
## finally, set the top-level headers
@header.each do |k, v|
next if v.nil? || v.empty?
- m.header[k] =
+ m.header[k] =
case v
when String
k.match(/subject/i) ? mime_encode_subject(v) : mime_encode_address(v)
when Array
v.map { |v| mime_encode_address v }.join ", "
@@ -452,11 +577,11 @@
end
f.puts
f.puts sanitize_body(@body.join("\n"))
f.puts sig_lines if full unless $config[:edit_signature]
- end
+ end
protected
def edit_field field
case field
@@ -477,10 +602,17 @@
contacts = BufferManager.ask_for_contacts :people, "#{field}: ", default
if contacts
text = contacts.map { |s| s.full_address }.join(", ")
@header[field] = parse_header field, text
+
+ if @account_selector and field == "From"
+ @account_user = @header["From"]
+ @account_selector.set_to nil
+ end
+
+ rerun_crypto_selector_hook
update
end
end
end
@@ -512,10 +644,10 @@
return [] if hook_sig == :none
return ["", "-- "] + hook_sig.split("\n") if hook_sig
## no hook, do default signature generation based on config.yaml
return [] unless from_email
- sigfn = (AccountManager.account_for(from_email) ||
+ sigfn = (AccountManager.account_for(from_email) ||
AccountManager.default_account).signature
if sigfn && File.exists?(sigfn)
["", "-- "] + File.readlines(sigfn).map { |l| l.chomp }
else