Class: Redwood::ThreadIndexMode

Inherits:
LineCursorMode show all
Defined in:
lib/sup/modes/thread_index_mode.rb

Overview

subclasses should implement:

  • is_relevant?

Constant Summary collapse

DATE_WIDTH =
Time::TO_NICE_S_MAX_LEN
MIN_FROM_WIDTH =
15
LOAD_MORE_THREAD_NUM =
20

Instance Attribute Summary

Attributes inherited from LineCursorMode

#curpos

Attributes inherited from ScrollMode

#botline, #leftcol, #topline

Attributes inherited from Mode

#buffer

Instance Method Summary collapse

Methods inherited from LineCursorMode

#draw

Methods inherited from ScrollMode

#at_bottom?, #at_top?, #cancel_search!, #col_jump, #col_left, #col_right, #continue_search_in_buffer, #draw, #ensure_mode_validity, #half_page_down, #half_page_up, #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_goto_pos, #search_in_buffer, #search_start_line

Methods inherited from Mode

#blur, #cancel_search!, #draw, #focus, #handle_input, #help_text, #in_search?, keymap, keymaps, #killable?, load_all_modes, make_name, #name, #pipe_to_process, register_keymap, #resolve_input, #save_to_file

Constructor Details

#initialize(hidden_labels = [], load_thread_opts = {}) ⇒ ThreadIndexMode

Returns a new instance of ThreadIndexMode.



60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/sup/modes/thread_index_mode.rb', line 60

def initialize hidden_labels=[], load_thread_opts={}
  super()
  @mutex = Mutex.new # covers the following variables:
  @threads = []
  @hidden_threads = {}
  @size_widget_width = nil
  @size_widgets = []
  @date_widget_width = nil
  @date_widgets = []
  @tags = Tagger.new self

  ## these guys, and @text and @lines, are not covered
  @load_thread = nil
  @load_thread_opts = load_thread_opts
  @hidden_labels = hidden_labels + LabelManager::HIDDEN_RESERVED_LABELS
  @date_width = DATE_WIDTH

  @interrupt_search = false

  initialize_threads # defines @ts and @ts_mutex
  update # defines @text and @lines

  UpdateManager.register self

  @save_thread_mutex = Mutex.new

  @last_load_more_size = nil
  to_load_more do |size|
    next if @last_load_more_size == 0
    load_threads :num => size,
                 :when_done => lambda { |num| @last_load_more_size = num }
  end
end

Instance Method Details

#[](i) ⇒ Object



96
# File 'lib/sup/modes/thread_index_mode.rb', line 96

def [] i; @text[i]; end

#actually_toggle_archived(t) ⇒ Object

returns an undo lambda



330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
# File 'lib/sup/modes/thread_index_mode.rb', line 330

def actually_toggle_archived t
  thread = t
  pos = curpos
  if t.has_label? :inbox
    t.remove_label :inbox
    UpdateManager.relay self, :archived, t.first
    lambda do
      thread.apply_label :inbox
      update_text_for_line pos
      UpdateManager.relay self,:unarchived, thread.first
    end
  else
    t.apply_label :inbox
    UpdateManager.relay self, :unarchived, t.first
    lambda do
      thread.remove_label :inbox
      update_text_for_line pos
      UpdateManager.relay self, :unarchived, thread.first
    end
  end
end

#actually_toggle_deleted(t) ⇒ Object

returns an undo lambda



377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
# File 'lib/sup/modes/thread_index_mode.rb', line 377

def actually_toggle_deleted t
  if t.has_label? :deleted
    t.remove_label :deleted
    add_or_unhide t.first
    UpdateManager.relay self, :undeleted, t.first
    lambda do
      t.apply_label :deleted
      hide_thread t
      UpdateManager.relay self, :deleted, t.first
    end
  else
    t.apply_label :deleted
    hide_thread t
    UpdateManager.relay self, :deleted, t.first
    lambda do
      t.remove_label :deleted
      add_or_unhide t.first
      UpdateManager.relay self, :undeleted, t.first
    end
  end
end

