@source.each do |thisoffset, theselabels|
m = Message.new :source => @source, :source_info => thisoffset, :labels => theselabels
Index.sync_message m
- UpdateManager.relay self, :add, m
+ UpdateManager.relay self, :added, m
my_message = m if thisoffset == offset
end
my_message
end
- def discard mid
- docid, entry = Index.load_entry_for_id mid
- raise ArgumentError, "can't find entry for draft: #{mid.inspect}" unless entry
- raise ArgumentError, "not a draft: source id #{entry[:source_id].inspect}, should be #{DraftManager.source_id.inspect} for #{mid.inspect} / docno #{docid}" unless entry[:source_id].to_i == DraftManager.source_id
+ def discard m
+ docid, entry = Index.load_entry_for_id m.id
+ raise ArgumentError, "can't find entry for draft: #{m.id.inspect}" unless entry
+ raise ArgumentError, "not a draft: source id #{entry[:source_id].inspect}, should be #{DraftManager.source_id.inspect} for #{m.id.inspect} / docno #{docid}" unless entry[:source_id].to_i == DraftManager.source_id
Index.drop_entry docid
File.delete @source.fn_for_offset(entry[:source_info])
- UpdateManager.relay self, :delete, mid
+ UpdateManager.relay self, :deleted, m
end
end
regen_text
end
- def handle_archived_update sender, t
- if contains_thread? t
- hide_thread t
- regen_text
- end
+ def handle_unarchived_update sender, m
+ add_or_unhide m
+ end
+
+ def handle_archived_update sender, m
+ t = thread_containing(m) or return
+ hide_thread t
+ regen_text
end
def status
super [], opts
end
- def is_relevant? m; @labels.all? { |l| m.has_label? l }; end
+ def is_relevant? m; @labels.all? { |l| m.has_label? l } end
def self.spawn_nicely label
label = LabelManager.label_for(label) unless label.is_a?(Symbol)
class ResumeMode < EditMessageMode
def initialize m
- @id = m.id
+ @m = m
@safe = false
header, body = parse_file m.draft_filename
case BufferManager.ask_yes_or_no "Discard draft?"
when true
- DraftManager.discard @id
+ DraftManager.discard @m
BufferManager.flash "Draft discarded."
true
when false
if edited?
DraftManager.write_draft { |f| write_message f, false }
- DraftManager.discard @id
+ DraftManager.discard @m
BufferManager.flash "Draft saved."
end
true
def send_message
if super
- DraftManager.discard @id
+ DraftManager.discard @m
@safe = true
end
end
def save_as_draft
@safe = true
- DraftManager.discard @id if super
+ DraftManager.discard @m if super
end
end
## are set, and the second to show the cursor having moved
update_text_for_line curpos
- UpdateManager.relay self, :read, t
+ UpdateManager.relay self, :read, t.first
end
end
threads.each { |t| select t }
end
- def handle_label_update sender, m
- t = @ts_mutex.synchronize { @ts.thread_for(m) } or return
- handle_label_thread_update sender, t
+ 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
+
+ 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
- def handle_label_thread_update sender, t
+ def handle_read_update sender, m
+ t = thread_containing(m) or return
l = @lines[t] or return
update_text_for_line l
- BufferManager.draw_screen
- end
-
- def handle_read_update sender, t
- l = @lines[t] or return
- update_text_for_line l
- BufferManager.draw_screen
end
def handle_archived_update *a; handle_read_update(*a); end
- def handle_deleted_update sender, t
- handle_read_update sender, t
+ def handle_deleted_update sender, m
+ t = thread_containing(m) or return
hide_thread t
regen_text
end
## overwrite me!
def is_relevant? m; false; end
- 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
+ def handle_added_update sender, m
+ add_or_unhide m
BufferManager.draw_screen
end
- def handle_delete_update sender, mid
+ def handle_deleted_update sender, m
@ts_mutex.synchronize do
- return unless @ts.contains_id? mid
- @ts.remove mid
+ return unless @ts.contains? m
+ @ts.remove_id m.id
end
update
- BufferManager.draw_screen
+ end
+
+ def handle_undeleted_update sender, m
+ add_or_unhide m
end
def update
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
+ UpdateManager.relay self, :unstarred, t.first
else
t.first.add_label :starred # add only to first
- UpdateManager.relay self, :starred, t
+ UpdateManager.relay self, :starred, t.first
end
end
def actually_toggle_archived t
if t.has_label? :inbox
t.remove_label :inbox
- UpdateManager.relay self, :archived, t
+ UpdateManager.relay self, :archived, t.first
else
t.apply_label :inbox
- UpdateManager.relay self, :unarchived, t
+ UpdateManager.relay self, :unarchived, t.first
end
end
def actually_toggle_spammed t
if t.has_label? :spam
t.remove_label :spam
- UpdateManager.relay self, :unspammed, t
+ UpdateManager.relay self, :unspammed, t.first
else
t.apply_label :spam
- UpdateManager.relay self, :spammed, t
+ UpdateManager.relay self, :spammed, t.first
end
end
def actually_toggle_deleted t
if t.has_label? :deleted
t.remove_label :deleted
- UpdateManager.relay self, :undeleted, t
+ UpdateManager.relay self, :undeleted, t.first
else
t.apply_label :deleted
- UpdateManager.relay self, :deleted, t
+ UpdateManager.relay self, :deleted, t.first
end
end
return unless user_labels
thread.labels = keepl + user_labels
user_labels.each { |l| LabelManager << l }
- update_text_for_line curpos
+ UpdateManager.relay self, :labeled, thread.first
end
def multi_edit_labels threads
protected
+ def add_or_unhide m
+ if @hidden_threads[m]
+ @hidden_threads.delete m
+ ## now it will re-appear when #update is called
+ else
+ Redwood::log "#{self}: adding: #{m}"
+ @ts_mutex.synchronize do
+ return unless is_relevant?(m) || @ts.is_relevant?(m)
+ @ts.load_thread_for_message m
+ end
+ end
+
+ update
+ end
+
+ def thread_containing m; @ts_mutex.synchronize { @ts.thread_for m } end
+
## used to tag threads by query. this can be made a lot more sophisticated,
## but for right now we'll do the obvious this.
def thread_match? t, query
@thread.labels = (reserved_labels + new_labels).uniq
new_labels.each { |l| LabelManager << l }
update
- UpdateManager.relay self, :label_thread, @thread
+ UpdateManager.relay self, :labeled, @thread.first
end
def toggle_starred
## TODO: don't recalculate EVERYTHING just to add a stupid little
## star to the display
update
- UpdateManager.relay self, :label, m
+ UpdateManager.relay self, :single_message_labeled, m
end
## called when someone presses enter when the cursor is highlighting
def archive_and_kill
@thread.remove_label :inbox
- UpdateManager.relay self, :archived, @thread
+ UpdateManager.relay self, :archived, @thread.first
BufferManager.kill_buffer_safely buffer
end
def delete_and_kill
@thread.apply_label :deleted
- UpdateManager.relay self, :deleted, @thread
+ UpdateManager.relay self, :deleted, @thread.first
BufferManager.kill_buffer_safely buffer
end
HookManager.run "before-add-message", :message => m
m = yield(m, offset, entry) or next
Index.sync_message m, docid, entry
- UpdateManager.relay self, :add, m unless entry
+ UpdateManager.relay self, :added, m unless entry
rescue MessageFormatError => e
Redwood::log "ignoring erroneous message at #{source}##{offset}: #{e.message}"
end
@source.each do |offset, labels|
m = Message.new :source => @source, :source_info => offset, :labels => @source.labels
Index.sync_message m
- UpdateManager.relay self, :add, m
+ UpdateManager.relay self, :added, m
end
end
end
## zero or more Threads. A Thread represents all the message related
## to a particular subject. Each Thread has one or more Containers. A
## Container is a recursive structure that holds the message tree as
-## determined by the references: and in-reply-to: headers. EAch
+## determined by the references: and in-reply-to: headers. Each
## Container holds zero or one messages. In the case of zero messages,
## it means we've seen a reference to the message but haven't (yet)
## seen the message itself.
@thread_by_subj = thread_by_subj
end
- def contains_id? id; @messages.member?(id) && !@messages[id].empty?; end
- def thread_for m
- (c = @messages[m.id]) && c.root.thread
- end
+ def thread_for_id mid; (c = @messages[mid]) && c.root.thread end
+ def contains_id? id; @messages.member?(id) && !@messages[id].empty? end
+ def thread_for m; thread_for_id m.id end
+ def contains? m; contains_id? m.id end
def delete_cruft
@threads.each { |k, v| @threads.delete(k) if v.empty? }
end
private :link
- def remove mid
+ def remove_id mid
return unless(c = @messages[mid])
c.parent.children.delete c if c.parent
module Redwood
+## Classic listener/sender paradigm. Handles communication between various
+## parts of Sup.
+##
+## Usage note: don't pass threads around. Neither thread nor message equality
+## is defined beyond standard object equality. For Thread equality, this is
+## because of computational cost. But message equality is trivial by comparing
+## message ids, so to communicate something about a particular thread, just
+## pass a representative message from it instead.
+##
+## This assumes that no message will be a part of more than one thread within
+## a single "view" (otherwise a message from a thread wouldn't uniquely
+## identify it). But that's true.
+
class UpdateManager
include Singleton