sup 0.4 → 0.5
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of sup might be problematic. Click here for more details.
- data/History.txt +8 -0
- data/Manifest.txt +1 -0
- data/Rakefile +11 -3
- data/ReleaseNotes +6 -0
- data/bin/sup +21 -6
- data/bin/sup-sync +3 -2
- data/bin/sup-sync-back +5 -1
- data/doc/FAQ.txt +1 -1
- data/lib/sup.rb +4 -3
- data/lib/sup/buffer.rb +14 -3
- data/lib/sup/contact.rb +1 -1
- data/lib/sup/crypto.rb +1 -1
- data/lib/sup/hook.rb +1 -1
- data/lib/sup/imap.rb +1 -1
- data/lib/sup/index.rb +62 -33
- data/lib/sup/maildir.rb +13 -11
- data/lib/sup/mbox.rb +20 -19
- data/lib/sup/mbox/loader.rb +3 -2
- data/lib/sup/mbox/ssh-file.rb +1 -0
- data/lib/sup/message-chunks.rb +4 -2
- data/lib/sup/message.rb +16 -8
- data/lib/sup/modes/compose-mode.rb +1 -1
- data/lib/sup/modes/contact-list-mode.rb +1 -1
- data/lib/sup/modes/edit-message-mode.rb +7 -3
- data/lib/sup/modes/inbox-mode.rb +1 -1
- data/lib/sup/modes/label-search-results-mode.rb +2 -2
- data/lib/sup/modes/reply-mode.rb +4 -0
- data/lib/sup/modes/scroll-mode.rb +22 -7
- data/lib/sup/modes/search-results-mode.rb +3 -3
- data/lib/sup/modes/thread-index-mode.rb +66 -22
- data/lib/sup/modes/thread-view-mode.rb +36 -16
- data/lib/sup/person.rb +6 -2
- data/lib/sup/poll.rb +3 -3
- data/lib/sup/sent.rb +3 -4
- data/lib/sup/tagger.rb +4 -2
- data/lib/sup/textfield.rb +1 -1
- data/lib/sup/thread.rb +4 -10
- data/test/test_maildir.rb +25 -0
- metadata +4 -2
data/lib/sup/maildir.rb
CHANGED
@@ -75,9 +75,11 @@ class Maildir < Source
|
|
75
75
|
with_file_for(id) { |f| f.readlines.join }
|
76
76
|
end
|
77
77
|
|
78
|
-
def scan_mailbox
|
78
|
+
def scan_mailbox opts={}
|
79
|
+
return unless @ids.empty? || opts[:rescan]
|
79
80
|
return if @last_scan && (Time.now - @last_scan) < SCAN_INTERVAL
|
80
81
|
|
82
|
+
Redwood::log "scanning maildir..."
|
81
83
|
cdir = File.join(@dir, 'cur')
|
82
84
|
ndir = File.join(@dir, 'new')
|
83
85
|
|
@@ -85,21 +87,21 @@ class Maildir < Source
|
|
85
87
|
raise FatalSourceError, "#{ndir} not a directory" unless File.directory? ndir
|
86
88
|
|
87
89
|
begin
|
88
|
-
@ids, @ids_to_fns =
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
ids_to_fns[id] = fn
|
94
|
-
end
|
95
|
-
[ids.sort, ids_to_fns]
|
90
|
+
@ids, @ids_to_fns = [], {}
|
91
|
+
(Dir[File.join(cdir, "*")] + Dir[File.join(ndir, "*")]).map do |fn|
|
92
|
+
id = make_id fn
|
93
|
+
@ids << id
|
94
|
+
@ids_to_fns[id] = fn
|
96
95
|
end
|
96
|
+
@ids.sort!
|
97
97
|
rescue SystemCallError, IOError => e
|
98
98
|
raise FatalSourceError, "Problem scanning Maildir directories: #{e.message}."
|
99
99
|
end
|
100
100
|
|
101
|
+
Redwood::log "done scanning maildir"
|
101
102
|
@last_scan = Time.now
|
102
103
|
end
|
104
|
+
synchronized :scan_mailbox
|
103
105
|
|
104
106
|
def each
|
105
107
|
scan_mailbox
|
@@ -120,8 +122,8 @@ class Maildir < Source
|
|
120
122
|
end
|
121
123
|
|
122
124
|
def end_offset
|
123
|
-
scan_mailbox
|
124
|
-
@ids.last
|
125
|
+
scan_mailbox :rescan => true
|
126
|
+
@ids.last + 1
|
125
127
|
end
|
126
128
|
|
127
129
|
def pct_done; 100.0 * (@ids.index(cur_offset) || 0).to_f / (@ids.length - 1).to_f; end
|
data/lib/sup/mbox.rb
CHANGED
@@ -20,29 +20,29 @@ module MBox
|
|
20
20
|
## when scanning over large mbox files.
|
21
21
|
while(line = f.gets)
|
22
22
|
case line
|
23
|
-
when /^(From):\s
|
24
|
-
/^(To):\s
|
25
|
-
/^(Cc):\s
|
26
|
-
/^(Bcc):\s
|
27
|
-
/^(Subject):\s
|
28
|
-
/^(Date):\s
|
29
|
-
/^(References):\s
|
30
|
-
/^(In-Reply-To):\s
|
31
|
-
/^(Reply-To):\s
|
32
|
-
/^(List-Post):\s
|
33
|
-
/^(List-Subscribe):\s
|
34
|
-
/^(List-Unsubscribe):\s
|
35
|
-
/^(Status):\s
|
36
|
-
when /^(Message-Id):\s
|
23
|
+
when /^(From):\s*(.*?)\s*$/i,
|
24
|
+
/^(To):\s*(.*?)\s*$/i,
|
25
|
+
/^(Cc):\s*(.*?)\s*$/i,
|
26
|
+
/^(Bcc):\s*(.*?)\s*$/i,
|
27
|
+
/^(Subject):\s*(.*?)\s*$/i,
|
28
|
+
/^(Date):\s*(.*?)\s*$/i,
|
29
|
+
/^(References):\s*(.*?)\s*$/i,
|
30
|
+
/^(In-Reply-To):\s*(.*?)\s*$/i,
|
31
|
+
/^(Reply-To):\s*(.*?)\s*$/i,
|
32
|
+
/^(List-Post):\s*(.*?)\s*$/i,
|
33
|
+
/^(List-Subscribe):\s*(.*?)\s*$/i,
|
34
|
+
/^(List-Unsubscribe):\s*(.*?)\s*$/i,
|
35
|
+
/^(Status):\s*(.*?)\s*$/i: header[last = $1] = $2
|
36
|
+
when /^(Message-Id):\s*(.*?)\s*$/i: header[mid_field = last = $1] = $2
|
37
37
|
|
38
38
|
## these next three can occur multiple times, and we want the
|
39
39
|
## first one
|
40
|
-
when /^(Delivered-To):\s
|
41
|
-
/^(X-Original-To):\s
|
42
|
-
/^(Envelope-To):\s
|
40
|
+
when /^(Delivered-To):\s*(.*)$/i,
|
41
|
+
/^(X-Original-To):\s*(.*)$/i,
|
42
|
+
/^(Envelope-To):\s*(.*)$/i: header[last = $1] ||= $2
|
43
43
|
|
44
|
-
when
|
45
|
-
when /^\S
|
44
|
+
when /^\r*$/: break
|
45
|
+
when /^\S+:/: last = nil # some other header we don't care about
|
46
46
|
else
|
47
47
|
header[last] += " " + line.chomp.gsub(/^\s+/, "") if last
|
48
48
|
end
|
@@ -65,6 +65,7 @@ module MBox
|
|
65
65
|
header
|
66
66
|
end
|
67
67
|
|
68
|
+
## never actually called
|
68
69
|
def read_body f
|
69
70
|
body = []
|
70
71
|
f.each_line do |l|
|
data/lib/sup/mbox/loader.rb
CHANGED
@@ -6,6 +6,7 @@ module MBox
|
|
6
6
|
|
7
7
|
class Loader < Source
|
8
8
|
yaml_properties :uri, :cur_offset, :usual, :archived, :id, :labels
|
9
|
+
attr_accessor :labels
|
9
10
|
|
10
11
|
## uri_or_fp is horrific. need to refactor.
|
11
12
|
def initialize uri_or_fp, start_offset=nil, usual=true, archived=false, id=nil, labels=[]
|
@@ -84,7 +85,7 @@ class Loader < Source
|
|
84
85
|
ret = ""
|
85
86
|
@mutex.synchronize do
|
86
87
|
@f.seek offset
|
87
|
-
until @f.eof? || (l = @f.gets) =~
|
88
|
+
until @f.eof? || (l = @f.gets) =~ /^\r*$/
|
88
89
|
ret += l
|
89
90
|
end
|
90
91
|
end
|
@@ -147,7 +148,7 @@ class Loader < Source
|
|
147
148
|
end
|
148
149
|
|
149
150
|
self.cur_offset = next_offset
|
150
|
-
[returned_offset, (
|
151
|
+
[returned_offset, (self.labels + [:unread]).uniq]
|
151
152
|
end
|
152
153
|
end
|
153
154
|
|
data/lib/sup/mbox/ssh-file.rb
CHANGED
data/lib/sup/message-chunks.rb
CHANGED
@@ -116,7 +116,9 @@ EOS
|
|
116
116
|
def initial_state; :open end
|
117
117
|
def viewable?; @lines.nil? end
|
118
118
|
def view_default! path
|
119
|
-
|
119
|
+
cmd = "/usr/bin/run-mailcap --action=view '#{@content_type}:#{path}' > /dev/null 2> /dev/null"
|
120
|
+
Redwood::log "running: #{cmd.inspect}"
|
121
|
+
system cmd
|
120
122
|
$? == 0
|
121
123
|
end
|
122
124
|
|
@@ -124,7 +126,7 @@ EOS
|
|
124
126
|
path = write_to_disk
|
125
127
|
ret = HookManager.run "mime-view", :content_type => @content_type,
|
126
128
|
:filename => path
|
127
|
-
view_default!
|
129
|
+
ret || view_default!(path)
|
128
130
|
end
|
129
131
|
|
130
132
|
def write_to_disk
|
data/lib/sup/message.rb
CHANGED
@@ -95,7 +95,8 @@ class Message
|
|
95
95
|
begin
|
96
96
|
Time.parse date
|
97
97
|
rescue ArgumentError => e
|
98
|
-
|
98
|
+
Redwood::log "faking date header for #{@id} due to error parsing date #{header['date'].inspect}: #{e.message}"
|
99
|
+
Time.now
|
99
100
|
end
|
100
101
|
else
|
101
102
|
Redwood::log "faking date header for #{@id}"
|
@@ -135,6 +136,10 @@ class Message
|
|
135
136
|
@dirty = true
|
136
137
|
end
|
137
138
|
|
139
|
+
def remove_ref ref
|
140
|
+
@dirty = true if @refs.delete ref
|
141
|
+
end
|
142
|
+
|
138
143
|
def snippet; @snippet || (chunks && @snippet); end
|
139
144
|
def is_list_message?; !@list_address.nil?; end
|
140
145
|
def is_draft?; @source.is_a? DraftLoader; end
|
@@ -143,11 +148,13 @@ class Message
|
|
143
148
|
@source.fn_for_offset @source_info
|
144
149
|
end
|
145
150
|
|
146
|
-
def sanitize_message_id mid; mid.gsub(/\s
|
151
|
+
def sanitize_message_id mid; mid.gsub(/\s+/, "")[0..254] end
|
147
152
|
|
148
153
|
def save index
|
149
|
-
|
154
|
+
return unless @dirty
|
155
|
+
index.sync_message self
|
150
156
|
@dirty = false
|
157
|
+
true
|
151
158
|
end
|
152
159
|
|
153
160
|
def has_label? t; @labels.member? t; end
|
@@ -248,13 +255,14 @@ EOS
|
|
248
255
|
with_source_errors_handled { @source.each_raw_message_line(@source_info, &b) }
|
249
256
|
end
|
250
257
|
|
251
|
-
|
258
|
+
## returns all the content from a message that will be indexed
|
259
|
+
def indexable_content
|
252
260
|
load_from_source!
|
253
261
|
[
|
254
|
-
from &&
|
255
|
-
to.map { |p|
|
256
|
-
cc.map { |p|
|
257
|
-
bcc.map { |p|
|
262
|
+
from && from.indexable_content,
|
263
|
+
to.map { |p| p.indexable_content },
|
264
|
+
cc.map { |p| p.indexable_content },
|
265
|
+
bcc.map { |p| p.indexable_content },
|
258
266
|
chunks.select { |c| c.is_a? Chunk::Text }.map { |c| c.lines },
|
259
267
|
Message.normalize_subj(subj),
|
260
268
|
].flatten.compact.join " "
|
@@ -20,7 +20,7 @@ class ComposeMode < EditMessageMode
|
|
20
20
|
end
|
21
21
|
|
22
22
|
def self.spawn_nicely opts={}
|
23
|
-
to = opts[:to] || BufferManager.ask_for_contacts(:people, "To: ") or return
|
23
|
+
to = opts[:to] || BufferManager.ask_for_contacts(:people, "To: ", [opts[:to_default]]) or return
|
24
24
|
cc = opts[:cc] || (BufferManager.ask_for_contacts(:people, "Cc: ") or return if $config[:ask_for_cc])
|
25
25
|
bcc = opts[:bcc] || (BufferManager.ask_for_contacts(:people, "Bcc: ") or return if $config[:ask_for_bcc])
|
26
26
|
subj = opts[:subj] || (BufferManager.ask(:subject, "Subject: ") or return if $config[:ask_for_subject])
|
@@ -148,9 +148,13 @@ EOS
|
|
148
148
|
def attach_file
|
149
149
|
fn = BufferManager.ask_for_filename :attachment, "File name (enter for browser): "
|
150
150
|
return unless fn
|
151
|
-
|
152
|
-
|
153
|
-
|
151
|
+
begin
|
152
|
+
@attachments << RMail::Message.make_file_attachment(fn)
|
153
|
+
@attachment_names << fn
|
154
|
+
update
|
155
|
+
rescue SystemCallError => e
|
156
|
+
BufferManager.flash "Can't read #{fn}: #{e.message}"
|
157
|
+
end
|
154
158
|
end
|
155
159
|
|
156
160
|
def delete_attachment
|
data/lib/sup/modes/inbox-mode.rb
CHANGED
@@ -9,7 +9,7 @@ class InboxMode < ThreadIndexMode
|
|
9
9
|
end
|
10
10
|
|
11
11
|
def initialize
|
12
|
-
super [:inbox, :sent], { :label => :inbox, :skip_killed => true }
|
12
|
+
super [:inbox, :sent, :draft], { :label => :inbox, :skip_killed => true }
|
13
13
|
raise "can't have more than one!" if defined? @@instance
|
14
14
|
@@instance = self
|
15
15
|
end
|
@@ -10,12 +10,12 @@ class LabelSearchResultsMode < ThreadIndexMode
|
|
10
10
|
end
|
11
11
|
|
12
12
|
register_keymap do |k|
|
13
|
-
k.add :refine_search, "Refine search", '
|
13
|
+
k.add :refine_search, "Refine search", '|'
|
14
14
|
end
|
15
15
|
|
16
16
|
def refine_search
|
17
17
|
label_query = @labels.size > 1 ? "(#{@labels.join('||')})" : @labels.first
|
18
|
-
query = BufferManager.ask :search, "query: ", "+label:#{label_query} "
|
18
|
+
query = BufferManager.ask :search, "refine query: ", "+label:#{label_query} "
|
19
19
|
return unless query && query !~ /^\s*$/
|
20
20
|
SearchResultsMode.spawn_from_query query
|
21
21
|
end
|
data/lib/sup/modes/reply-mode.rb
CHANGED
@@ -101,6 +101,10 @@ EOS
|
|
101
101
|
:recipient
|
102
102
|
end)
|
103
103
|
|
104
|
+
@headers.each do |k, v|
|
105
|
+
HookManager.run "before-edit", :header => v, :body => body
|
106
|
+
end
|
107
|
+
|
104
108
|
super :header => @headers[@type_selector.val], :body => body, :twiddles => false
|
105
109
|
add_selector @type_selector
|
106
110
|
end
|
@@ -12,7 +12,7 @@ class ScrollMode < Mode
|
|
12
12
|
|
13
13
|
attr_reader :status, :topline, :botline, :leftcol
|
14
14
|
|
15
|
-
COL_JUMP =
|
15
|
+
COL_JUMP = 2
|
16
16
|
|
17
17
|
register_keymap do |k|
|
18
18
|
k.add :line_down, "Down one line", :down, 'j', 'J'
|
@@ -64,14 +64,14 @@ class ScrollMode < Mode
|
|
64
64
|
end
|
65
65
|
|
66
66
|
start = @search_line || search_start_line
|
67
|
-
line = find_text @search_query, start
|
67
|
+
line, col = find_text @search_query, start
|
68
68
|
if line.nil? && (start > 0)
|
69
|
-
line = find_text @search_query, 0
|
69
|
+
line, col = find_text @search_query, 0
|
70
70
|
BufferManager.flash "Search wrapped to top!" if line
|
71
71
|
end
|
72
72
|
if line
|
73
73
|
@search_line = line + 1
|
74
|
-
|
74
|
+
search_goto_pos line, col, col + @search_query.length
|
75
75
|
buffer.mark_dirty
|
76
76
|
else
|
77
77
|
BufferManager.flash "Not found!"
|
@@ -86,7 +86,13 @@ class ScrollMode < Mode
|
|
86
86
|
end
|
87
87
|
|
88
88
|
## subclasses can override these two!
|
89
|
-
def
|
89
|
+
def search_goto_pos line, leftcol, rightcol
|
90
|
+
jump_to_line line
|
91
|
+
|
92
|
+
if rightcol > self.rightcol # if it's occluded...
|
93
|
+
jump_to_col [rightcol - buffer.content_width + 1, 0].max # move right
|
94
|
+
end
|
95
|
+
end
|
90
96
|
def search_start_line; @topline end
|
91
97
|
|
92
98
|
def col_left
|
@@ -144,9 +150,18 @@ protected
|
|
144
150
|
(start_line ... lines).each do |i|
|
145
151
|
case(s = self[i])
|
146
152
|
when String
|
147
|
-
|
153
|
+
match = s =~ regex
|
154
|
+
return [i, match] if match
|
148
155
|
when Array
|
149
|
-
|
156
|
+
offset = 0
|
157
|
+
s.each do |color, string|
|
158
|
+
match = string =~ regex
|
159
|
+
if match
|
160
|
+
return [i, offset + match]
|
161
|
+
else
|
162
|
+
offset += string.length
|
163
|
+
end
|
164
|
+
end
|
150
165
|
end
|
151
166
|
end
|
152
167
|
nil
|
@@ -9,13 +9,13 @@ class SearchResultsMode < ThreadIndexMode
|
|
9
9
|
end
|
10
10
|
|
11
11
|
register_keymap do |k|
|
12
|
-
k.add :refine_search, "Refine search", '
|
12
|
+
k.add :refine_search, "Refine search", '|'
|
13
13
|
end
|
14
14
|
|
15
15
|
def refine_search
|
16
|
-
query = BufferManager.ask :search, "query: ", (@qobj.to_s + " ")
|
16
|
+
query = BufferManager.ask :search, "refine query: ", (@qobj.to_s + " ")
|
17
17
|
return unless query && query !~ /^\s*$/
|
18
|
-
SearchResultsMode.spawn_from_query query
|
18
|
+
SearchResultsMode.spawn_from_query query
|
19
19
|
end
|
20
20
|
|
21
21
|
## a proper is_relevant? method requires some way of asking ferret
|
@@ -16,6 +16,10 @@ EOS
|
|
16
16
|
|
17
17
|
register_keymap do |k|
|
18
18
|
k.add :load_threads, "Load #{LOAD_MORE_THREAD_NUM} more threads", 'M'
|
19
|
+
k.add_multi "Load all threads (! to confirm) :", '!' do |kk|
|
20
|
+
kk.add :load_all_threads, "Load all threads (may list a _lot_ of threads)", '!'
|
21
|
+
end
|
22
|
+
k.add :cancel_search, "Cancel current search", :ctrl_g
|
19
23
|
k.add :reload, "Refresh view", '@'
|
20
24
|
k.add :toggle_archived, "Toggle archived status", 'a'
|
21
25
|
k.add :toggle_starred, "Star or unstar all messages in thread", '*'
|
@@ -50,6 +54,8 @@ EOS
|
|
50
54
|
@load_thread_opts = load_thread_opts
|
51
55
|
@hidden_labels = hidden_labels + LabelManager::HIDDEN_RESERVED_LABELS
|
52
56
|
@date_width = DATE_WIDTH
|
57
|
+
|
58
|
+
@interrupt_search = false
|
53
59
|
|
54
60
|
initialize_threads # defines @ts and @ts_mutex
|
55
61
|
update # defines @text and @lines
|
@@ -107,19 +113,32 @@ EOS
|
|
107
113
|
threads.each { |t| select t }
|
108
114
|
end
|
109
115
|
|
110
|
-
##
|
111
|
-
## the next thread without going to
|
112
|
-
## as a convenience.
|
116
|
+
## these two methods are called by thread-view-modes when the user
|
117
|
+
## wants to view the previous/next thread without going back to
|
118
|
+
## index-mode. we update the cursor as a convenience.
|
113
119
|
def launch_next_thread_after thread, &b
|
120
|
+
launch_another_thread thread, 1, &b
|
121
|
+
end
|
122
|
+
|
123
|
+
def launch_prev_thread_before thread, &b
|
124
|
+
launch_another_thread thread, -1, &b
|
125
|
+
end
|
126
|
+
|
127
|
+
def launch_another_thread thread, direction, &b
|
114
128
|
l = @lines[thread] or return
|
129
|
+
target_l = l + direction
|
115
130
|
t = @mutex.synchronize do
|
116
|
-
if
|
117
|
-
|
118
|
-
@threads[l + 1]
|
131
|
+
if target_l >= 0 && target_l < @threads.length
|
132
|
+
@threads[target_l]
|
119
133
|
end
|
120
|
-
end
|
134
|
+
end
|
121
135
|
|
122
|
-
|
136
|
+
if t # there's a next thread
|
137
|
+
set_cursor_pos target_l # move out of mutex?
|
138
|
+
select t, b
|
139
|
+
elsif b # no next thread. call the block anyways
|
140
|
+
b.call
|
141
|
+
end
|
123
142
|
end
|
124
143
|
|
125
144
|
def handle_single_message_labeled_update sender, m
|
@@ -166,10 +185,16 @@ EOS
|
|
166
185
|
end
|
167
186
|
|
168
187
|
def handle_deleted_update sender, m
|
169
|
-
@ts_mutex.synchronize
|
170
|
-
|
171
|
-
|
172
|
-
|
188
|
+
t = @ts_mutex.synchronize { @ts.thread_for m }
|
189
|
+
return unless t
|
190
|
+
hide_thread t
|
191
|
+
update
|
192
|
+
end
|
193
|
+
|
194
|
+
def handle_spammed_update sender, m
|
195
|
+
t = @ts_mutex.synchronize { @ts.thread_for m }
|
196
|
+
return unless t
|
197
|
+
hide_thread t
|
173
198
|
update
|
174
199
|
end
|
175
200
|
|
@@ -410,6 +435,7 @@ EOS
|
|
410
435
|
return unless user_labels
|
411
436
|
thread.labels = keepl + user_labels
|
412
437
|
user_labels.each { |l| LabelManager << l }
|
438
|
+
update_text_for_line curpos
|
413
439
|
UpdateManager.relay self, :labeled, thread.first
|
414
440
|
end
|
415
441
|
|
@@ -456,16 +482,22 @@ EOS
|
|
456
482
|
|
457
483
|
## TODO: figure out @ts_mutex in this method
|
458
484
|
def load_n_threads n=LOAD_MORE_THREAD_NUM, opts={}
|
485
|
+
@interrupt_search = false
|
459
486
|
@mbid = BufferManager.say "Searching for threads..."
|
487
|
+
|
488
|
+
ts_to_load = n
|
489
|
+
ts_to_load = ts_to_load + @ts.size unless n == -1 # -1 means all threads
|
490
|
+
|
460
491
|
orig_size = @ts.size
|
461
492
|
last_update = Time.now
|
462
|
-
@ts.load_n_threads(
|
493
|
+
@ts.load_n_threads(ts_to_load, opts) do |i|
|
463
494
|
if (Time.now - last_update) >= 0.25
|
464
495
|
BufferManager.say "Loaded #{i.pluralize 'thread'}...", @mbid
|
465
496
|
update
|
466
497
|
BufferManager.draw_screen
|
467
498
|
last_update = Time.now
|
468
499
|
end
|
500
|
+
break if @interrupt_search
|
469
501
|
end
|
470
502
|
@ts.threads.each { |th| th.labels.each { |l| LabelManager << l } }
|
471
503
|
|
@@ -485,11 +517,24 @@ EOS
|
|
485
517
|
end
|
486
518
|
end
|
487
519
|
|
520
|
+
def cancel_search
|
521
|
+
@interrupt_search = true
|
522
|
+
end
|
523
|
+
|
524
|
+
def load_all_threads
|
525
|
+
load_threads :num => -1
|
526
|
+
end
|
527
|
+
|
488
528
|
def load_threads opts={}
|
489
|
-
|
529
|
+
if opts[:num].nil?
|
530
|
+
n = ThreadIndexMode::LOAD_MORE_THREAD_NUM
|
531
|
+
else
|
532
|
+
n = opts[:num]
|
533
|
+
end
|
490
534
|
|
491
535
|
myopts = @load_thread_opts.merge({ :when_done => (lambda do |num|
|
492
536
|
opts[:when_done].call(num) if opts[:when_done]
|
537
|
+
|
493
538
|
if num > 0
|
494
539
|
BufferManager.flash "Found #{num.pluralize 'thread'}."
|
495
540
|
else
|
@@ -513,14 +558,12 @@ EOS
|
|
513
558
|
protected
|
514
559
|
|
515
560
|
def add_or_unhide m
|
516
|
-
|
517
|
-
@
|
518
|
-
## now it will re-appear when #update is called
|
519
|
-
else
|
520
|
-
@ts_mutex.synchronize do
|
521
|
-
return unless is_relevant?(m) || @ts.is_relevant?(m)
|
561
|
+
@ts_mutex.synchronize do
|
562
|
+
if (is_relevant?(m) || @ts.is_relevant?(m)) && !@ts.contains?(m)
|
522
563
|
@ts.load_thread_for_message m
|
523
564
|
end
|
565
|
+
|
566
|
+
@hidden_threads.delete @ts.thread_for(m)
|
524
567
|
end
|
525
568
|
|
526
569
|
update
|
@@ -612,7 +655,6 @@ protected
|
|
612
655
|
|
613
656
|
date = t.date.to_nice_s
|
614
657
|
|
615
|
-
new = t.has_label?(:unread)
|
616
658
|
starred = t.has_label?(:starred)
|
617
659
|
|
618
660
|
## format the from column
|
@@ -649,7 +691,9 @@ protected
|
|
649
691
|
p = dp || t.participants.any? { |p| AccountManager.is_account? p }
|
650
692
|
|
651
693
|
subj_color =
|
652
|
-
if
|
694
|
+
if t.has_label?(:draft)
|
695
|
+
:index_draft_color
|
696
|
+
elsif t.has_label?(:unread)
|
653
697
|
:index_new_color
|
654
698
|
elsif starred
|
655
699
|
:index_starred_color
|