#actually_toggle_spammed(t) ⇒ Object

returns an undo lambda



353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
# File 'lib/sup/modes/thread_index_mode.rb', line 353

def actually_toggle_spammed t
  thread = t
  if t.has_label? :spam
    t.remove_label :spam
    add_or_unhide t.first
    UpdateManager.relay self, :unspammed, t.first
    lambda do
      thread.apply_label :spam
      self.hide_thread thread
      UpdateManager.relay self,:spammed, thread.first
    end
  else
    t.apply_label :spam
    hide_thread t
    UpdateManager.relay self, :spammed, t.first
    lambda do
      thread.remove_label :spam
      add_or_unhide thread.first
      UpdateManager.relay self,:unspammed, thread.first
    end
  end
end

#actually_toggle_starred(t) ⇒ Object

returns an undo lambda



292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
# File 'lib/sup/modes/thread_index_mode.rb', line 292

def actually_toggle_starred t
  if t.has_label? :starred # if ANY message has a star
    t.remove_label :starred # remove from all
    UpdateManager.relay self, :unstarred, t.first
    lambda do
      t.first.add_label :starred
      UpdateManager.relay self, :starred, t.first
      regen_text
    end
  else
    t.first.add_label :starred # add only to first
    UpdateManager.relay self, :starred, t.first
    lambda do
      t.remove_label :starred
      UpdateManager.relay self, :unstarred, t.first
      regen_text
    end
  end
end

#apply_to_taggedObject



580
# File 'lib/sup/modes/thread_index_mode.rb', line 580

def apply_to_tagged; @tags.apply_to_tagged; end

#cancel_searchObject



714
715
716
# File 'lib/sup/modes/thread_index_mode.rb', line 714

def cancel_search
  @interrupt_search = true
end

#cleanupObject



541
542
543
544
545
546
547
548
549
550
551
552
553
# File 'lib/sup/modes/thread_index_mode.rb', line 541

def cleanup
  UpdateManager.unregister self

  if @load_thread
    @load_thread.kill
    BufferManager.clear @mbid if @mbid
    sleep 0.1 # TODO: necessary?
    BufferManager.erase_flash
  end
  dirty_threads = @mutex.synchronize { (@threads + @hidden_threads.keys).select { |t| t.dirty? } }
  fail "dirty threads remain" unless dirty_threads.empty?
  super
end

#contains_thread?(t) ⇒ Boolean

Returns:

  • (Boolean)


97
# File 'lib/sup/modes/thread_index_mode.rb', line 97

def contains_thread? t; @threads.include?(t) end

#edit_labelsObject



582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
# File 'lib/sup/modes/thread_index_mode.rb', line 582

def edit_labels
  thread = cursor_thread or return
  speciall = (@hidden_labels + LabelManager::RESERVED_LABELS).uniq

  old_labels = thread.labels
  pos = curpos

  keepl, modifyl = thread.labels.partition { |t| speciall.member? t }

  user_labels = BufferManager.ask_for_labels :label, "Labels for thread: ", modifyl.sort_by {|x| x.to_s}, @hidden_labels
  return unless user_labels

  thread.labels = Set.new(keepl) + user_labels
  user_labels.each { |l| LabelManager << l }
  update_text_for_line curpos

  UndoManager.register "labeling thread" do
    thread.labels = old_labels
    update_text_for_line pos
    UpdateManager.relay self, :labeled, thread.first
    Index.save_thread thread
  end

  UpdateManager.relay self, :labeled, thread.first
  Index.save_thread thread
end

#edit_messageObject



280
281
282
283
284
285
286
287
288
289
# File 'lib/sup/modes/thread_index_mode.rb', line 280

def edit_message
  return unless(t = cursor_thread)
  message, *_ = 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

#flush_indexObject



502
503
504
505
506
# File 'lib/sup/modes/thread_index_mode.rb', line 502

def flush_index
  @flush_id = BufferManager.say "Flushing index..."
  Index.save_index
  BufferManager.clear @flush_id
end

#forwardObject



659
660
661
662
663
664
665
# File 'lib/sup/modes/thread_index_mode.rb', line 659

