diff --git a/frontend/apps/reader/modules/readerdictionary.lua b/frontend/apps/reader/modules/readerdictionary.lua index bc379ddbe..b3bbd1769 100644 --- a/frontend/apps/reader/modules/readerdictionary.lua +++ b/frontend/apps/reader/modules/readerdictionary.lua @@ -14,6 +14,7 @@ local KeyValuePage = require("ui/widget/keyvaluepage") local LuaData = require("luadata") local MultiConfirmBox = require("ui/widget/multiconfirmbox") local NetworkMgr = require("ui/network/manager") +local Presets = require("ui/presets") local SortWidget = require("ui/widget/sortwidget") local Trapper = require("ui/trapper") local UIManager = require("ui/uimanager") @@ -165,6 +166,17 @@ function ReaderDictionary:init() if not lookup_history then lookup_history = LuaData:open(DataStorage:getSettingsDir() .. "/lookup_history.lua", "LookupHistory") end + + self.preset_obj = { + presets = G_reader_settings:readSetting("dict_presets", {}), + cycle_index = G_reader_settings:readSetting("dict_presets_cycle_index"), + dispatcher_name = "load_dictionary_preset", + saveCycleIndex = function(this) + G_reader_settings:saveSetting("dict_presets_cycle_index", this.cycle_index) + end, + buildPreset = function() return self:buildPreset() end, + loadPreset = function(preset) self:loadPreset(preset) end, + } end function ReaderDictionary:registerKeyEvents() @@ -281,9 +293,18 @@ function ReaderDictionary:addToMainMenu(menu_items) end) end, }, + { + text = _("Dictionary presets"), + help_text = _("This feature allows you to organize dictionaries into presets (for example, by language). You can quickly switch between these presets to change which dictionaries are used for lookups.\n\nNote: presets only store dictionaries, no other settings."), + sub_item_table_func = function() + return Presets.genPresetMenuItemTable(self.preset_obj, _("Create new preset from enabled dictionaries"), + function() return self.enabled_dict_names and #self.enabled_dict_names > 0 end) + end, + }, { text = _("Download dictionaries"), sub_item_table_func = function() return self:_genDownloadDictionariesMenu() end, + separator = true, }, { text_func = function() @@ -313,32 +334,40 @@ function ReaderDictionary:addToMainMenu(menu_items) separator = true, }, { - text = _("Enable dictionary lookup history"), + text = _("Dictionary lookup history"), checked_func = function() return not self.disable_lookup_history end, - callback = function() - self.disable_lookup_history = not self.disable_lookup_history - G_reader_settings:saveSetting("disable_lookup_history", self.disable_lookup_history) - end, - }, - { - text = _("Clean dictionary lookup history"), - enabled_func = function() - return lookup_history:has("lookup_history") - end, - keep_menu_open = true, - callback = function(touchmenu_instance) - UIManager:show(ConfirmBox:new{ - text = _("Clean dictionary lookup history?"), - ok_text = _("Clean"), - ok_callback = function() - -- empty data table to replace current one - lookup_history:reset{} - touchmenu_instance:updateItems() + sub_item_table = { + { + text = _("Enable dictionary lookup history"), + checked_func = function() + return not self.disable_lookup_history end, - }) - end, + callback = function() + self.disable_lookup_history = not self.disable_lookup_history + G_reader_settings:saveSetting("disable_lookup_history", self.disable_lookup_history) + end, + }, + { + text = _("Clean dictionary lookup history"), + enabled_func = function() + return lookup_history:has("lookup_history") + end, + keep_menu_open = true, + callback = function(touchmenu_instance) + UIManager:show(ConfirmBox:new{ + text = _("Clean dictionary lookup history?"), + ok_text = _("Clean"), + ok_callback = function() + -- empty data table to replace current one + lookup_history:reset{} + touchmenu_instance:updateItems() + end, + }) + end, + }, + }, separator = true, }, { -- setting used by dictquicklookup @@ -385,7 +414,7 @@ function ReaderDictionary:addToMainMenu(menu_items) } } if not is_docless then - table.insert(menu_items.dictionary_settings.sub_item_table, 3, { + table.insert(menu_items.dictionary_settings.sub_item_table, 2, { keep_menu_open = true, text = _("Set dictionary priority for this book"), help_text = _("This feature enables you to specify dictionary priorities on a per-book basis. Results from higher-priority dictionaries will be displayed first when looking up words. Only dictionaries that are currently active can be selected and prioritized."), @@ -859,31 +888,70 @@ function ReaderDictionary:dismissLookupInfo() end function ReaderDictionary:onShowDictionaryLookup() + local buttons = {} + local preset_names = Presets.getPresets(self.preset_obj) + if preset_names and #preset_names > 0 then + table.insert(buttons, { + { + text = _("Search with preset"), + callback = function() + local text = self.dictionary_lookup_dialog:getInputText() + if text == "" or text:match("^%s*$") then return end + local current_dict_state = self:buildPreset() + local button_dialog, dialog_buttons = nil, {} -- CI won't like it if we call it buttons :( so dialog_buttons + for _, preset_name in ipairs(preset_names) do + table.insert(dialog_buttons, { + { + align = "left", + text = preset_name, + callback = function() + self:loadPreset(self.preset_obj.presets[preset_name], true) + UIManager:close(button_dialog) + UIManager:close(self.dictionary_lookup_dialog) + self:onLookupWord(text, true, nil, nil, nil, + function() + self:loadPreset(current_dict_state, true) + end + ) + end, + } + }) + end + button_dialog = ButtonDialog:new{ + buttons = dialog_buttons, + shrink_unneeded_width = true, + } + UIManager:show(button_dialog) + end, + } + }) + end + + table.insert(buttons, { + { + text = _("Cancel"), + id = "close", + callback = function() + UIManager:close(self.dictionary_lookup_dialog) + end, + }, + { + text = _("Search dictionary"), + is_enter_default = true, + callback = function() + if self.dictionary_lookup_dialog:getInputText() == "" then return end + UIManager:close(self.dictionary_lookup_dialog) + -- Trust that input text does not need any cleaning (allows querying for "-suffix") + self:onLookupWord(self.dictionary_lookup_dialog:getInputText(), true) + end, + }, + }) + self.dictionary_lookup_dialog = InputDialog:new{ title = _("Enter a word or phrase to look up"), input = "", input_type = "text", - buttons = { - { - { - text = _("Cancel"), - id = "close", - callback = function() - UIManager:close(self.dictionary_lookup_dialog) - end, - }, - { - text = _("Search dictionary"), - is_enter_default = true, - callback = function() - if self.dictionary_lookup_dialog:getInputText() == "" then return end - UIManager:close(self.dictionary_lookup_dialog) - -- Trust that input text does not need any cleaning (allows querying for "-suffix") - self:onLookupWord(self.dictionary_lookup_dialog:getInputText(), true) - end, - }, - } - }, + buttons = buttons, } UIManager:show(self.dictionary_lookup_dialog) self.dictionary_lookup_dialog:onShowKeyboard() @@ -1459,4 +1527,64 @@ The current default (★) is enabled.]]) }) end +function ReaderDictionary:buildPreset() + local preset = { enabled_dict_names = {} } -- Only store the names of enabled dictionaries. + for _, name in ipairs(self.enabled_dict_names) do + preset.enabled_dict_names[name] = true + end + return preset +end + +function ReaderDictionary:loadPreset(preset, skip_notification) + if not preset.enabled_dict_names then return end + -- build a list of currently available dictionary names for validation + local available_dict_names = {} + for _, ifo in ipairs(available_ifos) do + available_dict_names[ifo.name] = true + end + -- Only enable dictionaries from the preset that are still available, and re-build self.dicts_disabled + -- to make sure dicts added after the creation of the preset, are disabled as well. + local dicts_disabled, valid_enabled_names = {}, {} + for _, ifo in ipairs(available_ifos) do + if preset.enabled_dict_names[ifo.name] then + table.insert(valid_enabled_names, ifo.name) + else + dicts_disabled[ifo.file] = true + end + end + -- update both settings and save + self.dicts_disabled = dicts_disabled + self.enabled_dict_names = valid_enabled_names + G_reader_settings:saveSetting("dicts_disabled", self.dicts_disabled) + self:onSaveSettings() + self:updateSdcvDictNamesOptions() + -- Show a message if any dictionaries from the preset are missing. + if not skip_notification and util.tableSize(preset.enabled_dict_names) > #valid_enabled_names then + local missing_dicts = {} + for preset_name, _ in pairs(preset.enabled_dict_names) do + if not available_dict_names[preset_name] then + table.insert(missing_dicts, preset_name) + end + end + UIManager:show(InfoMessage:new{ + text = _("Some dictionaries from this preset have been deleted or are no longer available:") .. "\n\n• " .. table.concat(missing_dicts, "\n• "), + }) + end +end + +function ReaderDictionary:onCycleDictionaryPresets() + return Presets.cycleThroughPresets(self.preset_obj, true) +end + +function ReaderDictionary:onLoadDictionaryPreset(preset_name) + return Presets.onLoadPreset(self.preset_obj, preset_name, true) +end + +function ReaderDictionary.getPresets() -- for Dispatcher + local dict_config = { + presets = G_reader_settings:readSetting("dict_presets", {}) + } + return Presets.getPresets(dict_config) +end + return ReaderDictionary diff --git a/frontend/apps/reader/modules/readerfooter.lua b/frontend/apps/reader/modules/readerfooter.lua index b52f3ba0f..a653f5e5a 100644 --- a/frontend/apps/reader/modules/readerfooter.lua +++ b/frontend/apps/reader/modules/readerfooter.lua @@ -9,12 +9,10 @@ local FrameContainer = require("ui/widget/container/framecontainer") local Geom = require("ui/geometry") local HorizontalGroup = require("ui/widget/horizontalgroup") local HorizontalSpan = require("ui/widget/horizontalspan") -local InfoMessage = require("ui/widget/infomessage") -local InputDialog = require("ui/widget/inputdialog") local LeftContainer = require("ui/widget/container/leftcontainer") local LineWidget = require("ui/widget/linewidget") -local MultiConfirmBox = require("ui/widget/multiconfirmbox") local MultiInputDialog = require("ui/widget/multiinputdialog") +local Presets = require("ui/presets") local ProgressWidget = require("ui/widget/progresswidget") local RightContainer = require("ui/widget/container/rightcontainer") local Size = require("ui/size") @@ -25,7 +23,6 @@ local VerticalGroup = require("ui/widget/verticalgroup") local VerticalSpan = require("ui/widget/verticalspan") local WidgetContainer = require("ui/widget/container/widgetcontainer") local datetime = require("datetime") -local ffiUtil = require("ffi/util") local logger = require("logger") local util = require("util") local T = require("ffi/util").template @@ -665,6 +662,13 @@ function ReaderFooter:init() self.custom_text = G_reader_settings:readSetting("reader_footer_custom_text", "KOReader") self.custom_text_repetitions = tonumber(G_reader_settings:readSetting("reader_footer_custom_text_repetitions", "1")) + + self.preset_obj = { + presets = G_reader_settings:readSetting("footer_presets", {}), + dispatcher_name = "load_footer_preset", + buildPreset = function() return self:buildPreset() end, + loadPreset = function(preset) self:loadPreset(preset) end, + } end function ReaderFooter:set_custom_text(touchmenu_instance) @@ -1710,7 +1714,7 @@ With this feature enabled, the current page is factored in, resulting in the cou text = _("Status bar presets"), separator = true, sub_item_table_func = function() - return self:genPresetMenuItemTable() + return Presets.genPresetMenuItemTable(self.preset_obj, nil, nil) end, }) table.insert(sub_items, { @@ -1930,91 +1934,6 @@ function ReaderFooter:genAlignmentMenuItems(value) } end -function ReaderFooter:genPresetMenuItemTable() - local footer_presets = G_reader_settings:readSetting("footer_presets", {}) - local items = { - { - text = _("Create new preset from current settings"), - keep_menu_open = true, - callback = function(touchmenu_instance) - self:createPresetFromCurrentSettings(touchmenu_instance) - end, - separator = true, - }, - } - for preset_name in ffiUtil.orderedPairs(footer_presets) do - table.insert(items, { - text = preset_name, - keep_menu_open = true, - callback = function() - self:loadPreset(footer_presets[preset_name]) - end, - hold_callback = function(touchmenu_instance, item) - UIManager:show(MultiConfirmBox:new{ - text = T(_("What would you like to do with preset '%1'?"), preset_name), - choice1_text = _("Delete"), - choice1_callback = function() - footer_presets[preset_name] = nil - UIManager:broadcastEvent(Event:new("DispatcherActionValueChanged", - { name = "load_footer_preset", old_value = preset_name, new_value = nil })) - table.remove(touchmenu_instance.item_table, item.idx) - touchmenu_instance:updateItems() - end, - choice2_text = _("Update"), - choice2_callback = function() - footer_presets[preset_name] = self:buildPreset() - UIManager:show(InfoMessage:new{ - text = T(_("Preset '%1' was updated with current settings"), preset_name), - timeout = 2, - }) - end, - }) - end, - }) - end - return items -end - -function ReaderFooter:createPresetFromCurrentSettings(touchmenu_instance) - local input_dialog - input_dialog = InputDialog:new{ - title = _("Enter preset name"), - buttons = { - { - { - text = _("Cancel"), - id = "close", - callback = function() - UIManager:close(input_dialog) - end, - }, - { - text = _("Save"), - is_enter_default = true, - callback = function() - local preset_name = input_dialog:getInputText() - if preset_name == "" or preset_name:match("^%s*$") then return end - local footer_presets = G_reader_settings:readSetting("footer_presets") - if footer_presets[preset_name] then - UIManager:show(InfoMessage:new{ - text = T(_("A preset named '%1' already exists. Please choose a different name."), preset_name), - timeout = 2, - }) - else - footer_presets[preset_name] = self:buildPreset() - UIManager:close(input_dialog) - touchmenu_instance.item_table = self:genPresetMenuItemTable() - touchmenu_instance:updateItems() - end - end, - }, - }, - }, - } - UIManager:show(input_dialog) - input_dialog:onShowKeyboard() -end - function ReaderFooter:buildPreset() return { footer = util.tableDeepCopy(self.settings), @@ -2048,25 +1967,14 @@ function ReaderFooter:loadPreset(preset) end function ReaderFooter:onLoadFooterPreset(preset_name) - local footer_presets = G_reader_settings:readSetting("footer_presets") - if footer_presets and footer_presets[preset_name] then - self:loadPreset(footer_presets[preset_name]) - end - return true + return Presets.onLoadPreset(self.preset_obj, preset_name, true) end function ReaderFooter.getPresets() -- for Dispatcher - local footer_presets = G_reader_settings:readSetting("footer_presets") - local actions = {} - if footer_presets and next(footer_presets) then - for preset_name in pairs(footer_presets) do - table.insert(actions, preset_name) - end - if #actions > 1 then - table.sort(actions) - end - end - return actions, actions + local footer_config = { + presets = G_reader_settings:readSetting("footer_presets", {}) + } + return Presets.getPresets(footer_config) end function ReaderFooter:addAdditionalFooterContent(content_func) diff --git a/frontend/apps/reader/modules/readerwikipedia.lua b/frontend/apps/reader/modules/readerwikipedia.lua index 18d1c10f2..a57002355 100644 --- a/frontend/apps/reader/modules/readerwikipedia.lua +++ b/frontend/apps/reader/modules/readerwikipedia.lua @@ -260,32 +260,40 @@ You can choose an existing folder, or use a default folder named "Wikipedia" in separator = true, }, { - text = _("Enable Wikipedia history"), + text = _("Wikipedia lookup history"), checked_func = function() return not self.disable_history end, - callback = function() - self.disable_history = not self.disable_history - G_reader_settings:saveSetting("wikipedia_disable_history", self.disable_history) - end, - }, - { - text = _("Clean Wikipedia history"), - enabled_func = function() - return wikipedia_history:has("wikipedia_history") - end, - keep_menu_open = true, - callback = function(touchmenu_instance) - UIManager:show(ConfirmBox:new{ - text = _("Clean Wikipedia history?"), - ok_text = _("Clean"), - ok_callback = function() - -- empty data table to replace current one - wikipedia_history:reset{} - touchmenu_instance:updateItems() + sub_item_table = { + { + text = _("Enable Wikipedia history"), + checked_func = function() + return not self.disable_history end, - }) - end, + callback = function() + self.disable_history = not self.disable_history + G_reader_settings:saveSetting("wikipedia_disable_history", self.disable_history) + end, + }, + { + text = _("Clean Wikipedia history"), + enabled_func = function() + return wikipedia_history:has("wikipedia_history") + end, + keep_menu_open = true, + callback = function(touchmenu_instance) + UIManager:show(ConfirmBox:new{ + text = _("Clean Wikipedia history?"), + ok_text = _("Clean"), + ok_callback = function() + -- empty data table to replace current one + wikipedia_history:reset{} + touchmenu_instance:updateItems() + end, + }) + end, + }, + }, separator = true, }, { -- setting used in wikipedia.lua diff --git a/frontend/dispatcher.lua b/frontend/dispatcher.lua index 9113ea317..d5f266474 100644 --- a/frontend/dispatcher.lua +++ b/frontend/dispatcher.lua @@ -34,6 +34,7 @@ local Device = require("device") local Event = require("ui/event") local FileManager = require("apps/filemanager/filemanager") local Notification = require("ui/widget/notification") +local ReaderDictionary = require("apps/reader/modules/readerdictionary") local ReaderFooter = require("apps/reader/modules/readerfooter") local ReaderHighlight = require("apps/reader/modules/readerhighlight") local ReaderZooming = require("apps/reader/modules/readerzooming") @@ -60,6 +61,8 @@ local settingsList = { collections_search = {category="none", event="ShowCollectionsSearchDialog", title=_("Collections search"), general=true, separator=true}, ---- dictionary_lookup = {category="none", event="ShowDictionaryLookup", title=_("Dictionary lookup"), general=true}, + load_dictionary_preset = {category="string", event="LoadDictionaryPreset", title=_("Load dictionary preset"), args_func=ReaderDictionary.getPresets, general=true}, + cycle_dictionary_preset = {category="none", event="CycleDictionaryPresets", title=_("Cycle through dictionary presets"), general=true,}, wikipedia_lookup = {category="none", event="ShowWikipediaLookup", title=_("Wikipedia lookup"), general=true, separator=true}, ---- show_menu = {category="none", event="ShowMenu", title=_("Show menu"), general=true}, @@ -300,6 +303,8 @@ local dispatcher_menu_order = { "collections_search", ---- "dictionary_lookup", + "load_dictionary_preset", + "cycle_dictionary_preset", "wikipedia_lookup", ---- "show_menu", diff --git a/frontend/ui/presets.lua b/frontend/ui/presets.lua new file mode 100644 index 000000000..29f1aae32 --- /dev/null +++ b/frontend/ui/presets.lua @@ -0,0 +1,325 @@ +--[[-- +This module provides a unified interface for managing presets across different KOReader modules. +It handles creation, loading, updating, and deletion of presets, as well as menu generation. + +Usage: + local Presets = require("ui/presets") + + -- 1. In your module's init() method, set up a preset object: + self.preset_obj = { + presets = G_reader_settings:readSetting("my_module_presets", {}), -- or custom storage + cycle_index = G_reader_settings:readSetting("my_module_presets_cycle_index"), -- optional, only needed if cycling through presets + dispatcher_name = "load_my_module_preset", -- must match dispatcher.lua entry + saveCycleIndex = function(this) -- Save cycle index to persistent storage + G_reader_settings:saveSetting("my_module_presets_cycle_index", this.cycle_index) + end, + buildPreset = function() return self:buildPreset() end, -- Closure to build a preset from current state + loadPreset = function(preset) self:loadPreset(preset) end, -- Closure to apply a preset to the module + } + + -- 2. Implement required methods in your module: + function MyModule:buildPreset() + return { + -- Return a table with the settings you want to save in the preset + setting1 = self.setting1, + setting2 = self.setting2, + enabled_features = self.enabled_features, + } + end + + function MyModule:loadPreset(preset) + -- Apply the preset settings to your module + self.setting1 = preset.setting1 + self.setting2 = preset.setting2 + self.enabled_features = preset.enabled_features + -- Update UI or perform other necessary changes + self:refresh() + end + + -- 3. Create menu items for presets: (Alternatively, you could call Presets.genPresetMenuItemTable directly from touchmenu_instance) + function MyModule:genPresetMenuItemTable(touchmenu_instance) + return Presets.genPresetMenuItemTable( + self.preset_obj, -- preset object + _("Create new preset from current settings"), -- optional: custom text for UI menu + function() return self:hasValidSettings() end, -- optional: function to enable/disable creating presets + ) + end + + -- 4. Load a preset by name (for dispatcher/event handling): + function MyModule:onLoadMyModulePreset(preset_name) + return Presets.onLoadPreset( + self.preset_obj, + preset_name, + true -- show notification + ) + end + + -- 5. Cycle through presets (for dispatcher/event handling): + function MyModule:onCycleMyModulePresets() + return Presets.cycleThroughPresets( + self.preset_obj, + true -- show notification + ) + end + + -- 6. Get list of available presets (for dispatcher): + function MyModule.getPresets() -- Note: This is a static method on MyModule + local config = { + presets = G_reader_settings:readSetting("my_module_presets", {}) + } + return Presets.getPresets(config) + end + + -- 7. Add to dispatcher.lua: + load_my_module_preset = { + category = "string", + event = "LoadMyModulePreset", + title = _("Load my module preset"), + args_func = MyModule.getPresets, + reader = true + }, + cycle_my_module_preset = { + category = "none", + event = "CycleMyModulePresets", + title = _("Cycle through my module presets"), + reader = true + }, + +Required preset_obj fields: + - presets: table containing saved presets + - cycle_index: current index for cycling through presets (optional, defaults to 0) + - dispatcher_name: string matching the dispatcher action name (for dispatcher integration) + - saveCycleIndex(this): function to save cycle index (optional, only needed if cycling is used) + +Required module methods: + - buildPreset(): returns a table with the current settings to save as a preset + - loadPreset(preset): applies the settings from the preset table to the module + +The preset system handles: + - Creating, updating, deleting, and renaming presets through UI dialogs + - Generating menu items with hold actions for preset management + - Saving/loading presets to/from G_reader_settings (or custom storage) + - Cycling through presets with wrap-around + - User notifications when presets are loaded/updated/created + - Integration with Dispatcher for gesture/hotkey/profile support + - Broadcasting events to update dispatcher when presets change + - Input validation and duplicate name prevention +--]] + +local ConfirmBox = require("ui/widget/confirmbox") +local Event = require("ui/event") +local InfoMessage = require("ui/widget/infomessage") +local InputDialog = require("ui/widget/inputdialog") +local Notification = require("ui/widget/notification") +local UIManager = require("ui/uimanager") +local ffiUtil = require("ffi/util") +local T = require("ffi/util").template +local _ = require("gettext") + +local Presets = {} + +function Presets.editPresetName(options, preset_obj, on_success_callback) + local input_dialog + input_dialog = InputDialog:new{ + title = options.title or _("Enter preset name"), + input = options.initial_value or "", + buttons = { + { + { + text = _("Cancel"), + id = "close", + callback = function() + UIManager:close(input_dialog) + end, + }, + { + text = options.confirm_button_text or _("Create"), + is_enter_default = true, + callback = function() + local entered_preset_name = input_dialog:getInputText() + if entered_preset_name == "" or entered_preset_name:match("^%s*$") then + UIManager:show(InfoMessage:new{ + text = _("Invalid preset name. Please choose a different name."), + timeout = 2, + }) + return false + end + if options.initial_value and entered_preset_name == options.initial_value then + UIManager:close(input_dialog) + return false + end + if preset_obj.presets[entered_preset_name] then + UIManager:show(InfoMessage:new{ + text = T(_("A preset named '%1' already exists. Please choose a different name."), entered_preset_name), + timeout = 2, + }) + return false + end + + -- If all validation passes, call the success callback + on_success_callback(entered_preset_name) + UIManager:close(input_dialog) + end, + }, + }, + }, + } + UIManager:show(input_dialog) + input_dialog:onShowKeyboard() +end + +function Presets.genPresetMenuItemTable(preset_obj, text, enabled_func) + local presets = preset_obj.presets + local items = { + { + text = text or _("Create new preset from current settings"), + keep_menu_open = true, + enabled_func = enabled_func, + callback = function(touchmenu_instance) + Presets.editPresetName({}, preset_obj, + function(entered_preset_name) + local preset_data = preset_obj.buildPreset() + preset_obj.presets[entered_preset_name] = preset_data + touchmenu_instance.item_table = Presets.genPresetMenuItemTable(preset_obj) + touchmenu_instance:updateItems() + end + ) + end, + separator = true, + }, + } + for preset_name in ffiUtil.orderedPairs(presets) do + table.insert(items, { + text = preset_name, + keep_menu_open = true, + callback = function() + preset_obj.loadPreset(presets[preset_name]) + -- There's no guarantee that it'll be obvious to the user that the preset was loaded so, we show a notification. + UIManager:show(InfoMessage:new{ + text = T(_("Preset '%1' loaded successfully."), preset_name), + timeout = 2, + }) + end, + hold_callback = function(touchmenu_instance, item) + UIManager:show(ConfirmBox:new{ + text = T(_("What would you like to do with preset '%1'?"), preset_name), + icon = "notice-question", + ok_text = _("Update"), + ok_callback = function() + UIManager:show(ConfirmBox:new{ + text = T(_("Are you sure you want to overwrite preset '%1' with current settings?"), preset_name), + ok_callback = function() + presets[preset_name] = preset_obj.buildPreset() + UIManager:show(InfoMessage:new{ + text = T(_("Preset '%1' was updated with current settings"), preset_name), + timeout = 2, + }) + end, + }) + end, + other_buttons_first = true, + other_buttons = { + { + { + text = _("Delete"), + callback = function() + UIManager:show(ConfirmBox:new{ + text = T(_("Are you sure you want to delete preset '%1'?"), preset_name), + ok_text = _("Delete"), + ok_callback = function() + presets[preset_name] = nil + local action_key = preset_obj.dispatcher_name + if action_key then + UIManager:broadcastEvent(Event:new("DispatcherActionValueChanged", { + name = action_key, + old_value = preset_name, + new_value = nil -- delete the action + })) + end + table.remove(touchmenu_instance.item_table, item.idx) + touchmenu_instance:updateItems() + end, + }) + end, + }, + { + text = _("Rename"), + callback = function() + Presets.editPresetName({ + title = _("Enter new preset name"), + initial_value = preset_name, + confirm_button_text = _("Rename"), + }, preset_obj, + function(new_name) + presets[new_name] = presets[preset_name] + presets[preset_name] = nil + local action_key = preset_obj.dispatcher_name + if action_key then + UIManager:broadcastEvent(Event:new("DispatcherActionValueChanged", { + name = action_key, + old_value = preset_name, + new_value = new_name + })) + end + touchmenu_instance.item_table = Presets.genPresetMenuItemTable(preset_obj) + touchmenu_instance:updateItems() + end) -- editPresetName + end, -- rename callback + }, + }, + }, -- end of other_buttons + }) -- end of ConfirmBox + end, -- hold_callback + }) -- end of table.insert + end -- for each preset + return items +end + + +function Presets.onLoadPreset(preset_obj, preset_name, show_notification) + local presets = preset_obj.presets + if presets and presets[preset_name] then + preset_obj.loadPreset(presets[preset_name]) + if show_notification then + Notification:notify(T(_("Preset '%1' was loaded"), preset_name)) + end + end + return true +end + +function Presets.cycleThroughPresets(preset_obj, show_notification) + local preset_names = Presets.getPresets(preset_obj) + if #preset_names == 0 then + Notification:notify(_("No presets available"), Notification.SOURCE_ALWAYS_SHOW) + return true -- we *must* return true here to prevent further event propagation, i.e multiple notifications + end + -- Get and increment index, wrap around if needed + local index = (preset_obj.cycle_index or 0) + 1 + if index > #preset_names then + index = 1 + end + local next_preset_name = preset_names[index] + preset_obj.loadPreset(preset_obj.presets[next_preset_name]) + preset_obj.cycle_index = index + preset_obj:saveCycleIndex() + if show_notification then + Notification:notify(T(_("Loaded preset: %1"), next_preset_name)) + end + return true +end + +function Presets.getPresets(preset_obj) + local presets = preset_obj.presets + local actions = {} + if presets and next(presets) then + for preset_name in pairs(presets) do + table.insert(actions, preset_name) + end + if #actions > 1 then + table.sort(actions) + end + end + return actions, actions +end + +return Presets diff --git a/frontend/ui/widget/dictquicklookup.lua b/frontend/ui/widget/dictquicklookup.lua index 576eec263..66e55c31c 100644 --- a/frontend/ui/widget/dictquicklookup.lua +++ b/frontend/ui/widget/dictquicklookup.lua @@ -21,6 +21,7 @@ local Size = require("ui/size") local TextWidget = require("ui/widget/textwidget") local TitleBar = require("ui/widget/titlebar") local Translator = require("ui/translator") +local Presets = require("ui/presets") local UIManager = require("ui/uimanager") local VerticalGroup = require("ui/widget/verticalgroup") local VerticalSpan = require("ui/widget/verticalspan") @@ -1404,57 +1405,96 @@ function DictQuickLookup:onForwardingPanRelease(arg, ges) end function DictQuickLookup:onLookupInputWord(hint) + local buttons = { + { + { + text = _("Translate"), + callback = function() + local text = self.input_dialog:getInputText() + if text ~= "" then + UIManager:close(self.input_dialog) + Translator:showTranslation(text, true) + end + end, + }, + { + text = _("Search Wikipedia"), + is_enter_default = self.is_wiki, + callback = function() + local text = self.input_dialog:getInputText() + if text ~= "" then + UIManager:close(self.input_dialog) + self.is_wiki = true + self:lookupWikipedia(false, text, true) + end + end, + }, + }, + { + { + text = _("Cancel"), + id = "close", + callback = function() + UIManager:close(self.input_dialog) + end, + }, + { + text = _("Search dictionary"), + is_enter_default = not self.is_wiki, + callback = function() + local text = self.input_dialog:getInputText() + if text ~= "" then + UIManager:close(self.input_dialog) + self.is_wiki = false + self.ui:handleEvent(Event:new("LookupWord", text, true)) + end + end, + }, + }, + } + local preset_names = Presets.getPresets(self.ui.dictionary.preset_obj) + if preset_names and #preset_names > 0 then + table.insert(buttons, 2, { + { + text = _("Search with preset"), + callback = function() + local text = self.input_dialog:getInputText() + if text == "" or text:match("^%s*$") then return end + local current_dict_state = self.ui.dictionary:buildPreset() + local button_dialog, dialog_buttons = nil, {} -- CI won't like it if we call it buttons :( so dialog_buttons + for _, preset_name in ipairs(preset_names) do + table.insert(dialog_buttons, { + { + align = "left", + text = preset_name, + callback = function() + self.ui.dictionary:loadPreset(self.ui.dictionary.preset_obj.presets[preset_name], true) + UIManager:close(button_dialog) + UIManager:close(self.input_dialog) + self.ui:handleEvent(Event:new("LookupWord", text, true, nil, nil, nil, + function() + -- Restore original preset _after_ lookup is complete + self.ui.dictionary:loadPreset(current_dict_state, true) + end + )) + end + } + }) + end + button_dialog = ButtonDialog:new{ + buttons = dialog_buttons, + shrink_unneeded_width = true, + } + UIManager:show(button_dialog) + end, + } + }) + end self.input_dialog = InputDialog:new{ title = _("Enter a word or phrase to look up"), input = hint, input_hint = hint, - buttons = { - { - { - text = _("Translate"), - callback = function() - local text = self.input_dialog:getInputText() - if text ~= "" then - UIManager:close(self.input_dialog) - Translator:showTranslation(text, true) - end - end, - }, - { - text = _("Search Wikipedia"), - is_enter_default = self.is_wiki, - callback = function() - local text = self.input_dialog:getInputText() - if text ~= "" then - UIManager:close(self.input_dialog) - self.is_wiki = true - self:lookupWikipedia(false, text, true) - end - end, - }, - }, - { - { - text = _("Cancel"), - id = "close", - callback = function() - UIManager:close(self.input_dialog) - end, - }, - { - text = _("Search dictionary"), - is_enter_default = not self.is_wiki, - callback = function() - local text = self.input_dialog:getInputText() - if text ~= "" then - UIManager:close(self.input_dialog) - self.is_wiki = false - self.ui:handleEvent(Event:new("LookupWord", text, true)) - end - end, - }, - }, - }, + buttons = buttons, } UIManager:show(self.input_dialog) self.input_dialog:onShowKeyboard()