mirror of
https://github.com/koreader/koreader
synced 2024-10-31 21:20:20 +00:00
6919e40db2
Yes, we already fixed that forcing portrait mode on KPV start, and document close. However, this is a fix that will prevent the crash if we don't force portrait. I'm not suggesting that we stop forcing portrait - I think we all agreed that FileChooser in landscape mode on K3 is not very usable. But, this might be handy on Touch or KPW (we'll see about that later), so it should be usable should we need it. To test this, you need to comment out self:setRotationMode(0) line in UniReader:inputLoop() in unireader.lua, thus removing forcing of portrait on document close .
715 lines
23 KiB
Lua
715 lines
23 KiB
Lua
require "rendertext"
|
|
require "keys"
|
|
require "graphics"
|
|
require "font"
|
|
require "filesearcher"
|
|
require "filehistory"
|
|
require "fileinfo"
|
|
require "inputbox"
|
|
require "selectmenu"
|
|
require "dialog"
|
|
require "readerchooser"
|
|
require "battery"
|
|
|
|
FileChooser = {
|
|
title_H = 40, -- title height
|
|
spacing = 36, -- spacing between lines
|
|
foot_H = 28, -- foot height
|
|
margin_H = 10, -- horisontal margin
|
|
|
|
-- state buffer
|
|
dirs = nil,
|
|
files = nil,
|
|
items = 0,
|
|
path = "",
|
|
page = 1,
|
|
current = 1,
|
|
oldcurrent = 0,
|
|
exception_message = nil,
|
|
|
|
pagedirty = true,
|
|
markerdirty = false,
|
|
perpage,
|
|
|
|
clipboard = lfs.currentdir() .. "/clipboard", -- NO finishing slash
|
|
before_clipboard, -- NuPogodi, 30.09.12: to store the path where jump to clipboard was made from
|
|
|
|
-- modes that configures the filechoser for users with various purposes & skills
|
|
filemanager_expert_mode, -- default value is defined in reader.lua
|
|
-- the definitions
|
|
BEGINNERS_MODE = 1, -- the filemanager content is restricted by files with reader-related extensions; safe renaming (no extension)
|
|
ADVANCED_MODE = 2, -- no extension-based filtering; renaming with extensions; appreciable danger to crash crengine by improper docs
|
|
ROOT_MODE = 3, -- TODO: all functions (including non-stable and dangerous)
|
|
}
|
|
|
|
-- NuPogodi, 29.09.12: simplified the code
|
|
function getProperTitleLength(txt, font_face, max_width)
|
|
while sizeUtf8Text(0, G_width, font_face, txt, true).x > max_width do
|
|
txt = txt:sub(2, -1)
|
|
end
|
|
return txt
|
|
end
|
|
|
|
-- NuPogodi, 29.09.12: avoid using widgets
|
|
function DrawTitle(text, lmargin, y, height, color, font_face)
|
|
local r = 6 -- radius for round corners
|
|
color = 3 -- redefine to ignore the input for background color
|
|
|
|
fb.bb:paintRect(1, 1, G_width-2, height - r, color)
|
|
blitbuffer.paintBorder(fb.bb, 1, height/2, G_width-2, height/2, height/2, color, r)
|
|
|
|
local t = BatteryLevel() .. os.date(" %H:%M")
|
|
r = sizeUtf8Text(0, G_width, font_face, t, true).x
|
|
renderUtf8Text(fb.bb, G_width-r-lmargin, height-10, font_face, t, true)
|
|
|
|
r = G_width - r - 2 * lmargin - 10 -- let's leave small gap
|
|
if sizeUtf8Text(0, G_width, font_face, text, true).x <= r then
|
|
renderUtf8Text(fb.bb, lmargin, height-10, font_face, text, true)
|
|
else
|
|
t = renderUtf8Text(fb.bb, lmargin, height-10, font_face, "...", true)
|
|
text = getProperTitleLength(text, font_face, r-t)
|
|
renderUtf8Text(fb.bb, lmargin+t, height-10, font_face, text, true)
|
|
end
|
|
end
|
|
|
|
function DrawFooter(text,font_face,h)
|
|
local y = G_height - 7
|
|
-- just dirty fix to have the same footer everywhere
|
|
local x = FileChooser.margin_H --(G_width / 2) - 50
|
|
renderUtf8Text(fb.bb, x, y, font_face, text.." - Press H for help", true)
|
|
end
|
|
|
|
function DrawFileItem(name,x,y,image)
|
|
-- define icon file for
|
|
if name == ".." then image = "upfolder" end
|
|
local fn = "./resources/"..image..".png"
|
|
-- check whether the icon file exists or not
|
|
if not io.open(fn, "r") then fn = "./resources/other.png" end
|
|
local iw = ImageWidget:new({ file = fn })
|
|
iw:paintTo(fb.bb, x, y - iw:getSize().h + 1)
|
|
-- then drawing filenames
|
|
local cface = Font:getFace("cfont", 22)
|
|
local xleft = x + iw:getSize().w + 9 -- the gap between icon & filename
|
|
local width = G_width - xleft - x
|
|
-- now printing the name
|
|
if sizeUtf8Text(xleft, G_width - x, cface, name, true).x < width then
|
|
renderUtf8Text(fb.bb, xleft, y, cface, name, true)
|
|
else
|
|
local lgap = sizeUtf8Text(0, width, cface, " ...", true).x
|
|
local handle = renderUtf8TextWidth(fb.bb, xleft, y, cface, name, true, width - lgap - x)
|
|
renderUtf8Text(fb.bb, handle.x + lgap + x, y, cface, " ...", true)
|
|
end
|
|
iw:free()
|
|
end
|
|
|
|
function getAbsolutePath(aPath)
|
|
local abs_path
|
|
if not aPath then
|
|
abs_path = aPath
|
|
elseif aPath:match('^//') then
|
|
abs_path = aPath:sub(2)
|
|
elseif aPath:match('^/') then
|
|
abs_path = aPath
|
|
elseif #aPath == 0 then
|
|
abs_path = '/'
|
|
else
|
|
local curr_dir = lfs.currentdir()
|
|
abs_path = aPath
|
|
if lfs.chdir(aPath) then
|
|
abs_path = lfs.currentdir()
|
|
lfs.chdir(curr_dir)
|
|
end
|
|
--Debug("rel: '"..aPath.."' abs:'"..abs_path.."'")
|
|
end
|
|
return abs_path
|
|
end
|
|
|
|
function FileChooser:readDir()
|
|
self.dirs = {}
|
|
self.files = {}
|
|
for f in lfs.dir(self.path) do
|
|
if lfs.attributes(self.path.."/"..f, "mode") == "directory" and f ~= "." and f~=".."
|
|
and not string.match(f, "^%.[^.]") then
|
|
table.insert(self.dirs, f)
|
|
elseif lfs.attributes(self.path.."/"..f, "mode") == "file"
|
|
and not string.match(f, "^%.[^.]") then
|
|
local file_type = string.lower(string.match(f, ".+%.([^.]+)") or "")
|
|
if ReaderChooser:getReaderByType(file_type) then
|
|
table.insert(self.files, f)
|
|
end
|
|
end
|
|
end
|
|
table.sort(self.dirs)
|
|
if self.path~="/" then table.insert(self.dirs,1,"..") end
|
|
table.sort(self.files)
|
|
end
|
|
|
|
function FileChooser:setPath(newPath)
|
|
local oldPath = self.path
|
|
local search_position = false
|
|
|
|
-- We only need to re-scan the directory for the correct
|
|
-- position if we are entering it via ".." entry.
|
|
-- Unfortunately, ".." reaches us in the form of an absolute path,
|
|
-- but we can use the following trick: if we are traversing via "..",
|
|
-- then the new pathname will always be shorter than the old pathname.
|
|
if oldPath ~= "" and #newPath < #oldPath then
|
|
search_position = true
|
|
end
|
|
|
|
-- treat ".." entry in the clipboard as
|
|
-- a special case, namely return to the directory saved
|
|
-- in self.before_clipboard
|
|
if self.before_clipboard then
|
|
newPath = self.before_clipboard
|
|
self.before_clipboard = nil
|
|
end
|
|
|
|
-- convert to the absolute path so that we can
|
|
-- traverse up to the parent via ".."
|
|
self.path = getAbsolutePath(newPath)
|
|
|
|
-- read the whole self.path directory
|
|
-- the error message is stored for display in the header.
|
|
local ret, msg = pcall(self.readDir, self)
|
|
if not ret then
|
|
self.exception_message = msg
|
|
return self:setPath(oldPath)
|
|
else
|
|
self.items = #self.dirs + #self.files
|
|
|
|
-- Dijkstra will hate me for this...
|
|
if newPath == oldPath then
|
|
return
|
|
end
|
|
|
|
if search_position then
|
|
-- extract the base part of oldPath, i.e. the actual directory name
|
|
local pos, _, oldPathBase = string.find(oldPath, "^.*/(.*)$")
|
|
|
|
-- now search for the base part of oldPath among self.dirs[]
|
|
for k, v in ipairs(self.dirs) do
|
|
if v == oldPathBase then
|
|
self.current, self.page = gotoTargetItem(k, self.items, self.current, self.page, self.perpage)
|
|
break
|
|
end
|
|
end
|
|
else
|
|
-- point the current position to ".." entry
|
|
self.page = 1
|
|
self.current = 1
|
|
end
|
|
end
|
|
end
|
|
|
|
function FileChooser:choose(ypos, height)
|
|
self.perpage = math.floor(height / self.spacing) - 2
|
|
self.pagedirty = true
|
|
self.markerdirty = false
|
|
|
|
self:addAllCommands()
|
|
|
|
while true do
|
|
local tface = Font:getFace("tfont", 25)
|
|
local fface = Font:getFace("ffont", 16)
|
|
local cface = Font:getFace("cfont", 22)
|
|
|
|
if self.pagedirty then
|
|
fb.bb:paintRect(0, ypos, fb.bb:getWidth(), fb.bb:getHeight(), 0)
|
|
local c
|
|
for c = 1, self.perpage do
|
|
local i = (self.page - 1) * self.perpage + c
|
|
if i <= #self.dirs then
|
|
DrawFileItem(self.dirs[i],self.margin_H,ypos+self.title_H+self.spacing*c,"folder")
|
|
elseif i <= self.items then
|
|
local file_type = string.lower(string.match(self.files[i-#self.dirs], ".+%.([^.]+)") or "")
|
|
DrawFileItem(self.files[i-#self.dirs],self.margin_H,ypos+self.title_H+self.spacing*c,file_type)
|
|
end
|
|
end
|
|
-- draw footer
|
|
all_page = math.ceil(self.items/self.perpage)
|
|
DrawFooter("Page "..self.page.." of "..all_page,fface,self.foot_H)
|
|
-- draw menu title
|
|
local msg = self.exception_message and self.exception_message:match("[^%:]+:%d+: (.*)") or self.path
|
|
self.exception_message = nil
|
|
-- draw header
|
|
DrawTitle(msg,self.margin_H,ypos,self.title_H,3,tface)
|
|
self.markerdirty = true
|
|
end
|
|
|
|
if self.markerdirty then
|
|
local ymarker = ypos + 8 + self.title_H
|
|
if not self.pagedirty then
|
|
if self.oldcurrent > 0 then
|
|
fb.bb:paintRect(self.margin_H, ymarker+self.spacing*self.oldcurrent, fb.bb:getWidth()-2*self.margin_H, 3, 0)
|
|
fb:refresh(1, self.margin_H, ymarker+self.spacing*self.oldcurrent, fb.bb:getWidth() - 2*self.margin_H, 3)
|
|
end
|
|
end
|
|
fb.bb:paintRect(self.margin_H, ymarker+self.spacing*self.current, fb.bb:getWidth()-2*self.margin_H, 3, 15)
|
|
if not self.pagedirty then
|
|
fb:refresh(1, self.margin_H, ymarker+self.spacing*self.current, fb.bb:getWidth()-2*self.margin_H, 3)
|
|
end
|
|
self.oldcurrent = self.current
|
|
self.markerdirty = false
|
|
end
|
|
|
|
if self.pagedirty then
|
|
fb:refresh(0, 0, ypos, fb.bb:getWidth(), fb.bb:getHeight())
|
|
self.pagedirty = false
|
|
end
|
|
|
|
local ev = input.saveWaitForEvent()
|
|
--Debug("key code:"..ev.code)
|
|
ev.code = adjustKeyEvents(ev)
|
|
if ev.type == EV_KEY and ev.value ~= EVENT_VALUE_KEY_RELEASE then
|
|
keydef = Keydef:new(ev.code, getKeyModifier())
|
|
Debug("key pressed: "..tostring(keydef))
|
|
|
|
command = self.commands:getByKeydef(keydef)
|
|
if command ~= nil then
|
|
Debug("command to execute: "..tostring(command))
|
|
ret_code = command.func(self, keydef)
|
|
else
|
|
Debug("command not found: "..tostring(command))
|
|
end
|
|
|
|
if ret_code == "break" then break end
|
|
|
|
if self.selected_item ~= nil then
|
|
Debug("# selected "..self.selected_item)
|
|
return self.selected_item
|
|
end
|
|
end -- if ev.type ==
|
|
end -- while
|
|
end
|
|
|
|
-- add available commands
|
|
function FileChooser:addAllCommands()
|
|
self.commands = Commands:new{}
|
|
|
|
self.commands:add(KEY_SPACE, nil, "Space",
|
|
"refresh file list",
|
|
function(self)
|
|
self:setPath(self.path)
|
|
self.pagedirty = true
|
|
end
|
|
)
|
|
self.commands:add(KEY_FW_DOWN, nil, "joypad down",
|
|
"next item",
|
|
function(self)
|
|
if self.current == self.perpage then
|
|
if self.page < (self.items / self.perpage) then
|
|
self.current = 1
|
|
self.page = self.page + 1
|
|
self.pagedirty = true
|
|
end
|
|
else
|
|
if self.page ~= math.floor(self.items / self.perpage) + 1
|
|
or self.current + (self.page-1)*self.perpage < self.items then
|
|
self.current = self.current + 1
|
|
self.markerdirty = true
|
|
end
|
|
end
|
|
end
|
|
)
|
|
self.commands:add(KEY_FW_UP, nil, "joypad up",
|
|
"previous item",
|
|
function(self)
|
|
if self.current == 1 then
|
|
if self.page > 1 then
|
|
self.current = self.perpage
|
|
self.page = self.page - 1
|
|
self.pagedirty = true
|
|
end
|
|
else
|
|
self.current = self.current - 1
|
|
self.markerdirty = true
|
|
end
|
|
end
|
|
)
|
|
-- NuPogodi, 01.10.12: fast jumps to items at positions 10, 20, .. 90, 0% within the list
|
|
local numeric_keydefs, i = {}
|
|
for i=1, 10 do numeric_keydefs[i]=Keydef:new(KEY_1+i-1, nil, tostring(i%10)) end
|
|
self.commands:addGroup("[1, 2 .. 9, 0]", numeric_keydefs,
|
|
"item at position 0%, 10% .. 90%, 100%",
|
|
function(self)
|
|
local target_item = math.ceil(self.items * (keydef.keycode-KEY_1) / 9)
|
|
self.current, self.page, self.markerdirty, self.pagedirty =
|
|
gotoTargetItem(target_item, self.items, self.current, self.page, self.perpage)
|
|
end
|
|
)
|
|
self.commands:add({KEY_PGFWD, KEY_LPGFWD}, nil, ">",
|
|
"next page",
|
|
function(self)
|
|
if self.page < (self.items / self.perpage) then
|
|
if self.current + self.page*self.perpage > self.items then
|
|
self.current = self.items - self.page*self.perpage
|
|
end
|
|
self.page = self.page + 1
|
|
self.pagedirty = true
|
|
else
|
|
self.current = self.items - (self.page-1)*self.perpage
|
|
self.markerdirty = true
|
|
end
|
|
end
|
|
)
|
|
self.commands:add({KEY_PGBCK, KEY_LPGBCK}, nil, "<",
|
|
"previous page",
|
|
function(self)
|
|
if self.page > 1 then
|
|
self.page = self.page - 1
|
|
self.pagedirty = true
|
|
else
|
|
self.current = 1
|
|
self.markerdirty = true
|
|
end
|
|
end
|
|
)
|
|
self.commands:add(KEY_G, nil, "G", -- NuPogodi, 01.10.12: goto page No.
|
|
"goto page",
|
|
function(self)
|
|
local n = math.ceil(self.items / self.perpage)
|
|
local page = NumInputBox:input(G_height-100, 100, "Page:", "current page "..self.page.." of "..n, true)
|
|
if pcall(function () page = math.floor(page) end) -- convert string to number
|
|
and page ~= self.page and page > 0 and page <= n then
|
|
self.page = page
|
|
if self.current + (page-1)*self.perpage > self.items then
|
|
self.current = self.items - (page-1)*self.perpage
|
|
end
|
|
end
|
|
self.pagedirty = true
|
|
end
|
|
)
|
|
self.commands:add(KEY_FW_RIGHT, nil, "joypad right",
|
|
"show document information",
|
|
function(self)
|
|
local folder = self.dirs[self.perpage*(self.page-1)+self.current]
|
|
if folder then
|
|
if folder == ".." then
|
|
warningUnsupportedFunction()
|
|
else
|
|
folder = self.path.."/"..folder
|
|
if FileInfo:show(folder) == "goto" then
|
|
self:setPath(folder)
|
|
end
|
|
end
|
|
else -- file info
|
|
FileInfo:show(self.path, self.files[self.perpage*(self.page-1)+self.current-#self.dirs])
|
|
end
|
|
self.pagedirty = true
|
|
end
|
|
)
|
|
self.commands:add({KEY_ENTER, KEY_FW_PRESS}, nil, "Enter",
|
|
"open document / goto folder",
|
|
function(self)
|
|
local newdir = self.dirs[self.perpage*(self.page-1)+self.current]
|
|
if newdir == ".." then
|
|
local path = string.gsub(self.path, "(.*)/[^/]+/?$", "%1")
|
|
self:setPath(path)
|
|
elseif newdir then
|
|
self:setPath(self.path.."/"..newdir)
|
|
else
|
|
self.pathfile = self.path.."/"..self.files[self.perpage*(self.page-1)+self.current - #self.dirs]
|
|
openFile(self.pathfile)
|
|
end
|
|
self.pagedirty = true
|
|
end
|
|
)
|
|
-- modified to delete both files and empty folders
|
|
self.commands:add(KEY_DEL, nil, "Del",
|
|
"delete selected item",
|
|
function(self)
|
|
local pos = self.perpage*(self.page-1)+self.current
|
|
if pos > #self.dirs then -- file
|
|
if InfoMessage.InfoMethod[MSG_CONFIRM] == 0 then -- silent regime
|
|
self:deleteFileAtPosition(pos)
|
|
else
|
|
InfoMessage:inform("Press 'Y' to confirm ", DINFO_NODELAY, 0, MSG_CONFIRM)
|
|
if ReturnKey() == KEY_Y then self:deleteFileAtPosition(pos) end
|
|
end
|
|
elseif self.dirs[pos] == ".." then
|
|
warningUnsupportedFunction()
|
|
else -- other folders
|
|
if InfoMessage.InfoMethod[MSG_CONFIRM] == 0 then -- silent regime
|
|
self:deleteFolderAtPosition(pos)
|
|
else
|
|
InfoMessage:inform("Press 'Y' to confirm ", DINFO_NODELAY, 0, MSG_CONFIRM)
|
|
if ReturnKey() == KEY_Y then self:deleteFolderAtPosition(pos) end
|
|
end
|
|
end
|
|
self.pagedirty = true
|
|
end -- function
|
|
)
|
|
-- make renaming flexible: it either keeps old extension (BEGINNERS_MODE) or
|
|
-- allows to rename the whole filename including the extension
|
|
self.commands:add(KEY_R, MOD_SHIFT, "R",
|
|
"rename file",
|
|
function(self)
|
|
local oldname = self:FullFileName()
|
|
if oldname then
|
|
-- NuPogodi, 04.09.2012: safe mode (keep old extensions)
|
|
-- Tigran, 18/08/12: corrected the rename operation to include extension.)
|
|
local name_we = self.files[self.perpage*(self.page-1)+self.current-#self.dirs]
|
|
local ext = ""
|
|
if self.filemanager_expert_mode <= self.BEGINNERS_MODE then
|
|
ext = "."..string.lower(string.match(oldname, ".+%.([^.]+)") or "")
|
|
name_we = string.sub(name_we, 1, -1-string.len(ext))
|
|
end
|
|
local newname = InputBox:input(0, 0, "New filename:", name_we)
|
|
if newname then
|
|
newname = self.path.."/"..newname..ext
|
|
os.rename(oldname, newname)
|
|
os.rename(DocToHistory(oldname), DocToHistory(newname))
|
|
self:setPath(self.path)
|
|
end
|
|
self.pagedirty = true
|
|
end
|
|
end
|
|
)
|
|
self.commands:add(KEY_M, MOD_ALT, "M",
|
|
"set user privilege level",
|
|
function(self)
|
|
self:changeFileChooserMode()
|
|
end
|
|
)
|
|
self.commands:add(KEY_E, nil, "E",
|
|
"configure event notifications",
|
|
function(self)
|
|
InfoMessage:chooseNotificatonMethods()
|
|
self.pagedirty = true
|
|
end
|
|
)
|
|
self.commands:addGroup("Vol-/+", {Keydef:new(KEY_VPLUS,nil), Keydef:new(KEY_VMINUS,nil)},
|
|
"decrease/increase sound volume",
|
|
function(self)
|
|
InfoMessage:incrSoundVolume(keydef.keycode == KEY_VPLUS and 1 or -1)
|
|
end
|
|
)
|
|
self.commands:addGroup(MOD_SHIFT.."Vol-/+", {Keydef:new(KEY_VPLUS,MOD_SHIFT), Keydef:new(KEY_VMINUS,MOD_SHIFT)},
|
|
"decrease/increase TTS-engine speed",
|
|
function(self)
|
|
InfoMessage:incrTTSspeed(keydef.keycode == KEY_VPLUS and 1 or -1)
|
|
end
|
|
)
|
|
self.commands:add({KEY_F, KEY_AA}, nil, "F, Aa",
|
|
"change font faces",
|
|
function(self)
|
|
Font:chooseFonts()
|
|
self.pagedirty = true
|
|
end
|
|
)
|
|
self.commands:add(KEY_H,nil,"H",
|
|
"show help page",
|
|
function(self)
|
|
HelpPage:show(0, G_height, self.commands, "Hotkeys "..G_program_version)
|
|
self.pagedirty = true
|
|
end
|
|
)
|
|
self.commands:add(KEY_L, nil, "L",
|
|
"show last documents",
|
|
function(self)
|
|
FileHistory:init()
|
|
FileHistory:choose("")
|
|
self.pagedirty = true
|
|
return nil
|
|
end
|
|
)
|
|
self.commands:add(KEY_S, nil, "S",
|
|
"search files (single space matches all)",
|
|
function(self)
|
|
local keywords = InputBox:input(0, 0, "Search:")
|
|
if keywords then
|
|
InfoMessage:inform("Searching... ", DINFO_NODELAY, 1, MSG_AUX)
|
|
FileSearcher:init( self.path )
|
|
FileSearcher:choose(keywords)
|
|
end
|
|
self.pagedirty = true
|
|
end
|
|
)
|
|
self.commands:add(KEY_C, MOD_SHIFT, "C",
|
|
"copy file to 'clipboard'",
|
|
function(self)
|
|
local file = self:FullFileName()
|
|
if file then
|
|
os.execute("cp "..InQuotes(file).." "..self.clipboard)
|
|
local fn = self.files[self.perpage*(self.page-1)+self.current - #self.dirs]
|
|
os.execute("cp "..InQuotes(DocToHistory(file)).." "
|
|
..InQuotes(DocToHistory(self.clipboard.."/"..fn)) )
|
|
InfoMessage:inform("File copied to clipboard ", DINFO_DELAY, 1, MSG_WARN)
|
|
end
|
|
end
|
|
)
|
|
self.commands:add(KEY_X, MOD_SHIFT, "X",
|
|
"move file to 'clipboard'",
|
|
function(self)
|
|
-- TODO (NuPogodi, 27.09.12): overwrite?
|
|
local file = self:FullFileName()
|
|
if file then
|
|
local fn = self.files[self.perpage*(self.page-1)+self.current - #self.dirs]
|
|
os.rename(file, self.clipboard.."/"..fn)
|
|
os.rename(DocToHistory(file), DocToHistory(self.clipboard.."/"..fn))
|
|
InfoMessage:inform("File moved to clipboard ", DINFO_DELAY, 0, MSG_WARN)
|
|
local pos = self.perpage*(self.page-1)+self.current
|
|
table.remove(self.files, pos-#self.dirs)
|
|
self.items = self.items - 1
|
|
self.current, self.page = gotoTargetItem(pos, self.items, pos, self.page, self.perpage)
|
|
self.pagedirty = true
|
|
end
|
|
end
|
|
)
|
|
self.commands:add(KEY_V, MOD_SHIFT, "V",
|
|
"paste file(s) from 'clipboard'",
|
|
function(self)
|
|
-- TODO (NuPogodi, 27.09.12): first test whether the clipboard is empty & answer respectively
|
|
-- TODO (NuPogodi, 27.09.12): overwrite?
|
|
InfoMessage:inform("Moving files from clipboard...", DINFO_NODELAY, 0, MSG_AUX)
|
|
for f in lfs.dir(self.clipboard) do
|
|
if lfs.attributes(self.clipboard.."/"..f, "mode") == "file" then
|
|
os.rename(self.clipboard.."/"..f, self.path.."/"..f)
|
|
os.rename(DocToHistory(self.clipboard.."/"..f), DocToHistory(self.path.."/"..f))
|
|
end
|
|
end
|
|
self:setPath(self.path)
|
|
self.pagedirty = true
|
|
end
|
|
)
|
|
self.commands:add(KEY_DOT, MOD_ALT, ".",
|
|
"toggle battery level logging",
|
|
function(self)
|
|
G_battery_logging = not G_battery_logging
|
|
InfoMessage:inform("Battery logging "..(G_battery_logging and "on " or "off "), DINFO_DELAY, 1, MSG_AUX)
|
|
G_reader_settings:saveSetting("G_battery_logging", G_battery_logging)
|
|
end
|
|
)
|
|
self.commands:add(KEY_B, MOD_SHIFT, "B",
|
|
"show content of 'clipboard'",
|
|
function(self)
|
|
-- save the current directory in order to
|
|
-- return from clipboard via ".." entry
|
|
local current_path = self.path
|
|
if self.clipboard ~= self.path then
|
|
self:setPath(self.clipboard)
|
|
self.before_clipboard = current_path
|
|
end
|
|
self.pagedirty = true
|
|
end
|
|
)
|
|
self.commands:add(KEY_N, MOD_SHIFT, "N",
|
|
"make new folder",
|
|
function(self)
|
|
local folder = InputBox:input(0, 0, "New Folder:")
|
|
if folder then
|
|
if lfs.mkdir(self.path.."/"..folder) then
|
|
self:setPath(self.path)
|
|
end
|
|
end
|
|
self.pagedirty = true
|
|
end
|
|
)
|
|
self.commands:add(KEY_K, MOD_SHIFT, "K",
|
|
"run calculator",
|
|
function(self)
|
|
local CalcBox = InputBox:new{ inputmode = MODE_CALC }
|
|
CalcBox:input(0, 0, "Calc ")
|
|
self.pagedirty = true
|
|
end
|
|
)
|
|
self.commands:addGroup("Home, Alt + Back", { Keydef:new(KEY_HOME, nil),Keydef:new(KEY_BACK, MOD_ALT)}, "exit",
|
|
function(self)
|
|
return "break"
|
|
end
|
|
)
|
|
end
|
|
|
|
-- returns full filename or nil (if folder)
|
|
function FileChooser:FullFileName()
|
|
local pos = self.current + self.perpage*(self.page-1) - #self.dirs
|
|
return pos > 0 and self.path.."/"..self.files[pos] or warningUnsupportedFunction()
|
|
end
|
|
|
|
-- returns the keycode of released key
|
|
function ReturnKey()
|
|
while true do
|
|
ev = input.saveWaitForEvent()
|
|
ev.code = adjustKeyEvents(ev)
|
|
if ev.type == EV_KEY and ev.value ~= EVENT_VALUE_KEY_RELEASE then
|
|
break
|
|
end
|
|
end
|
|
return ev.code
|
|
end
|
|
|
|
function InQuotes(text)
|
|
return "\""..text.."\""
|
|
end
|
|
|
|
--[[ NuPogodi, 04.09.2012: to make it more easy for users with various purposes and skills.
|
|
ATM, one may leave only silent toggling between BEGINNERS_MODE <> ADVANCED_MODE
|
|
-- But, in future, one more (the so called ROOT_MODE) might also be rather useful.
|
|
Switching this mode on should allow developers & beta-testers to use some unstable
|
|
and/or dangerous functions able to crash the reader. ]]
|
|
|
|
function FileChooser:changeFileChooserMode()
|
|
local face_list = { "Safe mode for beginners", "Advanced mode for experienced users", "Expert mode for beta-testers & developers" }
|
|
local modes_menu = SelectMenu:new{
|
|
menu_title = "Set user privilege level",
|
|
item_array = face_list,
|
|
current_entry = self.filemanager_expert_mode - 1,
|
|
}
|
|
local m = modes_menu:choose(0, G_height)
|
|
if m and m ~= self.filemanager_expert_mode then
|
|
if (self.filemanager_expert_mode == self.BEGINNERS_MODE and m > self.BEGINNERS_MODE)
|
|
or (m == self.BEGINNERS_MODE and self.filemanager_expert_mode > self.BEGINNERS_MODE) then
|
|
self.filemanager_expert_mode = m -- make sure that new mode is set before...
|
|
self:setPath(self.path) -- refreshing the folder content
|
|
else
|
|
self.filemanager_expert_mode = m
|
|
end
|
|
G_reader_settings:saveSetting("filemanager_expert_mode", self.filemanager_expert_mode)
|
|
end
|
|
-- NuPogodi, 26.09.2012: temporary place; when properly tested, might be commented / deleted the following line
|
|
InfoMessage:initInfoMessageSettings()
|
|
self.pagedirty = true
|
|
end
|
|
|
|
-- NuPogodi, 28.09.12: two following functions are extracted just to make the code more compact
|
|
function FileChooser:deleteFolderAtPosition(pos)
|
|
if lfs.rmdir(self.path.."/"..self.dirs[pos]) then
|
|
table.remove(self.dirs, pos) -- to avoid showing just deleted file
|
|
self.items = #self.dirs + #self.files
|
|
self.current, self.page = gotoTargetItem(pos, self.items, pos, self.page, self.perpage)
|
|
else
|
|
InfoMessage:inform("Directory not empty ", DINFO_DELAY, 1, MSG_ERROR)
|
|
end
|
|
end
|
|
|
|
function FileChooser:deleteFileAtPosition(pos)
|
|
local fullpath = self.path.."/"..self.files[pos-#self.dirs]
|
|
os.remove(fullpath) -- delete the file itself
|
|
os.remove(DocToHistory(fullpath)) -- and its history file, if any
|
|
table.remove(self.files, pos-#self.dirs) -- to avoid showing just deleted file
|
|
self.items = self.items - 1
|
|
self.current, self.page = gotoTargetItem(pos, self.items, pos, self.page, self.perpage)
|
|
end
|
|
|
|
-- NuPogodi, 01.10.12: jump to defined item in the itemlist
|
|
function gotoTargetItem(target_item, all_items, current_item, current_page, perpage)
|
|
target_item = math.max(math.min(target_item, all_items), 1)
|
|
local target_page = math.ceil(target_item/perpage)
|
|
local target_curr = (target_item -1) % perpage + 1
|
|
local pagedirty, markerdirty = false, false
|
|
if target_page ~= current_page then
|
|
current_page = target_page
|
|
pagedirty = true
|
|
markerdirty = true
|
|
elseif target_curr ~= current_item then
|
|
markerdirty = true
|
|
end
|
|
return target_curr, current_page, markerdirty, pagedirty
|
|
end
|
|
|
|
function warningUnsupportedFunction()
|
|
InfoMessage:inform("Unsupported function ", DINFO_DELAY, 1, MSG_WARN)
|
|
return nil
|
|
end
|