def forward
  t = cursor_thread or return
  m = t.latest_message
  return if m.nil? # probably won't happen
  m.load_from_source!
  ForwardMode.spawn_nicely :message => m
end

#handle_added_update(sender, m) ⇒ Object



199
200
201
202
# File 'lib/sup/modes/thread_index_mode.rb', line 199

def handle_added_update sender, m
  add_or_unhide m
  BufferManager.draw_screen
end

#handle_deleted_update(sender, m) ⇒ Object



232
233
234
235
236
237
# File 'lib/sup/modes/thread_index_mode.rb', line 232

def handle_deleted_update sender, m
  t = @ts_mutex.synchronize { @ts.thread_for m }
  return unless t
  hide_thread t
  update
end

#handle_killed_update(sender, m) ⇒ Object



239
240
241
242
243
244
# File 'lib/sup/modes/thread_index_mode.rb', line 239

def handle_killed_update sender, m
  t = @ts_mutex.synchronize { @ts.thread_for m }
  return unless t
  hide_thread t
  update
end

#handle_labeled_update(sender, m) ⇒ Object



175
176
177
178
179
180
181
182
# File 'lib/sup/modes/thread_index_mode.rb', line 175

def handle_labeled_update sender, m
  if(t = thread_containing(m))
    l = @lines[t] or return
    update_text_for_line l
  elsif is_relevant?(m)
    add_or_unhide m
  end
end

#handle_location_deleted_update(sender, m) ⇒ Object



215
216
217
218
219
220
221
222
# File 'lib/sup/modes/thread_index_mode.rb', line 215

def handle_location_deleted_update sender, m
  t = thread_containing(m)
  delete_thread t if t and t.first.id == m.id
  @ts_mutex.synchronize do
    @ts.delete_message m if t
  end
  update
end

#handle_simple_update(sender, m) ⇒ Object



184
185
186
187
188
# File 'lib/sup/modes/thread_index_mode.rb', line 184

def handle_simple_update sender, m
  t = thread_containing(m) or return
  l = @lines[t] or return
  update_text_for_line l
end

#handle_single_message_deleted_update(sender, m) ⇒ Object



224
225
226
227
228
229
230
# File 'lib/sup/modes/thread_index_mode.rb', line 224

def handle_single_message_deleted_update sender, m
  @ts_mutex.synchronize do
    return unless @ts.contains? m
    @ts.remove_id m.id
  end
  update
end

#handle_single_message_labeled_update(sender, m) ⇒ Object



169
170
171
172
173
# File 'lib/sup/modes/thread_index_mode.rb', line 169

def handle_single_message_labeled_update sender, m
  ## no need to do anything different here; we don't differentiate
  ## messages from their containing threads
  handle_labeled_update sender, m
end

#handle_spammed_update(sender, m) ⇒ Object



246
247
248
249
250
251
# File 'lib/sup/modes/thread_index_mode.rb', line 246

def handle_spammed_update sender, m
  t = @ts_mutex.synchronize { @ts.thread_for m }
  return unless t
  hide_thread t
  update
end

#handle_undeleted_update(sender, m) ⇒ Object



253
254
255
# File 'lib/sup/modes/thread_index_mode.rb', line 253

def handle_undeleted_update sender, m
  add_or_unhide m
end

#handle_unkilled_update(sender, m) ⇒ Object



257
258
259
# File 'lib/sup/modes/thread_index_mode.rb', line 257

def handle_unkilled_update sender, m
  add_or_unhide m
end

#handle_updated_update(sender, m) ⇒ Object



204
205
206
207
208
209
210
211
212
213
# File 'lib/sup/modes/thread_index_mode.rb', line 204

def handle_updated_update sender, m
  t = thread_containing(m) or return
  l = @lines[t] or return
  @ts_mutex.synchronize do
    @ts.delete_message m
    @ts.add_message m
  end
  Index.save_thread t, sync_back = false
  update_text_for_line l
end

#is_relevant?(m) ⇒ Boolean

overwrite me!

Returns:

  • (Boolean)


