mirror of
https://github.com/koreader/koreader.git
synced 2025-08-10 00:52:38 +00:00
Collections: sort (#13264)
This commit is contained in:
@@ -28,6 +28,8 @@ local FileManagerCollection = WidgetContainer:extend{
|
||||
}
|
||||
|
||||
function FileManagerCollection:init()
|
||||
self.doc_props_cache = {}
|
||||
self.updated_collections = {}
|
||||
self.ui.menu:registerToMainMenu(self)
|
||||
end
|
||||
|
||||
@@ -46,6 +48,13 @@ function FileManagerCollection:addToMainMenu(menu_items)
|
||||
}
|
||||
end
|
||||
|
||||
function FileManagerCollection:getDocProps(file)
|
||||
if self.doc_props_cache[file] == nil then
|
||||
self.doc_props_cache[file] = self.ui.bookinfo:getDocProps(file, nil, true) -- do not open the document
|
||||
end
|
||||
return self.doc_props_cache[file]
|
||||
end
|
||||
|
||||
-- collection
|
||||
|
||||
function FileManagerCollection:getCollectionTitle(collection_name)
|
||||
@@ -66,6 +75,8 @@ end
|
||||
function FileManagerCollection:onShowColl(collection_name)
|
||||
collection_name = collection_name or ReadCollection.default_collection_name
|
||||
self.coll_menu = BookList:new{
|
||||
name = "collections",
|
||||
path = collection_name,
|
||||
title_bar_left_icon = "appbar.menu",
|
||||
onLeftButtonTap = function() self:showCollDialog() end,
|
||||
onReturn = function()
|
||||
@@ -78,7 +89,6 @@ function FileManagerCollection:onShowColl(collection_name)
|
||||
ui = self.ui,
|
||||
_manager = self,
|
||||
_recreate_func = function() self:onShowColl(collection_name) end,
|
||||
collection_name = collection_name,
|
||||
}
|
||||
table.insert(self.coll_menu.paths, true) -- enable onReturn button
|
||||
self.coll_menu.close_callback = function()
|
||||
@@ -87,25 +97,60 @@ function FileManagerCollection:onShowColl(collection_name)
|
||||
self.coll_menu = nil
|
||||
self.match_table = nil
|
||||
end
|
||||
self:setCollate()
|
||||
self:updateItemTable()
|
||||
UIManager:show(self.coll_menu)
|
||||
return true
|
||||
end
|
||||
|
||||
function FileManagerCollection:updateItemTable(show_last_item, item_table)
|
||||
function FileManagerCollection:updateItemTable(item_table, focused_file)
|
||||
if item_table == nil then
|
||||
item_table = {}
|
||||
for _, item in pairs(ReadCollection.coll[self.coll_menu.collection_name]) do
|
||||
for _, item in pairs(ReadCollection.coll[self.coll_menu.path]) do
|
||||
if self:isItemMatch(item) then
|
||||
table.insert(item_table, item)
|
||||
local item_tmp = {
|
||||
file = item.file,
|
||||
text = item.text,
|
||||
order = item.order,
|
||||
attr = item.attr,
|
||||
mandatory = self.mandatory_func and self.mandatory_func(item) or util.getFriendlySize(item.attr.size or 0),
|
||||
}
|
||||
if self.item_func then
|
||||
self.item_func(item_tmp, self:getDocProps(item_tmp.file))
|
||||
end
|
||||
table.insert(item_table, item_tmp)
|
||||
end
|
||||
end
|
||||
if #item_table > 1 then
|
||||
table.sort(item_table, function(v1, v2) return v1.order < v2.order end)
|
||||
table.sort(item_table, self.sorting_func)
|
||||
end
|
||||
end
|
||||
local collection_name = self:getCollectionTitle(self.coll_menu.collection_name)
|
||||
local title = T("%1 (%2)", collection_name, #item_table)
|
||||
local title, subtitle = self:getBookListTitle(item_table)
|
||||
self.coll_menu:switchItemTable(title, item_table, -1, focused_file and { file = focused_file }, subtitle)
|
||||
end
|
||||
|
||||
function FileManagerCollection:isItemMatch(item)
|
||||
if self.match_table then
|
||||
if self.match_table.status then
|
||||
if self.match_table.status ~= BookList.getBookStatus(item.file) then
|
||||
return false
|
||||
end
|
||||
end
|
||||
if self.match_table.props then
|
||||
local doc_props = self:getDocProps(item.file)
|
||||
for prop, value in pairs(self.match_table.props) do
|
||||
if (doc_props[prop] or self.empty_prop) ~= value then
|
||||
return false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
function FileManagerCollection:getBookListTitle(item_table)
|
||||
local collection_title = self:getCollectionTitle(self.coll_menu.path)
|
||||
local title = T("%1 (%2)", collection_title, #item_table)
|
||||
local subtitle = ""
|
||||
if self.match_table then
|
||||
subtitle = {}
|
||||
@@ -125,27 +170,7 @@ function FileManagerCollection:updateItemTable(show_last_item, item_table)
|
||||
subtitle = table.concat(subtitle, " | ")
|
||||
end
|
||||
end
|
||||
local item_number = show_last_item and #item_table or -1
|
||||
self.coll_menu:switchItemTable(title, item_table, item_number, nil, subtitle)
|
||||
end
|
||||
|
||||
function FileManagerCollection:isItemMatch(item)
|
||||
if self.match_table then
|
||||
if self.match_table.status then
|
||||
if self.match_table.status ~= BookList.getBookStatus(item.file) then
|
||||
return false
|
||||
end
|
||||
end
|
||||
if self.match_table.props then
|
||||
local doc_props = self.ui.bookinfo:getDocProps(item.file, nil, true)
|
||||
for prop, value in pairs(self.match_table.props) do
|
||||
if (doc_props[prop] or self.empty_prop) ~= value then
|
||||
return false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return true
|
||||
return title, subtitle
|
||||
end
|
||||
|
||||
function FileManagerCollection:onSetDimensions(dimen)
|
||||
@@ -219,7 +244,8 @@ function FileManagerCollection:onMenuHold(item)
|
||||
{
|
||||
text = _("Remove from collection"),
|
||||
callback = function()
|
||||
ReadCollection:removeItem(file, self.collection_name)
|
||||
self._manager.updated_collections[self.path] = true
|
||||
ReadCollection:removeItem(file, self.path, true)
|
||||
close_dialog_update_callback()
|
||||
end,
|
||||
},
|
||||
@@ -263,6 +289,7 @@ function FileManagerCollection.getMenuInstance()
|
||||
end
|
||||
|
||||
function FileManagerCollection:showCollDialog()
|
||||
local collection_name = self.coll_menu.path
|
||||
local coll_not_empty = #self.coll_menu.item_table > 0
|
||||
local coll_dialog
|
||||
local function genFilterByStatusButton(button_status)
|
||||
@@ -284,7 +311,7 @@ function FileManagerCollection:showCollDialog()
|
||||
UIManager:close(coll_dialog)
|
||||
local prop_values = {}
|
||||
for idx, item in ipairs(self.coll_menu.item_table) do
|
||||
local doc_prop = self.ui.bookinfo:getDocProps(item.file, nil, true)[button_prop]
|
||||
local doc_prop = self:getDocProps(item.file)[button_prop]
|
||||
if doc_prop == nil then
|
||||
doc_prop = { self.empty_prop }
|
||||
elseif button_prop == "series" then
|
||||
@@ -344,18 +371,18 @@ function FileManagerCollection:showCollDialog()
|
||||
enabled = coll_not_empty,
|
||||
callback = function()
|
||||
UIManager:close(coll_dialog)
|
||||
self:onShowCollectionsSearchDialog(nil, self.coll_menu.collection_name)
|
||||
self:onShowCollectionsSearchDialog(nil, collection_name)
|
||||
end,
|
||||
}},
|
||||
{{
|
||||
text = _("Arrange books in collection"),
|
||||
enabled = coll_not_empty and self.match_table == nil,
|
||||
callback = function()
|
||||
UIManager:close(coll_dialog)
|
||||
self:showArrangeBooksDialog()
|
||||
end,
|
||||
}},
|
||||
{}, -- separator
|
||||
{{
|
||||
text = _("Arrange books in collection"),
|
||||
enabled = coll_not_empty,
|
||||
callback = function()
|
||||
UIManager:close(coll_dialog)
|
||||
self:sortCollection()
|
||||
end,
|
||||
}},
|
||||
{{
|
||||
text = _("Add all books from a folder"),
|
||||
callback = function()
|
||||
@@ -379,9 +406,10 @@ function FileManagerCollection:showCollDialog()
|
||||
path = G_reader_settings:readSetting("home_dir"),
|
||||
select_directory = false,
|
||||
onConfirm = function(file)
|
||||
if not ReadCollection:isFileInCollection(file, self.coll_menu.collection_name) then
|
||||
ReadCollection:addItem(file, self.coll_menu.collection_name)
|
||||
self:updateItemTable(true) -- show added item
|
||||
if not ReadCollection:isFileInCollection(file, collection_name) then
|
||||
self.updated_collections[collection_name] = true
|
||||
ReadCollection:addItem(file, collection_name)
|
||||
self:updateItemTable(nil, file) -- show added item
|
||||
self.files_updated = true
|
||||
end
|
||||
end,
|
||||
@@ -392,19 +420,21 @@ function FileManagerCollection:showCollDialog()
|
||||
}
|
||||
if self.ui.document then
|
||||
local file = self.ui.document.file
|
||||
local is_in_collection = ReadCollection:isFileInCollection(file, self.coll_menu.collection_name)
|
||||
local is_in_collection = ReadCollection:isFileInCollection(file, collection_name)
|
||||
table.insert(buttons, {{
|
||||
text_func = function()
|
||||
return is_in_collection and _("Remove current book from collection") or _("Add current book to collection")
|
||||
end,
|
||||
callback = function()
|
||||
UIManager:close(coll_dialog)
|
||||
self.updated_collections[collection_name] = true
|
||||
if is_in_collection then
|
||||
ReadCollection:removeItem(file, self.coll_menu.collection_name)
|
||||
ReadCollection:removeItem(file, collection_name, true)
|
||||
file = nil
|
||||
else
|
||||
ReadCollection:addItem(file, self.coll_menu.collection_name)
|
||||
ReadCollection:addItem(file, collection_name)
|
||||
end
|
||||
self:updateItemTable(not is_in_collection)
|
||||
self:updateItemTable(nil, file)
|
||||
self.files_updated = true
|
||||
end,
|
||||
}})
|
||||
@@ -429,7 +459,7 @@ function FileManagerCollection:showPropValueList(prop, prop_values)
|
||||
for _, idx in ipairs(item_idxs) do
|
||||
table.insert(item_table, self.coll_menu.item_table[idx])
|
||||
end
|
||||
self:updateItemTable(nil, item_table)
|
||||
self:updateItemTable(item_table)
|
||||
end,
|
||||
})
|
||||
end
|
||||
@@ -447,17 +477,104 @@ function FileManagerCollection:showPropValueList(prop, prop_values)
|
||||
UIManager:show(prop_menu)
|
||||
end
|
||||
|
||||
function FileManagerCollection:sortCollection()
|
||||
local sort_widget
|
||||
sort_widget = SortWidget:new{
|
||||
title = _("Arrange books in collection"),
|
||||
item_table = ReadCollection:getOrderedCollection(self.coll_menu.collection_name),
|
||||
callback = function()
|
||||
ReadCollection:updateCollectionOrder(self.coll_menu.collection_name, sort_widget.item_table)
|
||||
self:updateItemTable()
|
||||
function FileManagerCollection:setCollate(collate_id, collate_reverse)
|
||||
local coll_settings = ReadCollection.coll_settings[self.coll_menu.path]
|
||||
if collate_id == nil then
|
||||
collate_id = coll_settings.collate
|
||||
else
|
||||
coll_settings.collate = collate_id or nil
|
||||
end
|
||||
if collate_reverse == nil then
|
||||
collate_reverse = coll_settings.collate_reverse
|
||||
else
|
||||
coll_settings.collate_reverse = collate_reverse or nil
|
||||
end
|
||||
if collate_id then
|
||||
local collate = BookList.metadata_collates[collate_id] or BookList.collates[collate_id]
|
||||
self.item_func = collate.item_func
|
||||
self.mandatory_func = collate.mandatory_func
|
||||
self.sorting_func, self.sort_cache = collate.init_sort_func(self.sort_cache)
|
||||
if collate_reverse then
|
||||
local sorting_func_unreversed = self.sorting_func
|
||||
self.sorting_func = function(a, b) return sorting_func_unreversed(b, a) end
|
||||
end
|
||||
else -- manual
|
||||
self.item_func = nil
|
||||
self.mandatory_func = nil
|
||||
self.sorting_func = function(a, b) return a.order < b.order end
|
||||
end
|
||||
end
|
||||
|
||||
function FileManagerCollection:showArrangeBooksDialog()
|
||||
local collection_name = self.coll_menu.path
|
||||
local coll_settings = ReadCollection.coll_settings[collection_name]
|
||||
local curr_collate_id = coll_settings.collate
|
||||
local arrange_dialog
|
||||
local function genCollateButton(collate_id)
|
||||
local collate = BookList.metadata_collates[collate_id] or BookList.collates[collate_id]
|
||||
return {
|
||||
text = collate.text .. (curr_collate_id == collate_id and " ✓" or ""),
|
||||
callback = function()
|
||||
if curr_collate_id ~= collate_id then
|
||||
UIManager:close(arrange_dialog)
|
||||
self.updated_collections[collection_name] = true
|
||||
self:setCollate(collate_id)
|
||||
self:updateItemTable()
|
||||
end
|
||||
end,
|
||||
}
|
||||
end
|
||||
local buttons = {
|
||||
{
|
||||
genCollateButton("authors"),
|
||||
genCollateButton("title"),
|
||||
},
|
||||
{
|
||||
genCollateButton("keywords"),
|
||||
genCollateButton("series"),
|
||||
},
|
||||
{
|
||||
genCollateButton("natural"),
|
||||
genCollateButton("strcoll"),
|
||||
},
|
||||
{
|
||||
genCollateButton("size"),
|
||||
genCollateButton("access"),
|
||||
},
|
||||
{{
|
||||
text = _("Reverse sorting") .. (coll_settings.collate_reverse and " ✓" or ""),
|
||||
enabled = curr_collate_id and true or false, -- disabled for manual sorting
|
||||
callback = function()
|
||||
UIManager:close(arrange_dialog)
|
||||
self.updated_collections[collection_name] = true
|
||||
self:setCollate(nil, not coll_settings.collate_reverse)
|
||||
self:updateItemTable()
|
||||
end,
|
||||
}},
|
||||
{}, -- separator
|
||||
{{
|
||||
text = _("Manual sorting") .. (curr_collate_id == nil and " ✓" or ""),
|
||||
callback = function()
|
||||
UIManager:close(arrange_dialog)
|
||||
UIManager:show(SortWidget:new{
|
||||
title = _("Arrange books in collection"),
|
||||
item_table = self.coll_menu.item_table,
|
||||
callback = function()
|
||||
ReadCollection:updateCollectionOrder(collection_name, self.coll_menu.item_table)
|
||||
self.updated_collections[collection_name] = true
|
||||
self:setCollate(false, false)
|
||||
self:updateItemTable()
|
||||
end,
|
||||
})
|
||||
end,
|
||||
}},
|
||||
}
|
||||
UIManager:show(sort_widget)
|
||||
arrange_dialog = ButtonDialog:new{
|
||||
title = _("Sort by"),
|
||||
title_align = "center",
|
||||
buttons = buttons,
|
||||
}
|
||||
UIManager:show(arrange_dialog)
|
||||
end
|
||||
|
||||
function FileManagerCollection:addBooksFromFolder(include_subfolders)
|
||||
@@ -470,11 +587,12 @@ function FileManagerCollection:addBooksFromFolder(include_subfolders)
|
||||
util.findFiles(folder, function(file)
|
||||
files_found[file] = DocumentRegistry:hasProvider(file) or nil
|
||||
end, include_subfolders)
|
||||
local count = ReadCollection:addItemsMultiple(files_found, { [self.coll_menu.collection_name] = true })
|
||||
local count = ReadCollection:addItemsMultiple(files_found, { [self.coll_menu.path] = true })
|
||||
local text
|
||||
if count == 0 then
|
||||
text = _("No books added to collection")
|
||||
else
|
||||
self.updated_collections[self.coll_menu.path] = true
|
||||
text = T(N_("1 book added to collection", "%1 books added to collection", count), count)
|
||||
self:updateItemTable()
|
||||
self.files_updated = true
|
||||
@@ -485,9 +603,14 @@ function FileManagerCollection:addBooksFromFolder(include_subfolders)
|
||||
UIManager:show(path_chooser)
|
||||
end
|
||||
|
||||
function FileManagerCollection:onBookMetadataChanged()
|
||||
function FileManagerCollection:onBookMetadataChanged(prop_updated)
|
||||
local file
|
||||
if prop_updated then
|
||||
file = prop_updated.filepath
|
||||
self.doc_props_cache[file] = prop_updated.doc_props
|
||||
end
|
||||
if self.coll_menu then
|
||||
self.coll_menu:updateItems()
|
||||
self:updateItemTable(nil, file) -- keep showing the changed file
|
||||
end
|
||||
end
|
||||
|
||||
@@ -507,6 +630,7 @@ function FileManagerCollection:onShowCollList(file_or_selected_collections, call
|
||||
self.selected_collections = nil
|
||||
end
|
||||
self.coll_list = Menu:new{
|
||||
path = true, -- draw focus
|
||||
subtitle = "",
|
||||
covers_fullscreen = true,
|
||||
is_borderless = true,
|
||||
@@ -547,7 +671,7 @@ function FileManagerCollection:updateCollListItemTable(do_init, item_number)
|
||||
text = self:getCollectionTitle(name),
|
||||
mandatory = mandatory,
|
||||
name = name,
|
||||
order = ReadCollection.coll_order[name],
|
||||
order = ReadCollection.coll_settings[name].order,
|
||||
})
|
||||
end
|
||||
if #item_table > 1 then
|
||||
@@ -571,7 +695,6 @@ function FileManagerCollection:updateCollListItemTable(do_init, item_number)
|
||||
end
|
||||
elseif self.from_collection_name ~= nil then
|
||||
itemmatch = { text = self.from_collection_name }
|
||||
self.coll_list.path = true -- draw focus
|
||||
self.from_collection_name = nil
|
||||
end
|
||||
self.coll_list:switchItemTable(title, item_table, item_number or -1, itemmatch, subtitle)
|
||||
@@ -747,6 +870,7 @@ end
|
||||
|
||||
function FileManagerCollection:addCollection()
|
||||
local editCallback = function(name)
|
||||
self.updated_collections[name] = true
|
||||
ReadCollection:addCollection(name)
|
||||
local mandatory
|
||||
if self.selected_collections then
|
||||
@@ -759,7 +883,7 @@ function FileManagerCollection:addCollection()
|
||||
text = name,
|
||||
mandatory = mandatory,
|
||||
name = name,
|
||||
order = ReadCollection.coll_order[name],
|
||||
order = ReadCollection.coll_settings[name].order,
|
||||
})
|
||||
self:updateCollListItemTable(false, #self.coll_list.item_table) -- show added item
|
||||
end
|
||||
@@ -768,6 +892,7 @@ end
|
||||
|
||||
function FileManagerCollection:renameCollection(item)
|
||||
local editCallback = function(name)
|
||||
self.updated_collections[name] = true
|
||||
ReadCollection:renameCollection(item.name, name)
|
||||
self.coll_list.item_table[item.idx].text = name
|
||||
self.coll_list.item_table[item.idx].name = name
|
||||
@@ -781,6 +906,7 @@ function FileManagerCollection:removeCollection(item)
|
||||
text = _("Remove collection?") .. "\n\n" .. item.text,
|
||||
ok_text = _("Remove"),
|
||||
ok_callback = function()
|
||||
self.updated_collections[item.name] = true
|
||||
ReadCollection:removeCollection(item.name)
|
||||
table.remove(self.coll_list.item_table, item.idx)
|
||||
self:updateCollListItemTable()
|
||||
@@ -795,6 +921,7 @@ function FileManagerCollection:sortCollections()
|
||||
title = _("Arrange collections"),
|
||||
item_table = util.tableDeepCopy(self.coll_list.item_table),
|
||||
callback = function()
|
||||
self.updated_collections = { true } -- all
|
||||
ReadCollection:updateCollectionListOrder(sort_widget.item_table)
|
||||
self:updateCollListItemTable(true) -- init
|
||||
end,
|
||||
@@ -867,8 +994,7 @@ function FileManagerCollection:searchCollections(coll_name)
|
||||
if not DocumentRegistry:hasProvider(file) then
|
||||
return false
|
||||
end
|
||||
local book_props = self.ui.bookinfo:getDocProps(file, nil, true) -- do not open the document
|
||||
book_props.display_title = nil
|
||||
local book_props = self:getDocProps(file)
|
||||
if next(book_props) ~= nil and self.ui.bookinfo:findInProps(book_props, self.search_str, self.case_sensitive) then
|
||||
return true
|
||||
end
|
||||
@@ -907,7 +1033,7 @@ function FileManagerCollection:searchCollections(coll_name)
|
||||
return false
|
||||
end
|
||||
|
||||
local collections = coll_name and { coll_name = ReadCollection.coll[coll_name] } or ReadCollection.coll
|
||||
local collections = coll_name and { [coll_name] = ReadCollection.coll[coll_name] } or ReadCollection.coll
|
||||
local Trapper = require("ui/trapper")
|
||||
local info = InfoMessage:new{ text = _("Searching… (tap to cancel)") }
|
||||
UIManager:show(info)
|
||||
@@ -915,7 +1041,7 @@ function FileManagerCollection:searchCollections(coll_name)
|
||||
local completed, files_found, files_found_order = Trapper:dismissableRunInSubprocess(function()
|
||||
local match_cache, _files_found, _files_found_order = {}, {}, {}
|
||||
for collection_name, coll in pairs(collections) do
|
||||
local coll_order = ReadCollection.coll_order[collection_name]
|
||||
local coll_order = ReadCollection.coll_settings[collection_name].order
|
||||
for _, item in pairs(coll) do
|
||||
local file = item.file
|
||||
if match_cache[file] == nil then -- a book can be included to several collections
|
||||
@@ -960,9 +1086,10 @@ function FileManagerCollection:searchCollections(coll_name)
|
||||
new_coll_name = new_coll_name .. " " .. T(_"(in %1)", coll_name)
|
||||
self.coll_menu.close_callback()
|
||||
end
|
||||
ReadCollection:removeCollection(new_coll_name, true)
|
||||
ReadCollection:addCollection(new_coll_name, true)
|
||||
ReadCollection:addItemsMultiple(files_found, { [new_coll_name] = true }, true)
|
||||
self.updated_collections[new_coll_name] = true
|
||||
ReadCollection:removeCollection(new_coll_name)
|
||||
ReadCollection:addCollection(new_coll_name)
|
||||
ReadCollection:addItemsMultiple(files_found, { [new_coll_name] = true })
|
||||
ReadCollection:updateCollectionOrder(new_coll_name, files_found_order)
|
||||
if self.coll_list ~= nil then
|
||||
UIManager:close(self.coll_list)
|
||||
@@ -972,6 +1099,12 @@ function FileManagerCollection:searchCollections(coll_name)
|
||||
end
|
||||
end
|
||||
|
||||
function FileManagerCollection:onCloseWidget()
|
||||
if next(self.updated_collections) then
|
||||
ReadCollection:write(self.updated_collections)
|
||||
end
|
||||
end
|
||||
|
||||
-- external
|
||||
|
||||
function FileManagerCollection:genAddToCollectionButton(file_or_files, caller_pre_callback, caller_post_callback, button_disabled)
|
||||
@@ -984,6 +1117,9 @@ function FileManagerCollection:genAddToCollectionButton(file_or_files, caller_pr
|
||||
caller_pre_callback()
|
||||
end
|
||||
local caller_callback = function(selected_collections)
|
||||
for name in pairs(selected_collections) do
|
||||
self.updated_collections[name] = true
|
||||
end
|
||||
if is_single_file then
|
||||
ReadCollection:addRemoveItemMultiple(file_or_files, selected_collections)
|
||||
else -- selected files
|
||||
|
||||
@@ -813,6 +813,7 @@ function ReaderUI:onClose(full_refresh)
|
||||
if self.document ~= nil then
|
||||
file = self.document.file
|
||||
require("readhistory"):updateLastBookTime(self.tearing_down)
|
||||
require("readcollection"):updateLastBookTime(file)
|
||||
-- Serialize the most recently displayed page for later launch
|
||||
DocCache:serialize(file)
|
||||
logger.dbg("closing document")
|
||||
|
||||
@@ -9,27 +9,26 @@ local collection_file = DataStorage:getSettingsDir() .. "/collection.lua"
|
||||
|
||||
local ReadCollection = {
|
||||
coll = nil, -- hash table
|
||||
coll_order = nil, -- hash table
|
||||
coll_settings = nil, -- hash table
|
||||
last_read_time = 0,
|
||||
default_collection_name = "favorites",
|
||||
}
|
||||
|
||||
-- read, write
|
||||
|
||||
local function buildEntry(file, order, mandatory)
|
||||
local function buildEntry(file, order, attr)
|
||||
file = ffiUtil.realpath(file)
|
||||
if not file then return end
|
||||
if not mandatory then -- new item
|
||||
local attr = lfs.attributes(file)
|
||||
if not attr or attr.mode ~= "file" then return end
|
||||
mandatory = util.getFriendlySize(attr.size or 0)
|
||||
if file then
|
||||
attr = attr or lfs.attributes(file)
|
||||
if attr and attr.mode == "file" then
|
||||
return {
|
||||
file = file,
|
||||
text = file:gsub(".*/", ""),
|
||||
order = order,
|
||||
attr = attr,
|
||||
}
|
||||
end
|
||||
end
|
||||
return {
|
||||
file = file,
|
||||
text = file:gsub(".*/", ""),
|
||||
mandatory = mandatory,
|
||||
order = order,
|
||||
}
|
||||
end
|
||||
|
||||
function ReadCollection:_read()
|
||||
@@ -44,7 +43,7 @@ function ReadCollection:_read()
|
||||
end
|
||||
logger.dbg("ReadCollection: reading from collection file")
|
||||
self.coll = {}
|
||||
self.coll_order = {}
|
||||
self.coll_settings = {}
|
||||
for coll_name, collection in pairs(collections.data) do
|
||||
local coll = {}
|
||||
for _, v in ipairs(collection) do
|
||||
@@ -54,14 +53,11 @@ function ReadCollection:_read()
|
||||
end
|
||||
end
|
||||
self.coll[coll_name] = coll
|
||||
if not collection.settings then -- favorites, first run
|
||||
collection.settings = { order = 1 }
|
||||
end
|
||||
self.coll_order[coll_name] = collection.settings.order
|
||||
self.coll_settings[coll_name] = collection.settings or { order = 1 } -- favorites, first run
|
||||
end
|
||||
end
|
||||
|
||||
function ReadCollection:write(collection_name)
|
||||
function ReadCollection:write(updated_collections)
|
||||
local collections = LuaSettings:open(collection_file)
|
||||
for coll_name in pairs(collections.data) do
|
||||
if not self.coll[coll_name] then
|
||||
@@ -69,10 +65,11 @@ function ReadCollection:write(collection_name)
|
||||
end
|
||||
end
|
||||
for coll_name, coll in pairs(self.coll) do
|
||||
if not collection_name or coll_name == collection_name then
|
||||
local data = { settings = { order = self.coll_order[coll_name] } }
|
||||
if updated_collections == nil or updated_collections[1] or updated_collections[coll_name] then
|
||||
local is_manual_collate = not self.coll_settings[coll_name].collate or nil
|
||||
local data = { settings = self.coll_settings[coll_name] }
|
||||
for _, item in pairs(coll) do
|
||||
table.insert(data, { file = item.file, order = item.order })
|
||||
table.insert(data, { file = item.file, order = is_manual_collate and item.order })
|
||||
end
|
||||
collections:saveSetting(coll_name, data)
|
||||
end
|
||||
@@ -81,6 +78,18 @@ function ReadCollection:write(collection_name)
|
||||
collections:flush()
|
||||
end
|
||||
|
||||
function ReadCollection:updateLastBookTime(file)
|
||||
file = ffiUtil.realpath(file)
|
||||
if file then
|
||||
local now = os.time()
|
||||
for _, coll in pairs(self.coll) do
|
||||
if coll[file] then
|
||||
coll[file].attr.access = now
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- info
|
||||
|
||||
function ReadCollection:isFileInCollection(file, collection_name)
|
||||
@@ -111,23 +120,22 @@ function ReadCollection:getCollectionsWithFile(file)
|
||||
return collections
|
||||
end
|
||||
|
||||
function ReadCollection:getCollectionMaxOrder(collection_name)
|
||||
function ReadCollection:getCollectionNextOrder(collection_name)
|
||||
if self.coll_settings[collection_name].collate then return end
|
||||
local max_order = 0
|
||||
for _, item in pairs(self.coll[collection_name]) do
|
||||
if max_order < item.order then
|
||||
max_order = item.order
|
||||
end
|
||||
end
|
||||
return max_order
|
||||
return max_order + 1
|
||||
end
|
||||
|
||||
-- manage items
|
||||
|
||||
function ReadCollection:addItem(file, collection_name)
|
||||
local max_order = self:getCollectionMaxOrder(collection_name)
|
||||
local item = buildEntry(file, max_order + 1)
|
||||
local item = buildEntry(file, self:getCollectionNextOrder(collection_name))
|
||||
self.coll[collection_name][item.file] = item
|
||||
self:write(collection_name)
|
||||
end
|
||||
|
||||
function ReadCollection:addRemoveItemMultiple(file, collections_to_add)
|
||||
@@ -135,8 +143,7 @@ function ReadCollection:addRemoveItemMultiple(file, collections_to_add)
|
||||
for coll_name, coll in pairs(self.coll) do
|
||||
if collections_to_add[coll_name] then
|
||||
if not coll[file] then
|
||||
local max_order = self:getCollectionMaxOrder(coll_name)
|
||||
coll[file] = buildEntry(file, max_order + 1)
|
||||
coll[file] = buildEntry(file, self:getCollectionNextOrder(coll_name))
|
||||
end
|
||||
else
|
||||
if coll[file] then
|
||||
@@ -144,25 +151,20 @@ function ReadCollection:addRemoveItemMultiple(file, collections_to_add)
|
||||
end
|
||||
end
|
||||
end
|
||||
self:write()
|
||||
end
|
||||
|
||||
function ReadCollection:addItemsMultiple(files, collections_to_add, no_write)
|
||||
function ReadCollection:addItemsMultiple(files, collections_to_add)
|
||||
local count = 0
|
||||
for file in pairs(files) do
|
||||
file = ffiUtil.realpath(file) or file
|
||||
for coll_name in pairs(collections_to_add) do
|
||||
local coll = self.coll[coll_name]
|
||||
if not coll[file] then
|
||||
local max_order = self:getCollectionMaxOrder(coll_name)
|
||||
coll[file] = buildEntry(file, max_order + 1)
|
||||
coll[file] = buildEntry(file, self:getCollectionNextOrder(coll_name))
|
||||
count = count + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
if not no_write and count > 0 then
|
||||
self:write()
|
||||
end
|
||||
return count
|
||||
end
|
||||
|
||||
@@ -172,7 +174,7 @@ function ReadCollection:removeItem(file, collection_name, no_write) -- FM: delet
|
||||
if self.coll[collection_name][file] then
|
||||
self.coll[collection_name][file] = nil
|
||||
if not no_write then
|
||||
self:write(collection_name)
|
||||
self:write({ collection_name = true })
|
||||
end
|
||||
return true
|
||||
end
|
||||
@@ -223,11 +225,10 @@ end
|
||||
function ReadCollection:_updateItem(coll_name, file_name, new_filepath, new_path)
|
||||
local coll = self.coll[coll_name]
|
||||
local item_old = coll[file_name]
|
||||
local order, mandatory = item_old.order, item_old.mandatory
|
||||
new_filepath = new_filepath or new_path .. "/" .. item_old.text
|
||||
coll[file_name] = nil
|
||||
local item = buildEntry(new_filepath, order, mandatory) -- no lfs call
|
||||
local item = buildEntry(new_filepath, item_old.order, item_old.attr) -- no lfs call
|
||||
coll[item.file] = item
|
||||
coll[file_name] = nil
|
||||
end
|
||||
|
||||
function ReadCollection:updateItem(file, new_filepath) -- FM: rename file, move file
|
||||
@@ -290,46 +291,37 @@ function ReadCollection:updateCollectionOrder(collection_name, ordered_coll)
|
||||
for i, item in ipairs(ordered_coll) do
|
||||
coll[item.file].order = i
|
||||
end
|
||||
self:write(collection_name)
|
||||
end
|
||||
|
||||
-- manage collections
|
||||
|
||||
function ReadCollection:addCollection(coll_name, no_write)
|
||||
function ReadCollection:addCollection(coll_name)
|
||||
local max_order = 0
|
||||
for _, order in pairs(self.coll_order) do
|
||||
if max_order < order then
|
||||
max_order = order
|
||||
for _, settings in pairs(self.coll_settings) do
|
||||
if max_order < settings.order then
|
||||
max_order = settings.order
|
||||
end
|
||||
end
|
||||
self.coll_order[coll_name] = max_order + 1
|
||||
self.coll_settings[coll_name] = { order = max_order + 1 }
|
||||
self.coll[coll_name] = {}
|
||||
if not no_write then
|
||||
self:write()
|
||||
end
|
||||
end
|
||||
|
||||
function ReadCollection:renameCollection(coll_name, new_name)
|
||||
self.coll_order[new_name] = self.coll_order[coll_name]
|
||||
self.coll_settings[new_name] = self.coll_settings[coll_name]
|
||||
self.coll[new_name] = self.coll[coll_name]
|
||||
self.coll_order[coll_name] = nil
|
||||
self.coll_settings[coll_name] = nil
|
||||
self.coll[coll_name] = nil
|
||||
self:write(new_name)
|
||||
end
|
||||
|
||||
function ReadCollection:removeCollection(coll_name, no_write)
|
||||
self.coll_order[coll_name] = nil
|
||||
function ReadCollection:removeCollection(coll_name)
|
||||
self.coll_settings[coll_name] = nil
|
||||
self.coll[coll_name] = nil
|
||||
if not no_write then
|
||||
self:write()
|
||||
end
|
||||
end
|
||||
|
||||
function ReadCollection:updateCollectionListOrder(ordered_coll)
|
||||
for i, item in ipairs(ordered_coll) do
|
||||
self.coll_order[item.name] = i
|
||||
self.coll_settings[item.name].order = i
|
||||
end
|
||||
self:write()
|
||||
end
|
||||
|
||||
ReadCollection:_read()
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
local DocSettings = require("docsettings")
|
||||
local Menu = require("ui/widget/menu")
|
||||
local Utf8Proc = require("ffi/utf8proc")
|
||||
local datetime = require("datetime")
|
||||
local ffiUtil = require("ffi/util")
|
||||
local sort = require("sort")
|
||||
local util = require("util")
|
||||
local _ = require("gettext")
|
||||
local T = ffiUtil.template
|
||||
|
||||
@@ -9,6 +13,232 @@ local BookList = Menu:extend{
|
||||
is_borderless = true,
|
||||
is_popout = false,
|
||||
book_info_cache = {}, -- cache in the base class
|
||||
metadata_collates = {
|
||||
title = {
|
||||
text = _("Title"),
|
||||
item_func = function(item, doc_props)
|
||||
item.doc_props = doc_props
|
||||
end,
|
||||
init_sort_func = function()
|
||||
return function(a, b)
|
||||
return ffiUtil.strcoll(a.doc_props.display_title, b.doc_props.display_title)
|
||||
end
|
||||
end,
|
||||
},
|
||||
authors = {
|
||||
text = _("Authors"),
|
||||
item_func = function(item, doc_props)
|
||||
doc_props.authors = doc_props.authors or "\u{FFFF}" -- sorted last
|
||||
item.doc_props = doc_props
|
||||
end,
|
||||
init_sort_func = function()
|
||||
return function(a, b)
|
||||
if a.doc_props.authors ~= b.doc_props.authors then
|
||||
return ffiUtil.strcoll(a.doc_props.authors, b.doc_props.authors)
|
||||
end
|
||||
return ffiUtil.strcoll(a.doc_props.display_title, b.doc_props.display_title)
|
||||
end
|
||||
end,
|
||||
},
|
||||
series = {
|
||||
text = _("Series"),
|
||||
item_func = function(item, doc_props)
|
||||
doc_props.series = doc_props.series or "\u{FFFF}"
|
||||
item.doc_props = doc_props
|
||||
end,
|
||||
init_sort_func = function()
|
||||
return function(a, b)
|
||||
if a.doc_props.series ~= b.doc_props.series then
|
||||
return ffiUtil.strcoll(a.doc_props.series, b.doc_props.series)
|
||||
end
|
||||
if a.doc_props.series_index and b.doc_props.series_index then
|
||||
return a.doc_props.series_index < b.doc_props.series_index
|
||||
end
|
||||
return ffiUtil.strcoll(a.doc_props.display_title, b.doc_props.display_title)
|
||||
end
|
||||
end,
|
||||
},
|
||||
keywords = {
|
||||
text = _("Keywords"),
|
||||
item_func = function(item, doc_props)
|
||||
doc_props.keywords = doc_props.keywords or "\u{FFFF}"
|
||||
item.doc_props = doc_props
|
||||
end,
|
||||
init_sort_func = function()
|
||||
return function(a, b)
|
||||
return ffiUtil.strcoll(a.doc_props.keywords, b.doc_props.keywords)
|
||||
end
|
||||
end,
|
||||
},
|
||||
},
|
||||
collates = {
|
||||
strcoll = {
|
||||
text = _("name"),
|
||||
menu_order = 10,
|
||||
can_collate_mixed = true,
|
||||
init_sort_func = function()
|
||||
return function(a, b)
|
||||
return ffiUtil.strcoll(a.text, b.text)
|
||||
end
|
||||
end,
|
||||
},
|
||||
natural = {
|
||||
text = _("name (natural sorting)"),
|
||||
menu_order = 20,
|
||||
can_collate_mixed = true,
|
||||
init_sort_func = function(cache)
|
||||
local natsort
|
||||
natsort, cache = sort.natsort_cmp(cache)
|
||||
return function(a, b)
|
||||
return natsort(a.text, b.text)
|
||||
end, cache
|
||||
end,
|
||||
},
|
||||
access = {
|
||||
text = _("last read date"),
|
||||
menu_order = 30,
|
||||
can_collate_mixed = true,
|
||||
init_sort_func = function()
|
||||
return function(a, b)
|
||||
return a.attr.access > b.attr.access
|
||||
end
|
||||
end,
|
||||
mandatory_func = function(item)
|
||||
return datetime.secondsToDateTime(item.attr.access)
|
||||
end,
|
||||
},
|
||||
date = {
|
||||
text = _("date modified"),
|
||||
menu_order = 40,
|
||||
can_collate_mixed = true,
|
||||
init_sort_func = function()
|
||||
return function(a, b)
|
||||
return a.attr.modification > b.attr.modification
|
||||
end
|
||||
end,
|
||||
mandatory_func = function(item)
|
||||
return datetime.secondsToDateTime(item.attr.modification)
|
||||
end,
|
||||
},
|
||||
size = {
|
||||
text = _("size"),
|
||||
menu_order = 50,
|
||||
can_collate_mixed = false,
|
||||
init_sort_func = function()
|
||||
return function(a, b)
|
||||
return a.attr.size < b.attr.size
|
||||
end
|
||||
end,
|
||||
},
|
||||
type = {
|
||||
text = _("type"),
|
||||
menu_order = 60,
|
||||
can_collate_mixed = false,
|
||||
init_sort_func = function()
|
||||
return function(a, b)
|
||||
if (a.suffix or b.suffix) and a.suffix ~= b.suffix then
|
||||
return ffiUtil.strcoll(a.suffix, b.suffix)
|
||||
end
|
||||
return ffiUtil.strcoll(a.text, b.text)
|
||||
end
|
||||
end,
|
||||
item_func = function(item)
|
||||
item.suffix = util.getFileNameSuffix(item.text)
|
||||
end,
|
||||
},
|
||||
percent_unopened_first = {
|
||||
text = _("percent - unopened first"),
|
||||
bookinfo_required = true,
|
||||
menu_order = 70,
|
||||
can_collate_mixed = false,
|
||||
init_sort_func = function()
|
||||
return function(a, b)
|
||||
if a.opened == b.opened then
|
||||
if a.opened then
|
||||
return a.percent_finished < b.percent_finished
|
||||
end
|
||||
return ffiUtil.strcoll(a.text, b.text)
|
||||
end
|
||||
return b.opened
|
||||
end
|
||||
end,
|
||||
item_func = function(item, book_info)
|
||||
item.opened = book_info.been_opened
|
||||
-- smooth 2 decimal points (0.00) instead of 16 decimal points
|
||||
item.percent_finished = util.round_decimal(book_info.percent_finished or 0, 2)
|
||||
end,
|
||||
mandatory_func = function(item)
|
||||
return item.opened and string.format("%d\u{202F}%%", 100 * item.percent_finished) or "–"
|
||||
end,
|
||||
},
|
||||
percent_unopened_last = {
|
||||
text = _("percent - unopened last"),
|
||||
bookinfo_required = true,
|
||||
menu_order = 80,
|
||||
can_collate_mixed = false,
|
||||
init_sort_func = function()
|
||||
return function(a, b)
|
||||
if a.opened == b.opened then
|
||||
if a.opened then
|
||||
return a.percent_finished < b.percent_finished
|
||||
end
|
||||
return ffiUtil.strcoll(a.text, b.text)
|
||||
end
|
||||
return a.opened
|
||||
end
|
||||
end,
|
||||
item_func = function(item, book_info)
|
||||
item.opened = book_info.been_opened
|
||||
-- smooth 2 decimal points (0.00) instead of 16 decimal points
|
||||
item.percent_finished = util.round_decimal(book_info.percent_finished or 0, 2)
|
||||
end,
|
||||
mandatory_func = function(item)
|
||||
return item.opened and string.format("%d\u{202F}%%", 100 * item.percent_finished) or "–"
|
||||
end,
|
||||
},
|
||||
percent_natural = {
|
||||
-- sort 90% > 50% > 0% > on hold > unopened > 100% or finished
|
||||
text = _("percent – unopened – finished last"),
|
||||
bookinfo_required = true,
|
||||
menu_order = 90,
|
||||
can_collate_mixed = false,
|
||||
init_sort_func = function(cache)
|
||||
local natsort
|
||||
natsort, cache = sort.natsort_cmp(cache)
|
||||
local sortfunc = function(a, b)
|
||||
if a.sort_percent == b.sort_percent then
|
||||
return natsort(a.text, b.text)
|
||||
elseif a.sort_percent == 1 then
|
||||
return false
|
||||
elseif b.sort_percent == 1 then
|
||||
return true
|
||||
else
|
||||
return a.sort_percent > b.sort_percent
|
||||
end
|
||||
end
|
||||
return sortfunc, cache
|
||||
end,
|
||||
item_func = function(item, book_info)
|
||||
item.opened = book_info.been_opened
|
||||
local percent_finished = book_info.percent_finished
|
||||
local sort_percent
|
||||
if item.opened then
|
||||
-- books marked as "finished" or "on hold" should be considered the same as 100% and less than 0% respectively
|
||||
if book_info.status == "complete" then
|
||||
sort_percent = 1.0
|
||||
elseif book_info.status == "abandoned" then
|
||||
sort_percent = -0.01
|
||||
end
|
||||
end
|
||||
-- smooth 2 decimal points (0.00) instead of 16 decimal points
|
||||
item.sort_percent = sort_percent or util.round_decimal(percent_finished or -1, 2)
|
||||
item.percent_finished = percent_finished or 0
|
||||
end,
|
||||
mandatory_func = function(item)
|
||||
return item.opened and string.format("%d\u{202F}%%", 100 * item.percent_finished) or "–"
|
||||
end,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
function BookList:init()
|
||||
@@ -113,7 +343,13 @@ local status_strings = {
|
||||
|
||||
function BookList.getBookStatusString(status, with_prefix)
|
||||
local status_string = status and status_strings[status]
|
||||
return status_string and (with_prefix and T(_("Status: %1"), status_string:lower()) or status_string)
|
||||
if status_string then
|
||||
if with_prefix then
|
||||
status_string = Utf8Proc.lowercase(util.fixUtf8(status_string, "?"))
|
||||
return T(_("Status: %1"), status_string)
|
||||
end
|
||||
return status_string
|
||||
end
|
||||
end
|
||||
|
||||
return BookList
|
||||
|
||||
@@ -6,12 +6,10 @@ local Event = require("ui/event")
|
||||
local FileManagerShortcuts = require("apps/filemanager/filemanagershortcuts")
|
||||
local ReadCollection = require("readcollection")
|
||||
local UIManager = require("ui/uimanager")
|
||||
local datetime = require("datetime")
|
||||
local ffi = require("ffi")
|
||||
local ffiUtil = require("ffi/util")
|
||||
local filemanagerutil = require("apps/filemanager/filemanagerutil")
|
||||
local lfs = require("libs/libkoreader-lfs")
|
||||
local sort = require("sort")
|
||||
local util = require("util")
|
||||
local _ = require("gettext")
|
||||
local Screen = Device.screen
|
||||
@@ -76,175 +74,6 @@ local FileChooser = BookList:extend{
|
||||
},
|
||||
path_items = nil, -- hash, store last browsed location (item index) for each path
|
||||
goto_letter = true,
|
||||
collates = {
|
||||
strcoll = {
|
||||
text = _("name"),
|
||||
menu_order = 10,
|
||||
can_collate_mixed = true,
|
||||
init_sort_func = function(cache)
|
||||
return function(a, b)
|
||||
return ffiUtil.strcoll(a.text, b.text)
|
||||
end, cache
|
||||
end,
|
||||
},
|
||||
natural = {
|
||||
text = _("name (natural sorting)"),
|
||||
menu_order = 20,
|
||||
can_collate_mixed = true,
|
||||
init_sort_func = function(cache)
|
||||
local natsort
|
||||
natsort, cache = sort.natsort_cmp(cache)
|
||||
return function(a, b)
|
||||
return natsort(a.text, b.text)
|
||||
end, cache
|
||||
end
|
||||
},
|
||||
access = {
|
||||
text = _("last read date"),
|
||||
menu_order = 30,
|
||||
can_collate_mixed = true,
|
||||
init_sort_func = function(cache)
|
||||
return function(a, b)
|
||||
return a.attr.access > b.attr.access
|
||||
end, cache
|
||||
end,
|
||||
mandatory_func = function(item)
|
||||
return datetime.secondsToDateTime(item.attr.access)
|
||||
end,
|
||||
},
|
||||
date = {
|
||||
text = _("date modified"),
|
||||
menu_order = 40,
|
||||
can_collate_mixed = true,
|
||||
init_sort_func = function(cache)
|
||||
return function(a, b)
|
||||
return a.attr.modification > b.attr.modification
|
||||
end, cache
|
||||
end,
|
||||
mandatory_func = function(item)
|
||||
return datetime.secondsToDateTime(item.attr.modification)
|
||||
end,
|
||||
},
|
||||
size = {
|
||||
text = _("size"),
|
||||
menu_order = 50,
|
||||
can_collate_mixed = false,
|
||||
init_sort_func = function(cache)
|
||||
return function(a, b)
|
||||
return a.attr.size < b.attr.size
|
||||
end, cache
|
||||
end,
|
||||
},
|
||||
type = {
|
||||
text = _("type"),
|
||||
menu_order = 60,
|
||||
can_collate_mixed = false,
|
||||
init_sort_func = function(cache)
|
||||
return function(a, b)
|
||||
if (a.suffix or b.suffix) and a.suffix ~= b.suffix then
|
||||
return ffiUtil.strcoll(a.suffix, b.suffix)
|
||||
end
|
||||
return ffiUtil.strcoll(a.text, b.text)
|
||||
end, cache
|
||||
end,
|
||||
item_func = function(item)
|
||||
item.suffix = util.getFileNameSuffix(item.text)
|
||||
end,
|
||||
},
|
||||
percent_unopened_first = {
|
||||
text = _("percent - unopened first"),
|
||||
menu_order = 70,
|
||||
can_collate_mixed = false,
|
||||
init_sort_func = function(cache)
|
||||
return function(a, b)
|
||||
if a.opened == b.opened then
|
||||
if a.opened then
|
||||
return a.percent_finished < b.percent_finished
|
||||
end
|
||||
return ffiUtil.strcoll(a.text, b.text)
|
||||
end
|
||||
return b.opened
|
||||
end, cache
|
||||
end,
|
||||
item_func = function(item)
|
||||
local book_info = BookList.getBookInfo(item.path)
|
||||
item.opened = book_info.been_opened
|
||||
-- smooth 2 decimal points (0.00) instead of 16 decimal points
|
||||
item.percent_finished = util.round_decimal(book_info.percent_finished or 0, 2)
|
||||
end,
|
||||
mandatory_func = function(item)
|
||||
return item.opened and string.format("%d\u{202F}%%", 100 * item.percent_finished) or "–"
|
||||
end,
|
||||
},
|
||||
percent_unopened_last = {
|
||||
text = _("percent - unopened last"),
|
||||
menu_order = 80,
|
||||
can_collate_mixed = false,
|
||||
init_sort_func = function(cache)
|
||||
return function(a, b)
|
||||
if a.opened == b.opened then
|
||||
if a.opened then
|
||||
return a.percent_finished < b.percent_finished
|
||||
end
|
||||
return ffiUtil.strcoll(a.text, b.text)
|
||||
end
|
||||
return a.opened
|
||||
end, cache
|
||||
end,
|
||||
item_func = function(item)
|
||||
local book_info = BookList.getBookInfo(item.path)
|
||||
item.opened = book_info.been_opened
|
||||
-- smooth 2 decimal points (0.00) instead of 16 decimal points
|
||||
item.percent_finished = util.round_decimal(book_info.percent_finished or 0, 2)
|
||||
end,
|
||||
mandatory_func = function(item)
|
||||
return item.opened and string.format("%d\u{202F}%%", 100 * item.percent_finished) or "–"
|
||||
end,
|
||||
},
|
||||
percent_natural = {
|
||||
-- sort 90% > 50% > 0% > on hold > unopened > 100% or finished
|
||||
text = _("percent – unopened – finished last"),
|
||||
menu_order = 90,
|
||||
can_collate_mixed = false,
|
||||
init_sort_func = function(cache)
|
||||
local natsort
|
||||
natsort, cache = sort.natsort_cmp(cache)
|
||||
local sortfunc = function(a, b)
|
||||
if a.sort_percent == b.sort_percent then
|
||||
return natsort(a.text, b.text)
|
||||
elseif a.sort_percent == 1 then
|
||||
return false
|
||||
elseif b.sort_percent == 1 then
|
||||
return true
|
||||
else
|
||||
return a.sort_percent > b.sort_percent
|
||||
end
|
||||
end
|
||||
|
||||
return sortfunc, cache
|
||||
end,
|
||||
item_func = function(item)
|
||||
local book_info = BookList.getBookInfo(item.path)
|
||||
item.opened = book_info.been_opened
|
||||
local percent_finished = book_info.percent_finished
|
||||
local sort_percent
|
||||
if item.opened then
|
||||
-- books marked as "finished" or "on hold" should be considered the same as 100% and less than 0% respectively
|
||||
if book_info.status == "complete" then
|
||||
sort_percent = 1.0
|
||||
elseif book_info.status == "abandoned" then
|
||||
sort_percent = -0.01
|
||||
end
|
||||
end
|
||||
-- smooth 2 decimal points (0.00) instead of 16 decimal points
|
||||
item.sort_percent = sort_percent or util.round_decimal(percent_finished or -1, 2)
|
||||
item.percent_finished = percent_finished or 0
|
||||
end,
|
||||
mandatory_func = function(item)
|
||||
return item.opened and string.format("%d\u{202F}%%", 100 * item.percent_finished) or "–"
|
||||
end,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
-- Cache of content we knew of for directories that are not readable
|
||||
@@ -336,7 +165,8 @@ function FileChooser:getListItem(dirpath, f, fullpath, attributes, collate)
|
||||
item.bidi_wrap_func = BD.filename
|
||||
item.is_file = true
|
||||
if collate.item_func ~= nil then
|
||||
collate.item_func(item)
|
||||
local book_info = collate.bookinfo_required and BookList.getBookInfo(item.path)
|
||||
collate.item_func(item, book_info)
|
||||
end
|
||||
if show_file_in_bold ~= false then
|
||||
if item.opened == nil then -- could be set in item_func
|
||||
@@ -357,7 +187,7 @@ function FileChooser:getListItem(dirpath, f, fullpath, attributes, collate)
|
||||
item.text = item.text.."/"
|
||||
item.bidi_wrap_func = BD.directory
|
||||
if collate.can_collate_mixed and collate.item_func ~= nil then
|
||||
collate.item_func(item, self)
|
||||
collate.item_func(item)
|
||||
end
|
||||
if dirpath then -- file browser or PathChooser
|
||||
item.mandatory = self:getMenuItemMandatory(item)
|
||||
|
||||
@@ -713,7 +713,7 @@ function MosaicMenuItem:paintTo(bb, x, y)
|
||||
-- other paintings are anchored to the sub-widget (cover image)
|
||||
local target = self[1][1][1]
|
||||
|
||||
if self.entry.order == nil -- File manager, History
|
||||
if self.menu.name ~= "collections" -- do not show collection mark in collections
|
||||
and ReadCollection:isFileInCollections(self.filepath) then
|
||||
-- top right corner
|
||||
local ix, rect_ix
|
||||
|
||||
Reference in New Issue
Block a user