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.



59
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
# File 'lib/sup/modes/thread_index_mode.rb', line 59

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



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

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

#actually_toggle_archived(t) ⇒ Object

returns an undo lambda



298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
# File 'lib/sup/modes/thread_index_mode.rb', line 298

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



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

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



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

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



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

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



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

def apply_to_tagged; @tags.apply_to_tagged; end

#cancel_searchObject



682
683
684
# File 'lib/sup/modes/thread_index_mode.rb', line 682

def cancel_search
  @interrupt_search = true
end

#cleanupObject



509
510
511
512
513
514
515
516
517
518
519
520
521
# File 'lib/sup/modes/thread_index_mode.rb', line 509

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)


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

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

#edit_labelsObject



550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
# File 'lib/sup/modes/thread_index_mode.rb', line 550

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



248
249
250
251
252
253
254
255
256
257
# File 'lib/sup/modes/thread_index_mode.rb', line 248

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



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

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

#forwardObject



627
628
629
630
631
632
633
# File 'lib/sup/modes/thread_index_mode.rb', line 627

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



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

def handle_added_update sender, m
  add_or_unhide m
  BufferManager.draw_screen
end

#handle_deleted_update(sender, m) ⇒ Object



211
212
213
214
215
216
# File 'lib/sup/modes/thread_index_mode.rb', line 211

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



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

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_simple_update(sender, m) ⇒ Object



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

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



203
204
205
206
207
208
209
# File 'lib/sup/modes/thread_index_mode.rb', line 203

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



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

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



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

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



225
226
227
# File 'lib/sup/modes/thread_index_mode.rb', line 225

def handle_undeleted_update sender, m
  add_or_unhide m
end

#is_relevant?(m) ⇒ Boolean

overwrite me!

Returns:

  • (Boolean)


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

def is_relevant? m; false; end

#join_threadsObject



403
404
405
406
407
# File 'lib/sup/modes/thread_index_mode.rb', line 403

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



416
417
418
419
420
421
422
423
424
425
426
427
428
# File 'lib/sup/modes/thread_index_mode.rb', line 416

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



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

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

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



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

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.



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

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

#launch_prev_thread_before(thread, &b) ⇒ Object



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

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

#linesObject



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

def lines; @text.length; end

#load_all_threadsObject



686
687
688
# File 'lib/sup/modes/thread_index_mode.rb', line 686

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



645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
# File 'lib/sup/modes/thread_index_mode.rb', line 645

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



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

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



690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
# File 'lib/sup/modes/thread_index_mode.rb', line 690

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



577
578
579
580
581
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
608
609
610
611
612
613
614
# File 'lib/sup/modes/thread_index_mode.rb', line 577

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



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

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



477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
# File 'lib/sup/modes/thread_index_mode.rb', line 477

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_select(threads) ⇒ Object



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

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

#multi_toggle_archived(threads) ⇒ Object



376
377
378
379
380
381
382
# File 'lib/sup/modes/thread_index_mode.rb', line 376

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



457
458
459
460
461
462
463
# File 'lib/sup/modes/thread_index_mode.rb', line 457

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



392
393
394
395
396
# File 'lib/sup/modes/thread_index_mode.rb', line 392

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.



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

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



289
290
291
292
293
294
295
# File 'lib/sup/modes/thread_index_mode.rb', line 289

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



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

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

#reloadObject



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

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

#reply(type_arg = nil) ⇒ Object



616
617
618
619
620
621
622
623
# File 'lib/sup/modes/thread_index_mode.rb', line 616

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



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

def reply_all; reply :all; end

#resize(rows, cols) ⇒ Object



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

def resize rows, cols
  regen_text
  super
end

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

open up a thread view window



106
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
# File 'lib/sup/modes/thread_index_mode.rb', line 106

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



674
675
676
677
678
679
680
# File 'lib/sup/modes/thread_index_mode.rb', line 674

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

#tag_matchingObject



535
536
537
538
539
540
541
542
543
544
545
546
# File 'lib/sup/modes/thread_index_mode.rb', line 535

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



367
368
369
370
371
372
373
374
# File 'lib/sup/modes/thread_index_mode.rb', line 367

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



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

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

#toggle_newObject



384
385
386
387
388
389
390
# File 'lib/sup/modes/thread_index_mode.rb', line 384

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



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

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

#toggle_starredObject



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

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



523
524
525
526
527
528
# File 'lib/sup/modes/thread_index_mode.rb', line 523

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



530
531
532
533
# File 'lib/sup/modes/thread_index_mode.rb', line 530

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

#undoObject



229
230
231
# File 'lib/sup/modes/thread_index_mode.rb', line 229

def undo
  UndoManager.undo
end

#unsaved?Boolean

Returns:

  • (Boolean)


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

def unsaved?; dirty? end

#updateObject



233
234
235
236
237
238
239
240
241
242
243
244
245
246
# File 'lib/sup/modes/thread_index_mode.rb', line 233

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