197
# File 'lib/sup/modes/thread_index_mode.rb', line 197

def is_relevant? m; false; end

#join_threadsObject



435
436
437
438
439
# File 'lib/sup/modes/thread_index_mode.rb', line 435

def join_threads
  ## this command has no non-tagged form. as a convenience, allow this
  ## command to be applied to tagged threads without hitting ';'.
  @tags.apply_to_tagged :join_threads
end

#jump_to_next_newObject



448
449
450
451
452
453
454
455
456
457
458
459
460
# File 'lib/sup/modes/thread_index_mode.rb', line 448

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 there if necessary
    jump_to_line n unless n >= topline && n < botline
    set_cursor_pos n
  else
    BufferManager.flash "No new messages."
  end
end

#killObject



497
498
499
500
# File 'lib/sup/modes/thread_index_mode.rb', line 497

def kill
  t = cursor_thread or return
  multi_kill [t]
end

#launch_another_thread(thread, direction, &b) ⇒ Object



152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
# File 'lib/sup/modes/thread_index_mode.rb', line 152

def launch_another_thread thread, direction, &b
  l = @lines[thread] or return
  target_l = l + direction
  t = @mutex.synchronize do
    if target_l >= 0 && target_l < @threads.length
      @threads[target_l]
    end
  end

  if t # there's a next thread
    set_cursor_pos target_l # move out of mutex?
    select t, b
  elsif b # no next thread. call the block anyways
    b.call
  end
end

#launch_next_thread_after(thread, &b) ⇒ Object

these two methods are called by thread-view-modes when the user wants to view the previous/next thread without going back to index-mode. we update the cursor as a convenience.



144
145
146
# File 'lib/sup/modes/thread_index_mode.rb', line 144

def launch_next_thread_after thread, &b
  launch_another_thread thread, 1, &b
end

#launch_prev_thread_before(thread, &b) ⇒ Object



148
149
150
# File 'lib/sup/modes/thread_index_mode.rb', line 148

def launch_prev_thread_before thread, &b
  launch_another_thread thread, -1, &b
end

#linesObject



95
# File 'lib/sup/modes/thread_index_mode.rb', line 95

def lines; @text.length; end

#load_all_threadsObject



718
719
720
# File 'lib/sup/modes/thread_index_mode.rb', line 718

def load_all_threads
  load_threads :num => -1
end

#load_n_threads(n = LOAD_MORE_THREAD_NUM, opts = {}) ⇒ Object

TODO: figure out @ts_mutex in this method



677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
# File 'lib/sup/modes/thread_index_mode.rb', line 677

