Class: Redwood::ThreadIndexMode
Overview
subclasses should implement:
Constant Summary
collapse
- DATE_WIDTH =
Time::TO_NICE_S_MAX_LEN
- MIN_FROM_WIDTH =
15
- LOAD_MORE_THREAD_NUM =
20
Constants inherited
from ScrollMode
ScrollMode::COL_JUMP
Instance Attribute Summary
#curpos
Attributes inherited from ScrollMode
#botline, #leftcol, #topline
Attributes inherited from Mode
#buffer
Instance Method Summary
collapse
-
#[](i) ⇒ Object
-
#actually_toggle_archived(t) ⇒ Object
-
#actually_toggle_deleted(t) ⇒ Object
-
#actually_toggle_spammed(t) ⇒ Object
-
#actually_toggle_starred(t) ⇒ Object
-
#apply_to_tagged ⇒ Object
-
#cleanup ⇒ Object
-
#contains_thread?(t) ⇒ Boolean
-
#edit_labels ⇒ Object
-
#edit_message ⇒ Object
-
#forward ⇒ Object
-
#handle_add_update(sender, m) ⇒ Object
-
#handle_archived_update(*a) ⇒ Object
-
#handle_delete_update(sender, mid) ⇒ Object
-
#handle_deleted_update(sender, t) ⇒ Object
-
#handle_label_thread_update(sender, t) ⇒ Object
-
#handle_label_update(sender, m) ⇒ Object
-
#handle_read_update(sender, t) ⇒ Object
-
#initialize(hidden_labels = [], load_thread_opts = {}) ⇒ ThreadIndexMode
constructor
A new instance of ThreadIndexMode.
-
#is_relevant?(m) ⇒ Boolean
-
#jump_to_next_new ⇒ Object
-
#kill ⇒ Object
-
#lines ⇒ Object
-
#load_n_threads(n = LOAD_MORE_THREAD_NUM, opts = {}) ⇒ Object
TODO: figure out @ts_mutex in this method.
-
#load_n_threads_background(n = LOAD_MORE_THREAD_NUM, opts = {}) ⇒ Object
-
#load_threads(opts = {}) ⇒ Object
-
#multi_edit_labels(threads) ⇒ Object
-
#multi_kill(threads) ⇒ Object
-
#multi_select(threads) ⇒ Object
-
#multi_toggle_archived(threads) ⇒ Object
-
#multi_toggle_deleted(threads) ⇒ Object
see comment for multi_toggle_spam.
-
#multi_toggle_new(threads) ⇒ Object
-
#multi_toggle_spam(threads) ⇒ Object
both spam and deleted have the curious characteristic that you always want to hide the thread after either applying or removing that label.
-
#multi_toggle_starred(threads) ⇒ Object
-
#multi_toggle_tagged(threads) ⇒ Object
-
#reload ⇒ Object
-
#reply ⇒ Object
-
#resize(rows, cols) ⇒ Object
-
#save ⇒ Object
-
#select(t = nil) ⇒ Object
open up a thread view window.
-
#status ⇒ Object
-
#toggle_archived ⇒ Object
-
#toggle_deleted ⇒ Object
-
#toggle_new ⇒ Object
-
#toggle_spam ⇒ Object
-
#toggle_starred ⇒ Object
-
#toggle_tagged ⇒ Object
-
#toggle_tagged_all ⇒ Object
-
#update ⇒ Object
#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, #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, #resolve_input, #save_to_file
Constructor Details
#initialize(hidden_labels = [], load_thread_opts = {}) ⇒ ThreadIndexMode
Returns a new instance of ThreadIndexMode.
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
|
# File 'lib/sup/modes/thread-index-mode.rb', line 37
def initialize hidden_labels=[], load_thread_opts={}
super()
@mutex = Mutex.new @threads = {}
@hidden_threads = {}
@size_widget_width = nil
@size_widgets = {}
@tags = Tagger.new self
@load_thread = nil
@load_thread_opts = load_thread_opts
@hidden_labels = hidden_labels + LabelManager::HIDDEN_RESERVED_LABELS
@date_width = DATE_WIDTH
initialize_threads update
UpdateManager.register self
@last_load_more_size = nil
to_load_more do |size|
next if @last_load_more_size == 0
load_threads :num => 1, :background => false
load_threads :num => (size - 1),
:when_done => lambda { |num| @last_load_more_size = num }
end
end
|
Instance Method Details
67
|
# File 'lib/sup/modes/thread-index-mode.rb', line 67
def [] i; @text[i]; end
|
#actually_toggle_archived(t) ⇒ Object
197
198
199
200
201
202
203
204
205
|
# File 'lib/sup/modes/thread-index-mode.rb', line 197
def actually_toggle_archived t
if t.has_label? :inbox
t.remove_label :inbox
UpdateManager.relay self, :archived, t
else
t.apply_label :inbox
UpdateManager.relay self, :unarchived, t
end
end
|
#actually_toggle_deleted(t) ⇒ Object
217
218
219
220
221
222
223
224
225
|
# File 'lib/sup/modes/thread-index-mode.rb', line 217
def actually_toggle_deleted t
if t.has_label? :deleted
t.remove_label :deleted
UpdateManager.relay self, :undeleted, t
else
t.apply_label :deleted
UpdateManager.relay self, :deleted, t
end
end
|
#actually_toggle_spammed(t) ⇒ Object
207
208
209
210
211
212
213
214
215
|
# File 'lib/sup/modes/thread-index-mode.rb', line 207
def actually_toggle_spammed t
if t.has_label? :spam
t.remove_label :spam
UpdateManager.relay self, :unspammed, t
else
t.apply_label :spam
UpdateManager.relay self, :spammed, t
end
end
|
#actually_toggle_starred(t) ⇒ Object
175
176
177
178
179
180
181
182
183
|
# File 'lib/sup/modes/thread-index-mode.rb', line 175
def actually_toggle_starred t
if t.has_label? :starred t.remove_label :starred UpdateManager.relay self, :unstarred, t
else
t.first.add_label :starred UpdateManager.relay self, :starred, t
end
end
|
#apply_to_tagged ⇒ Object
354
|
# File 'lib/sup/modes/thread-index-mode.rb', line 354
def apply_to_tagged; @tags.apply_to_tagged; end
|
#contains_thread?(t) ⇒ Boolean
68
|
# File 'lib/sup/modes/thread-index-mode.rb', line 68
def contains_thread? t; @threads.include?(t) end
|
#edit_labels ⇒ Object
356
357
358
359
360
361
362
363
364
365
366
367
|
# File 'lib/sup/modes/thread-index-mode.rb', line 356
def edit_labels
thread = cursor_thread or return
speciall = (@hidden_labels + LabelManager::RESERVED_LABELS).uniq
keepl, modifyl = thread.labels.partition { |t| speciall.member? t }
user_labels = BufferManager.ask_for_labels :label, "Labels for thread: ", modifyl, @hidden_labels
return unless user_labels
thread.labels = keepl + user_labels
user_labels.each { |l| LabelManager << l }
update_text_for_line curpos
end
|
#edit_message ⇒ Object
164
165
166
167
168
169
170
171
172
173
|
# File 'lib/sup/modes/thread-index-mode.rb', line 164
def edit_message
return unless(t = cursor_thread)
message, *crap = t.find { |m, *o| m.has_label? :draft }
if message
mode = ResumeMode.new message
BufferManager.spawn "Edit message", mode
else
BufferManager.flash "Not a draft message!"
end
end
|
393
394
395
396
397
398
399
|
# File 'lib/sup/modes/thread-index-mode.rb', line 393
def forward
t = cursor_thread or return
m = t.latest_message
return if m.nil? m.load_from_source!
ForwardMode.spawn_nicely m
end
|
#handle_add_update(sender, m) ⇒ Object
135
136
137
138
139
140
141
142
|
# File 'lib/sup/modes/thread-index-mode.rb', line 135
def handle_add_update sender, m
@ts_mutex.synchronize do
return unless is_relevant?(m) || @ts.is_relevant?(m)
@ts.load_thread_for_message m
end
update
BufferManager.draw_screen
end
|
#handle_archived_update(*a) ⇒ Object
124
|
# File 'lib/sup/modes/thread-index-mode.rb', line 124
def handle_archived_update *a; handle_read_update(*a); end
|
#handle_delete_update(sender, mid) ⇒ Object
144
145
146
147
148
149
150
151
|
# File 'lib/sup/modes/thread-index-mode.rb', line 144
def handle_delete_update sender, mid
@ts_mutex.synchronize do
return unless @ts.contains_id? mid
@ts.remove mid
end
update
BufferManager.draw_screen
end
|
#handle_deleted_update(sender, t) ⇒ Object
126
127
128
129
130
|
# File 'lib/sup/modes/thread-index-mode.rb', line 126
def handle_deleted_update sender, t
handle_read_update sender, t
hide_thread t
regen_text
end
|
#handle_label_thread_update(sender, t) ⇒ Object
112
113
114
115
116
|
# File 'lib/sup/modes/thread-index-mode.rb', line 112
def handle_label_thread_update sender, t
l = @lines[t] or return
update_text_for_line l
BufferManager.draw_screen
end
|
#handle_label_update(sender, m) ⇒ Object
107
108
109
110
|
# File 'lib/sup/modes/thread-index-mode.rb', line 107
def handle_label_update sender, m
t = @ts_mutex.synchronize { @ts.thread_for(m) } or return
handle_label_thread_update sender, t
end
|
#handle_read_update(sender, t) ⇒ Object
118
119
120
121
122
|
# File 'lib/sup/modes/thread-index-mode.rb', line 118
def handle_read_update sender, t
l = @lines[t] or return
update_text_for_line l
BufferManager.draw_screen
end
|
#is_relevant?(m) ⇒ Boolean
133
|
# File 'lib/sup/modes/thread-index-mode.rb', line 133
def is_relevant? m; false; end
|
#jump_to_next_new ⇒ Object
255
256
257
258
259
260
261
262
263
264
265
266
267
|
# File 'lib/sup/modes/thread-index-mode.rb', line 255
def jump_to_next_new
n = @mutex.synchronize do
((curpos + 1) ... lines).find { |i| @threads[i].has_label? :unread } ||
(0 ... curpos).find { |i| @threads[i].has_label? :unread }
end
if n
jump_to_line n unless n >= topline && n < botline
set_cursor_pos n
else
BufferManager.flash "No new messages"
end
end
|
303
304
305
306
|
# File 'lib/sup/modes/thread-index-mode.rb', line 303
def kill
t = cursor_thread or return
multi_kill [t]
end
|
66
|
# File 'lib/sup/modes/thread-index-mode.rb', line 66
def lines; @text.length; end
|
#load_n_threads(n = LOAD_MORE_THREAD_NUM, opts = {}) ⇒ Object
TODO: figure out @ts_mutex in this method
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
|
# File 'lib/sup/modes/thread-index-mode.rb', line 411
def load_n_threads n=LOAD_MORE_THREAD_NUM, opts={}
@mbid = BufferManager.say "Searching for threads..."
orig_size = @ts.size
last_update = Time.now
@ts.load_n_threads(@ts.size + n, opts) do |i|
if (Time.now - last_update) >= 0.25
BufferManager.say "Loaded #{i.pluralize 'thread'}...", @mbid
update
BufferManager.draw_screen
last_update = Time.now
end
end
@ts.threads.each { |th| th.labels.each { |l| LabelManager << l } }
update
BufferManager.clear @mbid
@mbid = nil
BufferManager.draw_screen
@ts.size - orig_size
end
|
#load_n_threads_background(n = LOAD_MORE_THREAD_NUM, opts = {}) ⇒ Object
401
402
403
404
405
406
407
408
|
# File 'lib/sup/modes/thread-index-mode.rb', line 401
def load_n_threads_background n=LOAD_MORE_THREAD_NUM, opts={}
return if @load_thread @load_thread = Redwood::reporting_thread("load threads for thread-index-mode") do
num = load_n_threads n, opts
opts[:when_done].call(num) if opts[:when_done]
@load_thread = nil
end
end
|
#load_threads(opts = {}) ⇒ Object
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
|
# File 'lib/sup/modes/thread-index-mode.rb', line 441
def load_threads opts={}
n = opts[:num] || ThreadIndexMode::LOAD_MORE_THREAD_NUM
myopts = @load_thread_opts.merge({ :when_done => (lambda do |num|
opts[:when_done].call(num) if opts[:when_done]
if num > 0
BufferManager.flash "Found #{num.pluralize 'thread'}."
else
BufferManager.flash "No matches."
end
end)})
if opts[:background] || opts[:background].nil?
load_n_threads_background n, myopts
else
load_n_threads n, myopts
end
end
|
#multi_edit_labels(threads) ⇒ Object
369
370
371
372
373
374
375
376
377
378
379
380
381
382
|
# File 'lib/sup/modes/thread-index-mode.rb', line 369
def multi_edit_labels threads
answer = BufferManager.ask :add_labels, "add labels: "
return unless answer
user_labels = answer.split(/\s+/).map { |l| l.intern }
hl = user_labels.select { |l| @hidden_labels.member? l }
if hl.empty?
threads.each { |t| user_labels.each { |l| t.apply_label l } }
user_labels.each { |l| LabelManager << l }
else
BufferManager.flash "'#{hl}' is a reserved label!"
end
regen_text
end
|
#multi_kill(threads) ⇒ Object
308
309
310
311
312
313
314
315
|
# File 'lib/sup/modes/thread-index-mode.rb', line 308
def multi_kill threads
threads.each do |t|
t.apply_label :killed
hide_thread t
end
regen_text
BufferManager.flash "#{threads.size.pluralize 'Thread'} killed."
end
|
#multi_select(threads) ⇒ Object
103
104
105
|
# File 'lib/sup/modes/thread-index-mode.rb', line 103
def multi_select threads
threads.each { |t| select t }
end
|
#multi_toggle_archived(threads) ⇒ Object
233
234
235
236
|
# File 'lib/sup/modes/thread-index-mode.rb', line 233
def multi_toggle_archived threads
threads.each { |t| actually_toggle_archived t }
regen_text
end
|
#multi_toggle_deleted(threads) ⇒ Object
see comment for multi_toggle_spam
295
296
297
298
299
300
301
|
# File 'lib/sup/modes/thread-index-mode.rb', line 295
def multi_toggle_deleted threads
threads.each do |t|
actually_toggle_deleted t
hide_thread t
end
regen_text
end
|
#multi_toggle_new(threads) ⇒ Object
245
246
247
248
|
# File 'lib/sup/modes/thread-index-mode.rb', line 245
def multi_toggle_new threads
threads.each { |t| t.toggle_label :unread }
regen_text
end
|
#multi_toggle_spam(threads) ⇒ Object
both spam and deleted have the curious characteristic that you always want to hide the thread after either applying or removing that label. in all thread-index-views except for label-search-results-mode, when you mark a message as spam or deleted, you want it to disappear immediately; in LSRM, you only see deleted or spam emails, and when you undelete or unspam them you also want them to disappear immediately.
281
282
283
284
285
286
287
|
# File 'lib/sup/modes/thread-index-mode.rb', line 281
def multi_toggle_spam threads
threads.each do |t|
actually_toggle_spammed t
hide_thread t
end
regen_text
end
|
#multi_toggle_starred(threads) ⇒ Object
192
193
194
195
|
# File 'lib/sup/modes/thread-index-mode.rb', line 192
def multi_toggle_starred threads
threads.each { |t| actually_toggle_starred t }
regen_text
end
|
#multi_toggle_tagged(threads) ⇒ Object
250
251
252
253
|
# File 'lib/sup/modes/thread-index-mode.rb', line 250
def multi_toggle_tagged threads
@mutex.synchronize { @tags.drop_all_tags }
regen_text
end
|
70
71
72
73
74
|
# File 'lib/sup/modes/thread-index-mode.rb', line 70
def reload
drop_all_threads
BufferManager.draw_screen
load_threads :num => buffer.content_height
end
|
384
385
386
387
388
389
390
391
|
# File 'lib/sup/modes/thread-index-mode.rb', line 384
def reply
t = cursor_thread or return
m = t.latest_message
return if m.nil? m.load_from_source!
mode = ReplyMode.new m
BufferManager.spawn "Reply to #{m.subj}", mode
end
|
#resize(rows, cols) ⇒ Object
461
462
463
464
|
# File 'lib/sup/modes/thread-index-mode.rb', line 461
def resize rows, cols
regen_text
super
end
|
317
318
319
320
321
322
323
324
325
326
327
|
# File 'lib/sup/modes/thread-index-mode.rb', line 317
def save
dirty_threads = @mutex.synchronize { (@threads + @hidden_threads.keys).select { |t| t.dirty? } }
return if dirty_threads.empty?
BufferManager.say("Saving threads...") do |say_id|
dirty_threads.each_with_index do |t, i|
BufferManager.say "Saving modified thread #{i + 1} of #{dirty_threads.length}...", say_id
t.save Index
end
end
end
|
#select(t = nil) ⇒ Object
open up a thread view window
433
434
435
436
437
438
439
|
# File 'lib/sup/modes/thread-index-mode.rb', line 433
def status
if (l = lines) == 0
"line 0 of 0"
else
"line #{curpos + 1} of #{l} #{dirty? ? '*modified*' : ''}"
end
end
|
#toggle_archived ⇒ Object
227
228
229
230
231
|
# File 'lib/sup/modes/thread-index-mode.rb', line 227
def toggle_archived
t = cursor_thread or return
actually_toggle_archived t
update_text_for_line curpos
end
|
#toggle_deleted ⇒ Object
289
290
291
292
|
# File 'lib/sup/modes/thread-index-mode.rb', line 289
def toggle_deleted
t = cursor_thread or return
multi_toggle_deleted [t]
end
|
#toggle_new ⇒ Object
238
239
240
241
242
243
|
# File 'lib/sup/modes/thread-index-mode.rb', line 238
def toggle_new
t = cursor_thread or return
t.toggle_label :unread
update_text_for_line curpos
cursor_down
end
|
#toggle_spam ⇒ Object
269
270
271
272
|
# File 'lib/sup/modes/thread-index-mode.rb', line 269
def toggle_spam
t = cursor_thread or return
multi_toggle_spam [t]
end
|
#toggle_starred ⇒ Object
185
186
187
188
189
190
|
# File 'lib/sup/modes/thread-index-mode.rb', line 185
def toggle_starred
t = cursor_thread or return
actually_toggle_starred t
update_text_for_line curpos
cursor_down
end
|
#toggle_tagged ⇒ Object
342
343
344
345
346
347
|
# File 'lib/sup/modes/thread-index-mode.rb', line 342
def toggle_tagged
t = cursor_thread or return
@mutex.synchronize { @tags.toggle_tag_for t }
update_text_for_line curpos
cursor_down
end
|
#toggle_tagged_all ⇒ Object
349
350
351
352
|
# File 'lib/sup/modes/thread-index-mode.rb', line 349
def toggle_tagged_all
@mutex.synchronize { @threads.each { |t| @tags.toggle_tag_for t } }
regen_text
end
|
153
154
155
156
157
158
159
160
161
162
|
# File 'lib/sup/modes/thread-index-mode.rb', line 153
def update
@mutex.synchronize do
@threads = @ts.threads.select { |t| !@hidden_threads[t] }.sort_by { |t| t.date }.reverse
@size_widgets = @threads.map { |t| size_widget_for_thread t }
@size_widget_width = @size_widgets.max_of { |w| w.length }
end
regen_text
end
|