mirror of
https://github.com/koreader/koreader.git
synced 2025-08-10 00:52:38 +00:00
Clarify our OOP semantics across the codebase (#9586)
Basically: * Use `extend` for class definitions * Use `new` for object instantiations That includes some minor code cleanups along the way: * Updated `Widget`'s docs to make the semantics clearer. * Removed `should_restrict_JIT` (it's been dead code since https://github.com/koreader/android-luajit-launcher/pull/283) * Minor refactoring of LuaSettings/LuaData/LuaDefaults/DocSettings to behave (mostly, they are instantiated via `open` instead of `new`) like everything else and handle inheritance properly (i.e., DocSettings is now a proper LuaSettings subclass). * Default to `WidgetContainer` instead of `InputContainer` for stuff that doesn't actually setup key/gesture events. * Ditto for explicit `*Listener` only classes, make sure they're based on `EventListener` instead of something uselessly fancier. * Unless absolutely necessary, do not store references in class objects, ever; only values. Instead, always store references in instances, to avoid both sneaky inheritance issues, and sneaky GC pinning of stale references. * ReaderUI: Fix one such issue with its `active_widgets` array, with critical implications, as it essentially pinned *all* of ReaderUI's modules, including their reference to the `Document` instance (i.e., that was a big-ass leak). * Terminal: Make sure the shell is killed on plugin teardown. * InputText: Fix Home/End/Del physical keys to behave sensibly. * InputContainer/WidgetContainer: If necessary, compute self.dimen at paintTo time (previously, only InputContainers did, which might have had something to do with random widgets unconcerned about input using it as a baseclass instead of WidgetContainer...). * OverlapGroup: Compute self.dimen at *init* time, because for some reason it needs to do that, but do it directly in OverlapGroup instead of going through a weird WidgetContainer method that it was the sole user of. * ReaderCropping: Under no circumstances should a Document instance member (here, self.bbox) risk being `nil`ed! * Kobo: Minor code cleanups.
This commit is contained in:
@@ -5,13 +5,14 @@ in the so-called sidecar directory
|
||||
]]
|
||||
|
||||
local DataStorage = require("datastorage")
|
||||
local LuaSettings = require("luasettings")
|
||||
local dump = require("dump")
|
||||
local ffiutil = require("ffi/util")
|
||||
local lfs = require("libs/libkoreader-lfs")
|
||||
local logger = require("logger")
|
||||
local util = require("util")
|
||||
|
||||
local DocSettings = {}
|
||||
local DocSettings = LuaSettings:extend{}
|
||||
|
||||
local HISTORY_DIR = DataStorage:getHistoryDir()
|
||||
|
||||
@@ -102,18 +103,20 @@ end
|
||||
-- @treturn DocSettings object
|
||||
function DocSettings:open(docfile)
|
||||
--- @todo (zijiehe): Remove history_path, use only sidecar.
|
||||
local new = {}
|
||||
new.history_file = self:getHistoryPath(docfile)
|
||||
|
||||
local sidecar = self:getSidecarDir(docfile)
|
||||
-- NOTE: Beware, our new instance is new, but self is still DocSettings!
|
||||
local new = DocSettings:extend{}
|
||||
new.history_file = new:getHistoryPath(docfile)
|
||||
|
||||
local sidecar = new:getSidecarDir(docfile)
|
||||
new.sidecar = sidecar
|
||||
DocSettings:ensureSidecar(sidecar)
|
||||
-- If there is a file which has a same name as the sidecar directory, or
|
||||
-- the file system is read-only, we should not waste time to read it.
|
||||
-- If there is a file which has a same name as the sidecar directory,
|
||||
-- or the file system is read-only, we should not waste time to read it.
|
||||
if lfs.attributes(sidecar, "mode") == "directory" then
|
||||
-- New sidecar file name is metadata.{file last suffix}.lua. So we
|
||||
-- can handle two files with only different suffixes.
|
||||
new.sidecar_file = self:getSidecarFile(docfile)
|
||||
-- New sidecar file name is metadata.{file last suffix}.lua.
|
||||
-- So we can handle two files with only different suffixes.
|
||||
new.sidecar_file = new:getSidecarFile(docfile)
|
||||
new.legacy_sidecar_file = sidecar.."/"..
|
||||
ffiutil.basename(docfile)..".lua"
|
||||
end
|
||||
@@ -163,139 +166,7 @@ function DocSettings:open(docfile)
|
||||
new.data = {}
|
||||
end
|
||||
|
||||
return setmetatable(new, {__index = DocSettings})
|
||||
end
|
||||
|
||||
--[[-- Reads a setting, optionally initializing it to a default.
|
||||
|
||||
If default is provided, and the key doesn't exist yet, it is initialized to default first.
|
||||
This ensures both that the defaults are actually set if necessary,
|
||||
and that the returned reference actually belongs to the DocSettings object straight away,
|
||||
without requiring further interaction (e.g., saveSetting) from the caller.
|
||||
|
||||
This is mainly useful if the data type you want to retrieve/store is assigned/returned/passed by reference (e.g., a table),
|
||||
and you never actually break that reference by assigning another one to the same variable, (by e.g., assigning it a new object).
|
||||
c.f., https://www.lua.org/manual/5.1/manual.html#2.2
|
||||
|
||||
@param key The setting's key
|
||||
@param default Initialization data (Optional)
|
||||
]]
|
||||
function DocSettings:readSetting(key, default)
|
||||
-- No initialization data: legacy behavior
|
||||
if not default then
|
||||
return self.data[key]
|
||||
end
|
||||
|
||||
if not self:has(key) then
|
||||
self.data[key] = default
|
||||
end
|
||||
return self.data[key]
|
||||
end
|
||||
|
||||
--- Saves a setting.
|
||||
function DocSettings:saveSetting(key, value)
|
||||
self.data[key] = value
|
||||
return self
|
||||
end
|
||||
|
||||
--- Deletes a setting.
|
||||
function DocSettings:delSetting(key)
|
||||
self.data[key] = nil
|
||||
return self
|
||||
end
|
||||
|
||||
--- Checks if setting exists.
|
||||
function DocSettings:has(key)
|
||||
return self.data[key] ~= nil
|
||||
end
|
||||
|
||||
--- Checks if setting does not exist.
|
||||
function DocSettings:hasNot(key)
|
||||
return self.data[key] == nil
|
||||
end
|
||||
|
||||
--- Checks if setting is `true` (boolean).
|
||||
function DocSettings:isTrue(key)
|
||||
return self.data[key] == true
|
||||
end
|
||||
|
||||
--- Checks if setting is `false` (boolean).
|
||||
function DocSettings:isFalse(key)
|
||||
return self.data[key] == false
|
||||
end
|
||||
|
||||
--- Checks if setting is `nil` or `true`.
|
||||
function DocSettings:nilOrTrue(key)
|
||||
return self:hasNot(key) or self:isTrue(key)
|
||||
end
|
||||
|
||||
--- Checks if setting is `nil` or `false`.
|
||||
function DocSettings:nilOrFalse(key)
|
||||
return self:hasNot(key) or self:isFalse(key)
|
||||
end
|
||||
|
||||
--- Flips `nil` or `true` to `false`, and `false` to `nil`.
|
||||
--- e.g., a setting that defaults to true.
|
||||
function DocSettings:flipNilOrTrue(key)
|
||||
if self:nilOrTrue(key) then
|
||||
self:saveSetting(key, false)
|
||||
else
|
||||
self:delSetting(key)
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- Flips `nil` or `false` to `true`, and `true` to `nil`.
|
||||
--- e.g., a setting that defaults to false.
|
||||
function DocSettings:flipNilOrFalse(key)
|
||||
if self:nilOrFalse(key) then
|
||||
self:saveSetting(key, true)
|
||||
else
|
||||
self:delSetting(key)
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- Flips a setting between `true` and `nil`.
|
||||
function DocSettings:flipTrue(key)
|
||||
if self:isTrue(key) then
|
||||
self:delSetting(key)
|
||||
else
|
||||
self:saveSetting(key, true)
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- Flips a setting between `false` and `nil`.
|
||||
function DocSettings:flipFalse(key)
|
||||
if self:isFalse(key) then
|
||||
self:delSetting(key)
|
||||
else
|
||||
self:saveSetting(key, false)
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
-- Unconditionally makes a boolean setting `true`.
|
||||
function DocSettings:makeTrue(key)
|
||||
self:saveSetting(key, true)
|
||||
return self
|
||||
end
|
||||
|
||||
-- Unconditionally makes a boolean setting `false`.
|
||||
function DocSettings:makeFalse(key)
|
||||
self:saveSetting(key, false)
|
||||
return self
|
||||
end
|
||||
|
||||
--- Toggles a boolean setting
|
||||
function DocSettings:toggle(key)
|
||||
if self:nilOrFalse(key) then
|
||||
self:saveSetting(key, true)
|
||||
else
|
||||
self:saveSetting(key, false)
|
||||
end
|
||||
return self
|
||||
return new
|
||||
end
|
||||
|
||||
--- Serializes settings and writes them to `metadata.lua`.
|
||||
@@ -307,8 +178,7 @@ function DocSettings:flush()
|
||||
return
|
||||
end
|
||||
|
||||
-- If we can write to sidecar_file, we do not need to write to history_file
|
||||
-- anymore.
|
||||
-- If we can write to sidecar_file, we do not need to write to history_file anymore.
|
||||
local serials = {}
|
||||
if self.sidecar_file then
|
||||
table.insert(serials, self.sidecar_file)
|
||||
@@ -322,11 +192,10 @@ function DocSettings:flush()
|
||||
for _, f in ipairs(serials) do
|
||||
local directory_updated = false
|
||||
if lfs.attributes(f, "mode") == "file" then
|
||||
-- As an additional safety measure (to the ffiutil.fsync* calls
|
||||
-- used below), we only backup the file to .old when it has
|
||||
-- not been modified in the last 60 seconds. This should ensure
|
||||
-- in the case the fsync calls are not supported that the OS
|
||||
-- may have itself sync'ed that file content in the meantime.
|
||||
-- As an additional safety measure (to the ffiutil.fsync* calls used below),
|
||||
-- we only backup the file to .old when it has not been modified in the last 60 seconds.
|
||||
-- This should ensure in the case the fsync calls are not supported
|
||||
-- that the OS may have itself sync'ed that file content in the meantime.
|
||||
local mtime = lfs.attributes(f, "modification")
|
||||
if mtime < os.time() - 60 then
|
||||
logger.dbg("Rename ", f, " to ", f .. ".old")
|
||||
@@ -364,18 +233,13 @@ function DocSettings:flush()
|
||||
end
|
||||
end
|
||||
|
||||
function DocSettings:close()
|
||||
self:flush()
|
||||
end
|
||||
|
||||
function DocSettings:getFilePath()
|
||||
return self.filepath
|
||||
end
|
||||
|
||||
--- Purges (removes) sidecar directory.
|
||||
function DocSettings:purge(full)
|
||||
-- Remove any of the old ones we may consider as candidates
|
||||
-- in DocSettings:open()
|
||||
-- Remove any of the old ones we may consider as candidates in DocSettings:open()
|
||||
if self.history_file then
|
||||
os.remove(self.history_file)
|
||||
os.remove(self.history_file .. ".old")
|
||||
@@ -385,12 +249,10 @@ function DocSettings:purge(full)
|
||||
end
|
||||
if lfs.attributes(self.sidecar, "mode") == "directory" then
|
||||
if full then
|
||||
-- Asked to remove all the content of this .sdr directory,
|
||||
-- whether it's ours or not
|
||||
-- Asked to remove all the content of this .sdr directory, whether it's ours or not
|
||||
ffiutil.purgeDir(self.sidecar)
|
||||
else
|
||||
-- Only remove the files we know we may have created
|
||||
-- with our usual names.
|
||||
-- Only remove the files we know we may have created with our usual names.
|
||||
for f in lfs.dir(self.sidecar) do
|
||||
local fullpath = self.sidecar.."/"..f
|
||||
local to_remove = false
|
||||
@@ -398,8 +260,8 @@ function DocSettings:purge(full)
|
||||
-- Currently, we only create a single file in there,
|
||||
-- named metadata.suffix.lua (ie. metadata.epub.lua),
|
||||
-- with possibly backups named metadata.epub.lua.old and
|
||||
-- metadata.epub.lua.old_dom20180528, so all sharing the
|
||||
-- same base: self.sidecar_file
|
||||
-- metadata.epub.lua.old_dom20180528,
|
||||
-- so all sharing the same base: self.sidecar_file
|
||||
if util.stringStartsWith(fullpath, self.sidecar_file) then
|
||||
to_remove = true
|
||||
end
|
||||
@@ -414,8 +276,8 @@ function DocSettings:purge(full)
|
||||
os.remove(self.sidecar)
|
||||
end
|
||||
end
|
||||
-- We should have meet the candidate we used and remove it above. But in
|
||||
-- case we didn't, remove it
|
||||
-- We should have meet the candidate we used and remove it above.
|
||||
-- But in case we didn't, remove it.
|
||||
if self.filepath and lfs.attributes(self.filepath, "mode") == "file" then
|
||||
os.remove(self.filepath)
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user