def load_n_threads n=LOAD_MORE_THREAD_NUM, opts={}
  @interrupt_search = false
  @mbid = BufferManager.say "Searching for threads..."

  ts_to_load = n
  ts_to_load = ts_to_load + @ts.size unless n == -1 # -1 means all threads

  orig_size = @ts.size
  last_update = Time.now
  @ts.load_n_threads(ts_to_load, 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
    ::Thread.pass
    break if @interrupt_search
  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



667
668
669
670
671
672
673
674
# File 'lib/sup/modes/thread_index_mode.rb', line 667

def load_n_threads_background n=LOAD_MORE_THREAD_NUM, opts={}
  return if @load_thread # todo: wrap in mutex
  @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



722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
# File 'lib/sup/modes/thread_index_mode.rb', line 722

def load_threads opts={}
  if opts[:num].nil?
    n = ThreadIndexMode::LOAD_MORE_THREAD_NUM
  else
    n = opts[:num]
  end

  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



609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
# File 'lib/sup/modes/thread_index_mode.rb', line 609

def multi_edit_labels threads
  user_labels = BufferManager.ask_for_labels :labels, "Add/remove labels (use -label to remove): ", [], @hidden_labels
  return unless user_labels

  user_labels.map! { |l| (l.to_s =~ /^-/)? [l.to_s.gsub(/^-?/, '').to_sym, true] : [l, false] }
  hl = user_labels.select { |(l,_)| @hidden_labels.member? l }
  unless hl.empty?
    BufferManager.flash "'#{hl}' is a reserved label!"
    return
  end

  old_labels = threads.map { |t| t.labels.dup }

  threads.each do |t|
    user_labels.each do |(l, to_remove)|
      if to_remove
        t.remove_label l
      else
        t.apply_label l
        LabelManager << l
      end
    end
    UpdateManager.relay self, :labeled, t.first
  end

  regen_text

  UndoManager.register "labeling #{threads.size.pluralize 'thread'}" do
    threads.zip(old_labels).map do |t, old_labels|
      t.labels = old_labels
      UpdateManager.relay self, :labeled, t.first
      Index.save_thread t
    end
    regen_text
  end

  threads.each { |t| Index.save_thread t }
end

#multi_join_threads(threads) ⇒ Object



441
442
443
444
445
446
# File 'lib/sup/modes/thread_index_mode.rb', line 441

def multi_join_threads threads
  @ts.join_threads threads or return
  threads.each { |t| Index.save_thread t }
  @tags.drop_all_tags # otherwise we have tag pointers to invalid threads!
  update
end

#multi_kill(threads) ⇒ Object

m-m-m-m-MULTI-KILL



509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
# File 'lib/sup/modes/thread_index_mode.rb', line 509

def multi_kill threads
  UndoManager.register "killing/unkilling #{threads.size.pluralize 'threads'}" do
    threads.each do |t|
      if t.toggle_label :killed
        add_or_unhide t.first
      else
        hide_thread t
      end
    end.each do |t|
      UpdateManager.relay self, :labeled, t.first
      Index.save_thread t
    end
    regen_text
  end

  threads.each do |t|
    if t.toggle_label :killed
      hide_thread t
    else
      add_or_unhide t.first
    end
  end.each do |t|
    # send 'labeled'... this might be more specific
    UpdateManager.relay self, :labeled, t.first
    Index.save_thread t
  end

  killed, unkilled = threads.partition { |t| t.has_label? :killed }.map(&:size)
  BufferManager.flash "#{killed.pluralize 'thread'} killed, #{unkilled} unkilled"
  regen_text
end

#multi_read_and_archive(threads) ⇒ Object



766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
# File 'lib/sup/modes/thread_index_mode.rb', line 766

def multi_read_and_archive threads
  old_labels = threads.map { |t| t.labels.dup }

  threads.each do |t|
    t.remove_label :unread
    t.remove_label :inbox
    hide_thread t
  end
  regen_text

  UndoManager.register "reading and archiving #{threads.size.pluralize 'thread'}" do
    threads.zip(old_labels).each do |t, l|
      t.labels = l
      add_or_unhide t.first
      Index.save_thread t
    end
    regen_text
  end

  threads.each { |t| Index.save_thread t }
end

#multi_select(threads) ⇒ Object



137
138
139
# File 'lib/sup/modes/thread_index_mode.rb', line 137

def multi_select threads
  threads.each { |t| select t }
end

#multi_toggle_archived(threads) ⇒ Object



408
409
410
411
412
413
414
# File 'lib/sup/modes/thread_index_mode.rb', line 408

def multi_toggle_archived threads
  undos = threads.map { |t| actually_toggle_archived t }
  UndoManager.register "deleting/undeleting #{threads.size.pluralize 'thread'}", undos, lambda { regen_text },
                       lambda { threads.each { |t| Index.save_thread t } }
  regen_text
  threads.each { |t| Index.save_thread t }
end

#multi_toggle_deleted(threads) ⇒ Object

see comment for multi_toggle_spam



489
490
491
492
493
494
495
# File 'lib/sup/modes/thread_index_mode.rb', line 489

def multi_toggle_deleted threads
  undos = threads.map { |t| actually_toggle_deleted t }
  UndoManager.register "deleting/undeleting #{threads.size.pluralize 'thread'}",
                       undos, lambda { regen_text }, lambda { threads.each { |t| Index.save_thread t } }
  regen_text
  threads.each { |t| Index.save_thread t }
end

#multi_toggle_new(threads) ⇒ Object



424
425
426
427
428
# File 'lib/sup/modes/thread_index_mode.rb', line 424

def multi_toggle_new threads
  threads.each { |t| t.toggle_label :unread }
  regen_text
  threads.each { |t| Index.save_thread t }
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.



474
475
476
477
478
479
480
481
# File 'lib/sup/modes/thread_index_mode.rb', line 474

def multi_toggle_spam threads
  undos = threads.map { |t| actually_toggle_spammed t }
  threads.each { |t| HookManager.run("mark-as-spam", :thread => t) }
  UndoManager.register "marking/unmarking  #{threads.size.pluralize 'thread'} as spam",
                       undos, lambda { regen_text }, lambda { threads.each { |t| Index.save_thread t } }
  regen_text
  threads.each { |t| Index.save_thread t }
end

#multi_toggle_starred(threads) ⇒ Object



321
322
323
324
325
326
327
# File 'lib/sup/modes/thread_index_mode.rb', line 321

def multi_toggle_starred threads
  UndoManager.register "toggling #{threads.size.pluralize 'thread'} starred status",
    threads.map { |t| actually_toggle_starred t },
    lambda { threads.each { |t| Index.save_thread t } }
  regen_text
  threads.each { |t| Index.save_thread t }
end

#multi_toggle_tagged(threads) ⇒ Object



430
431
432
433
# File 'lib/sup/modes/thread_index_mode.rb', line 430

def multi_toggle_tagged threads
  @mutex.synchronize { @tags.drop_all_tags }
  regen_text
end

#read_and_archiveObject



747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
# File 'lib/sup/modes/thread_index_mode.rb', line 747

def read_and_archive
  return unless cursor_thread
  thread = cursor_thread # to make sure lambda only knows about 'old' cursor_thread

  was_unread = thread.labels.member? :unread
  UndoManager.register "reading and archiving thread" do
    thread.apply_label :inbox
    thread.apply_label :unread if was_unread
    add_or_unhide thread.first
    Index.save_thread thread
  end

  cursor_thread.remove_label :unread
  cursor_thread.remove_label :inbox
  hide_thread cursor_thread
  regen_text
  Index.save_thread thread
end

#reloadObject



99
100
101
102
103
104
# File 'lib/sup/modes/thread_index_mode.rb', line 99

def reload
  drop_all_threads
  UndoManager.clear
  BufferManager.draw_screen
  load_threads :num => buffer.content_height
end

#reply(type_arg = nil) ⇒ Object



648
649
650
651
652
653
654
655
# File 'lib/sup/modes/thread_index_mode.rb', line 648

def reply type_arg=nil
  t = cursor_thread or return
  m = t.latest_message
  return if m.nil? # probably won't happen
  m.load_from_source!
  mode = ReplyMode.new m, type_arg
  BufferManager.spawn "Reply to #{m.subj}", mode
end

#reply_allObject



657
# File 'lib/sup/modes/thread_index_mode.rb', line 657

def reply_all; reply :all; end

#resize(rows, cols) ⇒ Object



788
789
790
791
# File 'lib/sup/modes/thread_index_mode.rb', line 788

def resize rows, cols
  regen_text
  super
end

#select(t = nil, when_done = nil) ⇒ Object

open up a thread view window



107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
# File 'lib/sup/modes/thread_index_mode.rb', line 107

def select t=nil, when_done=nil
  t ||= cursor_thread or return

  Redwood::reporting_thread("load messages for thread-view-mode") do
    num = t.size
    message = "Loading #{num.pluralize 'message body'}..."
    BufferManager.say(message) do |sid|
      t.each_with_index do |(m, *_), i|
        next unless m
        BufferManager.say "#{message} (#{i}/#{num})", sid if t.size > 1
        m.load_from_source!
      end
    end
    mode = ThreadViewMode.new t, @hidden_labels, self
    BufferManager.spawn t.subj, mode
    BufferManager.draw_screen
    mode.jump_to_first_open if $config[:jump_to_open_message]
    BufferManager.draw_screen # lame TODO: make this unnecessary
    ## the first draw_screen is needed before topline and botline
    ## are set, and the second to show the cursor having moved

    t.remove_label :unread
    Index.save_thread t

    update_text_for_line curpos
    UpdateManager.relay self, :read, t.first
    when_done.call if when_done
  end
end

#statusObject



706
707
708
709
710
711
712
# File 'lib/sup/modes/thread_index_mode.rb', line 706

def status
  if (l = lines) == 0
    "line 0 of 0"
  else
    "line #{curpos + 1} of #{l}"
  end
end

#tag_matchingObject



567
568
569
570
571
572
573
574
575
576
577
578
# File 'lib/sup/modes/thread_index_mode.rb', line 567

def tag_matching
  query = BufferManager.ask :search, "tag threads matching (regex): "
  return if query.nil? || query.empty?
  query = begin
    /#{query}/i
  rescue RegexpError => e
    BufferManager.flash "error interpreting '#{query}': #{e.message}"
    return
  end
  @mutex.synchronize { @threads.each { |t| @tags.tag t if thread_matches?(t, query) } }
  regen_text
end

#toggle_archivedObject



399
400
401
402
403
404
405
406
# File 'lib/sup/modes/thread_index_mode.rb', line 399

def toggle_archived
  t = cursor_thread or return
  undo = actually_toggle_archived t
  UndoManager.register "deleting/undeleting thread #{t.first.id}", undo, lambda { update_text_for_line curpos },
                       lambda { Index.save_thread t }
  update_text_for_line curpos
  Index.save_thread t
end

#toggle_deletedObject



483
484
485
486
# File 'lib/sup/modes/thread_index_mode.rb', line 483

def toggle_deleted
  t = cursor_thread or return
  multi_toggle_deleted [t]
end

#toggle_newObject



416
417
418
419
420
421
422
# File 'lib/sup/modes/thread_index_mode.rb', line 416

def toggle_new
  t = cursor_thread or return
  t.toggle_label :unread
  update_text_for_line curpos
  cursor_down
  Index.save_thread t
end

#toggle_spamObject



462
463
464
465
# File 'lib/sup/modes/thread_index_mode.rb', line 462

def toggle_spam
  t = cursor_thread or return
  multi_toggle_spam [t]
end

#toggle_starredObject



312
313
314
315
316
317
318
319
# File 'lib/sup/modes/thread_index_mode.rb', line 312

def toggle_starred
  t = cursor_thread or return
  undo = actually_toggle_starred t
  UndoManager.register "toggling thread starred status", undo, lambda { Index.save_thread t }
  update_text_for_line curpos
  cursor_down
  Index.save_thread t
end

#toggle_taggedObject



555
556
557
558
559
560
# File 'lib/sup/modes/thread_index_mode.rb', line 555

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_allObject



562
563
564
565
# File 'lib/sup/modes/thread_index_mode.rb', line 562

def toggle_tagged_all
  @mutex.synchronize { @threads.each { |t| @tags.toggle_tag_for t } }
  regen_text
end

#undoObject



261
262
263
# File 'lib/sup/modes/thread_index_mode.rb', line 261

def undo
  UndoManager.undo
end

#unsaved?Boolean

Returns:

  • (Boolean)


94
# File 'lib/sup/modes/thread_index_mode.rb', line 94

def unsaved?; dirty? end

#updateObject



265
266
267
268
269
270
271
272
273
274
275
276
277
278
# File 'lib/sup/modes/thread_index_mode.rb', line 265

def update
  old_cursor_thread = cursor_thread
  @mutex.synchronize do
    ## let's see you do THIS in python
    @threads = @ts.threads.select { |t| !@hidden_threads.member?(t) }.select(&:has_message?).sort_by(&:sort_key)
    @size_widgets = @threads.map { |t| size_widget_for_thread t }
    @size_widget_width = @size_widgets.max_of { |w| w.display_length }
    @date_widgets = @threads.map { |t| date_widget_for_thread t }
    @date_widget_width = @date_widgets.max_of { |w| w.display_length }
  end
  set_cursor_pos @threads.index(old_cursor_thread)||curpos

  regen_text
end