sup 0.0.8 → 0.1
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 +6 -36
- data/History.txt +11 -0
- data/Manifest.txt +5 -0
- data/README.txt +13 -31
- data/Rakefile +3 -3
- data/bin/sup +167 -89
- data/bin/sup-add +39 -29
- data/bin/sup-config +57 -31
- data/bin/sup-sync +60 -54
- data/bin/sup-sync-back +143 -0
- data/doc/FAQ.txt +56 -19
- data/doc/Philosophy.txt +34 -33
- data/doc/TODO +76 -46
- data/doc/UserGuide.txt +142 -122
- data/lib/sup.rb +76 -36
- data/lib/sup/account.rb +27 -19
- data/lib/sup/buffer.rb +130 -44
- data/lib/sup/contact.rb +1 -1
- data/lib/sup/draft.rb +1 -2
- data/lib/sup/imap.rb +64 -19
- data/lib/sup/index.rb +95 -16
- data/lib/sup/keymap.rb +1 -1
- data/lib/sup/label.rb +31 -5
- data/lib/sup/maildir.rb +7 -5
- data/lib/sup/mbox.rb +34 -15
- data/lib/sup/mbox/loader.rb +30 -12
- data/lib/sup/mbox/ssh-loader.rb +7 -5
- data/lib/sup/message.rb +93 -44
- data/lib/sup/modes/buffer-list-mode.rb +1 -1
- data/lib/sup/modes/completion-mode.rb +55 -0
- data/lib/sup/modes/compose-mode.rb +6 -25
- data/lib/sup/modes/contact-list-mode.rb +1 -1
- data/lib/sup/modes/edit-message-mode.rb +119 -29
- data/lib/sup/modes/file-browser-mode.rb +108 -0
- data/lib/sup/modes/forward-mode.rb +3 -20
- data/lib/sup/modes/inbox-mode.rb +9 -12
- data/lib/sup/modes/label-list-mode.rb +28 -46
- data/lib/sup/modes/label-search-results-mode.rb +1 -16
- data/lib/sup/modes/line-cursor-mode.rb +44 -5
- data/lib/sup/modes/person-search-results-mode.rb +1 -16
- data/lib/sup/modes/reply-mode.rb +18 -31
- data/lib/sup/modes/resume-mode.rb +6 -6
- data/lib/sup/modes/scroll-mode.rb +6 -5
- data/lib/sup/modes/search-results-mode.rb +6 -17
- data/lib/sup/modes/thread-index-mode.rb +70 -28
- data/lib/sup/modes/thread-view-mode.rb +65 -29
- data/lib/sup/person.rb +71 -30
- data/lib/sup/poll.rb +13 -4
- data/lib/sup/rfc2047.rb +61 -0
- data/lib/sup/sent.rb +7 -5
- data/lib/sup/source.rb +12 -9
- data/lib/sup/suicide.rb +36 -0
- data/lib/sup/tagger.rb +6 -6
- data/lib/sup/textfield.rb +76 -14
- data/lib/sup/thread.rb +97 -123
- data/lib/sup/util.rb +167 -1
- metadata +30 -5
@@ -1,14 +1,14 @@
|
|
1
1
|
module Redwood
|
2
2
|
|
3
|
-
class ResumeMode <
|
3
|
+
class ResumeMode < EditMessageMode
|
4
4
|
def initialize m
|
5
|
-
super()
|
6
5
|
@id = m.id
|
7
|
-
@header, @body = parse_file m.draft_filename
|
8
|
-
@header.delete "Date"
|
9
|
-
@header["Message-Id"] = gen_message_id # generate a new'n
|
10
|
-
regen_text
|
11
6
|
@safe = false
|
7
|
+
|
8
|
+
header, body = parse_file m.draft_filename
|
9
|
+
header.delete "Date"
|
10
|
+
|
11
|
+
super :header => header, :body => body
|
12
12
|
end
|
13
13
|
|
14
14
|
def killable?
|
@@ -21,7 +21,7 @@ class ScrollMode < Mode
|
|
21
21
|
k.add :col_right, "Right one column", :right, 'l'
|
22
22
|
k.add :page_down, "Down one page", :page_down, 'n', ' '
|
23
23
|
k.add :page_up, "Up one page", :page_up, 'p', :backspace
|
24
|
-
k.add :
|
24
|
+
k.add :jump_to_start, "Jump to top", :home, '^', '1'
|
25
25
|
k.add :jump_to_end, "Jump to bottom", :end, '$', '0'
|
26
26
|
k.add :jump_to_left, "Jump to the left", '['
|
27
27
|
end
|
@@ -76,17 +76,18 @@ class ScrollMode < Mode
|
|
76
76
|
buffer.mark_dirty
|
77
77
|
end
|
78
78
|
|
79
|
+
def at_top?; @topline == 0 end
|
80
|
+
def at_bottom?; @botline == lines end
|
81
|
+
|
79
82
|
def line_down; jump_to_line @topline + 1; end
|
80
83
|
def line_up; jump_to_line @topline - 1; end
|
81
84
|
def page_down; jump_to_line @topline + buffer.content_height - @slip_rows; end
|
82
85
|
def page_up; jump_to_line @topline - buffer.content_height + @slip_rows; end
|
83
|
-
def
|
86
|
+
def jump_to_start; jump_to_line 0; end
|
84
87
|
def jump_to_end; jump_to_line lines - buffer.content_height; end
|
85
88
|
|
86
|
-
|
87
89
|
def ensure_mode_validity
|
88
|
-
@topline = @topline.clamp 0, lines - 1
|
89
|
-
@topline = 0 if @topline < 0 # empty
|
90
|
+
@topline = @topline.clamp 0, [lines - 1, 0].max
|
90
91
|
@botline = [@topline + buffer.content_height, lines].min
|
91
92
|
end
|
92
93
|
|
@@ -3,26 +3,15 @@ module Redwood
|
|
3
3
|
class SearchResultsMode < ThreadIndexMode
|
4
4
|
def initialize qobj
|
5
5
|
@qobj = qobj
|
6
|
-
super
|
6
|
+
super [], { :qobj => @qobj, :load_killed => true, :load_spam => false }
|
7
7
|
end
|
8
8
|
|
9
|
-
##
|
10
|
-
|
9
|
+
## a proper is_relevant? method requires some way of asking ferret
|
10
|
+
## if an in-memory object satisfies a query. i'm not sure how to do
|
11
|
+
## that yet. in the worst case i can make an in-memory index, add
|
12
|
+
## the message, and search against it to see if i have > 0 results,
|
13
|
+
## but that seems pretty insane.
|
11
14
|
|
12
|
-
def load_threads opts={}
|
13
|
-
n = opts[:num] || ThreadIndexMode::LOAD_MORE_THREAD_NUM
|
14
|
-
load_n_threads_background n, :qobj => @qobj,
|
15
|
-
:load_killed => true,
|
16
|
-
:load_spam => false,
|
17
|
-
:when_done =>(lambda do |num|
|
18
|
-
opts[:when_done].call if opts[:when_done]
|
19
|
-
if num > 0
|
20
|
-
BufferManager.flash "Found #{num} threads"
|
21
|
-
else
|
22
|
-
BufferManager.flash "No matches"
|
23
|
-
end
|
24
|
-
end)
|
25
|
-
end
|
26
15
|
end
|
27
16
|
|
28
17
|
end
|
@@ -1,7 +1,7 @@
|
|
1
|
-
require 'thread'
|
2
1
|
module Redwood
|
3
2
|
|
4
|
-
## subclasses should implement
|
3
|
+
## subclasses should implement:
|
4
|
+
## - is_relevant?
|
5
5
|
|
6
6
|
class ThreadIndexMode < LineCursorMode
|
7
7
|
DATE_WIDTH = Time::TO_NICE_S_MAX_LEN
|
@@ -27,21 +27,30 @@ class ThreadIndexMode < LineCursorMode
|
|
27
27
|
k.add :apply_to_tagged, "Apply next command to all tagged threads", ';'
|
28
28
|
end
|
29
29
|
|
30
|
-
def initialize
|
30
|
+
def initialize hidden_labels=[], load_thread_opts={}
|
31
31
|
super()
|
32
|
+
@mutex = Mutex.new
|
32
33
|
@load_thread = nil
|
33
|
-
@
|
34
|
-
@hidden_labels = hidden_labels + LabelManager::
|
34
|
+
@load_thread_opts = load_thread_opts
|
35
|
+
@hidden_labels = hidden_labels + LabelManager::HIDDEN_RESERVED_LABELS
|
35
36
|
@date_width = DATE_WIDTH
|
36
37
|
@from_width = FROM_WIDTH
|
37
38
|
@size_width = nil
|
38
|
-
|
39
|
+
|
39
40
|
@tags = Tagger.new self
|
40
41
|
|
41
42
|
initialize_threads
|
42
43
|
update
|
43
44
|
|
44
45
|
UpdateManager.register self
|
46
|
+
|
47
|
+
@last_load_more_size = nil
|
48
|
+
to_load_more do |size|
|
49
|
+
next if @last_load_more_size == 0
|
50
|
+
load_threads :num => 1, :background => false
|
51
|
+
load_threads :num => (size - 1),
|
52
|
+
:when_done => lambda { |num| @last_load_more_size = num }
|
53
|
+
end
|
45
54
|
end
|
46
55
|
|
47
56
|
def lines; @text.length; end
|
@@ -117,8 +126,8 @@ class ThreadIndexMode < LineCursorMode
|
|
117
126
|
|
118
127
|
def update
|
119
128
|
## let's see you do THIS in python
|
120
|
-
@threads = @ts.threads.select { |t| !@hidden_threads[t]
|
121
|
-
@size_width = (@threads.
|
129
|
+
@threads = @ts.threads.select { |t| !@hidden_threads[t] }.sort_by { |t| t.date }.reverse
|
130
|
+
@size_width = (@threads.max_of { |t| t.size } || 0).num_digits
|
122
131
|
regen_text
|
123
132
|
end
|
124
133
|
|
@@ -192,9 +201,10 @@ class ThreadIndexMode < LineCursorMode
|
|
192
201
|
end
|
193
202
|
|
194
203
|
def jump_to_next_new
|
195
|
-
n = ((curpos + 1) ... lines).find { |i| @threads[i].has_label? :unread }
|
196
|
-
n = (0 ... curpos).find { |i| @threads[i].has_label? :unread } unless n
|
204
|
+
n = ((curpos + 1) ... lines).find { |i| @threads[i].has_label? :unread } || (0 ... curpos).find { |i| @threads[i].has_label? :unread }
|
197
205
|
if n
|
206
|
+
## jump there if necessary
|
207
|
+
jump_to_line n unless n >= topline && n < botline
|
198
208
|
set_cursor_pos n
|
199
209
|
else
|
200
210
|
BufferManager.flash "No new messages"
|
@@ -241,12 +251,12 @@ class ThreadIndexMode < LineCursorMode
|
|
241
251
|
end
|
242
252
|
|
243
253
|
def save
|
244
|
-
|
254
|
+
dirty_threads = (@threads + @hidden_threads.keys).select { |t| t.dirty? }
|
255
|
+
return if dirty_threads.empty?
|
245
256
|
|
246
257
|
BufferManager.say("Saving threads...") do |say_id|
|
247
|
-
|
248
|
-
|
249
|
-
BufferManager.say "Saving thread #{i +1} of #{threads.length}...", say_id
|
258
|
+
dirty_threads.each_with_index do |t, i|
|
259
|
+
BufferManager.say "Saving modified thread #{i + 1} of #{dirty_threads.length}...", say_id
|
250
260
|
t.save Index
|
251
261
|
end
|
252
262
|
end
|
@@ -330,8 +340,8 @@ class ThreadIndexMode < LineCursorMode
|
|
330
340
|
end
|
331
341
|
|
332
342
|
def load_n_threads_background n=LOAD_MORE_THREAD_NUM, opts={}
|
333
|
-
return if @load_thread
|
334
|
-
@load_thread = Redwood::reporting_thread do
|
343
|
+
return if @load_thread # todo: wrap in mutex
|
344
|
+
@load_thread = Redwood::reporting_thread do
|
335
345
|
num = load_n_threads n, opts
|
336
346
|
opts[:when_done].call(num) if opts[:when_done]
|
337
347
|
@load_thread = nil
|
@@ -358,15 +368,35 @@ class ThreadIndexMode < LineCursorMode
|
|
358
368
|
BufferManager.draw_screen
|
359
369
|
@ts.size - orig_size
|
360
370
|
end
|
371
|
+
synchronized :load_n_threads
|
361
372
|
|
362
373
|
def status
|
363
374
|
if (l = lines) == 0
|
364
|
-
""
|
375
|
+
"line 0 of 0"
|
365
376
|
else
|
366
377
|
"line #{curpos + 1} of #{l} #{dirty? ? '*modified*' : ''}"
|
367
378
|
end
|
368
379
|
end
|
369
380
|
|
381
|
+
def load_threads opts={}
|
382
|
+
n = opts[:num] || ThreadIndexMode::LOAD_MORE_THREAD_NUM
|
383
|
+
|
384
|
+
myopts = @load_thread_opts.merge({ :when_done => (lambda do |num|
|
385
|
+
opts[:when_done].call(num) if opts[:when_done]
|
386
|
+
if num > 0
|
387
|
+
BufferManager.flash "Found #{num} threads"
|
388
|
+
else
|
389
|
+
BufferManager.flash "No matches"
|
390
|
+
end
|
391
|
+
end)})
|
392
|
+
|
393
|
+
if opts[:background] || opts[:background].nil?
|
394
|
+
load_n_threads_background n, myopts
|
395
|
+
else
|
396
|
+
load_n_threads n, myopts
|
397
|
+
end
|
398
|
+
end
|
399
|
+
|
370
400
|
protected
|
371
401
|
|
372
402
|
def cursor_thread; @threads[curpos]; end
|
@@ -406,16 +436,20 @@ protected
|
|
406
436
|
end
|
407
437
|
|
408
438
|
def author_text_for_thread t
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
t.authors.
|
413
|
-
|
439
|
+
t.authors.map do |p|
|
440
|
+
if AccountManager.is_account?(p)
|
441
|
+
"me"
|
442
|
+
elsif t.authors.size == 1
|
443
|
+
p.mediumname
|
444
|
+
else
|
445
|
+
p.shortname
|
446
|
+
end
|
447
|
+
end.uniq.join ","
|
414
448
|
end
|
415
449
|
|
416
450
|
def text_for_thread t
|
417
|
-
date = t.date.to_nice_s
|
418
|
-
from = author_text_for_thread
|
451
|
+
date = t.date.to_nice_s
|
452
|
+
from = author_text_for_thread t
|
419
453
|
if from.length > @from_width
|
420
454
|
from = from[0 ... (@from_width - 1)]
|
421
455
|
from += "." unless from[-1] == ?\s
|
@@ -427,12 +461,20 @@ protected
|
|
427
461
|
dp = t.direct_participants.any? { |p| AccountManager.is_account? p }
|
428
462
|
p = dp || t.participants.any? { |p| AccountManager.is_account? p }
|
429
463
|
|
430
|
-
base_color =
|
464
|
+
base_color =
|
465
|
+
if new
|
466
|
+
:index_new_color
|
467
|
+
elsif starred
|
468
|
+
:index_starred_color
|
469
|
+
else
|
470
|
+
:index_old_color
|
471
|
+
end
|
472
|
+
|
431
473
|
[
|
432
474
|
[:tagged_color, @tags.tagged?(t) ? ">" : " "],
|
433
|
-
[:none, sprintf("%#{@date_width}s
|
475
|
+
[:none, sprintf("%#{@date_width}s", date)],
|
476
|
+
(starred ? [:starred_color, "*"] : [:none, " "]),
|
434
477
|
[base_color, sprintf("%-#{@from_width}s", from)],
|
435
|
-
[:starred_color, starred ? "*" : " "],
|
436
478
|
[:none, t.size == 1 ? " " * (@size_width + 2) : sprintf("(%#{@size_width}d)", t.size)],
|
437
479
|
[:to_me_color, dp ? " >" : (p ? ' +' : " ")],
|
438
480
|
[base_color, t.subj + (t.subj.empty? ? "" : " ")],
|
@@ -447,7 +489,7 @@ protected
|
|
447
489
|
private
|
448
490
|
|
449
491
|
def initialize_threads
|
450
|
-
@ts = ThreadSet.new Index.instance
|
492
|
+
@ts = ThreadSet.new Index.instance, $config[:thread_by_subject]
|
451
493
|
@ts_mutex = Mutex.new
|
452
494
|
@hidden_threads = {}
|
453
495
|
end
|
@@ -2,10 +2,14 @@ module Redwood
|
|
2
2
|
|
3
3
|
class ThreadViewMode < LineCursorMode
|
4
4
|
## this holds all info we need to lay out a message
|
5
|
-
class
|
5
|
+
class MessageLayout
|
6
6
|
attr_accessor :top, :bot, :prev, :next, :depth, :width, :state, :color, :star_color, :orig_new
|
7
7
|
end
|
8
8
|
|
9
|
+
class ChunkLayout
|
10
|
+
attr_accessor :state
|
11
|
+
end
|
12
|
+
|
9
13
|
DATE_FORMAT = "%B %e %Y %l:%M%P"
|
10
14
|
INDENT_SPACES = 2 # how many spaces to indent child messages
|
11
15
|
|
@@ -14,7 +18,7 @@ class ThreadViewMode < LineCursorMode
|
|
14
18
|
k.add :show_header, "Show full message header", 'H'
|
15
19
|
k.add :toggle_expanded, "Expand/collapse item", :enter
|
16
20
|
k.add :expand_all_messages, "Expand/collapse all messages", 'E'
|
17
|
-
k.add :
|
21
|
+
k.add :edit_draft, "Edit draft", 'e'
|
18
22
|
k.add :expand_all_quotes, "Expand/collapse all quotes in a message", 'o'
|
19
23
|
k.add :jump_to_next_open, "Jump to next open message", 'n'
|
20
24
|
k.add :jump_to_prev_open, "Jump to previous open message", 'p'
|
@@ -30,27 +34,27 @@ class ThreadViewMode < LineCursorMode
|
|
30
34
|
k.add :archive_and_kill, "Archive thread and kill buffer", 'A'
|
31
35
|
end
|
32
36
|
|
33
|
-
## there are a couple important instance variables we hold to
|
34
|
-
##
|
35
|
-
##
|
36
|
-
##
|
37
|
-
##
|
38
|
-
##
|
39
|
-
## Person objects.
|
37
|
+
## there are a couple important instance variables we hold to format
|
38
|
+
## the thread and to provide line-based functionality. @layout is a
|
39
|
+
## map from Messages to MessageLayouts, and @chunk_layout from
|
40
|
+
## Chunks to ChunkLayouts. @message_lines is a map from row #s to
|
41
|
+
## Message objects. @chunk_lines is a map from row #s to Chunk
|
42
|
+
## objects. @person_lines is a map from row #s to Person objects.
|
40
43
|
|
41
44
|
def initialize thread, hidden_labels=[]
|
42
45
|
super()
|
43
46
|
@thread = thread
|
44
47
|
@hidden_labels = hidden_labels
|
45
48
|
|
46
|
-
@layout = {}
|
49
|
+
@layout = SavingHash.new { MessageLayout.new }
|
50
|
+
@chunk_layout = SavingHash.new { ChunkLayout.new }
|
47
51
|
earliest, latest = nil, nil
|
48
52
|
latest_date = nil
|
49
53
|
altcolor = false
|
54
|
+
|
50
55
|
@thread.each do |m, d, p|
|
51
56
|
next unless m
|
52
57
|
earliest ||= m
|
53
|
-
@layout[m] = Layout.new
|
54
58
|
@layout[m].state = initial_state_for m
|
55
59
|
@layout[m].color = altcolor ? :alternate_patina_color : :message_patina_color
|
56
60
|
@layout[m].star_color = altcolor ? :alternate_starred_patina_color : :starred_patina_color
|
@@ -146,13 +150,19 @@ class ThreadViewMode < LineCursorMode
|
|
146
150
|
def toggle_expanded
|
147
151
|
chunk = @chunk_lines[curpos] or return
|
148
152
|
case chunk
|
149
|
-
when Message
|
150
|
-
return if chunk.lines.length == 1 unless chunk.is_a? Message # too small to expand/close
|
153
|
+
when Message
|
151
154
|
l = @layout[chunk]
|
152
155
|
l.state = (l.state != :closed ? :closed : :open)
|
153
156
|
cursor_down if l.state == :closed
|
157
|
+
when Message::Quote, Message::Signature
|
158
|
+
return if chunk.lines.length == 1
|
159
|
+
toggle_chunk_expansion chunk
|
154
160
|
when Message::Attachment
|
155
|
-
|
161
|
+
if chunk.inlineable?
|
162
|
+
toggle_chunk_expansion chunk
|
163
|
+
else
|
164
|
+
view_attachment chunk
|
165
|
+
end
|
156
166
|
end
|
157
167
|
update
|
158
168
|
end
|
@@ -169,7 +179,7 @@ class ThreadViewMode < LineCursorMode
|
|
169
179
|
case chunk
|
170
180
|
when Message::Attachment
|
171
181
|
fn = BufferManager.ask :filename, "Save attachment to file: ", chunk.filename
|
172
|
-
save_to_file(fn) { |f| f.print chunk } if fn
|
182
|
+
save_to_file(fn) { |f| f.print chunk.raw_content } if fn
|
173
183
|
else
|
174
184
|
m = @message_lines[curpos]
|
175
185
|
fn = BufferManager.ask :filename, "Save message to file: "
|
@@ -177,11 +187,12 @@ class ThreadViewMode < LineCursorMode
|
|
177
187
|
end
|
178
188
|
end
|
179
189
|
|
180
|
-
def
|
190
|
+
def edit_draft
|
181
191
|
m = @message_lines[curpos] or return
|
182
192
|
if m.is_draft?
|
183
193
|
mode = ResumeMode.new m
|
184
194
|
BufferManager.spawn "Edit message", mode
|
195
|
+
BufferManager.kill_buffer self.buffer
|
185
196
|
mode.edit
|
186
197
|
else
|
187
198
|
BufferManager.flash "Not a draft message!"
|
@@ -241,27 +252,27 @@ class ThreadViewMode < LineCursorMode
|
|
241
252
|
def expand_all_messages
|
242
253
|
@global_message_state ||= :closed
|
243
254
|
@global_message_state = (@global_message_state == :closed ? :open : :closed)
|
244
|
-
@layout.each { |m, l| l.state = @global_message_state
|
255
|
+
@layout.each { |m, l| l.state = @global_message_state }
|
245
256
|
update
|
246
257
|
end
|
247
258
|
|
248
259
|
def collapse_non_new_messages
|
249
|
-
@layout.each { |m, l| l.state = l.orig_new ? :open : :closed
|
260
|
+
@layout.each { |m, l| l.state = l.orig_new ? :open : :closed }
|
250
261
|
update
|
251
262
|
end
|
252
263
|
|
253
264
|
def expand_all_quotes
|
254
265
|
if(m = @message_lines[curpos])
|
255
266
|
quotes = m.chunks.select { |c| (c.is_a?(Message::Quote) || c.is_a?(Message::Signature)) && c.lines.length > 1 }
|
256
|
-
numopen = quotes.inject(0) { |s, c| s + (@
|
267
|
+
numopen = quotes.inject(0) { |s, c| s + (@chunk_layout[c].state == :open ? 1 : 0) }
|
257
268
|
newstate = numopen > quotes.length / 2 ? :closed : :open
|
258
|
-
quotes.each { |c| @
|
269
|
+
quotes.each { |c| @chunk_layout[c].state = newstate }
|
259
270
|
update
|
260
271
|
end
|
261
272
|
end
|
262
273
|
|
263
274
|
def cleanup
|
264
|
-
@layout = @text = nil # for good luck
|
275
|
+
@layout = @chunk_layout = @text = nil # for good luck
|
265
276
|
end
|
266
277
|
|
267
278
|
def archive_and_kill
|
@@ -272,6 +283,12 @@ class ThreadViewMode < LineCursorMode
|
|
272
283
|
|
273
284
|
private
|
274
285
|
|
286
|
+
def toggle_chunk_expansion chunk
|
287
|
+
l = @chunk_layout[chunk]
|
288
|
+
l.state = (l.state != :closed ? :closed : :open)
|
289
|
+
cursor_down if l.state == :closed
|
290
|
+
end
|
291
|
+
|
275
292
|
def initial_state_for m
|
276
293
|
if m.has_label?(:starred) || m.has_label?(:unread)
|
277
294
|
:open
|
@@ -300,10 +317,13 @@ private
|
|
300
317
|
@text += chunk_to_lines m, nil, @text.length, depth, parent
|
301
318
|
next
|
302
319
|
end
|
303
|
-
l = @layout[m]
|
320
|
+
l = @layout[m]
|
321
|
+
|
322
|
+
## is this still necessary?
|
323
|
+
next unless @layout[m].state # skip discarded drafts
|
304
324
|
|
305
325
|
## build the patina
|
306
|
-
text = chunk_to_lines m, l.state, @text.length, depth, parent,
|
326
|
+
text = chunk_to_lines m, l.state, @text.length, depth, parent, l.color, l.star_color
|
307
327
|
|
308
328
|
l.top = @text.length
|
309
329
|
l.bot = @text.length + text.length # updated below
|
@@ -322,10 +342,18 @@ private
|
|
322
342
|
|
323
343
|
@text += text
|
324
344
|
prevm = m
|
325
|
-
if
|
345
|
+
if l.state != :closed
|
326
346
|
m.chunks.each do |c|
|
327
|
-
cl =
|
328
|
-
|
347
|
+
cl = @chunk_layout[c]
|
348
|
+
|
349
|
+
## set the default state for chunks
|
350
|
+
cl.state ||=
|
351
|
+
if c.is_a?(Message::Attachment) && c.inlineable?
|
352
|
+
:open
|
353
|
+
else
|
354
|
+
:closed
|
355
|
+
end
|
356
|
+
|
329
357
|
text = chunk_to_lines c, cl.state, @text.length, depth
|
330
358
|
(0 ... text.length).each do |i|
|
331
359
|
@chunk_lines[@text.length + i] = c
|
@@ -422,7 +450,13 @@ private
|
|
422
450
|
message_patina_lines(chunk, state, start, parent, prefix, color, star_color) +
|
423
451
|
(chunk.is_draft? ? [[[:draft_notification_color, prefix + " >>> This message is a draft. To edit, hit 'e'. <<<"]]] : [])
|
424
452
|
when Message::Attachment
|
425
|
-
[[[:
|
453
|
+
return [[[:attachment_color, "#{prefix}x Attachment: #{chunk.filename} (#{chunk.content_type})"]]] unless chunk.inlineable?
|
454
|
+
case state
|
455
|
+
when :closed
|
456
|
+
[[[:attachment_color, "#{prefix}+ Attachment: #{chunk.filename} (#{chunk.lines.length} lines)"]]]
|
457
|
+
when :open
|
458
|
+
[[[:attachment_color, "#{prefix}- Attachment: #{chunk.filename} (#{chunk.lines.length} lines)"]]] + chunk.lines.map { |line| [[:none, "#{prefix}#{line}"]] }
|
459
|
+
end
|
426
460
|
when Message::Text
|
427
461
|
t = chunk.lines
|
428
462
|
if t.last =~ /^\s*$/ && t.length > 1
|
@@ -455,9 +489,11 @@ private
|
|
455
489
|
success = a.view!
|
456
490
|
BufferManager.erase_flash
|
457
491
|
BufferManager.completely_redraw_screen
|
458
|
-
|
492
|
+
unless success
|
493
|
+
BufferManager.spawn "Attachment: #{a.filename}", TextMode.new(a.to_s)
|
494
|
+
BufferManager.flash "Couldn't execute view command, viewing as text."
|
495
|
+
end
|
459
496
|
end
|
460
|
-
|
461
497
|
end
|
462
498
|
|
463
499
|
end
|