Class: Redwood::ThreadViewMode
Defined Under Namespace
Classes: ChunkLayout, MessageLayout
Constant Summary
collapse
- DATE_FORMAT =
"%B %e %Y %l:%M%P"
- INDENT_SPACES =
how many spaces to indent child messages
2
Constants inherited
from ScrollMode
ScrollMode::COL_JUMP
Instance Attribute Summary
#curpos
Attributes inherited from ScrollMode
#botline, #leftcol, #status, #topline
Attributes inherited from Mode
#buffer
Instance Method Summary
collapse
#alias_contact
#draw
Methods inherited from ScrollMode
#at_bottom?, #at_top?, #cancel_search!, #col_left, #col_right, #continue_search_in_buffer, #draw, #ensure_mode_validity, #in_search?, #jump_to_col, #jump_to_end, #jump_to_left, #jump_to_line, #jump_to_start, #line_down, #line_up, #page_down, #page_up, #resize, #rightcol, #search_goto_line, #search_in_buffer, #search_start_line
Methods inherited from Mode
#blur, #cancel_search!, #draw, #focus, #handle_input, #help_text, #in_search?, #killable?, load_all_modes, make_name, #name, register_keymap, #resize, #resolve_input, #save_to_file, #status
Constructor Details
#initialize(thread, hidden_labels = []) ⇒ ThreadViewMode
there are a couple important instance variables we hold to format the thread and to provide line-based functionality. @layout is a map from Messages to MessageLayouts, and @chunk_layout from Chunks to ChunkLayouts. @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.
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
|
# File 'lib/sup/modes/thread-view-mode.rb', line 49
def initialize thread, hidden_labels=[]
super()
@thread = thread
@hidden_labels = hidden_labels
@layout = SavingHash.new { MessageLayout.new }
@chunk_layout = SavingHash.new { ChunkLayout.new }
earliest, latest = nil, nil
latest_date = nil
altcolor = false
@thread.each do |m, d, p|
next unless m
earliest ||= m
@layout[m].state = initial_state_for m
@layout[m].color = altcolor ? :alternate_patina_color : :message_patina_color
@layout[m].star_color = altcolor ? :alternate_starred_patina_color : :starred_patina_color
@layout[m].orig_new = m.has_label? :read
altcolor = !altcolor
if latest_date.nil? || m.date > latest_date
latest_date = m.date
latest = m
end
end
@layout[latest].state = :open if @layout[latest].state == :closed
@layout[earliest].state = :detailed if earliest.has_label?(:unread) || @thread.size == 1
@thread.remove_label :unread
regen_text
end
|
Instance Method Details
89
|
# File 'lib/sup/modes/thread-view-mode.rb', line 89
def [] i; @text[i]; end
|
#activate_chunk ⇒ Object
called when someone presses enter when the cursor is highlighting a chunk. for expandable chunks (including messages) we toggle open/closed state; for viewable chunks (like attachments) we view.
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
|
# File 'lib/sup/modes/thread-view-mode.rb', line 193
def activate_chunk
chunk = @chunk_lines[curpos] or return
layout =
if chunk.is_a?(Message)
@layout[chunk]
elsif chunk.expandable?
@chunk_layout[chunk]
end
if layout
layout.state = (layout.state != :closed ? :closed : :open)
update
elsif chunk.viewable?
view chunk
end
end
|
134
135
136
137
138
|
# File 'lib/sup/modes/thread-view-mode.rb', line 134
def alias
p = @person_lines[curpos] or return
alias_contact p
update
end
|
318
319
320
|
# File 'lib/sup/modes/thread-view-mode.rb', line 318
def cleanup
@layout = @chunk_layout = @text = nil end
|
#collapse_non_new_messages ⇒ Object
303
304
305
306
|
# File 'lib/sup/modes/thread-view-mode.rb', line 303
def collapse_non_new_messages
@layout.each { |m, l| l.state = l.orig_new ? :open : :closed }
update
end
|
#draw_line(ln, opts = {}) ⇒ Object
81
82
83
84
85
86
87
|
# File 'lib/sup/modes/thread-view-mode.rb', line 81
def draw_line ln, opts={}
if ln == curpos
super ln, :highlight => true
else
super
end
end
|
#edit_as_new ⇒ Object
210
211
212
213
214
215
|
# File 'lib/sup/modes/thread-view-mode.rb', line 210
def edit_as_new
m = @message_lines[curpos] or return
mode = ComposeMode.new(:body => m.quotable_body_lines, :to => m.to, :cc => m.cc, :subj => m.subj, :bcc => m.bcc)
BufferManager.spawn "edit as new", mode
mode.edit_message
end
|
#edit_labels ⇒ Object
156
157
158
159
160
161
162
163
164
165
|
# File 'lib/sup/modes/thread-view-mode.rb', line 156
def edit_labels
reserved_labels = @thread.labels.select { |l| LabelManager::RESERVED_LABELS.include? l }
new_labels = BufferManager.ask_for_labels :label, "Labels for thread: ", @thread.labels
return unless new_labels
@thread.labels = (reserved_labels + new_labels).uniq
new_labels.each { |l| LabelManager << l }
update
UpdateManager.relay self, :label_thread, @thread
end
|
#expand_all_messages ⇒ Object
296
297
298
299
300
301
|
# File 'lib/sup/modes/thread-view-mode.rb', line 296
def expand_all_messages
@global_message_state ||= :closed
@global_message_state = (@global_message_state == :closed ? :open : :closed)
@layout.each { |m, l| l.state = @global_message_state }
update
end
|
#expand_all_quotes ⇒ Object
308
309
310
311
312
313
314
315
316
|
# File 'lib/sup/modes/thread-view-mode.rb', line 308
def expand_all_quotes
if(m = @message_lines[curpos])
quotes = m.chunks.select { |c| (c.is_a?(Chunk::Quote) || c.is_a?(Chunk::Signature)) && c.lines.length > 1 }
numopen = quotes.inject(0) { |s, c| s + (@chunk_layout[c].state == :open ? 1 : 0) }
newstate = numopen > quotes.length / 2 ? :closed : :open
quotes.each { |c| @chunk_layout[c].state = newstate }
update
end
end
|
128
129
130
131
|
# File 'lib/sup/modes/thread-view-mode.rb', line 128
def forward
m = @message_lines[curpos] or return
ForwardMode.spawn_nicely m
end
|
#jump_to_first_open ⇒ Object
245
246
247
248
249
250
251
252
|
# File 'lib/sup/modes/thread-view-mode.rb', line 245
def jump_to_first_open
m = @message_lines[0] or return
if @layout[m].state != :closed
jump_to_message m
else
jump_to_next_open
end
end
|
#jump_to_message(m) ⇒ Object
281
282
283
284
285
286
287
288
289
290
291
292
293
294
|
# File 'lib/sup/modes/thread-view-mode.rb', line 281
def jump_to_message m
l = @layout[m]
left = l.depth * INDENT_SPACES
right = left + l.width
jump_to_line l.top unless l.top >= topline && l.top <= botline && l.bot >= topline && l.bot <= botline
jump_to_col left unless left >= leftcol && left <= rightcol && right >= leftcol && right <= rightcol
set_cursor_pos l.top
end
|
#jump_to_next_open ⇒ Object
254
255
256
257
258
259
260
261
262
|
# File 'lib/sup/modes/thread-view-mode.rb', line 254
def jump_to_next_open
return continue_search_in_buffer if in_search? m = @message_lines[curpos] or return
while nextm = @layout[m].next
break if @layout[nextm].state != :closed
m = nextm
end
jump_to_message nextm if nextm
end
|
#jump_to_prev_open ⇒ Object
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
|
# File 'lib/sup/modes/thread-view-mode.rb', line 264
def jump_to_prev_open
m = @message_lines[curpos] or return
top = @layout[m].top
if curpos == top
while(prevm = @layout[m].prev)
break if @layout[prevm].state != :closed
m = prevm
end
jump_to_message prevm if prevm
else
jump_to_message m
end
end
|
88
|
# File 'lib/sup/modes/thread-view-mode.rb', line 88
def lines; @text.length; end
|
104
105
106
107
108
|
# File 'lib/sup/modes/thread-view-mode.rb', line 104
def reply
m = @message_lines[curpos] or return
mode = ReplyMode.new m
BufferManager.spawn "Reply to #{m.subj}", mode
end
|
#save_to_disk ⇒ Object
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
|
# File 'lib/sup/modes/thread-view-mode.rb', line 217
def save_to_disk
chunk = @chunk_lines[curpos] or return
case chunk
when Chunk::Attachment
fn = BufferManager.ask_for_filename :filename, "Save attachment to file: ", chunk.filename
save_to_file(fn) { |f| f.print chunk.raw_content } if fn
else
m = @message_lines[curpos]
fn = BufferManager.ask_for_filename :filename, "Save message to file: "
return unless fn
save_to_file(fn) do |f|
m.each_raw_message_line { |l| f.print l }
end
end
end
|
140
141
142
143
144
145
|
# File 'lib/sup/modes/thread-view-mode.rb', line 140
def search
p = @person_lines[curpos] or return
mode = PersonSearchResultsMode.new [p]
BufferManager.spawn "Search for #{p.name}", mode
mode.load_threads :num => mode.buffer.content_height
end
|
98
99
100
101
102
|
# File 'lib/sup/modes/thread-view-mode.rb', line 98
def
m = @message_lines[curpos] or return
@layout[m].state = (@layout[m].state == :detailed ? :open : :detailed)
update
end
|
#toggle_label(m, label) ⇒ Object
177
178
179
180
181
182
183
184
185
186
187
|
# File 'lib/sup/modes/thread-view-mode.rb', line 177
def toggle_label m, label
if m.has_label? label
m.remove_label label
else
m.add_label label
end
update
UpdateManager.relay self, :label, m
end
|
#toggle_new ⇒ Object
172
173
174
175
|
# File 'lib/sup/modes/thread-view-mode.rb', line 172
def toggle_new
m = @message_lines[curpos] or return
toggle_label m, :unread
end
|
#toggle_starred ⇒ Object
167
168
169
170
|
# File 'lib/sup/modes/thread-view-mode.rb', line 167
def toggle_starred
m = @message_lines[curpos] or return
toggle_label m, :starred
end
|
#unsubscribe_from_list ⇒ Object
119
120
121
122
123
124
125
126
|
# File 'lib/sup/modes/thread-view-mode.rb', line 119
def unsubscribe_from_list
m = @message_lines[curpos] or return
if m.list_unsubscribe && m.list_unsubscribe =~ /<mailto:(.*?)\?(subject=(.*?))>/
ComposeMode.spawn_nicely :from => AccountManager.account_for(m.recipient_email), :to => [PersonManager.person_for($1)], :subj => $3
else
BufferManager.flash "Can't find List-Unsubscribe header for this message."
end
end
|