mirror of
https://github.com/koreader/koreader.git
synced 2025-08-10 00:52:38 +00:00
Hyphenation: add custom hyphenation rules (#7746)
The hyphenation of a word can be changed from its default by long pressing for 3 seconds and selecting 'Hyphenate'. These overrides are stored in a per-language file, i.e: koreader/settings/user-German.hyph.
This commit is contained in:
2
base
2
base
Submodule base updated: 96c002d526...502bba07e5
@@ -800,7 +800,7 @@ function ReaderDictionary:startSdcv(word, dict_names, fuzzy_search)
|
||||
-- dummy results
|
||||
final_results = {
|
||||
{
|
||||
dict = _("Not available"),
|
||||
dict = "",
|
||||
word = word,
|
||||
definition = lookup_cancelled and _("Dictionary lookup interrupted.") or _("No results."),
|
||||
no_result = true,
|
||||
@@ -849,22 +849,6 @@ function ReaderDictionary:stardictLookup(word, dict_names, fuzzy_search, box, li
|
||||
return
|
||||
end
|
||||
|
||||
-- If the user disabled all the dictionaries, go away.
|
||||
if dict_names and #dict_names == 0 then
|
||||
-- Dummy result
|
||||
local nope = {
|
||||
{
|
||||
dict = _("Not available"),
|
||||
word = word,
|
||||
definition = _("There are no enabled dictionaries.\nPlease check the 'Dictionary settings' menu."),
|
||||
no_result = true,
|
||||
lookup_cancelled = false,
|
||||
}
|
||||
}
|
||||
self:showDict(word, nope, box, link)
|
||||
return
|
||||
end
|
||||
|
||||
self:showLookupInfo(word, self.lookup_msg_delay)
|
||||
|
||||
self._lookup_start_tv = UIManager:getTime()
|
||||
|
||||
@@ -209,7 +209,7 @@ function ReaderFont:onSetFontSize(new_size)
|
||||
self.font_size = new_size
|
||||
self.ui.document:setFontSize(Screen:scaleBySize(new_size))
|
||||
self.ui:handleEvent(Event:new("UpdatePos"))
|
||||
Notification:notify(T(_("Font size set to: %1."), self.font_size))
|
||||
Notification:notify(T(_("Font size set to %1."), self.font_size))
|
||||
return true
|
||||
end
|
||||
|
||||
@@ -217,7 +217,7 @@ function ReaderFont:onSetLineSpace(space)
|
||||
self.line_space_percent = math.min(200, math.max(50, space))
|
||||
self.ui.document:setInterlineSpacePercent(self.line_space_percent)
|
||||
self.ui:handleEvent(Event:new("UpdatePos"))
|
||||
Notification:notify(T(_("Line spacing set to: %1%."), self.line_space_percent))
|
||||
Notification:notify(T(_("Line spacing set to %1%."), self.line_space_percent))
|
||||
return true
|
||||
end
|
||||
|
||||
@@ -225,7 +225,7 @@ function ReaderFont:onSetFontBaseWeight(weight)
|
||||
self.font_base_weight = weight
|
||||
self.ui.document:setFontBaseWeight(weight)
|
||||
self.ui:handleEvent(Event:new("UpdatePos"))
|
||||
Notification:notify(T(_("Font weight set to: %1."), optionsutil:getOptionText("SetFontBaseWeight", weight)))
|
||||
Notification:notify(T(_("Font weight set to %1."), optionsutil:getOptionText("SetFontBaseWeight", weight)))
|
||||
return true
|
||||
end
|
||||
|
||||
@@ -233,7 +233,7 @@ function ReaderFont:onSetFontHinting(mode)
|
||||
self.font_hinting = mode
|
||||
self.ui.document:setFontHinting(mode)
|
||||
self.ui:handleEvent(Event:new("UpdatePos"))
|
||||
Notification:notify(T(_("Font hinting set to: %1"), optionsutil:getOptionText("SetFontHinting", mode)))
|
||||
Notification:notify(T(_("Font hinting set to %1."), optionsutil:getOptionText("SetFontHinting", mode)))
|
||||
return true
|
||||
end
|
||||
|
||||
@@ -241,7 +241,7 @@ function ReaderFont:onSetFontKerning(mode)
|
||||
self.font_kerning = mode
|
||||
self.ui.document:setFontKerning(mode)
|
||||
self.ui:handleEvent(Event:new("UpdatePos"))
|
||||
Notification:notify(T(_("Font kerning set to: %1"), optionsutil:getOptionText("SetFontKerning", mode)))
|
||||
Notification:notify(T(_("Font kerning set to %1."), optionsutil:getOptionText("SetFontKerning", mode)))
|
||||
return true
|
||||
end
|
||||
|
||||
@@ -249,7 +249,7 @@ function ReaderFont:onSetWordSpacing(values)
|
||||
self.word_spacing = values
|
||||
self.ui.document:setWordSpacing(values)
|
||||
self.ui:handleEvent(Event:new("UpdatePos"))
|
||||
Notification:notify(T(_("Word spacing set to: %1%, %2%"), values[1], values[2]))
|
||||
Notification:notify(T(_("Word spacing set to %1%, %2%."), values[1], values[2]))
|
||||
return true
|
||||
end
|
||||
|
||||
@@ -257,7 +257,7 @@ function ReaderFont:onSetWordExpansion(value)
|
||||
self.word_expansion = value
|
||||
self.ui.document:setWordExpansion(value)
|
||||
self.ui:handleEvent(Event:new("UpdatePos"))
|
||||
Notification:notify(T(_("Word expansion set to: %1%."), value))
|
||||
Notification:notify(T(_("Word expansion set to %1%."), value))
|
||||
return true
|
||||
end
|
||||
|
||||
@@ -266,7 +266,7 @@ function ReaderFont:onSetFontGamma(gamma)
|
||||
self.ui.document:setGammaIndex(self.gamma_index)
|
||||
local gamma_level = self.ui.document:getGammaLevel()
|
||||
self.ui:handleEvent(Event:new("RedrawCurrentView"))
|
||||
Notification:notify(T(_("Font gamma set to: %1."), optionsutil:getOptionText("SetFontGamma", gamma_level)))
|
||||
Notification:notify(T(_("Font gamma set to %1."), optionsutil:getOptionText("SetFontGamma", gamma_level)))
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
@@ -169,6 +169,20 @@ function ReaderHighlight:init()
|
||||
}
|
||||
end)
|
||||
|
||||
-- User hyphenation dict
|
||||
self:addToHighlightDialog("11_user_dict", function(_self)
|
||||
return {
|
||||
text= _("Hyphenate"),
|
||||
show_in_highlight_dialog_func = function()
|
||||
return _self.ui.userHyph and _self.ui.userhyph:isAvailable() and not _self.selected_text.text:find("[ ,;-%.\n]")
|
||||
end,
|
||||
callback = function()
|
||||
_self.ui.userhyph:modifyUserEntry(_self.selected_text.text)
|
||||
_self:onClose()
|
||||
end,
|
||||
}
|
||||
end)
|
||||
|
||||
self.ui:registerPostInitCallback(function()
|
||||
self.ui.menu:registerToMainMenu(self)
|
||||
end)
|
||||
|
||||
@@ -138,27 +138,19 @@ end
|
||||
|
||||
function ReaderTypeset:onToggleEmbeddedStyleSheet(toggle)
|
||||
self:toggleEmbeddedStyleSheet(toggle)
|
||||
if toggle then
|
||||
Notification:notify(_("Enabled embedded styles."))
|
||||
else
|
||||
Notification:notify(_("Disabled embedded styles."))
|
||||
end
|
||||
Notification:notify(T( _("Embedded styles are %1."), optionsutil:getOptionText("ToggleEmbeddedStyleSheet", toggle)))
|
||||
return true
|
||||
end
|
||||
|
||||
function ReaderTypeset:onToggleEmbeddedFonts(toggle)
|
||||
self:toggleEmbeddedFonts(toggle)
|
||||
if toggle then
|
||||
Notification:notify(_("Enabled embedded fonts."))
|
||||
else
|
||||
Notification:notify(_("Disabled embedded fonts."))
|
||||
end
|
||||
Notification:notify(T( _("Embedded fonts are %1."), optionsutil:getOptionText("ToggleEmbeddedFonts", toggle)))
|
||||
return true
|
||||
end
|
||||
|
||||
function ReaderTypeset:onToggleImageScaling(toggle)
|
||||
self:toggleImageScaling(toggle)
|
||||
Notification:notify(T( _("Image scaling set to: %1"), optionsutil:getOptionText("ToggleImageScaling", toggle)))
|
||||
Notification:notify(T( _("Image scaling set to %1."), optionsutil:getOptionText("ToggleImageScaling", toggle)))
|
||||
return true
|
||||
end
|
||||
|
||||
@@ -169,7 +161,7 @@ end
|
||||
|
||||
function ReaderTypeset:onSetBlockRenderingMode(mode)
|
||||
self:setBlockRenderingMode(mode)
|
||||
Notification:notify(T( _("Render mode set to: %1"), optionsutil:getOptionText("SetBlockRenderingMode", mode)))
|
||||
Notification:notify(T( _("Render mode set to %1."), optionsutil:getOptionText("SetBlockRenderingMode", mode)))
|
||||
return true
|
||||
end
|
||||
|
||||
@@ -191,7 +183,7 @@ local OBSOLETED_CSS = {
|
||||
|
||||
function ReaderTypeset:onSetRenderDPI(dpi)
|
||||
self:setRenderDPI(dpi)
|
||||
Notification:notify(T( _("Zoom set to: %1"), optionsutil:getOptionText("SetRenderDPI", dpi)))
|
||||
Notification:notify(T( _("Zoom set to %1."), optionsutil:getOptionText("SetRenderDPI", dpi)))
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
@@ -237,6 +237,7 @@ When the book's language tag is not among our presets, no specific features will
|
||||
})
|
||||
self.text_lang_tag = lang_tag
|
||||
self.ui.document:setTextMainLang(lang_tag)
|
||||
self.ui:handleEvent(Event:new("TypographyLanguageChanged"))
|
||||
self.ui:handleEvent(Event:new("UpdatePos"))
|
||||
end,
|
||||
hold_callback = function(touchmenu_instance)
|
||||
@@ -427,8 +428,8 @@ These settings will apply to all books with any hyphenation dictionary.
|
||||
enabled_func = function()
|
||||
return self.hyphenation and not self.hyph_soft_hyphens_only
|
||||
end,
|
||||
separator = true,
|
||||
})
|
||||
table.insert(hyphenation_submenu, self.ui.userhyph:getMenuEntry())
|
||||
table.insert(hyphenation_submenu, {
|
||||
text_func = function()
|
||||
-- Show the current language default hyph dict (ie: English_US for zh)
|
||||
@@ -488,7 +489,7 @@ These settings will apply to all books with any hyphenation dictionary.
|
||||
end,
|
||||
})
|
||||
table.insert(hyphenation_submenu, {
|
||||
text = _("Soft-hyphens only"),
|
||||
text = _("Soft hyphens only"),
|
||||
callback = function()
|
||||
self.hyph_soft_hyphens_only = not self.hyph_soft_hyphens_only
|
||||
self.hyph_force_algorithmic = false
|
||||
@@ -760,6 +761,7 @@ function ReaderTypography:onReadSettings(config)
|
||||
logger.dbg("Typography lang: no lang set, using", self.text_lang_tag)
|
||||
end
|
||||
self.ui.document:setTextMainLang(self.text_lang_tag)
|
||||
self.ui:handleEvent(Event:new("TypographyLanguageChanged"))
|
||||
end
|
||||
|
||||
function ReaderTypography:onPreRenderDocument(config)
|
||||
@@ -780,6 +782,7 @@ function ReaderTypography:onPreRenderDocument(config)
|
||||
self.text_lang_tag = self.book_lang_tag
|
||||
self.ui.doc_settings:saveSetting("text_lang", self.text_lang_tag)
|
||||
self.ui.document:setTextMainLang(self.text_lang_tag)
|
||||
self.ui:handleEvent(Event:new("TypographyLanguageChanged"))
|
||||
self.ui:handleEvent(Event:new("UpdatePos"))
|
||||
end,
|
||||
enabled_func = function()
|
||||
@@ -809,6 +812,7 @@ function ReaderTypography:onPreRenderDocument(config)
|
||||
end
|
||||
self.text_lang_tag = self.book_lang_tag
|
||||
self.ui.document:setTextMainLang(self.text_lang_tag)
|
||||
self.ui:handleEvent(Event:new("TypographyLanguageChanged"))
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
228
frontend/apps/reader/modules/readeruserhyph.lua
Normal file
228
frontend/apps/reader/modules/readeruserhyph.lua
Normal file
@@ -0,0 +1,228 @@
|
||||
local DataStorage = require("datastorage")
|
||||
local Event = require("ui/event")
|
||||
local FFIUtil = require("ffi/util")
|
||||
local InfoMessage = require("ui/widget/infomessage")
|
||||
local InputDialog = require("ui/widget/inputdialog")
|
||||
local UIManager = require("ui/uimanager")
|
||||
local WidgetContainer = require("ui/widget/container/widgetcontainer")
|
||||
local lfs = require("libs/libkoreader-lfs")
|
||||
local logger = require("logger")
|
||||
local _ = require("gettext")
|
||||
local T = require("ffi/util").template
|
||||
|
||||
local ReaderUserHyph = WidgetContainer:new{
|
||||
-- return values from setUserHyphenationDict (crengine's UserHyphDict::init())
|
||||
USER_DICT_RELOAD = 0,
|
||||
USER_DICT_NOCHANGE = 1,
|
||||
USER_DICT_MALFORMED = 2,
|
||||
USER_DICT_ERROR_NOT_SORTED = 3,
|
||||
}
|
||||
|
||||
-- returns path to the user dictionary
|
||||
function ReaderUserHyph:getDictionaryPath()
|
||||
return FFIUtil.joinPath(DataStorage:getSettingsDir(),
|
||||
"user-" .. tostring(self.ui.document:getTextMainLangDefaultHyphDictionary():gsub(".pattern$", "")) .. ".hyph")
|
||||
end
|
||||
|
||||
-- Load the user dictionary suitable for the actual language
|
||||
-- if reload==true, force a reload
|
||||
-- Unload is done automatically when a new dictionary is loaded.
|
||||
function ReaderUserHyph:loadDictionary(name, reload)
|
||||
if G_reader_settings:isTrue("hyph_user_dict") and lfs.attributes(name, "mode") == "file" then
|
||||
local ret = self.ui.document:setUserHyphenationDict(name, reload)
|
||||
-- this should only happen, if a user edits a dictionary by hand or the user messed
|
||||
-- with the dictionary file by hand. -> Warning and disable.
|
||||
if ret == self.USER_DICT_ERROR_NOT_SORTED then
|
||||
UIManager:show(InfoMessage:new{
|
||||
text = T(_("The user dictionary\n%1\nis not alphabetically sorted.\n\nIt has been disabled."), name),
|
||||
})
|
||||
logger.warn("UserHyph: Dictionary " .. name .. " is not sorted alphabetically.")
|
||||
G_reader_settings:makeFalse("hyph_user_dict")
|
||||
elseif ret == self.USER_DICT_MALFORMED then
|
||||
UIManager:show(InfoMessage:new{
|
||||
text = T(_("The user dictionary\n%1\nhas corrupted entries.\n\nOnly valid entries will be used."), name),
|
||||
})
|
||||
logger.warn("UserHyph: Dictionary " .. name .. " has corrupted entries.")
|
||||
end
|
||||
else
|
||||
self.ui.document:setUserHyphenationDict() -- clear crengine user hyph dict
|
||||
end
|
||||
end
|
||||
|
||||
-- Reload on change of the hyphenation language
|
||||
function ReaderUserHyph:onTypographyLanguageChanged()
|
||||
self:loadUserDictionary()
|
||||
end
|
||||
|
||||
-- Reload on "ChangedUserDictionary" event,
|
||||
-- doesn't load dictionary if filesize and filename haven't changed
|
||||
-- if reload==true reload
|
||||
function ReaderUserHyph:loadUserDictionary(reload)
|
||||
self:loadDictionary(self:isAvailable() and self:getDictionaryPath() or "", reload and true or false)
|
||||
self.ui:handleEvent(Event:new("UpdatePos"))
|
||||
end
|
||||
|
||||
-- Functions to use with the UI
|
||||
|
||||
function ReaderUserHyph:isAvailable()
|
||||
return G_reader_settings:isTrue("hyph_user_dict") and self:_enabled()
|
||||
end
|
||||
|
||||
function ReaderUserHyph:_enabled()
|
||||
return self.ui.typography.hyphenation
|
||||
end
|
||||
|
||||
-- add Menu entry
|
||||
function ReaderUserHyph:getMenuEntry()
|
||||
return {
|
||||
text = _("Custom hyphenation rules"),
|
||||
help_text = _("The hyphenation of a word can be changed from its default by long pressing for 3 seconds and selecting 'Hyphenate'."),
|
||||
callback = function()
|
||||
local hyph_user_dict = not G_reader_settings:isTrue("hyph_user_dict")
|
||||
G_reader_settings:saveSetting("hyph_user_dict", hyph_user_dict)
|
||||
self:loadUserDictionary() -- not needed to force a reload here
|
||||
end,
|
||||
checked_func = function()
|
||||
return self:isAvailable()
|
||||
end,
|
||||
enabled_func = function()
|
||||
return self:_enabled()
|
||||
end,
|
||||
separator = true,
|
||||
}
|
||||
end
|
||||
|
||||
-- Helper functions for dictionary entries-------------------------------------------
|
||||
|
||||
-- checks if suggestion is well formated
|
||||
function ReaderUserHyph:checkHyphenation(suggestion, word)
|
||||
if suggestion:find("%-%-") then
|
||||
return false -- two or more consecutive '-'
|
||||
end
|
||||
|
||||
suggestion = suggestion:gsub("-","")
|
||||
if self.ui.document:getLowercasedWord(suggestion) == self.ui.document:getLowercasedWord(word) then
|
||||
return true -- characters match (case insensitive)
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function ReaderUserHyph:updateDictionary(word, hyphenation)
|
||||
local dict_file = self:getDictionaryPath()
|
||||
local new_dict_file = dict_file .. ".new"
|
||||
|
||||
local new_dict = io.open(new_dict_file, "w")
|
||||
if not new_dict then
|
||||
logger.err("UserHyph: could not open " .. new_dict_file)
|
||||
return
|
||||
end
|
||||
|
||||
local word_lower = self.ui.document:getLowercasedWord(word)
|
||||
local line
|
||||
|
||||
local dict = io.open(dict_file, "r")
|
||||
if dict then
|
||||
line = dict:read()
|
||||
--search entry
|
||||
while line and self.ui.document:getLowercasedWord(line:sub(1, line:find(";") - 1)) < word_lower do
|
||||
new_dict:write(line .. "\n")
|
||||
line = dict:read()
|
||||
end
|
||||
|
||||
-- last word = nil if EOF, else last_word=word if found in file, else last_word is word after the new entry
|
||||
if line then
|
||||
local last_word = self.ui.document:getLowercasedWord(line:sub(1, line:find(";") - 1))
|
||||
if last_word == self.ui.document:getLowercasedWord(word) then
|
||||
line = nil -- word found
|
||||
end
|
||||
else
|
||||
line = nil -- EOF
|
||||
end
|
||||
end
|
||||
|
||||
-- write new entry
|
||||
if hyphenation and hyphenation ~= "" then
|
||||
new_dict:write(string.format("%s;%s\n", word, hyphenation))
|
||||
end
|
||||
|
||||
-- write old entry if there was one
|
||||
if line then
|
||||
new_dict:write(line .. "\n")
|
||||
end
|
||||
|
||||
if dict then
|
||||
repeat
|
||||
line = dict:read()
|
||||
if line then
|
||||
new_dict:write(line .. "\n")
|
||||
end
|
||||
until (not line)
|
||||
dict:close()
|
||||
os.remove(dict_file)
|
||||
end
|
||||
|
||||
new_dict:close()
|
||||
os.rename(new_dict_file, dict_file)
|
||||
|
||||
self:loadUserDictionary(true) -- dictionary has changed, force a reload here
|
||||
end
|
||||
|
||||
function ReaderUserHyph:modifyUserEntry(word)
|
||||
if word:find("[ ,;-%.]") then return end -- no button if more than one word
|
||||
|
||||
if not self.ui.document then return end
|
||||
|
||||
local suggested_hyphenation = self.ui.document:getHyphenationForWord(word)
|
||||
|
||||
local input_dialog
|
||||
input_dialog = InputDialog:new{
|
||||
title = T(_("Hyphenate: %1"), word),
|
||||
description = _("Add hyphenation positions with hyphens ('-') or spaces (' ')."),
|
||||
input = suggested_hyphenation,
|
||||
old_hyph_lowercase = self.ui.document:getLowercasedWord(suggested_hyphenation),
|
||||
input_type = "string",
|
||||
buttons = {
|
||||
{
|
||||
{
|
||||
text = _("Cancel"),
|
||||
callback = function()
|
||||
UIManager:close(input_dialog)
|
||||
end,
|
||||
},
|
||||
{
|
||||
text = _("Remove"),
|
||||
callback = function()
|
||||
UIManager:close(input_dialog)
|
||||
self:updateDictionary(word)
|
||||
end,
|
||||
},
|
||||
{
|
||||
text = _("Save"),
|
||||
is_enter_default = true,
|
||||
callback = function()
|
||||
local new_suggestion = input_dialog:getInputText()
|
||||
new_suggestion = new_suggestion:gsub(" ","-") -- replace spaces with hyphens
|
||||
new_suggestion = new_suggestion:gsub("^-","") -- remove leading hypenations
|
||||
new_suggestion = new_suggestion:gsub("-$","") -- remove trailing hypenations
|
||||
|
||||
if self:checkHyphenation(new_suggestion, word) then
|
||||
-- don't save if no changes
|
||||
if self.ui.document:getLowercasedWord(new_suggestion) ~= input_dialog.old_hyph_lowercase then
|
||||
self:updateDictionary(word, new_suggestion)
|
||||
end
|
||||
UIManager:close(input_dialog)
|
||||
else
|
||||
UIManager:show(InfoMessage:new{
|
||||
text = T(_("Invalid hyphenation!"), self.dict_file),
|
||||
})
|
||||
end
|
||||
end,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
UIManager:show(input_dialog)
|
||||
input_dialog:onShowKeyboard()
|
||||
end
|
||||
|
||||
return ReaderUserHyph
|
||||
@@ -715,7 +715,7 @@ function ReaderView:onSetRotationMode(rotation)
|
||||
self.ui:handleEvent(Event:new("SetDimensions", new_screen_size))
|
||||
self.ui:onScreenResize(new_screen_size)
|
||||
self.ui:handleEvent(Event:new("InitScrollPageStates"))
|
||||
Notification:notify(T(_("Rotation mode set to: %1"), optionsutil:getOptionText("SetRotationMode", rotation)))
|
||||
Notification:notify(T(_("Rotation mode set to %1."), optionsutil:getOptionText("SetRotationMode", rotation)))
|
||||
return true
|
||||
end
|
||||
|
||||
@@ -849,12 +849,12 @@ function ReaderView:onGammaUpdate(gamma)
|
||||
if self.page_scroll then
|
||||
self.ui:handleEvent(Event:new("UpdateScrollPageGamma", gamma))
|
||||
end
|
||||
Notification:notify(T(_("Font gamma set to: %1."), gamma))
|
||||
Notification:notify(T(_("Font gamma set to %1."), gamma))
|
||||
end
|
||||
|
||||
function ReaderView:onFontSizeUpdate(font_size)
|
||||
self.ui:handleEvent(Event:new("ReZoom", font_size))
|
||||
Notification:notify(T(_("Font zoom set to: %1."), font_size))
|
||||
Notification:notify(T(_("Font zoom set to %1."), font_size))
|
||||
end
|
||||
|
||||
function ReaderView:onDefectSizeUpdate()
|
||||
@@ -874,7 +874,7 @@ function ReaderView:onSetViewMode(new_mode)
|
||||
self.view_mode = new_mode
|
||||
self.ui.document:setViewMode(new_mode)
|
||||
self.ui:handleEvent(Event:new("ChangeViewMode"))
|
||||
Notification:notify(T( _("View mode set to: %1"), optionsutil:getOptionText("SetViewMode", new_mode)))
|
||||
Notification:notify(T( _("View mode set to %1."), optionsutil:getOptionText("SetViewMode", new_mode)))
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -48,6 +48,7 @@ local ReaderStyleTweak = require("apps/reader/modules/readerstyletweak")
|
||||
local ReaderToc = require("apps/reader/modules/readertoc")
|
||||
local ReaderTypeset = require("apps/reader/modules/readertypeset")
|
||||
local ReaderTypography = require("apps/reader/modules/readertypography")
|
||||
local ReaderUserHyph = require("apps/reader/modules/readeruserhyph")
|
||||
local ReaderView = require("apps/reader/modules/readerview")
|
||||
local ReaderWikipedia = require("apps/reader/modules/readerwikipedia")
|
||||
local ReaderZooming = require("apps/reader/modules/readerzooming")
|
||||
@@ -314,6 +315,12 @@ function ReaderUI:init()
|
||||
view = self.view,
|
||||
ui = self
|
||||
})
|
||||
-- user hyphenation (must be registered before typography)
|
||||
self:registerModule("userhyph", ReaderUserHyph:new{
|
||||
dialog = self.dialog,
|
||||
view = self.view,
|
||||
ui = self
|
||||
})
|
||||
-- typography menu (replaces previous hyphenation menu / ReaderHyphenation)
|
||||
self:registerModule("typography", ReaderTypography:new{
|
||||
dialog = self.dialog,
|
||||
|
||||
@@ -132,11 +132,10 @@ function Device:init()
|
||||
device = self,
|
||||
event_map = require("device/android/event_map"),
|
||||
handleMiscEv = function(this, ev)
|
||||
local Event = require("ui/event")
|
||||
local UIManager = require("ui/uimanager")
|
||||
logger.dbg("Android application event", ev.code)
|
||||
if ev.code == C.APP_CMD_SAVE_STATE then
|
||||
UIManager:broadcastEvent(Event:new("SaveSettings"))
|
||||
return "SaveState"
|
||||
elseif ev.code == C.APP_CMD_DESTROY then
|
||||
UIManager:quit()
|
||||
elseif ev.code == C.APP_CMD_GAINED_FOCUS
|
||||
@@ -153,6 +152,7 @@ function Device:init()
|
||||
this.device.screen:resize()
|
||||
local new_size = this.device.screen:getSize()
|
||||
logger.info("Resizing screen to", new_size)
|
||||
local Event = require("ui/event")
|
||||
local FileManager = require("apps/filemanager/filemanager")
|
||||
UIManager:broadcastEvent(Event:new("SetDimensions", new_size))
|
||||
UIManager:broadcastEvent(Event:new("ScreenResize", new_size))
|
||||
@@ -166,8 +166,10 @@ function Device:init()
|
||||
end
|
||||
end
|
||||
-- to-do: keyboard connected, disconnected
|
||||
elseif ev.code == C.APP_CMD_RESUME then
|
||||
elseif ev.code == C.APP_CMD_START then
|
||||
local Event = require("ui/event")
|
||||
UIManager:broadcastEvent(Event:new("Resume"))
|
||||
elseif ev.code == C.APP_CMD_RESUME then
|
||||
if external.when_back_callback then
|
||||
external.when_back_callback()
|
||||
external.when_back_callback = nil
|
||||
@@ -205,11 +207,14 @@ function Device:init()
|
||||
end
|
||||
end
|
||||
end
|
||||
elseif ev.code == C.APP_CMD_PAUSE then
|
||||
elseif ev.code == C.APP_CMD_STOP then
|
||||
local Event = require("ui/event")
|
||||
UIManager:broadcastEvent(Event:new("Suspend"))
|
||||
elseif ev.code == C.AEVENT_POWER_CONNECTED then
|
||||
local Event = require("ui/event")
|
||||
UIManager:broadcastEvent(Event:new("Charging"))
|
||||
elseif ev.code == C.AEVENT_POWER_DISCONNECTED then
|
||||
local Event = require("ui/event")
|
||||
UIManager:broadcastEvent(Event:new("NotCharging"))
|
||||
elseif ev.code == C.AEVENT_DOWNLOAD_COMPLETE then
|
||||
android.ota.isRunning = false
|
||||
@@ -457,9 +462,9 @@ function Device:untar(archive, extract_to)
|
||||
end
|
||||
|
||||
function Device:download(link, name, ok_text)
|
||||
local UIManager = require("ui/uimanager")
|
||||
local ConfirmBox = require("ui/widget/confirmbox")
|
||||
local InfoMessage = require("ui/widget/infomessage")
|
||||
local UIManager = require("ui/uimanager")
|
||||
local ok = android.download(link, name)
|
||||
if ok == C.ADOWNLOAD_EXISTS then
|
||||
self:install()
|
||||
@@ -479,14 +484,12 @@ function Device:download(link, name, ok_text)
|
||||
end
|
||||
|
||||
function Device:install()
|
||||
local ConfirmBox = require("ui/widget/confirmbox")
|
||||
local Event = require("ui/event")
|
||||
local UIManager = require("ui/uimanager")
|
||||
local ConfirmBox = require("ui/widget/confirmbox")
|
||||
UIManager:show(ConfirmBox:new{
|
||||
text = _("Update is ready. Install it now?"),
|
||||
ok_text = _("Install"),
|
||||
ok_callback = function()
|
||||
UIManager:broadcastEvent(Event:new("SaveSettings"))
|
||||
android.ota.install()
|
||||
android.ota.isPending = false
|
||||
end,
|
||||
|
||||
@@ -488,7 +488,6 @@ local PocketBook626 = PocketBook:new{
|
||||
local PocketBook627 = PocketBook:new{
|
||||
model = "PBLux4",
|
||||
display_dpi = 212,
|
||||
isAlwaysPortrait = yes,
|
||||
}
|
||||
|
||||
-- PocketBook Touch Lux 5 (628)
|
||||
@@ -577,16 +576,6 @@ local PocketBook740_2 = PocketBook:new{
|
||||
}
|
||||
}
|
||||
|
||||
-- PocketBook InkPad Color (741)
|
||||
local PocketBook741 = PocketBook:new{
|
||||
model = "PBInkPadColor",
|
||||
display_dpi = 300,
|
||||
hasColorScreen = yes,
|
||||
canUseCBB = no, -- 24bpp
|
||||
isAlwaysPortrait = yes,
|
||||
usingForcedRotation = landscape_ccw,
|
||||
}
|
||||
|
||||
-- PocketBook Color Lux (801)
|
||||
local PocketBookColorLux = PocketBook:new{
|
||||
model = "PBColorLux",
|
||||
@@ -673,8 +662,6 @@ elseif codename == "PB740" then
|
||||
return PocketBook740
|
||||
elseif codename == "PB740-2" then
|
||||
return PocketBook740_2
|
||||
elseif codename == "PB741" then
|
||||
return PocketBook741
|
||||
elseif codename == "PocketBook 840" then
|
||||
return PocketBook840
|
||||
elseif codename == "PB1040" then
|
||||
|
||||
@@ -33,9 +33,7 @@ function M:new(o)
|
||||
end
|
||||
end
|
||||
end
|
||||
if o.is_user_list then
|
||||
logger.info(o:dump())
|
||||
end
|
||||
logger.info(o:dump())
|
||||
return o
|
||||
end
|
||||
|
||||
@@ -56,7 +54,7 @@ function M:checkMethod(role, method)
|
||||
end
|
||||
|
||||
function M:dump()
|
||||
local str = "user defined thirdparty apps\n"
|
||||
local str = (self.is_user_list and "user" or "platform") .. " thirdparty apps\n"
|
||||
for i, role in ipairs(roles) do
|
||||
local apps = self[role.."s"]
|
||||
for index, _ in ipairs(apps or {}) do
|
||||
|
||||
@@ -980,6 +980,25 @@ function CreDocument:setTextHyphenationSoftHyphensOnly(toggle)
|
||||
self._document:setStringProperty("crengine.textlang.hyphenation.soft.hyphens.only", toggle and 1 or 0)
|
||||
end
|
||||
|
||||
function CreDocument:setUserHyphenationDict(dict, reload)
|
||||
logger.dbg("CreDocument: set textlang hyphenation dict", dict or "none")
|
||||
return self._document:setUserHyphenationDict(dict or "", reload or false)
|
||||
end
|
||||
|
||||
function CreDocument:getHyphenationForWord(word)
|
||||
if word then
|
||||
return self._document:getHyphenationForWord(word)
|
||||
end
|
||||
return word
|
||||
end
|
||||
|
||||
function CreDocument:getLowercasedWord(word)
|
||||
if word then
|
||||
return self._document:getLowercasedWord(word)
|
||||
end
|
||||
return word
|
||||
end
|
||||
|
||||
function CreDocument:setTextHyphenationForceAlgorithmic(toggle)
|
||||
logger.dbg("CreDocument: set textlang hyphenation force algorithmic", toggle)
|
||||
self._document:setStringProperty("crengine.textlang.hyphenation.force.algorithmic", toggle and 1 or 0)
|
||||
|
||||
@@ -467,16 +467,16 @@ function UIManager:close(widget, refreshtype, refreshregion, refreshdither)
|
||||
end
|
||||
logger.dbg("close widget:", widget.name or widget.id or tostring(widget))
|
||||
local dirty = false
|
||||
-- First notify the closed widget to save its settings...
|
||||
-- Ensure all the widgets can get onFlushSettings event.
|
||||
widget:handleEvent(Event:new("FlushSettings"))
|
||||
-- ...and notify it that it ought to be gone now.
|
||||
-- first send close event to widget
|
||||
widget:handleEvent(Event:new("CloseWidget"))
|
||||
-- Make sure it's disabled by default and check if there are any widgets that want it disabled or enabled.
|
||||
-- make it disabled by default and check if any widget wants it disabled or enabled
|
||||
Input.disable_double_tap = true
|
||||
local requested_disable_double_tap = nil
|
||||
local is_covered = false
|
||||
local start_idx = 1
|
||||
-- Then remove all references to that widget on stack and refresh.
|
||||
-- then remove all references to that widget on stack and refresh
|
||||
for i = #self._window_stack, 1, -1 do
|
||||
if self._window_stack[i].widget == widget then
|
||||
self._dirty[self._window_stack[i].widget] = nil
|
||||
|
||||
@@ -210,6 +210,7 @@ function DateWidget:onCloseWidget()
|
||||
UIManager:setDirty(nil, function()
|
||||
return "ui", self.date_frame.dimen
|
||||
end)
|
||||
return true
|
||||
end
|
||||
|
||||
function DateWidget:onShow()
|
||||
|
||||
@@ -909,6 +909,7 @@ function DictQuickLookup:onCloseWidget()
|
||||
UIManager:setDirty(nil, function()
|
||||
return "flashui", nil
|
||||
end)
|
||||
return true
|
||||
end
|
||||
|
||||
function DictQuickLookup:onShow()
|
||||
|
||||
@@ -295,6 +295,7 @@ function DoubleSpinWidget:onCloseWidget()
|
||||
UIManager:setDirty(nil, function()
|
||||
return "ui", self.widget_frame.dimen
|
||||
end)
|
||||
return true
|
||||
end
|
||||
|
||||
function DoubleSpinWidget:onShow()
|
||||
|
||||
@@ -583,6 +583,7 @@ function FrontLightWidget:onCloseWidget()
|
||||
UIManager:setDirty(nil, function()
|
||||
return "flashui", self.light_frame.dimen
|
||||
end)
|
||||
return true
|
||||
end
|
||||
|
||||
function FrontLightWidget:onShow()
|
||||
|
||||
@@ -850,6 +850,7 @@ function ImageViewer:onCloseWidget()
|
||||
UIManager:setDirty(nil, function()
|
||||
return "flashui", self.main_frame.dimen
|
||||
end)
|
||||
return true
|
||||
end
|
||||
|
||||
return ImageViewer
|
||||
|
||||
@@ -206,15 +206,16 @@ function InfoMessage:onCloseWidget()
|
||||
end
|
||||
if self.invisible then
|
||||
-- Still invisible, no setDirty needed
|
||||
return
|
||||
return true
|
||||
end
|
||||
if self.no_refresh_on_close then
|
||||
return
|
||||
return true
|
||||
end
|
||||
|
||||
UIManager:setDirty(nil, function()
|
||||
return "ui", self[1][1].dimen
|
||||
end)
|
||||
return true
|
||||
end
|
||||
|
||||
function InfoMessage:onShow()
|
||||
|
||||
@@ -391,7 +391,6 @@ function InputDialog:init()
|
||||
scroll_callback = self._buttons_scroll_callback, -- nil if no Nav or Scroll buttons
|
||||
scroll = true,
|
||||
scroll_by_pan = self.scroll_by_pan,
|
||||
has_nav_bar = self.add_nav_bar,
|
||||
cursor_at_end = self.cursor_at_end,
|
||||
readonly = self.readonly,
|
||||
parent = self,
|
||||
|
||||
@@ -61,7 +61,6 @@ local InputText = InputContainer:new{
|
||||
for_measurement_only = nil, -- When the widget is a one-off used to compute text height
|
||||
do_select = false, -- to start text selection
|
||||
selection_start_pos = nil, -- selection start position
|
||||
is_keyboard_hidden = false, -- to be able to show the keyboard again when it was hidden (by VK itself)
|
||||
}
|
||||
|
||||
-- only use PhysicalKeyboard if the device does not have touch screen
|
||||
@@ -122,11 +121,6 @@ if Device:isTouchDevice() or Device:hasDPad() then
|
||||
function InputText:onTapTextBox(arg, ges)
|
||||
if self.parent.onSwitchFocus then
|
||||
self.parent:onSwitchFocus(self)
|
||||
else
|
||||
if self.is_keyboard_hidden == true then
|
||||
self:onShowKeyboard()
|
||||
self.is_keyboard_hidden = false
|
||||
end
|
||||
end
|
||||
if #self.charlist > 0 then -- Avoid cursor moving within a hint.
|
||||
local textwidget_offset = self.margin + self.bordersize + self.padding
|
||||
@@ -569,21 +563,12 @@ end
|
||||
|
||||
function InputText:onShowKeyboard(ignore_first_hold_release)
|
||||
Device:startTextInput()
|
||||
|
||||
self.keyboard.ignore_first_hold_release = ignore_first_hold_release
|
||||
UIManager:show(self.keyboard)
|
||||
return true
|
||||
end
|
||||
|
||||
function InputText:onHideKeyboard()
|
||||
if not self.has_nav_bar then
|
||||
UIManager:close(self.keyboard)
|
||||
Device:stopTextInput()
|
||||
self.is_keyboard_hidden = true
|
||||
end
|
||||
|
||||
return self.is_keyboard_hiddenend
|
||||
end
|
||||
|
||||
function InputText:onCloseKeyboard()
|
||||
UIManager:close(self.keyboard)
|
||||
Device:stopTextInput()
|
||||
|
||||
@@ -157,6 +157,7 @@ function KeyboardLayoutDialog:onCloseWidget()
|
||||
UIManager:setDirty(nil, function()
|
||||
return "ui", self[1][1].dimen
|
||||
end)
|
||||
return true
|
||||
end
|
||||
|
||||
return KeyboardLayoutDialog
|
||||
|
||||
@@ -38,6 +38,7 @@ function LinkBox:onCloseWidget()
|
||||
UIManager:setDirty(nil, function()
|
||||
return "partial", self.box
|
||||
end)
|
||||
return true
|
||||
end
|
||||
|
||||
function LinkBox:onShow()
|
||||
|
||||
@@ -381,6 +381,7 @@ function NaturalLightWidget:onCloseWidget()
|
||||
end)
|
||||
-- Tell frontlight widget that we're closed
|
||||
self.fl_widget:naturalLightConfigClose()
|
||||
return true
|
||||
end
|
||||
|
||||
function NaturalLightWidget:onShow()
|
||||
|
||||
@@ -182,6 +182,7 @@ function Notification:onCloseWidget()
|
||||
UIManager:setDirty(nil, function()
|
||||
return "ui", self.frame.dimen
|
||||
end)
|
||||
return true
|
||||
end
|
||||
|
||||
function Notification:onShow()
|
||||
|
||||
@@ -188,6 +188,7 @@ function OpenWithDialog:onCloseWidget()
|
||||
UIManager:setDirty(nil, function()
|
||||
return "ui", self.dialog_frame.dimen
|
||||
end)
|
||||
return true
|
||||
end
|
||||
|
||||
return OpenWithDialog
|
||||
|
||||
@@ -86,6 +86,7 @@ function QRMessage:onCloseWidget()
|
||||
UIManager:setDirty(nil, function()
|
||||
return "ui", self[1][1].dimen
|
||||
end)
|
||||
return true
|
||||
end
|
||||
|
||||
function QRMessage:onShow()
|
||||
|
||||
@@ -88,6 +88,7 @@ function ScreenSaverWidget:onCloseWidget()
|
||||
UIManager:setDirty(nil, function()
|
||||
return "full", self.main_frame.dimen
|
||||
end)
|
||||
return true
|
||||
end
|
||||
|
||||
return ScreenSaverWidget
|
||||
|
||||
@@ -338,6 +338,7 @@ function SkimToWidget:onCloseWidget()
|
||||
UIManager:setDirty(nil, function()
|
||||
return "ui", self.skimto_frame.dimen
|
||||
end)
|
||||
return true
|
||||
end
|
||||
|
||||
function SkimToWidget:onShow()
|
||||
|
||||
@@ -248,6 +248,7 @@ function SpinWidget:onCloseWidget()
|
||||
UIManager:setDirty(nil, function()
|
||||
return "ui", self.spin_frame.dimen
|
||||
end)
|
||||
return true
|
||||
end
|
||||
|
||||
function SpinWidget:onShow()
|
||||
|
||||
@@ -229,6 +229,7 @@ function TextViewer:onCloseWidget()
|
||||
UIManager:setDirty(nil, function()
|
||||
return "partial", self.frame.dimen
|
||||
end)
|
||||
return true
|
||||
end
|
||||
|
||||
function TextViewer:onShow()
|
||||
|
||||
@@ -195,6 +195,7 @@ function TimeWidget:onCloseWidget()
|
||||
UIManager:setDirty(nil, function()
|
||||
return "ui", self.time_frame.dimen
|
||||
end)
|
||||
return true
|
||||
end
|
||||
|
||||
function TimeWidget:onShow()
|
||||
|
||||
@@ -155,6 +155,7 @@ function TrapWidget:onCloseWidget()
|
||||
return "ui", self.frame.dimen
|
||||
end)
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
return TrapWidget
|
||||
|
||||
@@ -111,7 +111,7 @@ function VirtualKey:init()
|
||||
self.keyboard:delToStartOfLine()
|
||||
end
|
||||
--self.skiphold = true
|
||||
elseif self.label == "←" then
|
||||
elseif self.label =="←" then
|
||||
self.callback = function() self.keyboard:leftChar() end
|
||||
self.hold_callback = function()
|
||||
self.ignore_key_release = true
|
||||
@@ -127,13 +127,6 @@ function VirtualKey:init()
|
||||
self.callback = function() self.keyboard:upLine() end
|
||||
elseif self.label == "↓" then
|
||||
self.callback = function() self.keyboard:downLine() end
|
||||
self.hold_callback = function()
|
||||
self.ignore_key_release = true
|
||||
if not self.keyboard:onHideKeyboard() then
|
||||
-- Keyboard was *not* actually hidden: refresh the key to clear the highlight
|
||||
self:update_keyboard(false, true)
|
||||
end
|
||||
end
|
||||
else
|
||||
self.callback = function () self.keyboard:addChar(self.key) end
|
||||
self.hold_callback = function()
|
||||
@@ -527,17 +520,6 @@ function VirtualKeyPopup:init()
|
||||
virtual_key.hold_callback = nil
|
||||
-- close popup on hold release
|
||||
virtual_key.onHoldReleaseKey = function()
|
||||
-- NOTE: Check our *parent* key!
|
||||
if parent_key.ignore_key_release then
|
||||
parent_key.ignore_key_release = nil
|
||||
return true
|
||||
end
|
||||
Device:performHapticFeedback("LONG_PRESS")
|
||||
if virtual_key.keyboard.ignore_first_hold_release then
|
||||
virtual_key.keyboard.ignore_first_hold_release = false
|
||||
return true
|
||||
end
|
||||
|
||||
virtual_key:onTapSelect(true)
|
||||
UIManager:close(self)
|
||||
return true
|
||||
@@ -648,19 +630,13 @@ function VirtualKeyPopup:init()
|
||||
}
|
||||
if position_container.dimen.x < 0 then
|
||||
position_container.dimen.x = 0
|
||||
-- We effectively move the popup, which means the key underneath our finger may no longer *exactly* be parent_key.
|
||||
-- Make sure we won't close the popup right away, as that would risk being a *different* key, in order to make that less confusing.
|
||||
parent_key.ignore_key_release = true
|
||||
elseif position_container.dimen.x + keyboard_frame.dimen.w > Screen:getWidth() then
|
||||
position_container.dimen.x = Screen:getWidth() - keyboard_frame.dimen.w
|
||||
parent_key.ignore_key_release = true
|
||||
end
|
||||
if position_container.dimen.y < 0 then
|
||||
position_container.dimen.y = 0
|
||||
parent_key.ignore_key_release = true
|
||||
elseif position_container.dimen.y + keyboard_frame.dimen.h > Screen:getHeight() then
|
||||
position_container.dimen.y = Screen:getHeight() - keyboard_frame.dimen.h
|
||||
parent_key.ignore_key_release = true
|
||||
end
|
||||
|
||||
self[1] = position_container
|
||||
@@ -769,10 +745,6 @@ function VirtualKeyboard:onClose()
|
||||
return true
|
||||
end
|
||||
|
||||
function VirtualKeyboard:onHideKeyboard()
|
||||
return self.inputbox:onHideKeyboard()
|
||||
end
|
||||
|
||||
function VirtualKeyboard:onPressKey()
|
||||
self:getFocusItem():handleEvent(Event:new("TapSelect"))
|
||||
return true
|
||||
@@ -799,6 +771,7 @@ end
|
||||
|
||||
function VirtualKeyboard:onCloseWidget()
|
||||
self:_refresh(false)
|
||||
return true
|
||||
end
|
||||
|
||||
function VirtualKeyboard:initLayer(layer)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/bin/bash
|
||||
#!/bin/sh
|
||||
|
||||
export LC_ALL="en_US.UTF-8"
|
||||
|
||||
@@ -83,9 +83,6 @@ if [ "${STANDALONE}" != "true" ]; then
|
||||
[ -x /etc/init.d/connman ] && /etc/init.d/connman stop
|
||||
fi
|
||||
|
||||
CRASH_COUNT=0
|
||||
CRASH_TS=0
|
||||
CRASH_PREV_TS=0
|
||||
# **magic** values to request shell stuff. It starts at 85,
|
||||
# any number lower than that will exit this script.
|
||||
RESTART_KOREADER=85
|
||||
@@ -94,101 +91,18 @@ ENTER_QBOOKAPP=87
|
||||
RETURN_VALUE="${RESTART_KOREADER}"
|
||||
|
||||
# Loop forever until KOReader requests a normal exit.
|
||||
while [ "${RETURN_VALUE}" -ne 0 ]; do
|
||||
while [ "${RETURN_VALUE}" -ge "${RESTART_KOREADER}" ]; do
|
||||
|
||||
# move dictionaries from external storage to koreader private partition.
|
||||
find /mnt/public/dict -type f -exec mv -v \{\} /mnt/private/koreader/data/dict \; 2>/dev/null
|
||||
|
||||
if [ ${RETURN_VALUE} -eq ${RESTART_KOREADER} ]; then
|
||||
# Do an update check now, so we can actually update KOReader via the "Restart KOReader" menu entry ;).
|
||||
ko_update_check
|
||||
fi
|
||||
# Do an update check now, so we can actually update KOReader via the "Restart KOReader" menu entry ;).
|
||||
ko_update_check
|
||||
|
||||
# run KOReader
|
||||
./reader.lua "$@" >>crash.log 2>&1
|
||||
RETURN_VALUE=$?
|
||||
|
||||
if [ ${RETURN_VALUE} -ne 0 ] && [ "${RETURN_VALUE}" -ne "${ENTER_USBMS}" ] && [ "${RETURN_VALUE}" -ne "${ENTER_QBOOKAPP}" ] && [ "${RETURN_VALUE}" -ne "${RESTART_KOREADER}" ]; then
|
||||
# Increment the crash counter
|
||||
CRASH_COUNT=$((CRASH_COUNT + 1))
|
||||
CRASH_TS=$(date +'%s')
|
||||
# Reset it to a first crash if it's been a while since our last crash...
|
||||
if [ $((CRASH_TS - CRASH_PREV_TS)) -ge 20 ]; then
|
||||
CRASH_COUNT=1
|
||||
fi
|
||||
|
||||
# Check if the user requested to always abort on crash
|
||||
if grep -q '\["dev_abort_on_crash"\] = true' 'settings.reader.lua' 2>/dev/null; then
|
||||
ALWAYS_ABORT="true"
|
||||
# In which case, make sure we pause on *every* crash
|
||||
CRASH_COUNT=1
|
||||
else
|
||||
ALWAYS_ABORT="false"
|
||||
fi
|
||||
|
||||
# Show a fancy bomb on screen
|
||||
viewWidth=600
|
||||
viewHeight=800
|
||||
FONTH=16
|
||||
eval "$(./fbink -e | tr ';' '\n' | grep -e viewWidth -e viewHeight -e FONTH | tr '\n' ';')"
|
||||
# Compute margins & sizes relative to the screen's resolution, so we end up with a similar layout, no matter the device.
|
||||
# Height @ ~56.7%, w/ a margin worth 1.5 lines
|
||||
bombHeight=$((viewHeight / 2 + viewHeight / 15))
|
||||
bombMargin=$((FONTH + FONTH / 2))
|
||||
# With a little notice at the top of the screen, on a big gray screen of death ;).
|
||||
./fbink -q -b -c -B GRAY9 -m -y 1 "Don't Panic! (Crash n°${CRASH_COUNT} -> ${RETURN_VALUE})"
|
||||
if [ ${CRASH_COUNT} -eq 1 ]; then
|
||||
# Warn that we're waiting on a tap to continue...
|
||||
./fbink -q -b -O -m -y 2 "Tap the screen to continue."
|
||||
fi
|
||||
# U+1F4A3, the hard way, because we can't use \u or \U escape sequences...
|
||||
./fbink -q -b -O -m -t regular=./fonts/freefont/FreeSerif.ttf,px=${bombHeight},top=${bombMargin} -- $'\xf0\x9f\x92\xa3'
|
||||
# And then print the tail end of the log on the bottom of the screen...
|
||||
crashLog="$(tail -n 25 crash.log | sed -e 's/\t/ /g')"
|
||||
# The idea for the margins being to leave enough room for an fbink -Z bar, small horizontal margins, and a font size based on what 6pt looked like @ 265dpi
|
||||
./fbink -q -b -O -t regular=./fonts/droid/DroidSansMono.ttf,top=$((viewHeight / 2 + FONTH * 2 + FONTH / 2)),left=$((viewWidth / 60)),right=$((viewWidth / 60)),px=$((viewHeight / 64)) -- "${crashLog}"
|
||||
# So far, we hadn't triggered an actual screen refresh, do that now, to make sure everything is bundled in a single flashing refresh.
|
||||
./fbink -q -f -s
|
||||
# Cue a lemming's faceplant sound effect!
|
||||
|
||||
{
|
||||
echo "!!!!"
|
||||
echo "Uh oh, something went awry... (Crash n°${CRASH_COUNT}: $(date +'%x @ %X'))"
|
||||
echo "Running on Linux $(uname -r) ($(uname -v))"
|
||||
} >>crash.log 2>&1
|
||||
if [ ${CRASH_COUNT} -lt 5 ] && [ "${ALWAYS_ABORT}" = "false" ]; then
|
||||
echo "Attempting to restart KOReader . . ." >>crash.log 2>&1
|
||||
echo "!!!!" >>crash.log 2>&1
|
||||
fi
|
||||
|
||||
# Pause a bit if it's the first crash in a while, so that it actually has a chance of getting noticed ;).
|
||||
if [ ${CRASH_COUNT} -eq 1 ]; then
|
||||
# NOTE: We don't actually care about what head reads, we're just using it as a fancy sleep ;).
|
||||
# i.e., we pause either until the 15s timeout, or until the user touches the screen.
|
||||
timeout 15 head -c 24 /dev/input/event1 >/dev/null
|
||||
fi
|
||||
# Cycle the last crash timestamp
|
||||
CRASH_PREV_TS=${CRASH_TS}
|
||||
|
||||
# But if we've crashed more than 5 consecutive times, exit, because we wouldn't want to be stuck in a loop...
|
||||
# NOTE: No need to check for ALWAYS_ABORT, CRASH_COUNT will always be 1 when it's true ;).
|
||||
if [ ${CRASH_COUNT} -ge 5 ]; then
|
||||
echo "Too many consecutive crashes, aborting . . ." >>crash.log 2>&1
|
||||
echo "!!!! ! !!!!" >>crash.log 2>&1
|
||||
break
|
||||
fi
|
||||
|
||||
# If the user requested to always abort on crash, do so.
|
||||
if [ "${ALWAYS_ABORT}" = "true" ]; then
|
||||
echo "Aborting . . ." >>crash.log 2>&1
|
||||
echo "!!!! ! !!!!" >>crash.log 2>&1
|
||||
break
|
||||
fi
|
||||
else
|
||||
# Reset the crash counter if that was a sane exit/restart
|
||||
CRASH_COUNT=0
|
||||
fi
|
||||
|
||||
# check if KOReader requested to enter in mass storage mode.
|
||||
if [ "${RETURN_VALUE}" -eq "${ENTER_USBMS}" ]; then
|
||||
# NOTE: at this point we're sure that the safemode tool
|
||||
|
||||
@@ -33,16 +33,13 @@ ko_update_check() {
|
||||
# NOTE: See frontend/ui/otamanager.lua for a few more details on how we squeeze a percentage out of tar's checkpoint feature
|
||||
# NOTE: %B should always be 512 in our case, so let stat do part of the maths for us instead of using %s ;).
|
||||
FILESIZE="$(stat -c %b "${NEWUPDATE}")"
|
||||
# shellcheck disable=SC2003
|
||||
BLOCKS="$(expr "${FILESIZE}" / 20)"
|
||||
# shellcheck disable=SC2003
|
||||
CPOINTS="$(expr "${BLOCKS}" / 100)"
|
||||
export CPOINTS
|
||||
BLOCKS="$((FILESIZE / 20))"
|
||||
export CPOINTS="$((BLOCKS / 100))"
|
||||
# NOTE: We don't run as root, but folders created over USBMS are owned by root, which yields fun permission shenanigans...
|
||||
# c.f., https://github.com/koreader/koreader/issues/7581
|
||||
KO_PB_TARLOG="/tmp/.koreader.tar"
|
||||
# shellcheck disable=SC2016
|
||||
"${KOREADER_DIR}/tar" --no-same-permissions --no-same-owner --checkpoint="${CPOINTS}" --checkpoint-action=exec='printf "%s" $(expr ${TAR_CHECKPOINT} / ${CPOINTS}) > ${FBINK_NAMED_PIPE}' -C "/mnt/ext1" -xf "${NEWUPDATE}" 2>"${KO_PB_TARLOG}"
|
||||
"${KOREADER_DIR}/tar" --no-same-permissions --no-same-owner --checkpoint="${CPOINTS}" --checkpoint-action=exec='printf "%s" $((TAR_CHECKPOINT / CPOINTS)) > ${FBINK_NAMED_PIPE}' -C "/mnt/ext1" -xf "${NEWUPDATE}" 2>"${KO_PB_TARLOG}"
|
||||
fail=$?
|
||||
kill -TERM "${FBINK_PID}"
|
||||
# As mentioned above, filter out potential chmod & utime failures...
|
||||
@@ -119,12 +116,10 @@ while [ "${RETURN_VALUE}" -ne 0 ]; do
|
||||
# Did we crash?
|
||||
if [ "${RETURN_VALUE}" -ne 0 ] && [ "${RETURN_VALUE}" -ne ${KO_RC_RESTART} ]; then
|
||||
# Increment the crash counter
|
||||
# shellcheck disable=SC2003
|
||||
CRASH_COUNT="$(expr ${CRASH_COUNT} + 1)"
|
||||
CRASH_TS="$(date +'%s')"
|
||||
CRASH_COUNT=$((CRASH_COUNT + 1))
|
||||
CRASH_TS=$(date +'%s')
|
||||
# Reset it to a first crash if it's been a while since our last crash...
|
||||
# shellcheck disable=SC2003
|
||||
if [ "$(expr "${CRASH_TS}" - "${CRASH_PREV_TS}")" -ge 20 ]; then
|
||||
if [ $((CRASH_TS - CRASH_PREV_TS)) -ge 20 ]; then
|
||||
CRASH_COUNT=1
|
||||
fi
|
||||
|
||||
@@ -144,10 +139,8 @@ while [ "${RETURN_VALUE}" -ne 0 ]; do
|
||||
eval "$("${KOREADER_DIR}/fbink" -e | tr ';' '\n' | grep -e viewWidth -e viewHeight -e FONTH | tr '\n' ';')"
|
||||
# Compute margins & sizes relative to the screen's resolution, so we end up with a similar layout, no matter the device.
|
||||
# Height @ ~56.7%, w/ a margin worth 1.5 lines
|
||||
# shellcheck disable=SC2003
|
||||
bombHeight="$(expr ${viewHeight} / 2 + ${viewHeight} / 15)"
|
||||
# shellcheck disable=SC2003
|
||||
bombMargin="$(expr ${FONTH} + ${FONTH} / 2)"
|
||||
bombHeight=$((viewHeight / 2 + viewHeight / 15))
|
||||
bombMargin=$((FONTH + FONTH / 2))
|
||||
# With a little notice at the top of the screen, on a big gray screen of death ;).
|
||||
"${KOREADER_DIR}/fbink" -q -b -c -B GRAY9 -m -y 1 "Don't Panic! (Crash n°${CRASH_COUNT} -> ${RETURN_VALUE})"
|
||||
if [ ${CRASH_COUNT} -eq 1 ]; then
|
||||
@@ -156,12 +149,11 @@ while [ "${RETURN_VALUE}" -ne 0 ]; do
|
||||
fi
|
||||
# U+1F4A3, the hard way, because we can't use \u or \U escape sequences...
|
||||
# shellcheck disable=SC2039,SC3003
|
||||
"${KOREADER_DIR}/fbink" -q -b -O -m -t regular=${KOREADER_DIR}/fonts/freefont/FreeSerif.ttf,px="${bombHeight}",top="${bombMargin}" -- $'\xf0\x9f\x92\xa3'
|
||||
"${KOREADER_DIR}/fbink" -q -b -O -m -t regular=${KOREADER_DIR}/fonts/freefont/FreeSerif.ttf,px=${bombHeight},top=${bombMargin} -- $'\xf0\x9f\x92\xa3'
|
||||
# And then print the tail end of the log on the bottom of the screen...
|
||||
crashLog="$(tail -n 25 crash.log | sed -e 's/\t/ /g')"
|
||||
# The idea for the margins being to leave enough room for an fbink -Z bar, small horizontal margins, and a font size based on what 6pt looked like @ 265dpi
|
||||
# shellcheck disable=SC2003
|
||||
"${KOREADER_DIR}/fbink" -q -b -O -t regular=${KOREADER_DIR}/fonts/droid/DroidSansMono.ttf,top="$(expr ${viewHeight} / 2 + ${FONTH} '*' 2 + ${FONTH} / 2)",left="$(expr ${viewWidth} / 60)",right="$(expr ${viewWidth} / 60)",px="$(expr ${viewHeight} / 64)" -- "${crashLog}"
|
||||
"${KOREADER_DIR}/fbink" -q -b -O -t regular=${KOREADER_DIR}/fonts/droid/DroidSansMono.ttf,top=$((viewHeight / 2 + FONTH * 2 + FONTH / 2)),left=$((viewWidth / 60)),right=$((viewWidth / 60)),px=$((viewHeight / 64)) -- "${crashLog}"
|
||||
# So far, we hadn't triggered an actual screen refresh, do that now, to make sure everything is bundled in a single flashing refresh.
|
||||
${KOREADER_DIR}/fbink -q -f -s
|
||||
# Cue a lemming's faceplant sound effect!
|
||||
|
||||
@@ -612,7 +612,6 @@ end
|
||||
function OPDSBrowser:createNewDownloadDialog(path, buttons)
|
||||
self.download_dialog = ButtonDialogTitle:new{
|
||||
title = T(_("Download folder:\n%1\n\nDownload file type:"), BD.dirpath(path)),
|
||||
use_info_style = true,
|
||||
buttons = buttons
|
||||
}
|
||||
end
|
||||
@@ -651,10 +650,10 @@ function OPDSBrowser:showDownloads(item)
|
||||
table.insert(buttons, line)
|
||||
end
|
||||
table.insert(buttons, {})
|
||||
-- Set download folder and book info buttons.
|
||||
-- Set download folder button.
|
||||
table.insert(buttons, {
|
||||
{
|
||||
text = _("Select folder"),
|
||||
text = _("Select another folder"),
|
||||
callback = function()
|
||||
require("ui/downloadmgr"):new{
|
||||
onConfirm = function(path)
|
||||
@@ -668,18 +667,7 @@ function OPDSBrowser:showDownloads(item)
|
||||
end,
|
||||
}:chooseDir()
|
||||
end,
|
||||
},
|
||||
{
|
||||
text = _("Book information"),
|
||||
enabled = type(item.content) == "string",
|
||||
callback = function()
|
||||
local TextViewer = require("ui/widget/textviewer")
|
||||
UIManager:show(TextViewer:new{
|
||||
title = item.text,
|
||||
text = util.htmlToPlainTextIfHtml(item.content),
|
||||
})
|
||||
end,
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
self:createNewDownloadDialog(self.getCurrentDownloadDir(), buttons)
|
||||
|
||||
@@ -72,6 +72,10 @@ function OPDSParser:createFlatXTable(xlex, curr_element)
|
||||
end
|
||||
|
||||
function OPDSParser:parse(text)
|
||||
-- Murder Calibre's whole "content" block, because luxl doesn't really deal well with various XHTML quirks,
|
||||
-- as the list of crappy replacements below attests to...
|
||||
-- There's also a high probability of finding orphaned tags or badly nested ones in there, which will screw everything up.
|
||||
text = text:gsub('<content type="xhtml">.-</content>', '')
|
||||
-- luxl doesn't handle XML comments, so strip them
|
||||
text = text:gsub("<!%-%-.-%-%->", "")
|
||||
-- luxl is also particular about the syntax for self-closing, empty & orphaned tags...
|
||||
@@ -80,18 +84,8 @@ function OPDSParser:parse(text)
|
||||
text = text:gsub("<([bh]r)>", "<%1 />")
|
||||
-- Some OPDS catalogs wrap text in a CDATA section, remove it as it causes parsing problems
|
||||
text = text:gsub("<!%[CDATA%[(.-)%]%]>", function (s)
|
||||
return s:gsub("%p", {["&"] = "&", ["<"] = "<", [">"] = ">"})
|
||||
return s:gsub( "%p", {["&"] = "&", ["<"] = "<", [">"] = ">" } )
|
||||
end )
|
||||
|
||||
-- NOTE: OPDS content tags are likely to contain a bunch of HTML or XHTML. We do *NOT* want to let luxl parse that,
|
||||
-- because it doesn't really deal well with various XHTML quirks, as the list of crappy replacements above attests to...
|
||||
-- There's also a high probability of finding orphaned tags or badly nested ones in there, which would screw everything up.
|
||||
-- In any case, we just want to treat the whole thing as a single text node anyway, so, just mangle the markup to force luxl's hand.
|
||||
text = text:gsub('<content type=".-">', "<content>")
|
||||
text = text:gsub("<content>(.-)</content>", function (s)
|
||||
return '<content type="text">' .. s:gsub("%p", {["<"] = "<", [">"] = ">", ['"'] = """, ["'"] = "'"}) .. "</content>"
|
||||
end )
|
||||
|
||||
local xlex = luxl.new(text, #text)
|
||||
return assert(self:createFlatXTable(xlex))
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user