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



319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
# File 'lib/sup/modes/thread_index_mode.rb', line 319

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



366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
# File 'lib/sup/modes/thread_index_mode.rb', line 366

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



342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
# File 'lib/sup/modes/thread_index_mode.rb', line 342

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



281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
# File 'lib/sup/modes/thread_index_mode.rb', line 281

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



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

def apply_to_tagged; @tags.apply_to_tagged; end

#cancel_searchObject



703
704
705
# File 'lib/sup/modes/thread_index_mode.rb', line 703

def cancel_search
  @interrupt_search = true
end

#cleanupObject



530
531
532
533
534
535
536
537
538
539
540
541
542
# File 'lib/sup/modes/thread_index_mode.rb', line 530

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



571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
# File 'lib/sup/modes/thread_index_mode.rb', line 571

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



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

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



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

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

#forwardObject



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

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_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



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

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



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

def handle_undeleted_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



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

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



437
438
439
440
441
442
443
444
445
446
447
448
449
# File 'lib/sup/modes/thread_index_mode.rb', line 437

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



486
487
488
489
# File 'lib/sup/modes/thread_index_mode.rb', line 486

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



707
708
709
# File 'lib/sup/modes/thread_index_mode.rb', line 707

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



666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
# File 'lib/sup/modes/thread_index_mode.rb', line 666

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



656
657
658
659
660
661
662
663
# File 'lib/sup/modes/thread_index_mode.rb', line 656

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



711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
# File 'lib/sup/modes/thread_index_mode.rb', line 711

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



598
599
600
601
602
603
604
605
606
607
608
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
# File 'lib/sup/modes/thread_index_mode.rb', line 598

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



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

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



498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
# File 'lib/sup/modes/thread_index_mode.rb', line 498

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



755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
# File 'lib/sup/modes/thread_index_mode.rb', line 755

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



397
398
399
400
401
402
403
# File 'lib/sup/modes/thread_index_mode.rb', line 397

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



478
479
480
481
482
483
484
# File 'lib/sup/modes/thread_index_mode.rb', line 478

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



413
414
415
416
417
# File 'lib/sup/modes/thread_index_mode.rb', line 413

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.



463
464
465
466
467
468
469
470
# File 'lib/sup/modes/thread_index_mode.rb', line 463

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



310
311
312
313
314
315
316
# File 'lib/sup/modes/thread_index_mode.rb', line 310

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



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

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

#read_and_archiveObject



736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
# File 'lib/sup/modes/thread_index_mode.rb', line 736

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



637
638
639
640
641
642
643
644
# File 'lib/sup/modes/thread_index_mode.rb', line 637

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



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

def reply_all; reply :all; end

#resize(rows, cols) ⇒ Object



777
778
779
780
# File 'lib/sup/modes/thread_index_mode.rb', line 777

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



695
696
697
698
699
700
701
# File 'lib/sup/modes/thread_index_mode.rb', line 695

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

#tag_matchingObject



556
557
558
559
560
561
562
563
564
565
566
567
# File 'lib/sup/modes/thread_index_mode.rb', line 556

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



388
389
390
391
392
393
394
395
# File 'lib/sup/modes/thread_index_mode.rb', line 388

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



472
473
474
475
# File 'lib/sup/modes/thread_index_mode.rb', line 472

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

#toggle_newObject



405
406
407
408
409
410
411
# File 'lib/sup/modes/thread_index_mode.rb', line 405

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



451
452
453
454
# File 'lib/sup/modes/thread_index_mode.rb', line 451

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

#toggle_starredObject



301
302
303
304
305
306
307
308
# File 'lib/sup/modes/thread_index_mode.rb', line 301

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



544
545
546
547
548
549
# File 'lib/sup/modes/thread_index_mode.rb', line 544

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



551
552
553
554
# File 'lib/sup/modes/thread_index_mode.rb', line 551

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

#undoObject



250
251
252
# File 'lib/sup/modes/thread_index_mode.rb', line 250

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



254
255
256
257
258
259
260
261
262
263
264
265
266
267
# File 'lib/sup/modes/thread_index_mode.rb', line 254

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