sup 0.0.6 → 0.0.7
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of sup might be problematic. Click here for more details.
- data/HACKING +50 -0
- data/History.txt +12 -0
- data/Manifest.txt +2 -0
- data/Rakefile +5 -1
- data/bin/sup +24 -8
- data/bin/sup-add +106 -0
- data/bin/sup-import +85 -165
- data/doc/FAQ.txt +48 -11
- data/doc/TODO +45 -12
- data/doc/UserGuide.txt +54 -42
- data/lib/sup.rb +6 -2
- data/lib/sup/buffer.rb +20 -4
- data/lib/sup/contact.rb +22 -12
- data/lib/sup/draft.rb +27 -7
- data/lib/sup/imap.rb +107 -97
- data/lib/sup/index.rb +35 -25
- data/lib/sup/label.rb +2 -2
- data/lib/sup/mbox.rb +20 -15
- data/lib/sup/mbox/loader.rb +0 -1
- data/lib/sup/mbox/ssh-file.rb +58 -47
- data/lib/sup/mbox/ssh-loader.rb +13 -20
- data/lib/sup/message.rb +15 -9
- data/lib/sup/mode.rb +16 -6
- data/lib/sup/modes/compose-mode.rb +7 -3
- data/lib/sup/modes/contact-list-mode.rb +50 -25
- data/lib/sup/modes/edit-message-mode.rb +11 -8
- data/lib/sup/modes/inbox-mode.rb +19 -3
- data/lib/sup/modes/label-list-mode.rb +4 -12
- data/lib/sup/modes/log-mode.rb +6 -0
- data/lib/sup/modes/reply-mode.rb +19 -6
- data/lib/sup/modes/resume-mode.rb +13 -9
- data/lib/sup/modes/scroll-mode.rb +13 -2
- data/lib/sup/modes/thread-index-mode.rb +94 -43
- data/lib/sup/modes/thread-view-mode.rb +198 -130
- data/lib/sup/person.rb +1 -1
- data/lib/sup/poll.rb +60 -47
- data/lib/sup/sent.rb +3 -2
- data/lib/sup/source.rb +14 -6
- data/lib/sup/textfield.rb +1 -0
- data/lib/sup/thread.rb +76 -23
- data/lib/sup/update.rb +2 -3
- data/lib/sup/util.rb +13 -13
- metadata +14 -2
@@ -1,7 +1,13 @@
|
|
1
1
|
module Redwood
|
2
2
|
|
3
3
|
class ThreadViewMode < LineCursorMode
|
4
|
+
## this holds all info we need to lay out a message
|
5
|
+
class Layout
|
6
|
+
attr_accessor :top, :bot, :prev, :next, :depth, :width, :state, :color
|
7
|
+
end
|
8
|
+
|
4
9
|
DATE_FORMAT = "%B %e %Y %l:%M%P"
|
10
|
+
INDENT_SPACES = 2 # how many spaces to indent child messages
|
5
11
|
|
6
12
|
register_keymap do |k|
|
7
13
|
k.add :toggle_detailed_header, "Toggle detailed header", 'd'
|
@@ -16,34 +22,47 @@ class ThreadViewMode < LineCursorMode
|
|
16
22
|
k.add :collapse_non_new_messages, "Collapse all but new messages", 'N'
|
17
23
|
k.add :reply, "Reply to a message", 'r'
|
18
24
|
k.add :forward, "Forward a message", 'f'
|
25
|
+
k.add :alias, "Edit alias/nickname for a person", 'a'
|
26
|
+
k.add :edit_as_new, "Edit message as new", 'D'
|
19
27
|
k.add :save_to_disk, "Save message/attachment to disk", 's'
|
28
|
+
k.add :search, "Search for messages from particular people", 'S'
|
29
|
+
k.add :archive_and_kill, "Archive thread and kill buffer", 'A'
|
20
30
|
end
|
21
31
|
|
32
|
+
## there are a couple important instance variables we hold to lay
|
33
|
+
## out the thread and to provide line-based functionality. @layout
|
34
|
+
## is a map from Message and Chunk objects to Layout objects. (for
|
35
|
+
## chunks, we only use the state field right now.) @message_lines is
|
36
|
+
## a map from row #s to Message objects. @chunk_lines is a map from
|
37
|
+
## row #s to Chunk objects. @person_lines is a map from row #s to
|
38
|
+
## Person objects.
|
39
|
+
|
22
40
|
def initialize thread, hidden_labels=[]
|
23
41
|
super()
|
24
42
|
@thread = thread
|
25
|
-
@state = {}
|
26
43
|
@hidden_labels = hidden_labels
|
27
44
|
|
45
|
+
@layout = {}
|
28
46
|
earliest, latest = nil, nil
|
29
47
|
latest_date = nil
|
48
|
+
altcolor = false
|
30
49
|
@thread.each do |m, d, p|
|
31
50
|
next unless m
|
32
51
|
earliest ||= m
|
33
|
-
@
|
52
|
+
@layout[m] = Layout.new
|
53
|
+
@layout[m].state = initial_state_for m
|
54
|
+
@layout[m].color = altcolor ? :alternate_patina_color : :message_patina_color
|
55
|
+
altcolor = !altcolor
|
34
56
|
if latest_date.nil? || m.date > latest_date
|
35
57
|
latest_date = m.date
|
36
58
|
latest = m
|
37
59
|
end
|
38
60
|
end
|
39
61
|
|
40
|
-
@
|
41
|
-
@
|
62
|
+
@layout[latest].state = :open if @layout[latest].state == :closed
|
63
|
+
@layout[earliest].state = :detailed if earliest.has_label?(:unread) || @thread.size == 1
|
42
64
|
|
43
|
-
|
44
|
-
regen_chunks
|
45
|
-
regen_text
|
46
|
-
end
|
65
|
+
regen_text
|
47
66
|
end
|
48
67
|
|
49
68
|
def draw_line ln, opts={}
|
@@ -57,33 +76,47 @@ class ThreadViewMode < LineCursorMode
|
|
57
76
|
def [] i; @text[i]; end
|
58
77
|
|
59
78
|
def show_header
|
60
|
-
|
79
|
+
m = @message_lines[curpos] or return
|
61
80
|
BufferManager.spawn_unless_exists("Full header") do
|
62
81
|
TextMode.new m.raw_header
|
63
82
|
end
|
64
83
|
end
|
65
84
|
|
66
85
|
def toggle_detailed_header
|
67
|
-
|
68
|
-
@
|
86
|
+
m = @message_lines[curpos] or return
|
87
|
+
@layout[m].state = (@layout[m].state == :detailed ? :open : :detailed)
|
69
88
|
update
|
70
89
|
end
|
71
90
|
|
72
91
|
def reply
|
73
|
-
|
92
|
+
m = @message_lines[curpos] or return
|
74
93
|
mode = ReplyMode.new m
|
75
94
|
BufferManager.spawn "Reply to #{m.subj}", mode
|
76
95
|
end
|
77
96
|
|
78
97
|
def forward
|
79
|
-
|
98
|
+
m = @message_lines[curpos] or return
|
80
99
|
mode = ForwardMode.new m
|
81
100
|
BufferManager.spawn "Forward of #{m.subj}", mode
|
82
101
|
mode.edit
|
83
102
|
end
|
84
103
|
|
104
|
+
include CanAliasContacts
|
105
|
+
def alias
|
106
|
+
p = @person_lines[curpos] or return
|
107
|
+
alias_contact p
|
108
|
+
update
|
109
|
+
end
|
110
|
+
|
111
|
+
def search
|
112
|
+
p = @person_lines[curpos] or return
|
113
|
+
mode = PersonSearchResultsMode.new [p]
|
114
|
+
BufferManager.spawn "search for #{p.name}", mode
|
115
|
+
mode.load_threads :num => mode.buffer.content_height
|
116
|
+
end
|
117
|
+
|
85
118
|
def toggle_starred
|
86
|
-
|
119
|
+
m = @message_lines[curpos] or return
|
87
120
|
if m.has_label? :starred
|
88
121
|
m.remove_label :starred
|
89
122
|
else
|
@@ -92,49 +125,45 @@ class ThreadViewMode < LineCursorMode
|
|
92
125
|
## TODO: don't recalculate EVERYTHING just to add a stupid little
|
93
126
|
## star to the display
|
94
127
|
update
|
95
|
-
UpdateManager.relay :starred, m
|
128
|
+
UpdateManager.relay self, :starred, m
|
96
129
|
end
|
97
130
|
|
98
131
|
def toggle_expanded
|
99
|
-
|
132
|
+
chunk = @chunk_lines[curpos] or return
|
100
133
|
case chunk
|
101
134
|
when Message, Message::Quote, Message::Signature
|
102
|
-
|
103
|
-
|
135
|
+
return if chunk.lines.length == 1 unless chunk.is_a? Message # too small to expand/close
|
136
|
+
l = @layout[chunk]
|
137
|
+
l.state = (l.state != :closed ? :closed : :open)
|
138
|
+
cursor_down if l.state == :closed
|
104
139
|
when Message::Attachment
|
105
140
|
view_attachment chunk
|
106
141
|
end
|
107
142
|
update
|
108
143
|
end
|
109
144
|
|
110
|
-
def
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
File.open(fn, "w") { |f| yield f }
|
116
|
-
BufferManager.flash "Successfully wrote #{fn}."
|
117
|
-
rescue SystemCallError => e
|
118
|
-
BufferManager.flash "Error writing to file: #{e.message}"
|
119
|
-
end
|
145
|
+
def edit_as_new
|
146
|
+
m = @message_lines[curpos] or return
|
147
|
+
mode = ComposeMode.new(:body => m.basic_body_lines, :to => m.to, :cc => m.cc, :subj => m.subj, :bcc => m.bcc)
|
148
|
+
BufferManager.spawn "edit as new", mode
|
149
|
+
mode.edit
|
120
150
|
end
|
121
|
-
private :save
|
122
151
|
|
123
152
|
def save_to_disk
|
124
|
-
|
153
|
+
chunk = @chunk_lines[curpos] or return
|
125
154
|
case chunk
|
126
155
|
when Message::Attachment
|
127
|
-
fn = BufferManager.ask :filename, "
|
128
|
-
|
156
|
+
fn = BufferManager.ask :filename, "Save attachment to file: ", chunk.filename
|
157
|
+
save_to_file(fn) { |f| f.print chunk } if fn
|
129
158
|
else
|
130
159
|
m = @message_lines[curpos]
|
131
|
-
fn = BufferManager.ask :filename, "
|
132
|
-
|
160
|
+
fn = BufferManager.ask :filename, "Save message to file: "
|
161
|
+
save_to_file(fn) { |f| f.print m.raw_full_message } if fn
|
133
162
|
end
|
134
163
|
end
|
135
164
|
|
136
165
|
def edit_message
|
137
|
-
|
166
|
+
m = @message_lines[curpos] or return
|
138
167
|
if m.is_draft?
|
139
168
|
mode = ResumeMode.new m
|
140
169
|
BufferManager.spawn "Edit message", mode
|
@@ -144,23 +173,33 @@ class ThreadViewMode < LineCursorMode
|
|
144
173
|
end
|
145
174
|
end
|
146
175
|
|
176
|
+
def jump_to_first_open
|
177
|
+
m = @message_lines[0] or return
|
178
|
+
if @layout[m].state != :closed
|
179
|
+
jump_to_message m
|
180
|
+
else
|
181
|
+
jump_to_next_open
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
147
185
|
def jump_to_next_open
|
148
|
-
|
149
|
-
while nextm = @
|
150
|
-
break if @
|
186
|
+
m = @message_lines[curpos] or return
|
187
|
+
while nextm = @layout[m].next
|
188
|
+
break if @layout[nextm].state != :closed
|
151
189
|
m = nextm
|
152
190
|
end
|
153
191
|
jump_to_message nextm if nextm
|
154
192
|
end
|
155
193
|
|
156
194
|
def jump_to_prev_open
|
157
|
-
|
195
|
+
m = @message_lines[curpos] or return
|
158
196
|
## jump to the top of the current message if we're in the body;
|
159
197
|
## otherwise, to the previous message
|
160
|
-
|
198
|
+
|
199
|
+
top = @layout[m].top
|
161
200
|
if curpos == top
|
162
|
-
while(prevm = @
|
163
|
-
break if @
|
201
|
+
while(prevm = @layout[m].prev)
|
202
|
+
break if @layout[prevm].state != :closed
|
164
203
|
m = prevm
|
165
204
|
end
|
166
205
|
jump_to_message prevm if prevm
|
@@ -170,47 +209,52 @@ class ThreadViewMode < LineCursorMode
|
|
170
209
|
end
|
171
210
|
|
172
211
|
def jump_to_message m
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
212
|
+
l = @layout[m]
|
213
|
+
left = l.depth * INDENT_SPACES
|
214
|
+
right = left + l.width
|
215
|
+
|
216
|
+
## jump to the top line unless both top and bottom fit in the current view
|
217
|
+
jump_to_line l.top unless l.top >= topline && l.top <= botline && l.bot >= topline && l.bot <= botline
|
218
|
+
|
219
|
+
## jump to the left columns unless both left and right fit in the current view
|
220
|
+
jump_to_col left unless left >= leftcol && left <= rightcol && right >= leftcol && right <= rightcol
|
221
|
+
|
222
|
+
## either way, move the cursor to the first line
|
223
|
+
set_cursor_pos l.top
|
178
224
|
end
|
179
225
|
|
180
226
|
def expand_all_messages
|
181
227
|
@global_message_state ||= :closed
|
182
228
|
@global_message_state = (@global_message_state == :closed ? :open : :closed)
|
183
|
-
@
|
229
|
+
@layout.each { |m, l| l.state = @global_message_state if m.is_a? Message }
|
184
230
|
update
|
185
231
|
end
|
186
232
|
|
187
|
-
|
188
233
|
def collapse_non_new_messages
|
189
|
-
@
|
234
|
+
@layout.each { |m, l| l.state = m.has_label?(:unread) ? :open : :closed }
|
190
235
|
update
|
191
236
|
end
|
192
237
|
|
193
238
|
def expand_all_quotes
|
194
239
|
if(m = @message_lines[curpos])
|
195
|
-
quotes =
|
196
|
-
|
197
|
-
newstate =
|
198
|
-
quotes.each { |c| @
|
240
|
+
quotes = m.chunks.select { |c| (c.is_a?(Message::Quote) || c.is_a?(Message::Signature)) && c.lines.length > 1 }
|
241
|
+
numopen = quotes.inject(0) { |s, c| s + (@layout[c].state == :open ? 1 : 0) }
|
242
|
+
newstate = numopen > quotes.length / 2 ? :closed : :open
|
243
|
+
quotes.each { |c| @layout[c].state = newstate }
|
199
244
|
update
|
200
245
|
end
|
201
246
|
end
|
202
247
|
|
203
|
-
## kinda slow for large threads. TODO: fasterify
|
204
248
|
def cleanup
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
249
|
+
@thread.remove_label :unread
|
250
|
+
UpdateManager.relay self, :read, @thread
|
251
|
+
@layout = @text = nil
|
252
|
+
end
|
253
|
+
|
254
|
+
def archive_and_kill
|
255
|
+
@thread.remove_label :inbox
|
256
|
+
UpdateManager.relay self, :archived, @thread
|
257
|
+
BufferManager.kill_buffer_safely buffer
|
214
258
|
end
|
215
259
|
|
216
260
|
private
|
@@ -228,116 +272,141 @@ private
|
|
228
272
|
buffer.mark_dirty if buffer
|
229
273
|
end
|
230
274
|
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
end
|
235
|
-
|
275
|
+
## here we generate the actual content lines. we accumulate
|
276
|
+
## everything into @text, and we set @chunk_lines and
|
277
|
+
## @message_lines, and we update @layout.
|
236
278
|
def regen_text
|
237
279
|
@text = []
|
238
280
|
@chunk_lines = []
|
239
281
|
@message_lines = []
|
240
|
-
@
|
282
|
+
@person_lines = []
|
241
283
|
|
242
|
-
|
284
|
+
prevm = nil
|
243
285
|
@thread.each do |m, depth, parent|
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
##
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
286
|
+
unless m.is_a? Message # handle nil and :fake_root
|
287
|
+
@text += chunk_to_lines m, nil, @text.length, depth, parent
|
288
|
+
next
|
289
|
+
end
|
290
|
+
l = @layout[m] or next # TODO: figure out why this is nil sometimes
|
291
|
+
|
292
|
+
## build the patina
|
293
|
+
text = chunk_to_lines m, l.state, @text.length, depth, parent, @layout[m].color
|
294
|
+
|
295
|
+
l.top = @text.length
|
296
|
+
l.bot = @text.length + text.length # updated below
|
297
|
+
l.prev = prevm
|
298
|
+
l.next = nil
|
299
|
+
l.depth = depth
|
300
|
+
# l.state we preserve
|
301
|
+
l.width = 0 # updated below
|
302
|
+
@layout[l.prev].next = m if l.prev
|
303
|
+
|
255
304
|
(0 ... text.length).each do |i|
|
256
305
|
@chunk_lines[@text.length + i] = m
|
257
306
|
@message_lines[@text.length + i] = m
|
307
|
+
lw = text[i].flatten.select { |x| x.is_a? String }.map { |x| x.length }.sum
|
258
308
|
end
|
259
309
|
|
260
|
-
## sorry i store all this shit in an array. very, very sorry.
|
261
|
-
## also sorry about the * 2. very, very sorry.
|
262
|
-
@messages[m] = [@text.length, @text.length + text.length, prev_m, nil, depth]
|
263
|
-
@messages[prev_m][3] = m if prev_m
|
264
|
-
prev_m = m if m.is_a? Message
|
265
|
-
|
266
310
|
@text += text
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
311
|
+
prevm = m
|
312
|
+
if @layout[m].state != :closed
|
313
|
+
m.chunks.each do |c|
|
314
|
+
cl = (@layout[c] ||= Layout.new)
|
315
|
+
cl.state ||= :closed
|
316
|
+
text = chunk_to_lines c, cl.state, @text.length, depth
|
271
317
|
(0 ... text.length).each do |i|
|
272
318
|
@chunk_lines[@text.length + i] = c
|
273
319
|
@message_lines[@text.length + i] = m
|
320
|
+
lw = text[i].flatten.select { |x| x.is_a? String }.map { |x| x.length }.sum - (depth * INDENT_SPACES)
|
321
|
+
l.width = lw if lw > l.width
|
274
322
|
end
|
275
323
|
@text += text
|
276
324
|
end
|
277
|
-
@
|
325
|
+
@layout[m].bot = @text.length
|
278
326
|
end
|
279
327
|
end
|
280
328
|
end
|
281
329
|
|
282
|
-
def message_patina_lines m, state, parent, prefix
|
283
|
-
prefix_widget = [
|
330
|
+
def message_patina_lines m, state, start, parent, prefix, color
|
331
|
+
prefix_widget = [color, prefix]
|
284
332
|
widget =
|
285
333
|
case state
|
286
334
|
when :closed
|
287
|
-
[
|
335
|
+
[color, "+ "]
|
288
336
|
when :open, :detailed
|
289
|
-
[
|
337
|
+
[color, "- "]
|
290
338
|
end
|
291
339
|
imp_widget =
|
292
340
|
if m.has_label?(:starred)
|
293
341
|
[:starred_patina_color, "* "]
|
294
342
|
else
|
295
|
-
[
|
343
|
+
[color, " "]
|
296
344
|
end
|
297
345
|
|
298
346
|
case state
|
299
347
|
when :open
|
348
|
+
@person_lines[start] = m.from
|
300
349
|
[[prefix_widget, widget, imp_widget,
|
301
|
-
[
|
350
|
+
[color,
|
302
351
|
"#{m.from ? m.from.mediumname : '?'} to #{m.recipients.map { |l| l.shortname }.join(', ')} #{m.date.to_nice_s} (#{m.date.to_nice_distance_s})"]]]
|
352
|
+
|
303
353
|
when :closed
|
354
|
+
@person_lines[start] = m.from
|
304
355
|
[[prefix_widget, widget, imp_widget,
|
305
|
-
[
|
356
|
+
[color,
|
306
357
|
"#{m.from ? m.from.mediumname : '?'}, #{m.date.to_nice_s} (#{m.date.to_nice_distance_s}) #{m.snippet}"]]]
|
358
|
+
|
307
359
|
when :detailed
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
360
|
+
@person_lines[start] = m.from
|
361
|
+
from = [[prefix_widget, widget, imp_widget, [color, "From: #{m.from ? format_person(m.from) : '?'}"]]]
|
362
|
+
|
363
|
+
rest = []
|
364
|
+
unless m.to.empty?
|
365
|
+
m.to.each_with_index { |p, i| @person_lines[start + rest.length + from.length + i] = p }
|
366
|
+
rest += format_person_list " To: ", m.to
|
367
|
+
end
|
368
|
+
unless m.cc.empty?
|
369
|
+
m.cc.each_with_index { |p, i| @person_lines[start + rest.length + from.length + i] = p }
|
370
|
+
rest += format_person_list " Cc: ", m.cc
|
371
|
+
end
|
372
|
+
unless m.bcc.empty?
|
373
|
+
m.bcc.each_with_index { |p, i| @person_lines[start + rest.length + from.length + i] = p }
|
374
|
+
rest += format_person_list " Bcc: ", m.bcc
|
375
|
+
end
|
376
|
+
|
377
|
+
rest += [
|
378
|
+
" Date: #{m.date.strftime DATE_FORMAT} (#{m.date.to_nice_distance_s})",
|
379
|
+
" Subject: #{m.subj}",
|
380
|
+
(parent ? " In reply to: #{parent.from.mediumname}'s message of #{parent.date.strftime DATE_FORMAT}" : nil),
|
381
|
+
m.labels.empty? ? nil : " Labels: #{m.labels.join(', ')}",
|
382
|
+
].compact
|
383
|
+
|
384
|
+
from + rest.map { |l| [[color, prefix + " " + l]] }
|
320
385
|
end
|
321
386
|
end
|
322
387
|
|
323
|
-
def
|
388
|
+
def format_person_list prefix, people
|
389
|
+
ptext = people.map { |p| format_person p }
|
324
390
|
pad = " " * prefix.length
|
325
|
-
[prefix +
|
326
|
-
|
327
|
-
pad + e + (i ==
|
391
|
+
[prefix + ptext.first + (ptext.length > 1 ? "," : "")] +
|
392
|
+
ptext[1 .. -1].map_with_index do |e, i|
|
393
|
+
pad + e + (i == ptext.length - 1 ? "" : ",")
|
328
394
|
end
|
329
395
|
end
|
330
396
|
|
397
|
+
def format_person p
|
398
|
+
p.longname + (ContactManager.is_contact?(p) ? " (#{ContactManager.alias_for p})" : "")
|
399
|
+
end
|
331
400
|
|
332
|
-
def chunk_to_lines chunk, state, start, depth, parent=nil
|
333
|
-
prefix = "
|
401
|
+
def chunk_to_lines chunk, state, start, depth, parent=nil, color=nil
|
402
|
+
prefix = " " * INDENT_SPACES * depth
|
334
403
|
case chunk
|
335
404
|
when :fake_root
|
336
|
-
[[[:
|
405
|
+
[[[:missing_message_color, "#{prefix}<one or more unreceived messages>"]]]
|
337
406
|
when nil
|
338
|
-
[[[:
|
407
|
+
[[[:missing_message_color, "#{prefix}<an unreceived message>"]]]
|
339
408
|
when Message
|
340
|
-
message_patina_lines(chunk, state, parent, prefix) +
|
409
|
+
message_patina_lines(chunk, state, start, parent, prefix, color) +
|
341
410
|
(chunk.is_draft? ? [[[:draft_notification_color, prefix + " >>> This message is a draft. To edit, hit 'e'. <<<"]]] : [])
|
342
411
|
when Message::Attachment
|
343
412
|
[[[:mime_color, "#{prefix}+ MIME attachment #{chunk.content_type}#{chunk.desc ? ' (' + chunk.desc + ')': ''}"]]]
|
@@ -348,22 +417,20 @@ private
|
|
348
417
|
end
|
349
418
|
t.map { |line| [[:none, "#{prefix}#{line}"]] }
|
350
419
|
when Message::Quote
|
420
|
+
return [[[:quote_color, "#{prefix}#{chunk.lines.first}"]]] if chunk.lines.length == 1
|
351
421
|
case state
|
352
422
|
when :closed
|
353
|
-
[[[:quote_patina_color, "#{prefix}+ #{chunk.lines.length} quoted lines"]]]
|
423
|
+
[[[:quote_patina_color, "#{prefix}+ (#{chunk.lines.length} quoted lines)"]]]
|
354
424
|
when :open
|
355
|
-
|
356
|
-
[[[:quote_patina_color, "#{prefix}- #{chunk.lines.length} quoted lines"]]] +
|
357
|
-
t.map { |line| [[:quote_color, "#{prefix}#{line}"]] }
|
425
|
+
[[[:quote_patina_color, "#{prefix}- (#{chunk.lines.length} quoted lines)"]]] + chunk.lines.map { |line| [[:quote_color, "#{prefix}#{line}"]] }
|
358
426
|
end
|
359
427
|
when Message::Signature
|
428
|
+
return [[[:sig_patina_color, "#{prefix}#{chunk.lines.first}"]]] if chunk.lines.length == 1
|
360
429
|
case state
|
361
430
|
when :closed
|
362
|
-
[[[:sig_patina_color, "#{prefix}+ #{chunk.lines.length}-line signature"]]]
|
431
|
+
[[[:sig_patina_color, "#{prefix}+ (#{chunk.lines.length}-line signature)"]]]
|
363
432
|
when :open
|
364
|
-
|
365
|
-
[[[:sig_patina_color, "#{prefix}- #{chunk.lines.length}-line signature"]]] +
|
366
|
-
t.map { |line| [[:sig_color, "#{prefix}#{line}"]] }
|
433
|
+
[[[:sig_patina_color, "#{prefix}- (#{chunk.lines.length}-line signature)"]]] + chunk.lines.map { |line| [[:sig_color, "#{prefix}#{line}"]] }
|
367
434
|
end
|
368
435
|
else
|
369
436
|
raise "unknown chunk type #{chunk.class.name}"
|
@@ -372,9 +439,10 @@ private
|
|
372
439
|
|
373
440
|
def view_attachment a
|
374
441
|
BufferManager.flash "viewing #{a.content_type} attachment..."
|
375
|
-
a.view!
|
442
|
+
success = a.view!
|
376
443
|
BufferManager.erase_flash
|
377
444
|
BufferManager.completely_redraw_screen
|
445
|
+
BufferManager.flash "Couldn't execute view command." unless success
|
378
446
|
end
|
379
447
|
|
380
448
|
end
|