--[[ This plugin implements KOReader integration with *some* calibre features: - metadata search - wireless transfers This module handles the UI part of the plugin. --]] local BD = require("ui/bidi") local CalibreExtensions = require("extensions") local CalibreSearch = require("search") local CalibreWireless = require("wireless") local Dispatcher = require("dispatcher") local InfoMessage = require("ui/widget/infomessage") local LuaSettings = require("luasettings") local UIManager = require("ui/uimanager") local WidgetContainer = require("ui/widget/container/widgetcontainer") local _ = require("gettext") local C_ = _.pgettext local T = require("ffi/util").template local Calibre = WidgetContainer:extend{ name = "calibre", is_doc_only = false, } function Calibre:onCalibreSearch() CalibreSearch:ShowSearch() return true end function Calibre:onCalibreBrowseBy(field) CalibreSearch.search_value = "" CalibreSearch:find(field) return true end function Calibre:onNetworkDisconnected() self:closeWirelessConnection() end function Calibre:onSuspend() self:closeWirelessConnection() end function Calibre:onClose() self:closeWirelessConnection() end function Calibre:onStartWirelessConnection() self:startWirelessConnection() end function Calibre:onCloseWirelessConnection() self:closeWirelessConnection() end function Calibre:startWirelessConnection() if not CalibreWireless.calibre_socket then CalibreWireless:connect() end end function Calibre:closeWirelessConnection() if CalibreWireless.calibre_socket then CalibreWireless:disconnect() end end function Calibre:onDispatcherRegisterActions() Dispatcher:registerAction("calibre_search", { category="none", event="CalibreSearch", title=_("Calibre metadata search"), general=true,}) Dispatcher:registerAction("calibre_browse_tags", { category="none", event="CalibreBrowseBy", arg="tags", title=_("Browse all calibre tags"), general=true,}) Dispatcher:registerAction("calibre_browse_series", { category="none", event="CalibreBrowseBy", arg="series", title=_("Browse all calibre series"), general=true,}) Dispatcher:registerAction("calibre_browse_authors", { category="none", event="CalibreBrowseBy", arg="authors", title=_("Browse all calibre authors"), general=true,}) Dispatcher:registerAction("calibre_browse_titles", { category="none", event="CalibreBrowseBy", arg="title", title=_("Browse all calibre titles"), general=true, separator=true,}) Dispatcher:registerAction("calibre_start_connection", { category="none", event="StartWirelessConnection", title=_("Calibre wireless connect"), general=true,}) Dispatcher:registerAction("calibre_close_connection", { category="none", event="CloseWirelessConnection", title=_("Calibre wireless disconnect"), general=true,}) end function Calibre:init() CalibreWireless:init() self:onDispatcherRegisterActions() self.ui.menu:registerToMainMenu(self) end function Calibre:addToMainMenu(menu_items) menu_items.calibre = { -- its name is "calibre", but all our top menu items are uppercase. text = _("Calibre"), sub_item_table = { { text_func = function() if CalibreWireless.calibre_socket then return _("Disconnect") else return _("Connect") end end, separator = true, enabled_func = function() return G_reader_settings:nilOrTrue("calibre_wireless") end, callback = function() if not CalibreWireless.calibre_socket then CalibreWireless:connect() else CalibreWireless:disconnect() end end, }, { text = _("Search settings"), keep_menu_open = true, sub_item_table = self:getSearchMenuTable(), }, { text = _("Wireless settings"), keep_menu_open = true, sub_item_table = self:getWirelessMenuTable(), }, { text = _("JSON parser"), keep_menu_open = true, sub_item_table = self:getParserMenuTable(), }, } } -- insert the metadata search if G_reader_settings:isTrue("calibre_search_from_reader") or not self.ui.view then menu_items.find_book_in_calibre_catalog = { text = _("Calibre metadata search"), callback = function() CalibreSearch:ShowSearch() end } end end -- search options available from UI function Calibre:getSearchMenuTable() return { { text = _("Manage libraries"), separator = true, keep_menu_open = true, sub_item_table_func = function() local result = {} -- append previous scanned dirs to the list. local cache = LuaSettings:open(CalibreSearch.cache_libs.path) for path, _ in pairs(cache.data) do table.insert(result, { text = path, keep_menu_open = true, checked_func = function() return cache:isTrue(path) end, callback = function() cache:toggle(path) cache:flush() CalibreSearch:invalidateCache() end, }) end -- if there's no result then no libraries are stored if #result == 0 then table.insert(result, { text = _("No calibre libraries"), enabled = false }) end table.insert(result, 1, { text = _("Rescan disk for calibre libraries"), separator = true, callback = function() CalibreSearch:prompt() end, }) return result end, }, { text = _("Enable searches in the reader"), checked_func = function() return G_reader_settings:isTrue("calibre_search_from_reader") end, callback = function() G_reader_settings:toggle("calibre_search_from_reader") UIManager:show(InfoMessage:new{ text = _("This will take effect on next restart."), }) end, }, { text = _("Store metadata in cache"), checked_func = function() return G_reader_settings:nilOrTrue("calibre_search_cache_metadata") end, callback = function() G_reader_settings:flipNilOrTrue("calibre_search_cache_metadata") end, }, { text = _("Case sensitive search"), checked_func = function() return not G_reader_settings:nilOrTrue("calibre_search_case_insensitive") end, callback = function() G_reader_settings:flipNilOrTrue("calibre_search_case_insensitive") end, }, { text = _("Search by title"), checked_func = function() return G_reader_settings:nilOrTrue("calibre_search_find_by_title") end, callback = function() G_reader_settings:flipNilOrTrue("calibre_search_find_by_title") end, }, { text = _("Search by authors"), checked_func = function() return G_reader_settings:nilOrTrue("calibre_search_find_by_authors") end, callback = function() G_reader_settings:flipNilOrTrue("calibre_search_find_by_authors") end, }, { text = _("Search by series"), checked_func = function() return G_reader_settings:isTrue("calibre_search_find_by_series") end, callback = function() G_reader_settings:toggle("calibre_search_find_by_series") end, }, { text = _("Search by tag"), checked_func = function() return G_reader_settings:isTrue("calibre_search_find_by_tag") end, callback = function() G_reader_settings:toggle("calibre_search_find_by_tag") end, }, { text = _("Search by path"), checked_func = function() return G_reader_settings:isTrue("calibre_search_find_by_path") end, callback = function() G_reader_settings:toggle("calibre_search_find_by_path") end, }, } end -- wireless options available from UI function Calibre:getWirelessMenuTable() local function isEnabled() local enabled = G_reader_settings:nilOrTrue("calibre_wireless") return enabled and not CalibreWireless.calibre_socket end local t = { { text = _("Enable wireless client"), separator = true, enabled_func = function() return not CalibreWireless.calibre_socket end, checked_func = function() return G_reader_settings:nilOrTrue("calibre_wireless") end, callback = function() G_reader_settings:flipNilOrTrue("calibre_wireless") end, }, { text = _("Set password"), enabled_func = isEnabled, callback = function() CalibreWireless:setPassword() end, }, { text = _("Set inbox folder"), enabled_func = isEnabled, callback = function() CalibreWireless:setInboxDir() end, }, { text_func = function() local address = _("automatic") if G_reader_settings:has("calibre_wireless_url") then address = G_reader_settings:readSetting("calibre_wireless_url") address = string.format("%s:%s", address["address"], address["port"]) end return T(_("Server address (%1)"), BD.ltr(address)) end, enabled_func = isEnabled, sub_item_table = { { text = C_("Configuration type", "Automatic"), checked_func = function() return G_reader_settings:hasNot("calibre_wireless_url") end, callback = function() G_reader_settings:delSetting("calibre_wireless_url") end, }, { text = C_("Configuration type", "Manual"), checked_func = function() return G_reader_settings:has("calibre_wireless_url") end, callback = function(touchmenu_instance) local MultiInputDialog = require("ui/widget/multiinputdialog") local url_dialog local calibre_url = G_reader_settings:readSetting("calibre_wireless_url") local calibre_url_address, calibre_url_port if calibre_url then calibre_url_address = calibre_url["address"] calibre_url_port = calibre_url["port"] end url_dialog = MultiInputDialog:new{ title = _("Set custom calibre address"), fields = { { text = calibre_url_address, input_type = "string", hint = _("IP Address"), }, { text = calibre_url_port, input_type = "number", hint = _("Port"), }, }, buttons = { { { text = _("Cancel"), id = "close", callback = function() UIManager:close(url_dialog) end, }, { text = _("OK"), callback = function() local fields = url_dialog:getFields() if fields[1] ~= "" then local port = tonumber(fields[2]) if not port or port < 1 or port > 65355 then --default port port = 9090 end G_reader_settings:saveSetting("calibre_wireless_url", {address = fields[1], port = port }) end UIManager:close(url_dialog) if touchmenu_instance then touchmenu_instance:updateItems() end end, }, }, }, } UIManager:show(url_dialog) url_dialog:onShowKeyboard() end, }, }, }, } if not CalibreExtensions:isCustom() then table.insert(t, 2, { text = _("File formats"), enabled_func = isEnabled, sub_item_table_func = function() local submenu = { { text = _("About formats"), keep_menu_open = true, separator = true, callback = function() UIManager:show(InfoMessage:new{ text = string.format("%s: %s \n\n%s", _("Supported file formats"), CalibreExtensions:getInfo(), _("Unsupported formats will be converted by calibre to the first format of the list.")) }) end, } } for i, v in ipairs(CalibreExtensions.outputs) do table.insert(submenu, {}) submenu[i+1].text = v submenu[i+1].checked_func = function() if v == CalibreExtensions.default_output then return true end return false end submenu[i+1].callback = function() if type(v) == "string" and v ~= CalibreExtensions.default_output then CalibreExtensions.default_output = v G_reader_settings:saveSetting("calibre_wireless_default_format", CalibreExtensions.default_output) end end end return submenu end, }) end return t end function Calibre:getParserMenuTable() return { { text = _("Automatic"), help_text = _("The program will decide based on the size of the JSON file. Recommended"), checked_func = function() return G_reader_settings:hasNot("calibre_json_parser") end, callback = function() G_reader_settings:delSetting("calibre_json_parser") end, }, { text = _("Fast"), help_text = _("Faster parsing, but may not take too kindly to malformed input files"), checked_func = function() return G_reader_settings:readSetting("calibre_json_parser") == "fast" end, callback = function() G_reader_settings:saveSetting("calibre_json_parser", "fast") end, }, { text = _("Safe"), help_text = _("Slower, but safer. Useful if you're experiencing problems with the other modes"), checked_func = function() return G_reader_settings:readSetting("calibre_json_parser") == "safe" end, callback = function() G_reader_settings:saveSetting("calibre_json_parser", "safe") end, }, { text = _("Legacy"), help_text = _("Fast, but requires more RAM, only recommended on modest library sizes (or beefier devices)"), checked_func = function() return G_reader_settings:readSetting("calibre_json_parser") == "legacy" end, callback = function() G_reader_settings:saveSetting("calibre_json_parser", "legacy") end, }, } end return Calibre