This allows the thread index view to present oldest messages first
instead of always presenting newer messages at the top of the list.
This can be toggled using the 'o' key.
A config file option :inbox_newest_first controls whether this is
turned on by default when viewing the inbox
Refine searches inherit the order from the originating thread index.
Signed-off-by: Keith Packard <keithp@keithp.com>
- SearchResultsMode.spawn_from_query $opts[:search]
+ SearchResultsMode.spawn_from_query $opts[:search], true
end
until Redwood::exceptions.nonempty? || $die
end
until Redwood::exceptions.nonempty? || $die
when :search
query = BufferManager.ask :search, "search all messages: "
next unless query && query !~ /^\s*$/
when :search
query = BufferManager.ask :search, "search all messages: "
next unless query && query !~ /^\s*$/
- SearchResultsMode.spawn_from_query query
+ SearchResultsMode.spawn_from_query query, true
- SearchResultsMode.spawn_from_query "is:unread"
+ SearchResultsMode.spawn_from_query "is:unread", InboxMode.newest_first
when :list_labels
labels = LabelManager.all_labels.map { |l| LabelManager.string_for l }
user_label = bm.ask_with_completions :label, "Show threads with label (enter for listing): ", labels
when :list_labels
labels = LabelManager.all_labels.map { |l| LabelManager.string_for l }
user_label = bm.ask_with_completions :label, "Show threads with label (enter for listing): ", labels
k.add :refine_search, "Refine search", '|'
end
k.add :refine_search, "Refine search", '|'
end
+ def self.newest_first
+ if !$config[:inbox_newest_first].nil?
+ $config[:inbox_newest_first]
+ else
+ true
+ end
+ end
+
def initialize
super [:inbox, :sent, :draft], { :label => :inbox, :skip_killed => true }
raise "can't have more than one!" if defined? @@instance
@@instance = self
def initialize
super [:inbox, :sent, :draft], { :label => :inbox, :skip_killed => true }
raise "can't have more than one!" if defined? @@instance
@@instance = self
+ @newest_first = InboxMode.newest_first
end
def is_relevant? m; (m.labels & [:spam, :deleted, :killed, :inbox]) == Set.new([:inbox]) end
end
def is_relevant? m; (m.labels & [:spam, :deleted, :killed, :inbox]) == Set.new([:inbox]) end
def refine_search
text = BufferManager.ask :search, "refine query: ", "label:inbox AND "
return unless text && text !~ /^\s*$/
def refine_search
text = BufferManager.ask :search, "refine query: ", "label:inbox AND "
return unless text && text !~ /^\s*$/
- SearchResultsMode.spawn_from_query text
+ SearchResultsMode.spawn_from_query text, @newest_first
end
## label-list-mode wants to be able to raise us if the user selects
end
## label-list-mode wants to be able to raise us if the user selects
module Redwood
class LabelSearchResultsMode < ThreadIndexMode
module Redwood
class LabelSearchResultsMode < ThreadIndexMode
+ def initialize labels, newest_first
@labels = labels
opts = { :labels => @labels }
opts[:load_deleted] = true if labels.include? :deleted
opts[:load_spam] = true if labels.include? :spam
super [], opts
@labels = labels
opts = { :labels => @labels }
opts[:load_deleted] = true if labels.include? :deleted
opts[:load_spam] = true if labels.include? :spam
super [], opts
+ @newest_first = newest_first
end
register_keymap do |k|
end
register_keymap do |k|
label_query = @labels.size > 1 ? "(#{@labels.join('||')})" : @labels.first
query = BufferManager.ask :search, "refine query: ", "+label:#{label_query} "
return unless query && query !~ /^\s*$/
label_query = @labels.size > 1 ? "(#{@labels.join('||')})" : @labels.first
query = BufferManager.ask :search, "refine query: ", "+label:#{label_query} "
return unless query && query !~ /^\s*$/
- SearchResultsMode.spawn_from_query query
+ SearchResultsMode.spawn_from_query query, @newest_first
end
def is_relevant? m; @labels.all? { |l| m.has_label? l } end
end
def is_relevant? m; @labels.all? { |l| m.has_label? l } end
when :inbox
BufferManager.raise_to_front InboxMode.instance.buffer
else
when :inbox
BufferManager.raise_to_front InboxMode.instance.buffer
else
- b, new = BufferManager.spawn_unless_exists("All threads with label '#{label}'") { LabelSearchResultsMode.new [label] }
+ b, new = BufferManager.spawn_unless_exists("All threads with label '#{label}'") { LabelSearchResultsMode.new [label], true }
b.mode.load_threads :num => b.content_height if new
end
end
b.mode.load_threads :num => b.content_height if new
end
end
module Redwood
class SearchResultsMode < ThreadIndexMode
module Redwood
class SearchResultsMode < ThreadIndexMode
+ def initialize query, newest_first
@query = query
super [], query
@query = query
super [], query
+ @newest_first = newest_first
end
register_keymap do |k|
end
register_keymap do |k|
def refine_search
text = BufferManager.ask :search, "refine query: ", (@query[:text] + " ")
return unless text && text !~ /^\s*$/
def refine_search
text = BufferManager.ask :search, "refine query: ", (@query[:text] + " ")
return unless text && text !~ /^\s*$/
- SearchResultsMode.spawn_from_query text
+ SearchResultsMode.spawn_from_query text, @newest_first
end
## a proper is_relevant? method requires some way of asking ferret
end
## a proper is_relevant? method requires some way of asking ferret
## the message, and search against it to see if i have > 0 results,
## but that seems pretty insane.
## the message, and search against it to see if i have > 0 results,
## but that seems pretty insane.
- def self.spawn_from_query text
+ def self.spawn_from_query text, newest_first
begin
query = Index.parse_query(text)
return unless query
short_text = text.length < 20 ? text : text[0 ... 20] + "..."
begin
query = Index.parse_query(text)
return unless query
short_text = text.length < 20 ? text : text[0 ... 20] + "..."
- mode = SearchResultsMode.new query
+ mode = SearchResultsMode.new query, newest_first
BufferManager.spawn "search: \"#{short_text}\"", mode
mode.load_threads :num => mode.buffer.content_height
rescue Index::ParseError => e
BufferManager.spawn "search: \"#{short_text}\"", mode
mode.load_threads :num => mode.buffer.content_height
rescue Index::ParseError => e
k.add :tag_matching, "Tag matching threads", 'g'
k.add :apply_to_tagged, "Apply next command to all tagged threads", '+', '='
k.add :join_threads, "Force tagged threads to be joined into the same thread", '#'
k.add :tag_matching, "Tag matching threads", 'g'
k.add :apply_to_tagged, "Apply next command to all tagged threads", '+', '='
k.add :join_threads, "Force tagged threads to be joined into the same thread", '#'
+ k.add :toggle_sort, "Toggle newest first/last sort order", 'o'
k.add :undo, "Undo the previous action", 'u'
end
k.add :undo, "Undo the previous action", 'u'
end
@hidden_labels = hidden_labels + LabelManager::HIDDEN_RESERVED_LABELS
@date_width = DATE_WIDTH
@hidden_labels = hidden_labels + LabelManager::HIDDEN_RESERVED_LABELS
@date_width = DATE_WIDTH
@interrupt_search = false
initialize_threads # defines @ts and @ts_mutex
@interrupt_search = false
initialize_threads # defines @ts and @ts_mutex
+ def toggle_sort
+ @newest_first = !@newest_first
+ update
+ end
+
def update
@mutex.synchronize do
## let's see you do THIS in python
def update
@mutex.synchronize do
## let's see you do THIS in python
- @threads = @ts.threads.select { |t| !@hidden_threads[t] }.sort_by { |t| [t.date, t.first.id] }.reverse
+ @threads = @ts.threads.select { |t| !@hidden_threads[t] }.sort_by { |t| [t.date, t.first.id] }
+ if @newest_first
+ @threads = @threads.reverse
+ end
@size_widgets = @threads.map { |t| size_widget_for_thread t }
@size_widget_width = @size_widgets.max_of { |w| w.display_length }
end
@size_widgets = @threads.map { |t| size_widget_for_thread t }
@size_widget_width = @size_widgets.max_of { |w| w.display_length }
end
+ if !@newest_first
+ n = -1
+ end
myopts = @load_thread_opts.merge({ :when_done => (lambda do |num|
opts[:when_done].call(num) if opts[:when_done]
myopts = @load_thread_opts.merge({ :when_done => (lambda do |num|
opts[:when_done].call(num) if opts[:when_done]